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