はじめに
コマンドライン起動系のPythonのプロジェクトを定期実行やサービス化して様々な環境で安定して実行させたい場合、やはりDocker化するという選択肢があります。最近、私なりにその方法が固まってきたので共有します。
今回 コマンドライン起動系と書いているのは「Web系」の場合は少し違うかもな、と思ったからです。Pythonは 自分で使いたいVersionと OSに含まれるバージョンが競合したり、ライブラリのInstallが結構大変だったりするので(pipあまり賢くない...)、Docker化してリリースするのはかなり有効に思えます。
個人的要求
- git にpushしたりすることなくコンテナ化したい
- コンテナ化にかかる時間は最小限にしたい
- 環境情報や鍵情報は実行時に指定したい
Version
- Docker for Mac: Version 1.13.0-rc4-beta34.1 (14853)
方法
DIR構成
以下のような構成にしておきます。
.
├── .dockerignore
├── .env
├── .gitignore
├── docker
│ └── data_lab
│ ├── Dockerfile
│ └── docker-compose.yml
├── keys
│ └── gcloud-secret.json
├── requirements.txt
├── ..others..
└── src
├── labs/__init__.py
└── labs/awesomes/hogehoge.py
それぞれの用途
-
docker/<コンテナ名>/
: 作成したいコンテナ毎に作成するDIR。今回はdata_lab
というコンテナを作ります。 -
src/
: Pythonのプログラムが置いてあるDIR。ここがPYTHONPATH
のrootになるイメージです。 -
.dockerignore
: DockerのCOPY
コマンドを実行したときに除外したいファイルを列挙することができる。今回は主にsrc/
以下にある余分なファイル(鍵情報や無用な生成物)を除外するために使います。詳細はこちら。また、Docker DaemonへのContextデータの転送量を減らして高速化するためにも使います(参考)。 -
requirements.txt
: pipでInstallするライブラリファイル(pip freeze > requirements.txt
) -
keys/
: 鍵情報の置き場所。コンテナには含めずに、コンテナ実行時に動的にホスト側からMountします。
Dockerfile
大事なこと
-
requirements.txt
を先にCOPYしてpipを実行し、 開発したソースコード(src/
以下)を最後にCOPYすることで、ちょっとファイルを変更したりしてもライブラリのInstallの再実行が不要になります。 -
src/
以下だけCOPYする(必要なDIRのみCOPYする)ことで、Dockerfileやその他の細々したファイル更新の影響を受けなくします。 -
src/
以下にコンテナに含みたくないファイルがあれば.dockerignore
に記述しておきます。
Dockerfile
FROM python:3.5.2
MAINTAINER myaccount@gmail.com
# Install GCloudSDK
WORKDIR /root
ENV CLOUDSDK_PYTHON=/usr/bin/python2
RUN curl -L -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-121.0.0-linux-x86.tar.gz \
&& tar xzf google-cloud-sdk-121.0.0-linux-x86.tar.gz \
&& ./google-cloud-sdk/install.sh --usage-reporting=true --path-update=true --bash-completion=true --rc-path=/root/.bashrc
ENV PATH=/root/google-cloud-sdk/bin:$PATH
# Install Libraries
RUN mkdir -p /var/lib/data_lab
WORKDIR /var/lib/data_lab
COPY ./requirements.txt ./
RUN pip install -r requirements.txt
# Copy Sources
COPY ./src/ ./src/
補足
- GoogleCloudSDK: 不要なら削ります
docker-compose.yml
大事なこと
-
build/context
を指定することで、ビルド時のcontextディレクトリを指定できます(contextディレクトリ以下しかCOPYできないようなので、 これを指定しないと、src/
などがCOPYができません)。 -
Dockerfile
内の相対PATHは上記のContextディレクトリになります。 -
volumes
: 実行時にホストからコンテナにマウントするファイルをやディレクトリを指定します。ここで 環境情報(python-dotenv
を使っているので.env
ファイルをコピーしておきます)や 鍵情報を渡します。 -
PYTHONPATH
: せっかくなので指定しておきます
version: '2'
services:
data_lab:
image: 9999999999.dkr.ecr.ap-northeast-1.amazonaws.com/data-lab
build:
context: ../..
dockerfile: docker/data_lab/Dockerfile
working_dir: /var/lib/data_lab
volumes:
- ../../.env:/var/lib/data_lab/.env
- ../../keys/:/var/lib/data_lab/keys/
environment:
PYTHONPATH: /var/lib/data_lab/src
ImageをBuildするとき
どうも Dockerの現在のVersion(私がここで使っているVersion)では、 docker-compose build
にBugがあって、.dockerignore
の**
が上手く効かないようです(体験済み)。代わりにdocker build
を使えば回避できるとか。
参考:https://github.com/docker/docker-py/issues/1117
なので、.dockerignore
に **
を使いたい場合は、
通常ならば
cd docker/data_lab
docker-compose build
で済むところを
cd docker/data_lab
docker build ../.. -f ./Dockerfile -t 9999999999.dkr.ecr.ap-northeast-1.amazonaws.com/data-lab
などとしないといけないかもしれません(これで一応回避はできました)。
実行するとき
例えば、 以下のような補助的なShellスクリプトを docker/data_lab/exec.sh
として作っておきます。
#!/bin/sh
cd $(dirname $0)
exec docker-compose run data_lab $@
で、以下のようにコンテナ内でコマンドを実行することができます。
sh docker/data_lab/exec.sh python src/labs/awesomes/hogehoge.py arg1 arg2 ...
さいごに
昔は、一旦gitにpushして、コンテナ内でcloneして、、、とやっていたのでだいぶ楽になりました。
ソースコードを更新する度にコンテナを再作成する必要がありますが、Filesystem Layer的な差分は少ないのでそれほど無駄も無いのかなと思っています。
追記1(2017/01/05)
この記事を公開した直後に「公式のpython:3.5.2
のDocker Image使ったら良いんじゃね?」という指摘を受けまして、特にそれで問題が無かったので修正しました。この公式イメージでは、python
やpython3
などが Python3.5.2になって、 python2
が Python2.7.9 になっています。
追記2(2017/01/05)
Buildについての注意事項を追記しました。