返回首页

K8s 滚动更新无损发布误区 — RollingUpdate 真相与真正无感发布体系

📅 创建于 2026-05-12 🔄 更新于 2026-05-12 📝 565 字

K8s 滚动更新无损发布误区

RollingUpdate 解决的是"Pod 替换问题",从来不是"业务无感知问题"。 K8s 只保证"容器层可用"——Pod 启动、健康探针返回正常,它就认为没问题。 业务真正关心的"用户无感知",需要一整套流量治理体系来支撑。

一、RollingUpdate 的本质

Kubernetes 默认发布策略:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 25%
    maxSurge: 25%

核心逻辑只有两步,循环执行直到替换完成:

  1. 创建一部分新 Pod(maxSurge 控制最多多创建多少)
  2. 删除一部分旧 Pod(maxUnavailable 控制最多能宕机多少)

它能保证的 3 个"基础底线":

  • 不会一次性把所有 Pod 都删掉
  • 集群中始终有 Pod 存活
  • 最终 Deployment 收敛到新版本

但它不保证的"业务层面问题":

  • ❌ 用户请求不丢失、不报错
  • ❌ 流量能均匀切换到新旧 Pod
  • ❌ 长连接、Session 不中断、不丢失
  • ❌ 新旧版本接口、数据兼容
  • ❌ 数据一致性,不会出现污染、错乱

二、最危险的时刻:新旧版本共存期

滚动更新真正的风险不是"升级完成后",而是"新旧版本同时在线"的那段时间

2.1 数据库兼容翻车(最经典、最隐蔽)

v2 版本新增数据库字段后,v2 Pod 写入带新字段的数据,但 v1 Pod 不认识:

  • JSON 反序列化失败,接口报错
  • ORM 框架抛异常,无法处理新字段
  • v1 Pod 处理数据时覆盖 v2 写入的字段
  • 数据回写污染,旧逻辑破坏新数据

最可怕的是: 系统没挂、Pod 正常运行,但数据已悄悄坏了。等发现时回滚都救不回来。

2.2 接口协议不兼容(半故障状态)

新旧版本共存时,返回格式不同(如 {"code":0} vs {"success":true}),导致:

  • 网关缓存、微服务调用、MQ 消费、RPC 接口全乱套
  • "部分请求成功、部分失败",用户反复刷新才能访问
  • 日志无明显报错,Pod 全是 Ready,排查极其困难

2.3 Session / 缓存不兼容

更新涉及缓存、登录态相关改动时:

  • Redis Key 格式变化、JWT 结构调整
  • Token 加密方式改变
  • 部分用户频繁掉线,需要反复登录
  • 部分请求报 401 权限错误,部分正常

三、Ready ≠ 真正可用

很多团队"自欺欺人"的 readinessProbe:

readinessProbe:
  httpGet:
    path: /healthz    # 只检查 HTTP 200

应用启动后往往还需要一段时间才能真正"准备好":

问题 说明
JVM 预热 刚启动时响应极慢,需要预热过程
本地缓存/配置 还没加载完成
依赖连接 MQ、Elasticsearch、数据库等还没建立
数据预热 查询直接超时

结果: Pod 显示 Ready,流量瞬间打进来,应用直接超时、报错。

正确做法: 使用真正能反映业务就绪状态的探针。

探针类型 检查内容 建议
基础存活 进程存活 /healthz 或 TCP 端口
业务就绪 依赖服务可用 + 缓存加载完成 + 数据预热完毕 自定义端点,聚合所有依赖状态

详见 k8s-probes-guide(探针完整配置指南)以及 pod-troubleshooting(三探针配合建议)。


四、最隐蔽的坑:"假灰度"陷阱

很多人以为滚动更新时流量均匀分配——这只是理想状态。

在以下场景中,流量分配严重不均:

场景 问题
HTTP KeepAlive 部分旧 Pod 持续占用老连接,新 Pod 几乎没流量
HTTP/2 / gRPC / WebSocket 长连接场景流量更难重新分配

