DockerでDjangoとcronを実行する方法

docker Django cron

こんばんはエンジニアの眠れない夜です。

django-uwsgi-nginxをベースにcronを実行する方法です。

これからDjangoで開発を始めるという方はdjango-uwsgi-nginx-cronを作ったのでこちらをクローンして開発を始めるととてもスムーズに進められます。

git clone https://github.com/sleepless-se/django-uwsgi-nginx-cron

GitHubからクローンを作成し、その中のappフォルダの中にDjangoのプロジェクトを作成しています。(この辺は本家と同じです。Nginx uwsgi django を一気に起動してくれるのでとても重宝しています。)

cronを実行しようとDockerfileの中でいろいろやってみたのですがなかなかうまくいかなかったのでdjango-uwsgi-nginx-cronを作成しました。

その中で、cronが起動するとNginxが起動しなくなり、Nginxが起動するとcronが起動するというあっちを立てればこっちが立たず。みたいな状況になりました。

最終的にsupervisor-app.confを編集すればいいんだ!ということに気づいたのでその紹介です。

supervisor-appを編集

標準ではこの様になっています。

[program:app-uwsgi]
command = /usr/local/bin/uwsgi --ini /home/docker/code/uwsgi.ini

[program:nginx-app]
command = /usr/sbin/nginx

ここにcronを追加します。

[program:app-uwsgi]
command = /usr/local/bin/uwsgi --ini /home/docker/code/uwsgi.ini

[program:nginx-app]
command = /usr/sbin/nginx

[program:cron]
command =cron -f
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

まずはこれを保存します。

cronのファイルと実行ファイルを用意

続いてcronの定義と実行ファイルを用意します。

ここで注意が必要なのがコンテナの環境変数をcronでは引き継ぎません。

なので、コンテナの環境変数を書き出して、cron実行時に読み込ませる必要があります。

[Docker]コンテナの環境変数を書き出す

コンテナを起動して docker ps でコンテナIDを確認してから

docker exec -it コンテナID /bin/bashでコンテナに入ります。

そして、下記のコードを実行します。

printenv | awk '{print "export " $1}' > /root/env.sh

/root/env.sh にファイルが作成されるのでcat で表示します。

cat /root/env.sh

表示したら、内容をコピーします。

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export LANG=ja_JP.utf8
export TZ=Asia/Tokyo
export PYTHONPATH=/usr/bin/python3

↑こちらに書いているコードはもう少しいろいろ表示されるのですが、コンテナを起動するたびに変わる環境変数があるので読み込んでもエラーになります。

私は不要だったので削除しました。多分必要なのはこれだけのはず。他にも必要なものがあれば各自追加してください。

保存先はローカルのPCで、下記のようにcronフォルダを作ってその中にenv.shというファイルを作って保存します。

Docker django cron実行

環境変数のとり方はこちらの記事を参考にさせていただきました。

cronファイルを用意する

cronフォルダの中にcrontabというファイルを作って、その中にcronを書いていきます。

# m h dom mon dow user command
* * * * * . /home/docker/code/cron/env.sh && export >> /home/docker/code/cron/export.log

※ crontabの最後には必ず改行を1行入れましょう。これがないとcronでインポートしたときにエラーが出ます。これ↓

new crontab file is missing newline before EOF, can't install.

コマンドの解説
1 . /home/docker/code/cron/env.shで先程保存した環境変数を読み込んでいます。これがないとパスが通っていないのでcronを実行してもpythonやdjangoが動かない。という事態になります。.sourceと同意です。

2 &&の後に実行したいコマンドを書きます。ここでは環境変数がきちんと読み込まれているかを確認するためにexportを実行しています。

3 最後に、そのログを出力すると行った感じです。

Dockerにcronをインストールする

DockerファイルのCMDの直前あたりに下記のコマンドを追記します。

RUN apt-get clean && apt-get update && apt-get install -y locales tzdata cron

#for encode setting
# set the locale
ENV LANG ja_JP.UTF-8
ENV TZ=Asia/Tokyo

RUN chmod 0744 /home/docker/code/cron/*
RUN crontab /home/docker/code/cron/crontab

大事なのは初めの1行と最後の2行です。1行目でcronをインストールしています。最後から2行目でcronフォルダ直下の先ほど作成したファイルに実行権限を与えています。

実行権限を与え忘れて、「あれ?動かない‥?」ってよくなるやつです。(私だけでしょうか?)

最後の1行はcronを読み込んでいます。

これだけでもいいのですが、cronを利用するということはタイムゾーンの設定が必要なのでTZとかLANGの設定を追加しています。

これがないと、標準時でcronが実行されたり、日本語を含むファイルを実行したときに文字コードでエラーになったりして苦しみます。

長くなりましたが、django-uwsgi-nginx-cronを使ってもらえばdjangoとcronをサクッと動かせる環境が手に入ります(^^)v

※ django-uwsgi-nginxを使ったことがない方はReadmeをご確認ください。

djangoのモデルなどを使ったスクリプトを作る場合はカスタムコマンドを作ってそれをcronから呼び出すといいです。カスタムコマンドの作成方法はこちら

[Django]カスタムコマンドの作成方法と便利なコード

1 Comment

DockerでDjangoのカスタムコマンドを実行すると複数回実行された話。 | エンジニアの眠れない夜 へ返信する コメントをキャンセル

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください