Dockerコンテナー内でcronを実行する



Dockerコンテナでcronを実行することは、複雑ではないにしても非常に具体的なことです。 ネットワークには、このトピックに関する決定とアイデアがたくさんあります。 最も一般的な(そして単純な)実行方法の1つを次に示します。

cron -f
      
      





ただし、このようなソリューション(および他のほとんどのソリューション)には、すぐに回避するのが非常に難しいいくつかの欠点があります。





ログ



標準のDockerツールを使用してログを表示する問題は、比較的簡単に解決できます。 これを行うには、cronジョブログを書き込むファイルを決定するだけで十分です。 これが/var/log/cron.logであるとします:

* * * * * www-data task.sh >> /var/log/cron.log 2>&1







コマンドでコンテナを起動した後:

 cron && tail -f /var/log/cron.log
      
      





Dockerログを使用して、タスクを完了した結果を常に確認できます。



/var/log/cron.logをコンテナの標準出力にリダイレクトすることにより、同様の効果を実現できます。

 ln -sf /dev/stdout /var/log/cron.log
      
      





UPD :この方法では、この方法は機能しません。



cronタスクがログを異なるファイルに書き込む場合、おそらく、tailを使用するオプションは、複数のログを同時に「監視」できるため、望ましいでしょう。

 cron && tail -f /var/log/task1.log /var/log/task2.log
      
      





UPD :名前付きパイプ(FIFO)の形式でログのファイルを作成する方が便利です。 これにより、コンテナ内に不要な情報が蓄積されるのを防ぎ、ログローテーションタスクをDockerに割り当てます。 例:

 mkfifo --mode 0666 /var/log/cron.log
      
      







環境変数



cronタスクの環境変数の設定に関するトピックの情報を調べると、後者がいわゆる認証プラグイン(PAM)を使用できることがわかりました。 一見したところ、これは主題に関係のない事実です。 ただし、PAMには、cronを含む、PAMを使用するサービス(または認証モジュール)の環境変数を定義および再定義する機能があります。 すべての設定は/etc/security/pam_env.confファイルで行われます(Debian / Ubuntuの場合)。 つまり、このファイルに記述されている変数はすべてのcronジョブの環境に自動的に入ります。



しかし、1つ、または2つの問題があります。 ファイルの構文(その説明)は、一見すると思いがけないものになります。 2番目の問題は、コンテナを起動するときに、pam_env.conf内で環境変数を転送する方法です。



2番目の問題について経験豊富なDockerユーザーは、おそらくdocker-entrypoint.shと呼ばれるライフハックを使用でき、正しいと言うでしょう。 このライフハックの本質は、コンテナの起動時に実行される特別なスクリプトを記述することです。これは、CMDにリストされているか、コマンドラインで渡されるパラメータのエントリポイントです。 スクリプトは、たとえば次のようにDockerfile内に記述できます。

 ENTRYPOINT ["/docker-entrypoint.sh"]
      
      





そして彼のコードは特別な方法で書かなければなりません:

docker-entrypoint.sh
 #!/usr/bin/env bash set -e #      /etc/security/pam_env.conf exec "$@"
      
      







少し後で環境変数の転送に戻りましょうが、ここではpam_env.confファイルの構文について説明します。 このファイルで変数を記述する場合、2つのディレクティブDEFAULTおよびOVERRIDEを使用して値を指定できます。 1つ目では、変数のデフォルト値を指定できます(現在の環境でまったく定義されていない場合)。2つ目では、変数の値をオーバーライドできます(この変数の値が現在の環境にある場合)。 これらの2つのケースに加えて、例としてのファイルはより複雑なケースを説明していますが、概してDEFAULTにのみ関心があります。 合計で、cronで使用される環境変数の値を決定するには、次の例を使用できます。

 VAR DEFAULT="value"
      
      





指定された変数が存在しない(または異なる値を持つ)ターゲット環境内でファイルコンテキストが実行されるため、この場合のには変数名(たとえば、$ VAR)を含めないでください。



ただし、さらに簡単に実行できます(何らかの理由で、この方法はpam_env.confの例では説明されていません)。 この環境で既に定義されているかどうかに関係なく、ターゲット環境の変数が指定された値を持っていることに満足している場合、上記の行の代わりに、簡単に書くことができます:

 VAR="value"
      
      





ここで、cronジョブは、必要に応じて$ PWD、$ USER、および$ PATHを置き換えることができないことに注意する必要があります。これは、cronが独自の信念に基づいてこれらの変数の値を割り当てるためです。 もちろん、 さまざまなハックを利用できますが、その中には労働者もいますが、これはあなたの裁量です。



