はじめに
docker上でpythonのプログラムを定期実行しようと思い,cronで動かそうと思ったらかなりハマったのでそのメモ.
pythonならscheduleやcronのライブラリがあるのでそれらを利用すれば早いし,そもそもdockerプロセス自体をcronで定期実行すれば良いが,1コンテナ1プロセスの精神や,移植性を考えて無理やりコンテナ内で動かした.
実験に使ったプログラムはこちら
実行するPythonプログラム
# coding: utf-8
import json
f = open( './test.json' )
json_data = json.load( f )
f.close()
print( json_data )
{
"国語":90,
"数学":100,
"理科":85,
"社会":100,
"英語":20
}
テスト用に,単純にjsonを解析して,コンソール上に出力するだけのプログラムを作った.このプログラムの実行結果は以下の通り.
$ python load_json.py
{'国語': 90, '数学': 100, '理科': 85, '社会': 100, '英語': 20}
これをコンテナ上でcronで定期実行してみる.
PythonのDockerコンテナの作成
FROM python:latest
RUN pip install --upgrade pip
RUN apt-get update
RUN apt-get install -y cron
# cron設定ファイルの移動
ADD python-cron /etc/cron.d/python-cron
RUN chmod 0644 /etc/cron.d/python-cron
ADD ./ /
CMD cron && touch /etc/cron.d/simple-cron && tail -f /dev/null
* * * * * root python /load_json.py >> /var/log/cron.log 2>&1
cronでpythonプログラムの出力結果を/var/log/cron.log
に出力する.コンテナにログインし.正しく実行できているかを確認する.
root@798d72ea2442:/# cat var/log/cron.log
root@798d72ea2442:/#
root@798d72ea2442:/# python /load_json.py
{'国語': 90, '数学': 100, '理科': 85, '社会': 100, '英語': 20}
root@798d72ea2442:/#
コンテナ内での実行はうまくいくのだが,cronの実行ではうまくいっていない.
cronのプログラムの実行の方でエラーが起きて,実行できていないっぽい.
シェルスクリプトにプログラムを記述してそれを実行
先人の投稿を見てみると,cronではシェルスクリプトにプログラムを記述して,それを定期実行させているケースがほとんどだった.ここは思考停止でパクってやってみる.
FROM python:latest
RUN pip install --upgrade pip
RUN apt-get update
RUN apt-get install -y cron
# cron設定ファイルの移動
ADD python-cron /etc/cron.d/python-cron
RUN chmod 0644 /etc/cron.d/python-cron
ADD ./ /
RUN chmod +x /script.sh #追加
CMD cron && touch /etc/cron.d/simple-cron && tail -f /dev/null
python /load_json.py
ファイルが準備できたらコンテナを動かし,動作確認.
root@8004b8e2114c:/# cat /var/log/cron.log
{u'\u7406\u79d1': 85, u'\u793e\u4f1a': 100, u'\u56fd\u8a9e': 90, u'\u6570\u5b66': 100, u'\u82f1\u8a9e': 20}
実行はできたが,文字化けが起きている.
root@8004b8e2114c:/# python
Python 3.6.4 (default, Mar 14 2018, 17:49:05)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.getdefaultencoding())
utf-8
対話モードでpython文字コードを確認して見ても,utf-8となっており,間違っていないはず...
解決:Cronの環境設定
コンテナ内ではプログラムが実行できるのに,cronで定期実行できないのは,文字コードが異なるため,エラーが出て止まっていた.調べたところ,cronではcron内部で環境変数が独立しているため,pythonなどのプログラムは,環境変数を設定しなければ実行ができなかったり,文字化けが起きることがあるらしい.
こちらを参考に,環境変数をcron実行時に読み込ませ,その後pythonプログラムを実行するようにした.
流れとしては,
-
init.sh
でコンテナ内の環境変数の出力とcronの実行. - cronで指定した時間に
script.sh
を実行. -
script.sh
で実行したいプログラムの前に,コンテナ内の環境変数をcronに反映させる. - 実行したいプログラムの実行.
#!/bin/sh
printenv | awk '{print "export " $1}' > /root/env.sh
/usr/sbin/cron -f
#!/bin/bash
. /root/env.sh
python /load_json.py >> /var/log/cron.log
合わせてDockerfileも,init.sh
を実行するように修正する.
FROM python:latest
RUN pip install --upgrade pip
RUN apt-get update
RUN apt-get install -y cron
# cron設定ファイルの移動
ADD python-cron /etc/cron.d/python-cron
RUN chmod 0644 /etc/cron.d/python-cron
ADD ./ /
# init.shにも実行権限の追加
RUN chmod +x /script.sh /init.sh
# CMD cron && touch /etc/cron.d/simple-cron && tail -f /dev/null
CMD /init.sh
動作確認.
root@e411646bab21:/# cat /var/log/cron.log
{'国語': 90, '数学': 100, '理科': 85, '社会': 100, '英語': 20}
まとめ
docker内で動かしたいプログラムをcron実行させるに結構手間がかかることがわかった.
もっとスマートなやり方があったら誰か教えて欲しいです.
参考
https://qiita.com/rerofumi/items/fc0126c4e985b78f769b
https://qiita.com/jmatsu/items/0a5d80abe188b09644c1