Docker容器优雅停机需信号送达、应用响应与清理高效协同:设--stop-timeout为清理耗时1.2–1.5倍,用exec格式CMD或tini/--init确保SIGTERM抵达PID 1,应用层主动捕获并执行关闭逻辑,验证日志、退出码及实际耗时。

要让 Docker 容器在收到 SIGTERM 后真正完成优雅停机,不能只调大 --stop-timeout,而必须让它和应用层响应、信号传递路径协同生效。超时时间只是“窗口”,窗口里能否做完清理,取决于三件事:信号是否送达 PID 1、应用是否监听并执行逻辑、清理动作本身是否足够快。
运行时显式设置 stop-timeout
这是最直接的控制点,决定 SIGTERM 和 SIGKILL 之间的等待时长:
- 启动容器时加参数:
docker run --stop-timeout=60 -d my-app(单位秒,建议设为应用最大清理耗时的 1.2–1.5 倍) - Docker Compose 中配置:
stop_grace_period: 60s(注意 YAML 缩进,该值对docker-compose down和up --force-recreate均生效) - Kubernetes 场景下,
terminationGracePeriodSeconds: 60必须 ≥ stop-timeout,否则会被提前强杀 - 临时调试可用:
docker stop --time=45 my-container,不修改服务定义,适合验证阶段
确保 SIGTERM 能抵达应用主进程
很多容器停不下来,根本不是超时短,而是信号压根没传到应用:
- 避免 shell 形式 CMD:
CMD java -jar app.jar会让/bin/sh成为 PID 1,它不转发信号 - 改用 exec 格式:
CMD ["java", "-jar", "app.jar"],让 Java 进程直接受控 - 若需 shell 环境(如加载环境变量),Dockerfile 中引入 tini:
ENTRYPOINT ["/sbin/tini", "--"],再接 CMD - 或启动时加
--init参数:docker run --init --stop-timeout=60 my-app,自动注入轻量 init 进程
应用层必须主动响应 SIGTERM 并执行清理
超时再长,应用不写关闭逻辑也没用。关键是在收到信号后做这几件事:
- Java(Spring Boot):启用
server.shutdown=graceful,设spring.lifecycle.timeout-per-shutdown-phase=45s,并在@PreDestroy或SmartLifecycle.stop()中 flush 缓存、关闭线程池、断开 DB 连接 - Node.js:监听
process.on('SIGTERM', () => { server.close(() => process.exit(0)); }),注意server.close()是异步,不能直接process.exit() - Go:用
signal.Notify(c, syscall.SIGTERM)捕获,调http.Server.Shutdown()并配context.WithTimeout控制总耗时 - 数据库类容器(如 PostgreSQL):确保其原生支持 SIGTERM(默认支持),配合
--stop-timeout给够 checkpoint 和 WAL 刷盘时间
验证是否真正生效
别只看命令返回成功,得确认行为符合预期:
- 执行
docker stop -t 60 my-app后,立刻查日志:docker logs my-app | tail -20,找received SIGTERM、shutting down gracefully、cache flushed等明确标记 - 检查退出码:
docker inspect my-app --format='{{.State.ExitCode}}',正常应为0;若为137,说明被 SIGKILL 强制终止,链路某处卡住了 - 观察实际停机耗时:在应用启动时打时间戳,收到 SIGTERM 时再打一个,对比是否 ≤ 设置的 timeout 值


















