📝
コンテナのエントリーポイントでexecコマンドを利用したプロセス管理とシャットダウンについて
ECS のアプリケーションを正常にシャットダウンする方法を読んで、
プロセスは子プロセスを生成し、その子プロセスの親になります。 SIGTERM のような停止シグナルによってプロセスが停止されると、親プロセスは、その子プロセスを正常にシャットダウンし、その後自身をシャットダウンする責任があります。
コンテナの ENTRYPOINT を /bin/sh -c my-app に設定している例を見ていきます。この例では、以下の理由から my-app は正常にシャットダウンしません。
1. sh がエントリプロセスとして実行され、さらに my-app プロセスを生成します。これは sh の子プロセスとして実行されます。
2. sh は、コンテナが停止したときに SIGTERM シグナルを受信しますが、my-app に渡されず、このシグナルに対してアクションを実行するように設定されていません。
3. その後コンテナが SIGKILL シグナルを受信すると、sh とすべての子プロセスが直ちに終了します。
デフォルトでは、shell は SIGTERM を無視します。したがって、shell をアプリケーションのエントリポイントとして使用する場合は、細心の注意が必要です。エントリポイントで shell を安全に使用するには、2 つの方法があります。1) シェルスクリプトを介して実行される実際のアプリケーションに exec というプレフィックスをつける。
の説明についてrailsのDockerfileを参考にしながら確認する。
railsがデフォルトで作成するbin/docker-entrypoint
を確認すると、exec "${@}
のコマンドでshellが起動するようになっている。
execはいまのシェルプロセスを指定したコマンドに置き換えるため、shがエントリーポイントではなくなる。
#!/bin/bash -e
# Enable jemalloc for reduced memory usage and latency.
if [ -z "${LD_PRELOAD+x}" ]; then
LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
export LD_PRELOAD
fi
# If running the rails server then create or migrate existing database
if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
./bin/rails db:prepare
fi
exec "${@}"
execコマンドを利用した場合と利用していない場合でプロセス管理とシャットダウンについて動作確認する。
execコマンドを利用する場合
❯ docker exec -it 35ef7d247a65 bash
rails@35ef7d247a65:/rails$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
rails 1 1.8 0.0 1231728 6788 ? Ssl 14:23 0:00 /usr/local/bundle/ruby/3.3.0/gems/thruster-0.1.9-aa
rails 18 8.6 1.2 704056 102700 ? Sl 14:23 0:01 puma 6.5.0 (tcp://0.0.0.0:3000) [rails]
execコマンドを利用すると、./bin/docker-entrypoint
がエントリーポイントではなく、bin/thruster
を実行するプロセスがPID1となる。
このときにコンテナを停止させると、SIGTERMシグナルを受け取り、graceful shutdownができる。
❯ docker stop 35ef7d247a65
# logs
{"time":"2024-12-16T14:57:04.336753881Z","level":"INFO","msg":"Relaying signal to upstream process","signal":"terminated"}
- Gracefully stopping, waiting for requests to finish
Exiting
{"time":"2024-12-16T14:57:04.387902631Z","level":"INFO","msg":"Server stopping"}
{"time":"2024-12-16T14:57:04.388332964Z","level":"INFO","msg":"Server stopped"}
execコマンドを利用しない場合
❯ docker exec -it 35ef7d247a65 bash
rails@8e6ae8f4257f:/rails$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
rails 1 0.0 0.0 4208 2976 ? Ss 14:38 0:00 /bin/bash -e /rails/bin/docker-entrypoint ./bin/thrust ./bin/rails server
rails 12 1.6 0.0 1231472 6788 ? Sl 14:38 0:00 /usr/local/bundle/ruby/3.3.0/gems/thruster-0.1.9-aarch64-linux/exe/aarch64-linux/thrust ./bin/rails server
rails 19 8.7 1.2 704056 102636 ? Sl 14:38 0:01 puma 6.5.0 (tcp://0.0.0.0:3000) [rails]
execコマンドを利用しない場合は、エントリーポイントのshが親プロセスとなっている。
このときにコンテナを停止させると、SIGTERMシグナルが無視され、10秒後にSIGKILLで強制終了される
❯ docker stop 35ef7d247a65
まとめ
コンテナを正常にシャットダウンする場合には、SIGTERMシグナルを適切に処理する必要がある。
shはSIGTERNを無視するため、アプリケーションのエントリーポイントとしてはexecコマンドの利用が適切
参考
execコマンドでの実行は当初から実装されている。
sidekiqコンテナのプロセス管理も同様。
Discussion