docker stop 实现无损滚动更新需应用、容器与LB协同:先摘流再停机。核心是应用响应SIGTERM时关闭监听、等待请求完成并切换/readyz状态为503,LB通过短间隔健康检查(≤3秒)快速剔除节点,最后执行docker stop -t 30确保优雅终止。
使用
配合负载均衡实现无损滚动更新,核心在于让容器在真正停止前,先从流量池中优雅摘除,避免新请求被路由到即将关闭的实例。这需要应用层、容器运行时和反向代理三者协同,而非仅靠
自身。
理解 docker stop 的默认行为
默认发送
信号给容器主进程,等待 10 秒(可设
)后若未退出则发
强制终止。但该操作本身不通知上游负载均衡器——如果 LB 仍把请求转发过来,就会导致 502/连接拒绝等错误。
要实现“无损”,必须在发
前或同时,让 LB 主动剔除该节点。常见做法有:
LB 支持健康检查(如 Nginx 的
、Traefik 的 readiness probe),应用需提供
或类似端点,并在收到终止信号后主动返回非健康状态
通过 API 动态下线节点(如 Consul、Nacos 注册中心 + LB 插件)
使用支持服务发现与生命周期钩子的编排工具(如 Docker Swarm 的
+
,或 Kubernetes 的 preStop hook)
关键:应用需支持优雅退出与就绪探针
容器内应用必须能响应
并执行以下动作:
停止接收新连接(如关闭 HTTP server 的监听 socket)
等待正在处理的请求完成(设置合理的超时,如 30 秒)
同步更新健康检查接口返回值(例如将
从
变为
,触发 LB 快速摘流)
示例(Node.js):
在收到后,立即调用,并在回调中退出;同时让路由在关闭过程中返回。
负载均衡侧需配合快速感知与摘流
LB 的健康检查间隔和失败阈值直接影响“无损”效果。建议配置:
健康检查间隔 ≤ 3 秒,连续失败次数设为 1~2 次
避免使用长连接保活导致旧连接持续转发(如 Nginx 的
应短于应用优雅停机超时)
若用 DNS 负载均衡(如 CoreDNS + SRV),需确保客户端支持 TTL 刷新并及时重解析
以 Nginx 为例,可结合 upstream 的
和应用提供的
接口做被动健康检查。
滚动更新流程示意(手动或脚本化)
不依赖 Swarm/K8s 时,可按如下顺序控制单节点更新:
调用 LB API 或修改配置,将目标容器 IP/端口从 upstream 中临时移除(或标记为 down)
等待 1~2 个健康检查周期,确认 LB 不再转发新请求
执行
,给应用留出处理存量请求的时间
启动新容器,待其
返回 200 后,再将其加回 LB upstream
整个过程需幂等、可重试,建议封装为带状态检查的 shell 或 Python 脚本,避免人工误操作。
docker stopdocker stopdocker stopSIGTERM--timeSIGKILLSIGTERMhealth_check/healthzupdate-parallelismupdate-delaySIGTERM/readyz200503process.on('SIGTERM', ...)server.close()/readyz503keepalive_timeoutmax_fails=1 fail_timeout=3s/readyzdocker stop -t 30 container_name/readyz