202号室の手記

数学やコンピュータの小ネタを中心に書きます.

Dockerコンテナをホスト側のcronで実行する

やりたいこと

  • とあるプログラムを実行するDockerコンテナを毎日定刻に起動したい。
  • 処理が完了したらコンテナは消去したい。

つまり、次のコマンドをcronで実行したい。

$ docker run -it --rm my_image my_command

やったこと

crontabにそのまま書けばいいじゃん!
と思ったが、そうは東京医科歯科大学

$ crontab -e
0 7 * * * docker run -it --rm my_image my_command

定刻になっても、うんともすんともしない。

まずは、crontabの実行環境で、dockerコマンドにパスが通ってんのかが気になった。

$ which docker
/usr/bin/docker
$ crontab -e 
* * * * * echo $PATH > /tmp/env.txt

結果は、

$ cat /tmp/env.txt
PATH=/usr/bin:/bin

.bash_profileに書いてあるようなユーザ定義の環境変数は受け継がれていないが、 少なくともdockerコマンドへのパスは通っているようだ。

デフォルトの/var/log/cronログファイルには、実行結果までは出力されない。
そこで、エラーチェックのために、次のように設定した。

* * * * * docker run -it --rm my_image my_command > /tmp/cron.log 2>&1

結果は、

$ cat /tmp/cron.log
the input device is not a TTY

となっていた。 実端末(TTY)からの入力でないことで怒られている。

原因はイカの通り。

cronで指定されたコマンドは実端末から実行される訳ではない。
実際、

$ crontab -e
* * * * * tty > /tmp/tty.txt

とすると、

$ cat /tmp/tty.txt
not a tty

となる。
docker runコマンドの-itオプションは、 実行時の端末をコンテナ内のプロセスに割り当てるものであるため、 割り当てるべき端末がない、と怒っていたのだ。

結論

crontabには、次のように書けば良いです。(毎朝7時に実行したい場合)

0 7 * * * docker run --rm my_image my_command

こうしてできたのが、↓のbotです。

twitter.com

以上です。