Dockerのコンテナを起動する際に何らかのデーモンを起動させたい、という要求は結構多い(というか実務的にはむしろそれが主目的ではないか)と思うのだが、「何らかのデーモン」がバックグラウンド・プロセスとして動作する代物だと、若干ハードルが上がるようだ。
デーモンがフォアグラウンドで動作するならば、教科書通りDockerfileのCMD
にデーモン起動のコマンドを記述すればよい。例えば次のような内容だ。
FROM amazonlinux:latest # 色々と準備(省略) # コンテナ起動時に自作デーモン my-daemon を起動する。 CMD /usr/local/sbin/my-daemon -l /usr/local/etc/my-daemon.conf
DockerfileのENTRYPOINT
やCMD
は、フォアグラウンドで動作するアプリケーションを前提としている節がある。ENTRYPOINT
ないしCMD
に記述されたコマンドは、コンテナ起動時にPID=1のプロセスとして実行される。フォアグラウンドで動作するアプリケーションならば、自身のプロセス(PID=1のプロセス)は実行状態のままとなり、コンテナを停止するまで生存し続ける。
※より正確に書くと、docker run
実行時にオプション--init
を付与しなかったコンテナにおいては、ENTRYPOINT
やCMD
にexec形式で記述されたコマンドが、PID=1のプロセスとして実行される。シェル形式で記述した場合、そのコマンドは/bin/sh -c
の引数として実行されるが、この時PID=1のプロセスは/bin/sh -c
であり、その子プロセスとして「ユーザが記述したコマンド」が実行される。
ところで、非コンテナ環境で運用してきた秘伝のデーモンを組み込もうとした時、例えば/etc/init.d
の起動スクリプト経由でバックグラウンド・プロセスとして起動させる、みたいな構成*1であることが結構多い。
起動スクリプト内でdeamon(8)を使っているならまだしも、中にはソースコード内でdaemon(3)を使って自前でバックグラウンド化している(しかもフォアグラウンドで実行させる術がない)アプリケーションもあったりする。
残念ながら、CMD
の内容が「バックグラウンド動作するデーモンを起動する」だけであると、コンテナ自体が起動した直後に停止してしまう。例えば次のような内容だと、ユーザの意図した動作にならない。
FROM amazonlinux:latest # 色々と準備(省略) # コンテナ起動時に秘伝のデーモン magic-daemon を起動する。 # ==> デーモンは起動するが、コンテナ自体が直ぐに停止してしまう。 CMD /etc/init.d/magic-daemon start
CMD
の内容に従ってデーモン起動のコマンドを実行した直後に、その処理を行ったPID=1のプロセスが終了してしまう。そのため、コンテナ自体が直ぐに停止してしまうのだ。
ではどうすればよいかといえば、CMD
に「直ぐには終了しない処理」を記述すればよい。デーモン起動のコマンドを実行した後に、例えば以下のようなコマンドを実行するのだ*2。
/bin/sh
sleep infinity
tail -f /dev/null
- これも無限待ち状態になるが、他者に意図が伝わりにくい書き方だと思う。
while : ; do sleep 1; done
- 実はこれが一番手堅い方法かもしれない。
CMD
に複数のコマンドを記述するには、ラッパーとなるシェルスクリプトを用意しても良いが(というか個人的にはそれを推奨するが)、シェル形式を用いて次のように記述するのもひとつの手だろう。例えば次のような塩梅だ。
FROM amazonlinux:latest # 色々と準備(省略) # コンテナ起動時に秘伝のデーモン magic-daemon を起動する。 # その後、コンテナを終了させないようにする。 CMD /etc/init.d/magic-daemon start; tail -f /dev/null
ところで上記の書き方は、コンテナを停止する際に問題を引き起こす可能性がある。回避するためには、ラッパーとなるシェルスクリプトを用意する必要がある。
(次回に続く)