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
の扱いについては一度整理してみたいと思っています。
以上です。
コメント