docker stop 的真實流程#

docker stop 看起來只是「停掉 container」,背後其實是一段固定的 signal 流程,由 docker daemon → containerd → shim 一路送到 container 內 PID 1。

  • 步驟 1:發送 SIGTERM 給 container 內 PID 1
  • 步驟 2:等待最多 N 秒(預設 10 秒,可用 --time / -t 調整)
  • 步驟 3:若 container 還沒結束,發送 SIGKILL
  • 步驟 4:清理 container
docker stop my-app                # 預設 10 秒寬限
docker stop -t 30 my-app          # 改成 30 秒
docker stop -t 0 my-app           # 立刻 SIGKILL

寬限時間是「等待」,不是「保證在這時間內結束」。如果應用 5 秒就 graceful exit,docker stop 也會立刻回來。

docker kill#

docker kill 是直接發 signal,不走「先 TERM 再 KILL」流程。預設送 SIGKILL,但可以指定其他 signal。

docker kill my-app                  # 等同 docker kill -s KILL
docker kill -s HUP my-app           # 對 PID 1 發 SIGHUP(觸發 reload)
docker kill -s USR1 my-app          # 自訂訊號
  • 適合手動觸發 reload、dump、profiling 等動作
  • 沒有寬限時間,signal 立即發送

docker rm -f#

docker rm -f 是「強制移除」。對於還在跑的 container,行為是:

  • 對 PID 1 送 SIGKILL(不發 SIGTERM)
  • 等待 container 結束
  • 移除 container 紀錄

docker rm -f 跳過 graceful shutdown。在生產環境用它意味著放棄應用 cleanup 機會,是緊急手段。

Kubernetes 的 graceful shutdown#

Kubernetes 對應的設計是 Pod 的 terminationGracePeriodSeconds,預設 30 秒。

  • 流程:preStop hook(如果有)→ SIGTERM → 等待 → SIGKILL
  • 期間 endpoint controller 會把 Pod 從 service 拿掉
  • 應用要在這段時間完成現有請求、關閉 DB

Shell Mode 為什麼接不到 SIGTERM#

Shell mode(CMD python app.py)會被 docker 翻成 /bin/sh -c "python app.py",PID 1 變成 /bin/sh,python 是 PID 2。docker stop 的 SIGTERM 送到 /bin/sh 後通常不會被轉發(詳細推導見 06-irresponsible-pid1),於是只能走到 SIGKILL。修法是改用 exec form:CMD ["python", "app.py"]

如果一定要保留 shell(例如要做變數展開),下面兩個技巧可以救回 graceful shutdown。

技巧一:在 entrypoint 用 exec#

ENTRYPOINT ["/entrypoint.sh"]
#!/bin/sh
set -e
echo "starting with cfg=$APP_CFG"
exec python app.py        # exec:用 python 取代 shell,PID 1 變成 python

exec 在 shell 中是把當前 process 替換成新 program,shell 不再存在。container PID 1 自然變成 python。

技巧二:用 trap + wait 在 shell 中轉發#

#!/bin/bash
term() {
    kill -TERM "$child" 2>/dev/null
}
trap term TERM INT

python app.py &
child=$!
wait "$child"

第二種寫法對 dash 不一定成立。dash 在某些版本裡 wait 不會被 signal 中斷成功 trap。Ubuntu 預設 /bin/sh = dash,下一章會展開。

docker exec 不會 stop container#

順帶澄清:docker exec 是另起一個 process 進 container,跟 stop / signal 流程無關。docker stop 影響的是 PID 1,docker exec 起的 process 是 PID 1 的 sibling,會在 container 終止時被一起清掉。

一張流程圖(流程順序)#

  • docker stop my-app -t 10
  • dockerd → containerd → shim
  • shim 對 container PID 1 發 SIGTERM
  • 計時 10 秒
  • 若 PID 1 已退出 → 結束,回收 container
  • 若 PID 1 還活著 → shim 發 SIGKILL → 強制回收

延伸閱讀#

  • Docker docs:Best practices for writing Dockerfiles(exec form vs shell form)
  • Kubernetes docs:Pod Lifecycle / Termination
  • moby/moby 原始碼:daemon/stop.go