Docker stop 默认向 PID 1 发送 SIGTERM,因容器中 PID 1 是 init 进程且为用户命令本身;若未响应则 10 秒后发 SIGKILL。关键在确保应用直接作为 PID 1 运行并正确处理信号。

Docker stop 默认向容器内 PID 1 进程发送 SIGTERM 信号,等待指定超时(默认 10 秒)后若进程未退出,再发 SIGKILL 强制终止。
为什么是 PID 1?
Linux 容器中,PID 1 是 init 进程,具有特殊地位:它不会被父进程 wait,也不继承默认信号处理行为;所有子进程的“孤儿”都会被它收养。Docker 将用户指定的命令作为 PID 1 启动,因此信号只能直接送达该进程——不是 shell、不是 wrapper 脚本,而是真正运行在 /proc/1/cmdline 中的那个程序。
常见误区是以为 docker stop 会向整个进程树广播信号。实际上 Docker 不做递归 kill,也不模拟 systemd 或 tini 的信号转发逻辑。是否响应 SIGTERM、是否优雅关闭、是否清理子进程,完全取决于 PID 1 自身实现。
信号能否被正确接收?关键看启动方式
- 直接运行二进制(如 nginx -g "daemon off;" 或 python app.py):PID 1 即应用本身,能直接捕获 SIGTERM,可自行注册 handler 实现 graceful shutdown。
- 经 sh -c 启动(如 sh -c "exec nginx -g 'daemon off;'"):PID 1 是 sh,而 sh 默认忽略 SIGTERM;除非显式 trap 并转发,否则容器会“假死”——看似没退出,实则应用已僵住。
- 使用 exec 形式(exec nginx -g "daemon off;"):替换当前 shell 进程,让 nginx 真正成为 PID 1,避免中间层拦截信号。
如何验证信号是否到达 PID 1?
可在容器内临时加日志或调试:
- 用 strace -p 1 -e trace=signal 观察 PID 1 是否收到 SIGTERM(需容器内有 strace 且以 CAP_SYS_PTRACE 启动);
- 在应用启动前插入简单 trap(如 trap 'echo "got SIGTERM" >> /tmp/shutdown.log' TERM),确认 shell 层是否捕获;
- 检查 docker inspect $CONTAINER_ID | jq '.State.Status' 和 .State.StoppedReason,辅助判断是正常退出还是被 SIGKILL 终止。
超时与强制终止不可控?可以调整
docker stop 支持 -t 参数指定等待秒数,例如 docker stop -t 30 myapp。这个值也会透传给 docker daemon,在调用 containerd 的 Stop 函数时作为 deadline 使用。若业务需要更长的清理时间(如写完 checkpoint、刷盘、断开长连接),应合理设置该值,而非依赖默认 10 秒。
注意:增大超时不能解决 PID 1 忽略信号的问题,只能缓解“来不及退出”的情况;根本解法仍是确保 PID 1 正确处理 SIGTERM。










