Docker容器优雅停止需三层面协同:运行时设--stop-timeout、镜像用exec格式CMD或tini接管信号、应用层实现SIGTERM响应与缓存清理逻辑,否则默认10秒易触发SIGKILL导致数据丢失。
要让 docker 容器在停止时有足够时间完成缓存刷盘、连接释放等操作,关键不是“延长某处固定时长”,而是打通信号传递 → 应用响应 → 资源清理 → 真正退出的全链路。对含大量缓存的应用(如 redis 持久化、java 应用批量落库、node.js 内存队列清空),默认 10 秒往往不够,需从运行时、镜像构建、应用代码三层面协同配置。
设置容器级停止超时时间
Docker 默认等待 10 秒后发 SIGKILL,可通过以下方式延长:
- 运行容器时加
--stop-timeout=60:例如docker run --stop-timeout=60 -d my-app,给应用最多 60 秒处理 SIGTERM - Docker Compose 中写入:
stop_grace_period: 60s(YAML 缩进需准确) - Kubernetes 中对应
terminationGracePeriodSeconds: 60,且建议该值 ≥ 应用自身清理最大耗时
确保 SIGTERM 能真正抵达应用主进程
很多镜像因启动方式问题导致信号被拦截:
- 避免使用 shell 形式 CMD:
CMD java -jar app.jar会让 sh 充当 PID 1,不转发信号 - 改用 exec 格式:
CMD ["java", "-jar", "app.jar"],使 Java 进程直接受控 - 若必须用 shell 启动(如需环境变量预处理),在 Dockerfile 中引入 tini:
FROM openjdk:17-slim<br> RUN apt-get update && apt-get install -y tini<br> ENTRYPOINT ["/sbin/tini", "--"]<br> CMD ["java", "-jar", "app.jar"]
应用层主动配合缓存安全关闭
超时只是“窗口”,清理逻辑必须由应用自己实现:
-
Java(Spring Boot):启用优雅关机,配置
server.shutdown=graceful和spring.lifecycle.timeout-per-shutdown-phase=45s;在@PreDestroy或SmartLifecycle.stop()中触发缓存 flush、线程池 shutdownNow 等 -
Node.js:监听
process.on('SIGTERM', () => { server.close(); cache.flush().then(() => process.exit(0)); }),避免process.exit()提前中断 -
Go:用
signal.Notify(c, syscall.SIGTERM)捕获,调用cache.Close()+http.Server.Shutdown(),并用 context.WithTimeout 控制总耗时
验证是否真正生效
别只看命令返回成功,要确认行为符合预期:
- 执行
docker stop -t 60 my-app后,观察日志是否出现“开始刷缓存”“等待连接关闭”等明确清理标记 - 检查退出码:
docker inspect my-app --format='{{.State.ExitCode}}',正常优雅退出应为0;若为137,说明被 SIGKILL 强制终止,超时或应用未响应 - 对关键缓存(如本地文件、Redis RDB),停机后手动校验数据完整性,确认无截断或丢失











