目标
默认滚动更新允许四分之一的 Pod 不可用,这会把每次部署变成一次短暂容量削减。把 maxUnavailable 设为零、改用 surge,意味着替补 Pod 先跑起来并通过就绪检查,前任才会退出。再加上覆盖端点传播延迟的 preStop 停顿,整个更新在数学上就不可能拒绝请求。
步骤要点
- 先测基线:流量持续时用默认策略 kubectl apply,统计更新窗口内的连接错误数。
- 把策略改为 maxSurge: 1 / maxUnavailable: 0,新 Pod 先启动、容量从不清零。
- 就绪探针指向真实的 /readyz(校验数据库、缓存、迁移),探针才是真正的流量开关。
- 加 preStop sleep 5,覆盖“API 已标记终止但节点代理仍在路由”的传播窗口。
- kubectl set image 触发真实更新并重跑压测,全程非 200 响应应为零。
排错提示
设了 maxUnavailable 0 仍报错,是应用就绪比探针延迟慢,调大 initialDelaySeconds 或让 /readyz 反映真实预热完成;surge Pod 调度不上是集群没余量,要预留 requests 预算或开节点自动扩缩;长请求仍被切断说明 5 秒只够路由收敛不够请求完成,把 terminationGracePeriodSeconds 提到 p99 之上并在应用内自行排水。
适用边界
零停机的前提是就绪探针反映真实可服务性,恒真的探针让整套方案失效;surge 需要集群余量,跑满的集群无法安全滚动。
代码示例
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
containers:
- name: app
readinessProbe:
httpGet: { path: /readyz, port: 3000 }
lifecycle:
preStop:
exec: { command: ["sleep", "5"] }