优雅停机是保障服务稳定的关键,需正确处理SIGTERM信号、延长stop-timeout、应用层注册清理逻辑、Dockerfile用exec启动或tini、K8s配terminationGracePeriodSeconds并配合就绪探针。

在生产环境中,Docker 容器的 优雅停机(Graceful Shutdown) 不是可选项,而是保障服务稳定、数据一致和用户体验的关键环节。默认情况下,Docker 发送 SIGTERM 后等待 10 秒即强制发送 SIGKILL,若应用未及时完成清理(如处理完请求、刷盘、断开数据库连接、释放锁等),极易引发超时、丢数据、状态不一致等问题。
理解容器停止生命周期与信号传递机制
Docker stop 命令本质是向容器主进程(PID 1)发送 SIGTERM,等待其自行退出;超时后发 SIGKILL 强制终止。关键点在于:
- 容器内必须有进程能正确接收并响应 SIGTERM(很多基础镜像中 sh/bash 不转发信号,需用
exec启动或使用tini等 init 工具) - 应用代码需注册 SIGTERM 处理逻辑,暂停接收新请求、等待活跃请求完成、执行清理动作
- Docker 的
--stop-timeout参数决定了 SIGTERM 和 SIGKILL 之间的等待时间,默认 10s,常需根据业务场景调大
应用层:实现可中断的服务生命周期管理
以常见语言为例:
-
Java(Spring Boot 2.3+):启用
spring.lifecycle.timeout-per-shutdown-phase=30s,配合@PreDestroy或SmartLifecycle执行清理;Web 容器(如 Tomcat)会自动进入“暂停接受新请求 → 等待现存请求结束 → 关闭连接”流程 -
Node.js:监听
process.on('SIGTERM', ...),关闭 HTTP server 并等待server.close()回调,再退出进程;避免直接process.exit() -
Go:用
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)捕获信号,调用srv.Shutdown(ctx)等待 HTTP 连接 graceful 结束
Dockerfile 与运行时配置要点
确保信号可穿透、进程可响应:
- 使用
exec形式启动命令:CMD ["java", "-jar", "app.jar"](而非CMD java -jar app.jar),避免 shell 作为 PID 1 拦截信号 - 若必须用 shell 启动,添加轻量级 init(如
FROM --platform=linux/amd64 alpine:latest+RUN apk add --no-cache tini+ENTRYPOINT ["/sbin/tini", "--"]) - 启动容器时显式设置超时:
docker run --stop-timeout=45 ...;编排工具中(如 Docker Compose)写入:stop_grace_period: 45s - Kubernetes 场景下,还需配置
terminationGracePeriodSeconds(建议 ≥ stop_timeout),并确保 readinessProbe 在收到 SIGTERM 后快速失败,使流量不再流入
验证与可观测性补充手段
优雅停机不能只靠理论,要可观测、可验证:
- 在应用日志中打印 “Received SIGTERM”, “Shutting down server…”, “All requests completed, exiting” 等关键事件
- 压测时模拟滚动更新,用
curl -I http://svc/healthz观察就绪探针何时失效,结合日志确认请求是否真正零丢失 - 对长连接服务(如 WebSocket、gRPC stream),额外增加连接 draining 机制(如主动发送 disconnect 通知、延长连接空闲超时)
- 借助 Prometheus + Grafana 监控容器重启次数、平均停机耗时、异常退出率,建立基线并告警突增

















