3. K8s 滚动更新原理:maxSurge、maxUnavailable、minReadySeconds、progressDeadlineSeconds 详解

Kubernetes 的滚动更新(Rolling Update)是 Deployment 控制器默认的更新策略。其核心目标是在不中断服务的前提下,逐步用新版本 Pod 替换旧版本 Pod。为了精确控制这一过程的速率、可用性边界与超时行为,K8s 提供了四个关键参数:maxSurge、maxUnavailable、minReadySeconds 和 progressDeadlineSeconds。理解这四个参数是设计可靠发布流水线的基础。

3.1 核心参数速览

参数 类型 默认值 作用域 核心作用
maxSurge 整数或百分比 25% 滚动更新期间 允许超出期望副本数的最大 Pod 数量
maxUnavailable 整数或百分比 25% 滚动更新期间 允许不可用 Pod 的最大数量
minReadySeconds 整数(秒) 0 Pod 就绪检测 Pod 进入 Ready 状态后,等待多少秒才视为可用
progressDeadlineSeconds 整数(秒) 600 更新进度监控 滚动更新未完成时的最大容忍时间,超时则标记为失败

3.2 滚动更新流程与参数协同

下图展示了当 replicas=10、maxSurge=25%、maxUnavailable=25% 时,滚动更新的典型执行过程。控制器通过“先扩容再缩容”或“先缩容再扩容”的组合动作,确保整个更新期间集群内总 Pod 数量与可用 Pod 数量始终在安全边界内。

关键公式:
更新期间最大 Pod 总数 = desiredReplicas + ceil(desiredReplicas × maxSurge)
更新期间最小可用 Pod 数 = desiredReplicas - ceil(desiredReplicas × maxUnavailable)
滚动更新参数协同流程图 滚动更新参数协同流程图 (replicas=10, maxSurge=25%, maxUnavailable=25%) 初始状态 旧版本 Pod × 10 步骤1: 扩容新Pod 新版本 Pod +3 (maxSurge) 步骤2: 缩容旧Pod 旧版本 Pod -3 (maxUnavailable) 最终状态 循环执行步骤1→步骤2,直到所有旧Pod被替换 每次迭代:新Pod就绪后,再缩容旧Pod 参数约束边界 maxSurge = 25% 最大Pod总数 ≤ 10 + 3 = 13 maxUnavailable = 25% 最小可用Pod数 ≥ 10 - 3 = 7 progressDeadlineSeconds = 600s 整个更新过程必须在600秒内完成,否则Deployment标记为Progressing=False

3.3 参数详解与配置示例

3.3.1 maxSurge

定义:滚动更新期间,允许超出期望副本数(replicas)的最大 Pod 数量。可以是整数(如 2)或百分比(如 25%),百分比会向上取整。

  • 作用:控制更新速度的上限。值越大,允许同时创建的新 Pod 越多,更新越快,但瞬时资源消耗也越高。
  • 典型场景:
    • 快速发布:设置较大的值(如 50% 或 100%),适合低负载或资源充裕的环境。
    • 保守发布:设置较小的值(如 10% 或 1),适合对资源敏感或需要精细控制流量的场景。

3.3.2 maxUnavailable

定义:滚动更新期间,允许处于不可用状态的 Pod 的最大数量。同样支持整数或百分比,百分比向下取整。

  • 作用:保证服务可用性的底线。值越小,对服务可用性的保护越强,但更新速度会变慢。
  • 典型场景:
    • 高可用要求:设置 0 或 1,确保任何时候最多只有极少数 Pod 不可用。
    • 批量更新:设置较大的值(如 50%),允许一次性下线较多旧 Pod,适合非关键服务。
最佳实践:在生产环境中,建议将 maxUnavailable 设置为 0 或 1,配合 maxSurge 控制扩容速度,实现“先创建新 Pod,再销毁旧 Pod”的零停机更新模式。

3.3.3 minReadySeconds

定义:Pod 进入 Ready 状态后,需要保持该状态至少 minReadySeconds 秒,才会被控制器视为“可用”。

  • 作用:防止 Pod 在启动初期因短暂抖动(如 JVM 预热、缓存加载)被误判为就绪,从而避免滚动更新过快推进导致问题。
  • 典型场景:
    • Java 应用:设置 30-60 秒,等待 JVM 完成类加载和 JIT 编译。
    • 缓存密集型服务:设置 10-30 秒,确保缓存预热完成。

3.3.4 progressDeadlineSeconds

定义:滚动更新开始后,如果 Deployment 在指定时间内未能完成更新(即 ReplicaSet 的期望副本数未达到),则控制器会将 Deployment 的 Progressing 条件标记为 False,并停止自动推进。

  • 作用:作为更新超时兜底机制,避免因镜像拉取失败、Pod 崩溃循环、就绪检查卡死等问题导致更新无限期挂起。
  • 典型场景:
    • 默认值 600 秒(10 分钟)适用于大多数场景。
    • 对于大型集群或镜像拉取较慢的环境,可适当增大(如 900 或 1200)。
    • 对于快速迭代的 CI/CD 流水线,可设置较小的值(如 300)以快速失败并触发回滚。

3.4 完整 Deployment 配置示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%        # 允许最多超出 3 个 Pod
      maxUnavailable: 0    # 不允许任何 Pod 不可用(零停机)
  minReadySeconds: 30      # Pod 就绪后等待 30 秒才视为可用
  progressDeadlineSeconds: 600  # 10 分钟超时
  template:
    spec:
      containers:
      - name: app
        image: my-app:v2
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

3.5 参数组合策略对比

策略名称 maxSurge maxUnavailable minReadySeconds 适用场景
零停机快速更新 25% 0 10 生产环境,要求服务不降级,可接受短暂资源峰值
保守慢速更新 1 0 60 关键业务,需要逐个验证新版本稳定性
快速批量替换 50% 25% 5 非生产环境或低优先级服务,追求速度
资源受限环境 10% 10% 30 集群资源紧张,需要严格控制 Pod 总数

3.6 常见陷阱与避坑指南

⚠️ 陷阱1:maxUnavailable 与 PDB 冲突
如果同时设置了 PodDisruptionBudget(PDB),滚动更新必须同时满足 PDB 和 maxUnavailable 的约束。例如,PDB 要求最少可用 Pod 为 8,而 maxUnavailable 允许不可用 Pod 为 3,则实际不可用 Pod 数不能超过 min(3, replicas - PDB.minAvailable)。这可能导致更新速度比预期更慢。
⚠️ 陷阱2:minReadySeconds 设置过小
如果 minReadySeconds 为 0,Pod 一旦通过 readinessProbe 就立即被视为可用。对于启动较慢的应用(如需要加载大量数据或建立连接池),这可能导致滚动更新在 Pod 尚未完全准备好时就继续推进,引发流量异常。
✅ 推荐做法:
  1. 始终为生产环境设置 maxUnavailable: 0,配合 maxSurge 控制速度。
  2. 根据应用启动时间合理设置 minReadySeconds,建议至少为 readinessProbe 初始延迟的 2-3 倍。
  3. 将 progressDeadlineSeconds 设置为 CI/CD 流水线超时时间的 80%,以便在流水线层面提前感知更新失败。
  4. 在测试环境中使用与生产环境相同的参数组合进行验证,避免参数差异导致意外行为。