Docker×Djangoで cron を設定する方法

docker Django cron

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

以前ご紹介したDjangoを https 化した状態でデプロイできるDockerコンテナを使ってcronを設定する方法を紹介します。

このリポジトリを利用しない場合でも supervisor を使ってcronを設定する方法と同じなのでDjangoを利用しているか否かにかかわらず他のプロジェクトにも応用可能です。

記事全体にDjango的要素は無いのですが、利用しているリポジトリがDjango用なのでこのようなタイトルにしました。

Docker+Djangoでcronを実行!設定概要

Dockerでcronを実行するまでのステップは大きく分けて3つのステップに分かれています。

  1. cron実行用ファイルを準備
  2. DockerfIleを編集
  3. supervisor-app.confを編集

個人的にはcron実行用ファイルを準備するところで環境変数を書き出すところがDockerでcronを実行するポイントだと思います。

それでは順番に見ていましょう。

cron実行用ファイルを準備

今回は django-uwsgi-nginx/django-uwsgi-nginx 直下に作成します。同じフォルダ名が続いていて分かりづらいのですが、appフォルダがある階層です。ここにcronフォルダを作ります。

この中に3つのファイルを用意します。

Docker+Djangoで cron を設定する方法

crontab cronの設定内容を書きます。
env.txt Docker内の環境変数を書き出したものです。
read_mail.sh cronから呼び出されるシェルスクリプトです。ファイル名は自由に設定して大丈夫です。crontabファイル内に直接記述しても良いのですが、長くなりそうなので別ファイルにしました。(この辺はお好みにあわせて。)

crontab cronの設定内容

普通にcronファイルを書けば大丈夫です。今回はシェルスクリプトに内容をまとめたいのでシェルスクリプトを参照しています。


# m h dom mon dow user	command
* * * * * /code/cron/read_mail.sh

ファイルの一番最後の行に改行を入れておかないと`crontab ファイル名`で読み込んだ時に下記のようなエラーが出ることがあります。

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

ファイルの最後に改行を入れておきましょう。

シェルファイルははDocker内の/code/cron/にあとでコピーします。

env.txt Docker内の環境変数を書き出す方法

Docker内の環境変数はビルドするときと実行するときでは引き継がれないようです。環境変数をファイルに書き出して、cron実行前に読み込む。という面倒なことが必要です。

Docker内にdocker exec -it コンテナID /bin/bash で入ります。その後に下記のコードを実行します。


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

/root/env.txt の中身をcatで確認します。出てきた環境変数をコピーしてenv.txtをcronフォルダの中に保存します。

毎回変化する環境変数も一緒に記述されているので私はこれだけに減らしました。


export PYTHONPATH=/usr/bin/python3
export TERM=xterm
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PWD=/root
export LANG=C.UTF-8
export TZ=Asia/Tokyo
export SHLVL=1
export HOME=/root
export DEBCONF_NOWARNINGS=yes
export LESSOPEN=|
export LESSCLOSE=/usr/bin/lesspipe
export _=/usr/bin/printenv
export OLDPWD=/var/log

それから、cron からPythonを動かすとエンコードエラーがでます。いわゆる文字化けです。

UnicodeEncodeError: 'ascii' codec can't encode characters in position 32-40: ordinal not in range(128)

なので下記の環境変数を一緒に入れておくことでその問題を解決できます。


export PYTHONIOENCODING=UTF-8
export LANG=C.UTF-8
export LANGUAGE=en_US:en
export LC_CTYPE="C.UTF-8"
export LC_NUMERIC="C.UTF-8"
export LC_TIME="C.UTF-8"
export LC_COLLATE="C.UTF-8"
export LC_MONETARY="C.UTF-8"
export LC_MESSAGES="C.UTF-8"
export LC_PAPER="C.UTF-8"
export LC_NAME="C.UTF-8"
export LC_ADDRESS="C.UTF-8"
export LC_TELEPHONE="C.UTF-8"
export LC_MEASUREMENT="C.UTF-8"
export LC_IDENTIFICATION="C.UTF-8"
export LC_ALL=

自分で確認するのが面倒な人はこちらをコピペすれば大丈夫です。

read_mail.sh の内容

ポイントは先程作成したenv.txtをコマンド実行前に読み込むことです。
※ 実行しているコマンドは適当です。
※ cron実行時のコマンドは絶対パスで指定します。/usr/bin/python3

Python

#!/bin/sh
. /code/cron/env.txt#環境変数を読み込む。 .はsourceと同意
/usr/bin/python3 /code/app/manage.py read_mail >> /var/log/read_mail 2>&1

2.DockerfIleを編集

下記のコードをDockerfileの最後の方、EXPOSEの上辺りにコピペします。


RUN apt-get install -y cron
RUN chmod 0744 /code/cron/*
RUN crontab /code/cron/crontab

やっていることは

  • cronのインストール
  • cronフォルダ内のファイルに実行権限を与える
  • cronを読み込む

この3つです。

/code/cron/*でいきなりどうしてそこにcronフォルダがあるの?と思う方もいるかも知れませんがdjango-uwsgi-nginxのリポジトリを利用している場合は70行目辺りで COPY . /code/でDocker内にコピーされています。

その他の方は COPY /path/to/cron_folder/ /code/ で、先程作成したcronフォルダを/code/の直下に保存してください。

3.supervisor-app.confを編集

最後にsupervisorの設定を行います。

django-uwsgi-nginx/django-uwsgi-nginx/supervisor-app.conf というファイルがあるのでこのファイルに下記のコードを最後に追加します。


[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

django-uwsgi-nginxのリポジトリを利用している人はこれで設定完了です。それ以外の方はDockerfileに下記の2つの設定が必要です。



COPY path/to/supervisor-app.conf /etc/supervisor/conf.d/
CMD ["supervisord", "-n"]

上記の設定ファイルをDocker内にコピーしてスパーバイザーを起動しています。

最後に

こちらのリポジトリを利用されている方向けの内容とそうでない人向けに両方書いたら少しややこしい感じになってしまいました。

また、利用していない方向けには上記の説明だけでは足りないところもあるかもしれないので十分に説明しれず申し訳ないです。

うまくいかない所があればコメントか、Twitterで問い合わせをいただければ可能な限り返信・加筆修正させていただきますm(_ _)m

エンジニアの眠れない夜

コメントを残す

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