Dockerコンテナ(Compose)でDjangoコマンドを環境変数込みで定期実行する方法

ノウハウ
スポンサーリンク

Dockerコンテナで定期的(分次、時次、日次、週次、月次など)で実行したいときに、
Cronは有効な解決方法です。
この記事では、Dockerコンテナ(Compose)でDjangoコマンドを環境変数込みで定期実行する方法をまとめました。

目次

 


 

Dockerコンテナで"環境変数込みで"Cronを実行するには?

DockerコンテナでCronを実行するには以下の2パターンがあると思います。
ここではポータビリティを重視するため、前者の「コンテナにCronを実装する方法」を採用して説明します。

Dcokerコンテナで"環境変数込みで"Cronを実行する方法
  • コンテナ内のCronで実行する
    ・ホストに依存しないため、ポータビリティが高い
    ホストの環境変数が使えないため、いろいろな工夫が必要
  • ホストのCronからdocker execコマンド等でコンテナ内の処理を実行する
    ・ホストに依存してしまうため、ポータビリティが下がる

 

コンテナ内のCronで実行する方法

Dockerコンテナ内のcronでは、ホストPCからコンテナ内に注入された環境変数が反映されないため、
いろいろと工夫する必要があります。[1]
※crontabに直接Djangoコマンドを入力した場合、「KeyError:」となってしまいます。詳しくは「補足 crontabに直接Djangoコマンドを入力した場合」にまとめています。

そのため、「実行スクリプト(runCronProcess.sh)」と「環境変数リスト化スクリプト(initEnv.sh)」、「環境変数設定スクリプト(env.sh)」の3つのスクリプトで実装します。

3つのスクリプトの概要
  • 実行スクリプト(runCronProcess.sh)
    ・cronで呼び出されるスクリプト
    ・「環境変数設定スクリプト」を実行した後に、ターゲットの処理を実行する
  • 環境変数リスト化スクリプト(initEnv.sh)
    ・Dockerコンテナが起動したときに呼び出されるスクリプト
    ・コンテナに注入された環境変数から「環境変数設定スクリプト」を生成する
  • 境変数設定スクリプト(env.sh)
    ・実行スクリプトで呼び出されるスクリプト
    ・コンテナに注入された環境変数を設定する

 

DockerFileに追記する

以下の処理を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分間隔で呼び出されるようにしています)です。

 

スクリプトを記述する

runCronProcess.sh
#!/bin/sh
. /root/env.sh
python /code/manage.py testcommand 2>&1

・「. /root/env.sh」の下にcronで実行させたい処理を追加していきます。
・Djangoの格納場所コマンドはご自分の環境に合わせて変更してください。上の例では/codeにDjangoのsrcがあるような設定となっています。

 

initEnv.sh
#!/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で環境変数リスト化スクリプトの実行処理を追加

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」が実行されていることが分かります。

Dockerコンテナのログ
[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の環境変数が注入されないため、エラーとなってしまいます。

Dockerfile(例)
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 を実行し実行ログを出力する方法

 

以上!

コメント

タイトルとURLをコピーしました