后果: 你以为完成了"灰度验证"新版本没问题,但实际上新版本根本没被充分测试。直到最后一个旧 Pod 被删除,全部流量瞬间打到新版本——如果新版本有 bug,系统直接炸掉。


五、更安全的发布策略

5.1 蓝绿发布(Blue-Green Deployment)

Blue(旧版本)→ 验证 Green(新版本)→ 一次性切流
维度 说明
原理 两套完全独立的环境,验证无误后一次性切流
回滚 极其迅速,直接切回 Blue
资源成本 高(需两套环境)
数据同步 复杂
适合场景 核心业务、金融系统、支付系统、高 SLA 要求

5.2 金丝雀发布(Canary)

真正的灰度发布——按流量比例逐步放量,而非按 Pod 数量:

1% → 5% → 10% → 50% → 100%

更精细的放量策略:

  • 先给内部员工放量 → 验证无问题再对外
  • 按地域放量(北京先更、上海后更)
  • 按用户等级放量(普通用户先更、VIP 最后更)

支撑工具: Istio / Linkerd(Service Mesh)、Argo Rollouts、Flagger、Nginx Ingress Canary


六、真正的"无感发布"体系

6.1 优雅终止(Graceful Shutdown)

不配置优雅终止的后果:Pod 删除瞬间 TCP 连接被强断,请求失败、长连接中断。

apiVersion: v1
kind: Pod
spec:
  terminationGracePeriodSeconds: 60
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command:
          - /bin/sh
          - -c
          - sleep 30    # 等待 LB 摘除流量

原理: Pod 进入 Terminating 状态 → preStop 执行 → 等待流量排空 → SIGTERM 发向主进程 → 进程优雅退出。

6.2 连接排空(Connection Draining)

Ingress、SLB、Service Mesh(如 Istio)必须配置连接排空,等待旧 Pod 上的连接自然结束后再删除 Pod。否则旧 Pod 已退出但 LB 还在转发流量 → 502 报错。

K8s Service 层面:Readiness Gate + terminationGracePeriodSeconds 配合。

6.3 真正的 Readiness 探针

不只是 /healthz 200 OK,要让探针真正检查业务就绪状态:

  • 缓存是否加载完成
  • 数据库、MQ 等依赖是否就绪
  • 核心配置是否同步完成

6.4 可观测性驱动发布

不靠 kubectl rollout status,而靠实时监控数据判断:

  • 接口错误率、RT、P99 延迟
  • CPU、内存、GC 等资源指标
  • 用户投诉、核心接口 SLA

一旦出现异常 → 自动回滚。这才是"可控发布"。


七、总结

RollingUpdate 不是最安全的发布策略,它只是 K8s 提供的最低保底方案。

真正成熟的生产发布,是一套组合拳:

RollingUpdate(基础能力)
+ 蓝绿/金丝雀(可控灰度)
+ 优雅终止 + 连接排空(流量治理)
+ 业务级 Readiness 探针(真实就绪)
+ 可观测性 + 自动回滚(安全网)

发布的核心不是"Pod 替换成功",而是"用户无感知、业务不中断"。


关联页面

页面 关联点
k8s-probes-guide 探针基础配置(readinessProbe 在滚动更新中的应用)
pod-troubleshooting lifecycle hooks / preStop / 三探针配合建议
k8s-service-access-troubleshooting 连接排空与流量治理
k8s-statefulset-guide StatefulSet 有序滚动更新策略对比
k8s-resource-limits-configuration 资源限制与 JVM 预热配合
service-troubleshooting Service Endpoints 与滚动更新的关系
k8s-cicd-architecture-guide K8s CI/CD 全链路(Argo CD / Helm),滚动更新在 CI/CD 中的应用
k8s-production-incident-case-studies K8s 生产 10 大故障复盘(PDB/滚动更新/ConfigMap/HPA 等实战案例)
k8s-multicluster-istio-canary K8s 多集群 + Istio 灰度发布与流量治理生产指南 — 全球多活架构、五层治理设计、Cana
k8s-load-balancing-deep-practice K8s 负载均衡深度实践(连接排空/流量治理/无损发布场景互补)