零停机更新需 readinessProbe、livenessProbe、preStop 和滚动参数协同;漏任一环易致502。根本原因是新Pod被立即加入endpoints但Go服务未完成初始化,旧Pod因preStop与endpoint摘除异步而残留流量。

零停机更新在 Kubernetes 中不是默认行为,而是必须靠 readinessProbe、livenessProbe、preStop 和滚动更新参数共同配合才能达成。漏掉任意一环,502 或连接中断就很可能发生。
为什么 Go 服务上线后还会返回 502?
根本原因在于:Kubernetes 默认把新 Pod 加入 Service 的 endpoints 是“立即”的,但 Go 应用的 HTTP 服务器可能还没真正 bind 完成或完成初始化(比如 DB 连接池未建好)。没有 readinessProbe,Service 就会把流量发给一个“活着但没准备好”的 Pod。
-
readinessProbe必须配置,且initialDelaySeconds要大于 Go 启动耗时(通常 3–8 秒足够,但需实测) - 探测路径(如
/ready)应真实检查关键依赖:HTTP server 已监听、DB ping 通、Redis 连接可用 - 避免用
exec类型探针检查端口是否监听(Linux 的netstat可能返回假阳性) - 若使用
httpGet,确保 Go 服务在探测路径返回 200 且无 body 或极简 body,避免引入额外 GC 压力
滚动更新时旧 Pod 为什么还在收请求?
旧 Pod 终止流程中,preStop 和 endpoint 摘除是异步的。如果 preStop 执行太快(比如只 sleep 1 秒),而 endpoint controller 还没来得及从 Service 的 endpoints 列表里删掉它,请求就会打过去然后失败。
- 在 Deployment 的容器 spec 中加
lifecycle.preStop.exec.command,例如:["sh", "-c", "sleep 10"] - 更可靠的做法是在 Go 代码中监听
SIGTERM,收到后先关闭 HTTP server 的 listener,再等待活跃连接超时(如srv.Shutdown(ctx)),最后退出 -
preStop的 sleep 时间建议 ≥readinessProbe.periodSeconds+ 网络传播延迟(通常设为 10–15 秒较稳妥) - 别依赖
terminationGracePeriodSeconds替代preStop—— 它只控制 SIGKILL 发送时机,不参与 endpoint 摘除逻辑
maxSurge 和 maxUnavailable 怎么设才安全?
这两个参数决定滚动更新节奏,直接影响是否出现“全量不可用”或“资源过载”。对 Go 服务来说,它们不是越大越好,也不是越小越稳,要结合副本数和 QPS 综合判断。
- 默认值
maxSurge: 25%+maxUnavailable: 25%在 replicas=4 时意味着:最多启 5 个 Pod,最少保 3 个在线 —— 表面安全,但若新版本启动慢,旧 Pod 又被快速驱逐,仍可能短暂低于 3 个健康实例 - 保守设置(适合支付类 Go 服务):
maxSurge: 1,maxUnavailable: 0—— 每次只增 1 个新 Pod,旧 Pod 必须通过readinessProbe后才开始删下一个 - 激进设置(适合读多写少的内部 API):
maxSurge: 1,maxUnavailable: 1—— 允许短暂少一个副本,但要求readinessProbe响应极快( - 注意:
maxUnavailable: 0不代表永不不可用 —— 若新 Pod 卡在 readiness 失败,Deployment 会卡住,需要人工介入
真正的零停机不取决于某个 YAML 字段,而在于整个链路:Go 应用能否快速响应就绪探测、能否优雅处理 SIGTERM、Kubernetes 是否及时摘除 endpoint、以及你是否验证过每种异常路径(如 DB 临时不可达时 /ready 返回 503)。上线前用 kubectl get endpoints -w 配合压测工具观察 endpoint 变更时机,比任何文档都管用。


















