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 變成 pythonexec 在 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