📝

コンテナのエントリーポイントでexecコマンドを利用したプロセス管理とシャットダウンについて

2024/12/17に公開

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コマンドでの実行は当初から実装されている。
https://github.com/rails/rails/pull/46762/files#diff-8a32b771c35eb43341216d3efb062fdf07566bb1a0740483ac313c0d2f4c5672R8

sidekiqコンテナのプロセス管理も同様。
https://speakerdeck.com/techouse/depuroiworen-saretanode-jiao-watutatong-rinidepuroisitarazhang-hai-ninatutajian-an-noyarakasiwoyue-eteyuke?slide=140

Discussion