docker上で動くdjangoアプリのバッチ処理をsupervisordとcronで行う。
背景
今までにもバッチ処理に関する記事は書いていましたが、今回はdocker+supervisord+cronの組み合わせでdjangoのmanage.pyから使える自作コマンドの実行を行います。
djangoが動くコンテナを利用してcronを動かすので、supervisordを使うことにしました。
ちなみに、Docker コンテナ内のコマンドをホスト側から定期実行する | ikapblogで書いた、dockerのホストのcronを用いたバッチ処理の方法を改めたものになります。やはりコンテナの独立性というか、ホストありきな仕組みはよくないと思うので、今回の方式に変更しました。
そのため、今までgunicornを実行していたdockerコンテナはsupervisordを実行するように変更して、さらにcron用の定義も追加する、という流れで作業していきます。
supervisordの導入
まずは、バッチ処理の導入前に、gunicornをフォアグラウンドで使っていたところをsupervisordの下で動作するようにします。
そのためにsupervisordをインストールします。DockerfileのRUNコマンドの途中で、pip install --no-cache-dir supervisor && \を挿入する形でインストールしました。
pythonイメージを使っていない場合は、pipを導入する必要があると思います。yumにはsupervisorのパッケージは無いようでした。dockerイメージではあまり無いとは思いますが、ディストリビューションによってはsupervisordが同梱されている場合があるようです。(公式インストールページに記載あり: Installing — Supervisor 4.2.2 documentation)
インストールが終わったら、CMDでgunicornを起動していたところを、supervisordを起動させます。
# ./Dockerfile
# Dockerfileの最後の行にて
CMD ["supervisord", "-c", "./supervisord.conf"]
今回、configファイルは-cオプションで作業用ディレクトリのsupervisord.confを使用するようにします。その内容は:
; ./supervisord.conf
[supervisord]
nodaemon=true
[program:webapp]
command=gunicorn --chdir <app_dir> --bind :8000 <app_module>.wsgi:application
directory=/opt/service/app/src ; <app_dir>があるディレクトリ
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
--chdirで指定している<app_dir>はmanage.pyがあるディレクトリです。作業用ディレクトリは1階層上なのでこのような指定をしています。<app_module>は--chdirで指定したディレクトリからみて、wsgi.pyのあるディレクトリ(モジュール)を指定します。(基本的に、django-admin startprojectで指定した名称のはずです。そのため、<app_dir>と同じ場合がほとんどです。)
directory=のところで<app_dir>のパスを指定すれば、command=の--chdir <app_dir>と<app_module>.は不要になると思います。
また、ログはdocker利用の視点的には標準出力してほしいので、最後の4行でプロセスのstdoutとstderrをOSのstdoutとstderrに出力させています。これを指定しないと、cronで標準出力してもdockerコンテナのログとして出力されません。
gunicornの置き換え作業の場合は一旦ここでチェックしてみるといいと思います。
cronの導入
cronの導入はcrontabの設定と、crondの起動に大別できます。cron自体はdockerイメージに組み込まれている場合がほとんどなので、インストール作業は不要です。
Dockerfileへの追加
Dockerfileへの追加は、上述のRUNコマンドでsupervisordをインストールするより前に行っておきます。もちろん後でもいいです。
# ./Dockerfile
# CMDより前に
COPY ./crontab /etc/crontabs/mycrontab
RUN crontab -c /etc/crontabs/ /etc/crontabs/mycrontab
#...
定期実行するコマンドを記述したテーブルを持ったcrontabファイルをコンテナにコピーして、crontabコマンドでテーブルをインストールしています。ファイルとコマンドともにcrontabである点に注意しましょう。ファイルの./crontabの方は違うファイル名でも問題ありません。
こうすることで、定義した定期実行用コマンドがcronデーモンで実行されるようにしています。
./crontabファイルの定義はこうなっています:
# ./crontab
15 1 * * * cd /opt/service/app/src && python <app_dir>/manage.py <custom_command> 2>&1
毎日01:15にcdして自作コマンドを実行します。cronでのエラーは標準出力にやって、mailしないようにしているつもりです。この辺りはもう少し改善したいです。
標準出力されれば、dockerのログとして収集できます。
supervisordへの追加
supervisordへの追加は、上の方で述べたprogramの例とほぼ同じことを./supervisord.confへ追記します:
; ./supervisord.conf
; ...
[program:crond]
command=crond -f
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
注意点としては、crondはフォアグラウンドで起動することと、dockerでのログのためにstdoutとstderrをいじることがあるくらいです。
ここまでくれば、コンテナをリビルドして作業完了となります。お疲れさまでした。
おわり
supervisordではデフォルトでリスタートはしてくれますが、細かい調整は必要だと思います。
cronの扱いについては一度整理してみたいと思っています。
以上です。



コメント