docker stop や docker-compose downが遅いとき
docker-compose downがなんか遅いなーと思っていたら、entrypoint.sh
の書き方がよくなかった。 exec your-command
としないと、 SIGTERM
シグナルがプロセスに届かず、タイムアウト(10秒)を待って SIGKILL
される。
確認
例としてflaskアプリをgunicornで動かしてみる。
ファイルの準備
Dockerfile
from python:3.9-slim RUN pip install flask gunicorn COPY app.py /
app.py
from flask import Flask app = Flask(__name__) @app.route("/") def konnnichiha(): return "<p>Konnnichiha!</p>"
entrypoint.sh
#!/bin/bash gunicorn -w 1 -b 0.0.0.0:5000 app:app
entrypoint-exec.sh
#!/bin/bash exec gunicorn -w 1 -b 0.0.0.0:5000 app:app
テスト
コンテナを実行する。
$ chmod 755 entrypoint* $ docker build -t flask-test . $ docker run -d --name test-flask\ -v $(pwd)/entrypoint.sh:/entrypoint.sh\ --entrypoint '/entrypoint.sh'\ test-flask $ docker run -d --name test-flask-exec\ -v $(pwd)/entrypoint-exec.sh:/entrypoint.sh\ --entrypoint '/entrypoint.sh'\ test-flask
exec版はentrypoint.shがいなくなっている。
$ docker top test-flask UID PID PPID C STIME TTY TIME CMD root 935376 935355 0 23:26 ? 00:00:00 /bin/bash /entrypoint.sh root 935446 935376 0 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app root 935448 935446 0 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app $ docker top test-flask-exec UID PID PPID C STIME TTY TIME CMD root 935610 935591 2 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app root 935658 935610 1 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app
終了してみると、execなしはタイムアウト待機分の10秒余計にかかる。
$ time docker stop test-flask test-flask real 0m10.228s user 0m0.019s sys 0m0.019s $ time docker stop test-flask-exec test-flask-exec real 0m0.323s user 0m0.020s sys 0m0.020s
execありはシグナルを受け取ってgracefulに終了している。
$ docker logs test-flask [2021-05-20 03:26:16 +0000] [7] [INFO] Starting gunicorn 20.1.0 [2021-05-20 03:26:16 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7) [2021-05-20 03:26:16 +0000] [7] [INFO] Using worker: sync [2021-05-20 03:26:16 +0000] [8] [INFO] Booting worker with pid: 8 $ docker logs test-flask-exec [2021-05-20 03:26:22 +0000] [1] [INFO] Starting gunicorn 20.1.0 [2021-05-20 03:26:22 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1) [2021-05-20 03:26:22 +0000] [1] [INFO] Using worker: sync [2021-05-20 03:26:22 +0000] [8] [INFO] Booting worker with pid: 8 [2021-05-20 03:27:02 +0000] [1] [INFO] Handling signal: term [2021-05-20 03:27:02 +0000] [8] [INFO] Worker exiting (pid: 8) [2021-05-20 03:27:02 +0000] [1] [INFO] Shutting down: Master
掃除。
$ docker rm test-flask test-flask-exec
しくみ
docker stopやdocker-compose downは、SIGTERM
を送ってデフォルトの10秒 (ref https://docs.docker.com/engine/reference/commandline/stop/) たっても終了しない場合は、 SIGKILL
が送信される。
PID 1が bash entrypoint.sh
だと、シグナルが子プロセスに届かない。 exec command
で command
を実行すると command
が PID 1になるので、シグナルを受け取れる。
exec command
は command
がシェルを置き換える。man bashより:
exec [-cl] [-a name] [command [arguments]] If command is specified, it replaces the shell. No new process is created. The arguments become the arguments to command. (略)
なお、シグナルを受け取った後でシェルで他の処理をしたいときは、exec
ではなく trap
する
ref: https://docs.docker.com/engine/reference/builder/#entrypoint
他にもコンテナの終了が遅いときは、CMD/ENTRYPOINT
の書き方とか、そもそも SIGTERM
では終了しないプロセスとか、いくつか原因になりそうな事がある。
ref: https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop