Dockerコンテナで定期的(分次、時次、日次、週次、月次など)で実行したいときに、
Cronは有効な解決方法です。
この記事では、Dockerコンテナ(Compose)でDjangoコマンドを環境変数込みで定期実行する方法をまとめました。
目次
Dockerコンテナで"環境変数込みで"Cronを実行するには?
DockerコンテナでCronを実行するには以下の2パターンがあると思います。
ここではポータビリティを重視するため、前者の「コンテナにCronを実装する方法」を採用して説明します。
コンテナ内のCronで実行する方法
Dockerコンテナ内のcronでは、ホストPCからコンテナ内に注入された環境変数が反映されないため、
いろいろと工夫する必要があります。[1]
※crontabに直接Djangoコマンドを入力した場合、「KeyError:」となってしまいます。詳しくは「補足 crontabに直接Djangoコマンドを入力した場合」にまとめています。
そのため、「実行スクリプト(runCronProcess.sh)」と「環境変数リスト化スクリプト(initEnv.sh)」、「環境変数設定スクリプト(env.sh)」の3つのスクリプトで実装します。
DockerFileに追記する
以下の処理をDockerfileに追記します。
RUN apt-get install -y cron
RUN mkdir /log && mkdir /scrip
COPY /initEnv.sh /script/
COPY /runCronProcess.sh /script/
RUN echo '*/1 * * * * root . /script/runCronProcess.sh' >> /etc/crontab
ENTRYPOINT ["/script/initEnv.sh"]
・COPYするinitEnv.shとrunCronProcess.shのディレクトリやファイル名はご自分の環境に合わせて変更してください。
・赤色下線のところが本命の処理(1分間隔で呼び出されるようにしています)です。
スクリプトを記述する
#!/bin/sh
. /root/env.sh
python /code/manage.py testcommand 2>&1
・「. /root/env.sh
」の下にcronで実行させたい処理を追加していきます。
・Djangoの格納場所やコマンドはご自分の環境に合わせて変更してください。上の例では/codeにDjangoのsrcがあるような設定となっています。
#!/bin/bash -e
SHELL_NAME='initEnv.sh'
echo "[$SHELL_NAME] START"
printenv | awk '{print "export " $1}' > /root/env.sh
cron
echo "[$SHELL_NAME] FINISH"
exec $@
docker-compose.ymlで環境変数リスト化スクリプトの実行処理を追加
command: >
bash -c "
source /script/initEnv.sh &&
uwsgi --socket :${APP_UWSGI_PORT} --module config.wsgi
"
・Djangoを起動する前に、initEnv.shの処理を追加します。なお、上の例ではDjangoを起動するためにuwsgiを実行する想定としています。
実行してみる
以下のコマンドを実行してDockerコンテナを再起動します。
(ホストPCでdocker-compose.ymlがあるディレクトリに移動して)
$ docker-compose build --no-cache
$ docker-compose up -d
起動時のDockerコンテナのログを(docker logs
等で)確認すると、
「initEnv.sh」が実行されていることが分かります。
[2023-01-18 17:35:12.215] [initEnv.sh] START
[2023-01-18 17:35:12.215] [initEnv.sh] FINISH
cronの結果はコンテナ内の/log/test.logに出力しています。
中身を確認すると以下のように、Djangoコマンドが1分間隔で実行されているはずです。
補足 crontabに直接Djangoコマンドを入力した場合
crontabに直接Djangoコマンドを入力した場合、DjangoにホストPCの環境変数が注入されないため、エラーとなってしまいます。
RUN echo '*/1 * * * * root python /code/manage.py testcommand 2>&1' >> /etc/crontab
Traceback (most recent call last):
File "/code/manage.py", line 22, in <module>
main()
File "/code/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 386, in execute
settings.INSTALLED_APPS
File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 92, in __getattr__
self._setup(name)
File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 79, in _setup
self._wrapped = Settings(settings_module)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 190, in __init__
mod = importlib.import_module(self.SETTINGS_MODULE)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/code/config/settings.py", line 87, in <module>
'NAME': os.environ['XXXX'],
~~~~~~~~~~^^^^^^^^^^^^^^^^
File "<frozen os>", line 679, in __getitem__
KeyError: 'XXXX'
参考
[1] Docker コンテナ内でタスクを cron 起動する
[2] Docker コンテナ内で cron を実行し実行ログを出力する方法
以上!
コメント