最後に、現在のすべての変数をcronジョブの環境に転送する必要がある場合、この場合、次のスクリプトを使用できます。

docker-entrypoint.sh
 #!/usr/bin/env bash set -e #       env | while read -r LINE; do #    'env'  #     ,     "=" (. IFS) IFS="=" read VAR VAL <<< ${LINE} #      ,    sed --in-place "/^${VAR}/d" /etc/security/pam_env.conf || true #        echo "${VAR} DEFAULT=\"${VAL}\"" >> /etc/security/pam_env.conf done exec "$@"
      
      







イメージ内の/etc/cron.dフォルダーにprint_envスクリプトを配置し、コンテナー(Dockerfileを参照)を実行することにより、このソリューションが機能することを確認できます。

print_env
 * * * * * www-data env >> /var/log/cron.log 2>&1
      
      







Dockerfile
 FROM debian:jessie RUN apt-get clean && apt-get update && apt-get install -y cron RUN rm -rf /var/lib/apt/lists/* RUN mkfifo --mode 0666 /var/log/cron.log COPY docker-entrypoint.sh / COPY print_env /etc/cron.d ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/bin/bash", "-c", "cron && tail -f /var/log/cron.log"]
      
      







コンテナ打ち上げ
 docker build --tag cron_test . docker run --detach --name cron --env "CUSTOM_ENV=custom_value" cron_test docker logs -f cron #   
      
      









正常なシャットダウン



記述されたコンテナがcronで正常に完了できない理由については、Dockerデーモンが内部で実行されているサービスと通信する方法に言及する必要があります。 このようなサービス(プロセス)はPID = 1で始まり、このPIDでのみDockerが機能します。 つまり、Dockerはコンテナーに制御信号を送信するたびに、PID = 1のプロセスにアドレスします。 docker stopの場合はSIGTERMであり、プロセスが続行する場合は10秒後にSIGKILLになります。 「/ bin / bash -c」が開始に使用されるため(「CMD cron && tail -f /var/log/cron.log」の場合、Dockerは暗黙的に「/ bin / bash -c」を使用します)、その後、PID = 1はprocess / bin / bashを受け取り、cronとtailはすでに他のPIDを受け取ります。これは明白な理由で予測することが不可能です。



したがって、docker stop cronコマンドを実行すると、SIGTERMはprocess / bin / bash -cを受信し、このモードでは受信した信号を無視します(もちろんSIGKILLを除く)。



この場合の最初の考えは通常です-何らかの方法でテールプロセスを「テール」する必要があります。 まあ、これは簡単です:

 docker exec cron killall -HUP tail
      
      





涼しい、コンテナはすぐに動作を停止します。 優雅さについては本当ですが、いくつかの疑問があります。 また、エラーコードはまだゼロではありません。 一般に、この道をたどって問題を解決することはできませんでした。



ちなみに、 cron -fコマンドを使用してコンテナを起動しても目的の結果は得られません。この場合、cronは信号への応答を拒否します。



終了コードがゼロの真の正常なシャットダウン



残っていることが1つだけあります-cronデーモンを起動するための別個のスクリプトを作成し、制御信号に正しく応答することができます。 これは比較的簡単です。以前にbashに書き込む必要がなかったとしても、信号処理をプログラムする能力があるという情報を見つけることができます( trapコマンドを使用)。 たとえば、このようなスクリプトは次のようになります。

start-cron
 #!/usr/bin/env bash #  cron service cron start #  SIGINT  SIGTERM   trap "service cron stop; exit" SIGINT SIGTERM
      
      







何らかの方法でこのスクリプトを無限に動作させることができれば(信号が受信されるまで)。 そして、ここで覗かれた別のライフハックが助けになります 。つまり、スクリプトの最後にそのような行を追加します。

 tail -f /var/log/cron.log & wait $!
      
      





または、cronジョブがログを異なるファイルに書き込む場合:

 tail -f /var/log/task1.log /var/log/task2.log & wait $!
      
      







おわりに



その結果、Dockerコンテナー内でcronを実行し、最初の制限を回避し、2番目の規則を順守するための効果的なソリューションとなりました。コンテナーの通常の停止と再起動の可能性があります。



最後に、記事に記載されているすべてのものが別個のDockerイメージとして設計されているリンクrenskiy / cronを提供します。



All Articles