健康下线需应用、容器运行时、负载均衡器协同:应用响应SIGTERM关闭端口并返回503;容器用exec格式启动并设stop-timeout≥45s;LB需≤3秒探测/readyz且快速摘流。
要让一个应用节点在 docker 中健康下线,关键不是“怎么执行 docker stop”,而是“在 docker stop 触发前,让流量彻底停止进入”。这需要应用、容器运行时、负载均衡器三方协同动作,缺一不可。
应用层必须响应 SIGTERM 并主动声明不可用
收到 SIGTERM 后,应用不能只等进程退出,而要立即做三件事:
- 关闭监听端口(如 HTTP server.close()),拒绝新连接
- 将就绪探针接口(如
/readyz)返回 503 Service Unavailable,而非 200 - 等待存量请求自然结束,超时建议设为 20–45 秒(例如 Spring Boot 配置
spring.lifecycle.timeout-per-shutdown-phase=30s)
容器配置要确保信号不被拦截、宽限期足够
如果 PID 1 是 shell,SIGTERM 就无法传给真正应用进程——这是最常见的失败原因:
- Dockerfile 中避免
CMD java -jar app.jar(shell 格式),改用 exec 格式:CMD ["java", "-jar", "app.jar"] - 若需环境变量预处理,用
tini作为 init 进程:ENTRYPOINT ["/sbin/tini", "--"] - 启动时显式延长优雅终止时间:
docker run --stop-timeout=45,或 Compose 中写stop_grace_period: 45s
负载均衡器必须短周期探测并快速摘流
LB 不知道你要停,就不会主动切走流量——这是无损下线最容易被忽略的一环:
- 健康检查间隔 ≤ 3 秒,失败阈值设为 1~2 次(比如 Nginx 的
max_fails=1 fail_timeout=3s) - 健康检查目标必须是应用的就绪接口(如
/readyz),且该接口在收到 SIGTERM 后立即返回 503 - 避免长连接保活干扰:Nginx 的
keepalive_timeout应小于应用优雅停机超时(如设为 25s)
滚动更新顺序必须严格遵循“先摘流、再停机”
手动或脚本化更新时,不能直接 docker stop,而应按确定顺序操作:
- 调用 LB API 或修改 upstream 配置,将目标节点临时标记为 down(或从服务发现中注销)
- 等待 1~2 个健康检查周期(例如 3~6 秒),确认 /readyz 已返回 503 且 LB 不再转发新请求
- 此时再执行
docker stop -t 45 container_name,让应用完成清理并退出











