自分が書いた統計方面のコードをあちこちに持ち回して動かす必要が出てきたので、Dockerを利用する。
本稿はUbuntu(開発機)でDockerをインストールして必要な環境を整え、CentOS(検証機)上で動かした際の手順とその他調べたことをメモしたもの。
Ubuntuへのインストール。apt-getでそのまま入ることは入るけど、既にサポート切れのバージョンが入ってしまうらしい。なのでレポジトリを追加して入れる。
インストール方法はこちらを参照した。
$ sudo apt-get update $ sudo apt-get install apt-transport-https ca-certificates $ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
/etc/apt/sources.list.d/docker.listに下記の行を記述する。下記は14.04と16.04の例。記述内容はUbuntuのバージョンによって変わるので注意。
# 14.04 deb https://apt.dockerproject.org/repo ubuntu-trusty main # 16.04 deb https://apt.dockerproject.org/repo ubuntu-xenial main
記述したら、updateしてからインストール。linux-image-extraが必須らしいのでそれも入れておく。
$ sudo apt-get update $ sudo apt-get install linux-image-extra-$(uname -r) $ sudo apt-get install docker-engine $ sudo apt-get install docker.io
動くか確認。
$ sudo service docker start $ sudo docker run hello-world
実行するとあれこれ標準出力に表示される。下記のようなメッセージが含まれていれば成功。
Hello from Docker. This message shows that your installation appears to be working correctly.
このままだとsudoしないとdockerコマンドが動かないので、groupaddでdockerグループをユーザに追加しておく。
sudo groupadd docker sudo gpasswd -a ${USER} docker sudo service docker restart
これで当該ユーザでdockerを実行できるようになった。
$ docker info
一度入り直さないと反映されないかも。
/etc/yum.repos.d/docker.repoを編集(新規作成)して、下記の行を記述する。
[dockerrepo] name=Docker Repository baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/ enabled=1 gpgcheck=1 gpgkey=https://yum.dockerproject.org/gpg
yumでインストールしてservice start、hello-worldで確認。
sudo yum install docker-engine sudo service docker start sudo docker run hello-world
実行するとあれこれ標準出力に表示される。下記のようなメッセージが含まれていれば成功。
Hello from Docker. This message shows that your installation appears to be working correctly.
グループも追加しておく。
sudo groupadd docker sudo gpasswd -a ${USER} docker sudo service docker restart
試しにlsするとslになる簡単なコンテナを作成する。
DockerのImageの作り方は下記ページに載っている。
https://docs.docker.com/mac/step_four/
とりあえず手順に従ってディレクトリを作り、Dockerfileという名前のファイルを作成する。
$ mkdir mydockerbuild $ cd mydockerbuild $ touch Dockerfile
aliasでlsをslにすり替えるところは.bash_aliasesでやる予定なので、下記のような記述のファイルをbash_aliasesという名前で作っておく。
alias ls="/usr/games/sl"
作成したDockerfileファイルに設定を書き込む。
FROM buildpack-deps:jessie # build-essentialとslを入れる RUN apt-get -y update RUN apt-get -y install build-essential sl # ユーザを作っておく RUN useradd -ms /bin/bash username # ユーザのホームディレクトリから開始する感じにしておく USER username WORKDIR /home/username # さっき作ったbash_aliasesを入れておく COPY bash_aliases /home/username/.bash_aliases
記述が終わったら、下記コマンドでbuild。
$ docker build -t example/lssl .
終わったら登録されたか確認。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE example/lssl latest e433e4894f0a 36 seconds ago 621.9 MB buildpack-deps jessie fc94bd166fc4 2 weeks ago 606.6 MB
無事、example/lsslという名前でイメージが登録された。
じゃ、立ち上げてlsしてみる。
$ docker run -i -t example/lssl /bin/bash $ ls
無事、汽車が走りました。めでたい。
引数の-iはinteractive。bashのような対話形式での利用をする場合は-iを付けておく。
Pythonと必要なライブラリをpipでインストールしたイメージを用意する。Anacondaという手もあるけど、不要なものまでいろいろ入るので必要なものを選んで入れて自分用のイメージを作ろう。
Python用のdockerイメージは下記のものが参考になる。
https://hub.docker.com/_/python/
Python3のソースを落としてきてmakeしてるようだ。apt-getで入れるとどのバージョンまで入れられるかわからないし、ソースから落とした方がわかりやすくて良いね。
requirements.txt というファイルを作って、pipで入れるパッケージを箇条書きにするだけで、好みのパッケージを入れた版のpython実行環境が作れるようだ。
試しに requirements.txt に下記を記述。
numpy scipy
example.py というファイルに下記を記述。
import numpy as np print( np.mean( [1, 9, 5, 2, 3] ) )
これをdockerで実行してみる。
Dockerfile は下記のような感じで。
FROM python:3-onbuild CMD [ "python", "./example.py" ]
じゃ、bulidして実行してみましょ。
$ docker build -t example/python3script . $ docker run -t example/python3script
これで計算結果(4.0)が出力される。スクリプトの実行環境としては十分に使える。
出来上がったImageを他のサーバで動かしてみる。
イメージを保存して、対象のサーバに飛ばして、ロードする。
# イメージの保存 $ docker save example/python3script | gzip -c > python-docker-image.tar.gz # 転送と解凍 $ scp python-docker-image.tar.gz $ ssh your_server $ gunzip python-docker-image.tar.gz # ロード $ docker load -i python-docker-image.tar $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE example/python3script latest e02887be3fbd About an hour ago 1.053 GB
gzip圧縮しても345Mとそれなりに大きい。
これを実行してみる。
$ docker run -t example/python3script
ちゃんと計算結果が出力された。
Pythonのスクリプト実行環境はできたけど、できればPythonでコードを書く為の開発環境も作りたい。
ということで先ほど使ったpython:3-onbuildの記述を参考に、自前でDockerfileを書いてみる。
FROM buildpack-deps:jessie # aptで入っているpythonを削除 RUN apt-get purge -y python.* # LANGの設定 ENV LANG C.UTF-8 # Pythonのバージョン ENV PYTHON_VERSION 3.4.4 # pipのバージョン ENV PYTHON_PIP_VERSION 8.1.1 # apt-getで入れるものを入れる RUN apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ vim \ git \ gfortran \ liblapack-dev \ gzip \ bzip2 \ python-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Pythonのインストール RUN set -ex \ && curl -fSL "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" -o python.tar.xz \ && mkdir -p /usr/src/python \ && tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \ && rm python.tar.xz \ && cd /usr/src/python \ && ./configure --enable-shared --enable-unicode=ucs4 \ && make -j$(nproc) \ && make install \ && ldconfig \ && pip3 install --no-cache-dir --upgrade --ignore-installed pip==$PYTHON_PIP_VERSION \ && find /usr/local \ \( -type d -a -name test -o -name tests \) \ -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ -exec rm -rf '{}' + \ && rm -rf /usr/src/python ~/.cache # SymbolicLinkを作っておく RUN cd /usr/local/bin \ && ln -s easy_install-3.5 easy_install \ && ln -s idle3 idle \ && ln -s pydoc3 pydoc \ && ln -s python3 python \ && ln -s python3-config python-config # pipinstall RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY pip_packages.txt /usr/src/app/ RUN pip3 install --no-cache-dir -r pip_packages.txt # ユーザ設定 RUN useradd -ms /bin/bash dev USER dev WORKDIR /home/dev # vimrcをコピーしておく COPY vimrc /home/username/.vimrc
Pythonのverifyチェックのところを外しているなど、ちょっと行儀は良くない記述になっている。
実行する前に下記のファイルを用意しておく。
$ cp ~/.vimrc vimrc $ touch pip_packages.txt
buildして実行する。
$ docker build -t example/mypy $ docker run -i -t example/mypy /bin/bash
これでとりあえず開発環境は手に入った。あとはvimのプラグインとか必要なものは適宜入れておく。
上記の内容でJupyterを使う場合、リモートのDockerで動かすとセキュリティ的に微妙になるのでパスワード認証付きでsshで通信するようにしておく。
まずはパスワードのハッシュ値の作成。
$ jupyter console
from notebook.auth import passwd passwd() #=> sha1:********************************************
上記で出力されたハッシュをコピーしておく。
鍵を作る。
$ openssl req -x509 -nodes -newkey rsa:2048 -keyout mycert.key -out mycert.pem
jupyter_notebook_config.py を用意する。下記のような内容。passwordのハッシュ値は先ほどコピーした値。
c = get_config() c.NotebookApp.certfile = u'/usr/share/ca-certificates/jupyter/mycert.pem' c.NotebookApp.ip = '*' c.NotebookApp.password = u'sha1:********************************************' c.NotebookApp.open_browser = False c.NotebookApp.port = 18888
次にJupyterの起動用のスクリプトを用意。名前はstart_jupyter.shとでもしておこうか。
#!/bin/bash exec jupyter notebook \ --no-browser \ --certfile=/usr/share/ca-certificates/jupyter/mycert.pem \ --keyfile=/usr/share/ca-certificates/jupyter/mycert.key \ --notebook-dir=/home/dev/notebook \ --ip=0.0.0.0 \ --port=18888 > /dev/null 2>&1 &
Dockerfileを一部修正して、これらのキーとかをコピーするようにしておく。下記のようにユーザ作成した後のところに処理を埋め込んだ。
# ユーザ作成 RUN useradd -ms /bin/bash dev # Jupyter用のキー用意 RUN mkdir -p /usr/share/ca-certificates/jupyter COPY mycert.pem /usr/share/ca-certificates/jupyter/ COPY mycert.key /usr/share/ca-certificates/jupyter/ RUN chown -R dev:dev /usr/share/ca-certificates/jupyter RUN chmod -R 700 /usr/share/ca-certificates/jupyter # Jupyterの実行用スクリプト用意 RUN mkdir -p /home/dev/bin COPY start_jupyter.sh /home/dev/bin/ RUN chown -R dev:dev /home/dev/bin RUN chmod 755 /home/dev/bin/start_jupyter.sh # devユーザ処理 USER dev # Jupyterのconfigファイルを用意 RUN mkdir /home/dev/.jupyter COPY jupyter_notebook_config.py /home/dev/.jupyter/ # jupyterのディレクトリと起動スクリプトを用意しておく RUN mkdir -p /home/dev/notebook
Docker上でJupyterは18888で起動しているので、適当なポートに対して紐付けた状態で立ち上げる。(下記例だとlocalhost:8080がDocker上の18888になる)
$ docker run -i -p 8080:18888 -t mwsoft/mljupyter /bin/bash $ bin/start_jupyter.sh
https://localhost:8080/ にリクエストすると、Docker上のJupyterに繋がる。パスワードかけてるとはいえ安全とも言えないので、個人的には外向きには開けずにSOCKS経由で使ってる。それでもSSL通信は