DockerでPythonの実行環境を作ったメモ

概要

自分が書いた統計方面のコードをあちこちに持ち回して動かす必要が出てきたので、Dockerを利用する。

本稿はUbuntu(開発機)でDockerをインストールして必要な環境を整え、CentOS(検証機)上で動かした際の手順とその他調べたことをメモしたもの。

@CretedDate 2016/04/30
@Versions docker 1.6.2

インストール(Ubuntu)

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

一度入り直さないと反映されないかも。

インストール(CentOS)

/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な環境を用意する

試しに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の実行

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の持ち運び

出来上がった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でコードを書く為の開発環境も作りたい。

ということで先ほど使った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通信は