定期的な巡回とか、DBの古いレコードを定期的に削除するとか、決まった作業を cron で実行する場面は多いと思います。
そういった cron で処理したい環境を Docker コンテナとしてパッケージするときのお話。
#Docker コンテナで cron を使う
Docker コンテナ内部は init 等を使っていないので daemon の類は基本動かしません。
コンテナという管理単位を考えると、1コンテナ 1機能で、起動するのも 1つのタスクとするのが標準的な考えです。
普段ホストで定期作業のために使っている cron は daemon として起動しているものですが、daemon が使えないと無理のように見えますがそうではありません。cron というタスク1つだけが起動しているコンテナにすれば良いのです。
具体的にはコンテナの起動コマンドを cron -f
とします。
cron そのものを起動し、なおかつ foreground 実行で daemon 実行させないという形にします。
Dockerfile では
RUN apt-get install -y cron
RUN echo '*/5 * * * * root /path/to/your_command' >> /etc/crontab
CMD ["cron", "-f"]
と言うような設定となります。
とまあここまでは教科書通りのおさらい。
#cron で起動するタスクに Docker コンテナの環境変数を渡す
ここからが本題。
##なにが問題なのか
cron が起動するタスクは cron のもつ独自の環境変数空間にあり、cron -f
で起動したとしても Docker コンテナ起動時の環境変数を引き継ぎません。
ですが、Docker コンテナを操作するために環境変数を介して設定を与えることが多々あります。具体的には --link
オプションで他のコンテナ情報を与えるときなどです。
データベースコンテナのアドレスとポートを link で与えたとしても、cron 起動時にそれを引き継ぎませんので結果 cron で起動されたタスクでデータベース接続先が分からないといった事になります。
これでちょっと悩んだというのが当記事の発端なのでした。
##取りあえず解決してみた
割と悩んで色々試したのですが、うまい解決方法を見つけられず以下の様な対処法を取ってみました。
- Docker コンテナ起動時に環境変数をファイルに書き出した後 cron を起動
- cron が起動するスクリプトの頭で保存して置いた環境変数列挙のファイルを読み込み
- 環境変数ファイルを読み込んだあと、目的である cron タスクを起動
具体的なスクリプトは以下のようになります
#!/bin/sh
printenv | awk '{print "export " $1}' > /root/env.sh
/usr/sbin/cron -f
環境変数を env.sh に保存してから cron を foreground で起動するスクリプト。
Docker コンテナの CMD ではこのスクリプトを起動します。
#!/bin/sh
. /root/env.sh
yourwork
cron から起動するのはこちらのスクリプト。
env.sh を読み込んでから、目的となるコマンドやスクリプトを実行します。
##取りあえず動いている
なんとなくやっつけ感ただよう対処ですが、動いているのでいまのところコレで良しとしています。