滚动更新卡在“Waiting for rollout to finish”的根本原因是Golang服务未正确配合Kubernetes生命周期:未监听SIGTERM、未用context.WithTimeout()调用srv.Shutdown()、readinessProbe未检查真实就绪状态(如DB连接)、maxSurge/maxUnavailable配置不当导致新旧Pod切换失序。

滚动更新卡在 Waiting for rollout to finish,基本就是 Golang 应用没配合好 Kubernetes 的生命周期控制——不是镜像推错了,也不是 YAML 写漏了字段,而是信号没接住、探针没配对、超时没对齐。
Go 程序必须监听 SIGTERM 并调用 srv.Shutdown()
Kubernetes 终止 Pod 时只发 SIGTERM,不会等你慢慢关。如果 Go 主程序没监听这个信号,进程立刻退出,所有 HTTP 连接被硬断开,客户端收到 RST 或超时。
-
http.Server.Close()是暴力关闭,会中断所有活跃连接;srv.Shutdown()才是标准做法:拒绝新请求、等待已有 handler 返回 - 必须传入带超时的
context.WithTimeout(),且超时时间要小于 Deployment 中的terminationGracePeriodSeconds(比如代码里设 30s,K8s 配 45s) - 别在 HTTP handler 里启动 goroutine 后立刻返回——
Shutdown()不等后台 goroutine,请求可能实际没完成就退出 - 记得显式关闭 DB 连接池、取消长轮询 context、释放文件句柄等资源,否则进程看似退出,后台泄漏还在
readinessProbe 必须基于真实服务状态,不能只靠端口连通
如果 readinessProbe 用 tcpSocket 或只检查端口是否监听,新 Pod 可能还没加载完配置、DB 连接池还没建好,就被加入 Service,立刻开始收流量,结果大量 5xx。
- 暴露一个真实健康检查端点,比如
/healthz,里面检查 DB ping、缓存连通性、关键依赖就绪状态 -
readinessProbe.httpGet.path必须指向这个端点,不要只写/ -
initialDelaySeconds要大于服务冷启动耗时(比如初始化 DB 连接池 + 加载配置要 8s,那就设 12s) -
periodSeconds设小一点(如 2–3s),让 K8s 尽快感知就绪,避免新 Pod 长时间挂起
maxSurge 和 maxUnavailable 必须显式配置,且值要对得上副本数
这两个参数不是“可选”,而是滚动更新能否不丢请求的决定性开关。默认值或模糊百分比在低副本场景下极易失效。
立即学习“go语言免费学习笔记(深入)”;
-
maxSurge: 0是无效配置,会导致新 Pod 根本起不来,rollout 卡死 - 副本数为 2 时,
maxSurge: 25%实际计算为 0(向下取整),可能触发“先删后启”,造成秒级不可用 - 强一致性或核心服务建议用
maxSurge: 1+maxUnavailable: 0:先启一个新 Pod,等它通过 readinessProbe 后再删旧 Pod - 集群资源紧张时可用
maxSurge: 25%+maxUnavailable: 25%,但必须搭配快速就绪探针(periodSeconds: 2,timeoutSeconds: 1)
更新后不能只看 kubectl rollout status 成功就认为完事
kubectl rollout status 返回成功,只表示 Deployment 的 spec 已提交、控制器已接收变更。真正滚动完成,要确认两个状态同时满足:
-
status.updatedReplicas == status.replicas:所有副本都已更新到新镜像 -
status.availableReplicas == status.replicas:所有副本都已通过 readinessProbe 并处于 Ready 状态 - 用
kubectl get deployment <name> -o wide</name>查AVAILABLE和UP-TO-DATE列,两者数值必须一致且等于REPLICAS - 如果卡在
UP-TO-DATE满但AVAILABLE不满,大概率是 readinessProbe 失败或超时,得查kubectl describe pod的 Events 和 Conditions
最常被忽略的一点:Golang 服务启动慢(比如加载大配置、预热缓存、建 DB 连接池),和 maxSurge 过大会形成负反馈——多个 Pod 同时启动,把下游 DB 的连接池打爆,导致 readinessProbe 全部失败,整个 rollout 僵住。这时候不是调大超时就能解决,得从启动路径优化入手。


















