問題
Docker上でRails (Puma) を実行中に docker stop
でコンテナを停止させようとするとExit 1 (SIGHUP) が発生します。
ECSやKubernetesを利用している場合、コンテナが正しく終了せず、予期しない問題を引き起こす可能性があります。
% docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
xxx rails "bundle exec rails s…" 44 seconds ago Exited (1) 3 seconds ago api
xxx
原因
俗に言う「PID 1問題1」が原因。PID 1はinitプロセスと呼ばれるもので、システム起動時にカーネルによって呼び出される特別なプロセスです。initプロセスはシグナルハンドリングや子プロセスの生成、ゾンビプロセスの削除などを行います。
今回はRails (2)を起動した際にPID 1が使われてしまい、シグナルを正しくハンドリングできないといった問題が発生していました。
# コンテナ上でtopコマンドを実行した結果
PID USER PR NI VIRT RES %CPU %MEM TIME+ S COMMAND
1 root 20 0 2.2m 1.5m 0.0 0.1 0:00.02 S /bin/bash /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.
プロセスを見ると、PID 1でRailsが起動していることが分かります。
対策
tini や dumb-init といったプログラムを使うことで、PID 1の子プロセスとしてアプリケーションを起動することが可能となります。
docker-compose 3.7以降が利用可能であれば、docker-compose.yml
に init
パラメータを付けることで問題を回避することができます (3)。
# Railsがinitプロセスの子プロセスとして起動する
PID USER PR NI VIRT RES %CPU %MEM TIME+ S COMMAND
1 root 20 0 1.0m 0.0m 0.0 0.0 0:00.03 S /sbin/docker-init -- /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.0
6 root 20 0 2.2m 1.5m 0.0 0.1 0:00.00 S `- /bin/bash /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.0
docker-compose down
実行後にプロセスを見ると、143 (SIGTERM) でコンテナが停止しています。
% docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------
api /bin/docker-entrypoint.sh ... Exit 143
ECSを利用している場合
タスク定義に initProcessEnabled: true
(4) を追加します。initProcessEnabled
はdocker run
の --init
に相当します。
Kubernetesを利用している場合
tiniなどの軽量initを使う方法もありますが、Kubernetes 1.17からは Share Process Namespace (5) を使うことで問題を回避できるようです。