文章目录
- Deployment 滚动更新机制详解(企业级面试与运维实战)
- 一、Deployment 如何管理 Pod?(三层级联关系)
- 二、滚动更新完整流程(以 3 副本为例)
- 第一阶段:创建新 RS
- 第二阶段:逐步滚动(以默认策略 maxSurge=25%, maxUnavailable=25% 为例)
- 第三阶段:完成
- 三、滚动更新核心参数(面试必考)
- 1. maxUnavailable(最大不可用数)
- 2. maxSurge(最大峰值数)
- 3. 两个参数如何配合?
- 四、滚动更新的状态与控制(运维核心技能)
- 1. 查看更新进度
- 2. 查看历史版本(每个版本对应一个 RS)
- 3. 暂停与恢复(金丝雀发布的核心手法)
- 4. 手动控制更新比例(精细灰度)
- 5. 回滚
- 五、滚动更新卡住的排查(生产环境最高频问题)
- 现象
- 排查思路(四步定位)
- 六、滚动更新故障与应对(运维实战)
- 场景 1:新版本有严重 Bug,需要立即止损
- 场景 2:读不到新版本日志,想先全部切过去再排查
- 场景 3:更新过程中想终止并保持现状
- 场景 4:Deployment 更新超时自动标记失败
- 七、面试高频追问(加分要点)
- 1. 滚动更新期间,服务真的不中断吗?
- 2. 为什么回滚能"一键完成"?
- 3. 如何控制回滚历史的保留数量?
- 4. 滚动更新与 Recreate 策略的区别?
- 5. 滚动更新对 etcd 和 apiserver 的压力?
- 八、一个完整的生产级滚动更新示例
Deployment 滚动更新机制详解(企业级面试与运维实战)
Deployment 的滚动更新是 Kubernetes 生产环境中最核心的发布机制。它解决的问题是:更新应用时,如何做到服务不中断、流量不丢失、故障可回滚。下面我从原理到实操、从面试到排障,完整拆解。
一、Deployment 如何管理 Pod?(三层级联关系)
在深入滚动更新之前,先理解 Deployment 的层级结构——这是整个滚动更新的地基:
Deployment(用户操作入口) │ 管理 ▼ ReplicaSet(版本快照,每个版本对应一个 RS) │ 管理 ▼ Pod(实际运行容器)核心结论:
- Deployment不直接管理 Pod,它只管理 ReplicaSet
- 每次修改 Deployment 的 Pod 模板(镜像、环境变量等),都会创建一个新的 ReplicaSet
- 旧版本对应旧 RS,新版本对应新 RS,滚动更新就是在新旧两个 RS 之间"搬"副本数
关键标志:kubectl get deploy输出的DESIRED是 Pod 总数,而每个 RS 的DESIRED是各自的副本数,两者之和恒等于 Deployment 的期望副本数(更新过程中)。
二、滚动更新完整流程(以 3 副本为例)
假设当前 Deployment 有 3 个副本,镜像版本为nginx:1.19,现在要更新到nginx:1.21:
初始状态: Deployment (replicas=3) ├── RS-v1 (nginx:1.19, replicas=3) ← 当前版本 └── RS-v2 (nginx:1.21, replicas=0) ← 尚未创建 执行 kubectl set image deploy/nginx nginx=nginx:1.21第一阶段:创建新 RS
Deployment (replicas=3) ├── RS-v1 (nginx:1.19, replicas=0 → 3, maxUnavailable=1) └── RS-v2 (nginx:1.21, replicas=0 → 1) ← 新 RS 创建,先扩容 1 个- 创建 RS-v2,
replicas=0(初始) - 根据
maxUnavailable和maxSurge计算可扩缩容的幅度(见下文参数详解) - RS-v2 先扩容到 1(因为
maxSurge=1,允许超出期望 1 个)
第二阶段:逐步滚动(以默认策略 maxSurge=25%, maxUnavailable=25% 为例)
第 1 轮:RS-v2: 1, RS-v1: 2(总 3 个,均在服务中) 第 2 轮:RS-v2: 2, RS-v1: 1 第 3 轮:RS-v2: 3, RS-v1: 0每轮动作:
- 新 RS 扩容(+1):创建新版本 Pod,等待其就绪(Readiness Probe 通过)
- 旧 RS 缩容(-1):等新 Pod 就绪后,才删除一个旧版本 Pod
- 循环往复,直到所有副本都切到新版本
第三阶段:完成
Deployment (replicas=3) ├── RS-v1 (nginx:1.19, replicas=0) ← 保留但缩容为 0(用于回滚) └── RS-v2 (nginx:1.21, replicas=3) ← 当前版本关键点:
- 旧 RS不会删除,只是缩容到 0。这是为了支持一键回滚
- 新 Pod 必须通过Readiness Probe才会被视为"就绪"
- 如果新 Pod 一直不就绪,滚动更新会卡住(不会继续缩容旧 RS)
三、滚动更新核心参数(面试必考)
滚动更新的节奏由strategy.rollingUpdate下的两个参数控制:
spec:strategy:type:RollingUpdaterollingUpdate:maxUnavailable:25%# 更新过程中允许最多 25% 的副本不可用maxSurge:25%# 更新过程中允许超出期望副本数最多 25%1. maxUnavailable(最大不可用数)
- 含义:滚动更新过程中,允许同时"不可用"(正在被替换)的 Pod 最大数量
- 作用:保证服务有足够的可用副本,避免全部 Pod 同时被替换导致服务中断
- 默认 25%(向上取整):3 副本 → 允许 1 个不可用(3 × 25% = 0.75 → 向上取整 = 1)
2. maxSurge(最大峰值数)
- 含义:滚动更新过程中,允许超出期望副本数的最大 Pod 数量
- 作用:决定新版本 Pod 可以"提前"创建多少个,加快更新速度
- 默认 25%(向上取整):3 副本 → 允许超出 1 个(3 × 25% = 0.75 → 向上取整 = 1)
3. 两个参数如何配合?
滚动更新的节奏可以理解为:
每轮可缩容旧 Pod 数 = maxUnavailable 每轮可扩容新 Pod 数 = maxSurge极端场景举例:
| 配置 | 效果 |
|---|---|
maxUnavailable: 1, maxSurge: 1 | 一次只更新 1 个 Pod,更新速度慢但最安全 |
maxUnavailable: 100%, maxSurge: 0% | 先缩容所有旧 Pod(全部不可用),再创建新 Pod →等价于 Recreate 策略(先删后建) |
maxUnavailable: 0%, maxSurge: 100% | 先创建全部新 Pod(峰值翻倍,消耗双倍资源),全部就绪后再删旧 Pod → 零停机但资源占用高(蓝绿发布思想) |
四、滚动更新的状态与控制(运维核心技能)
1. 查看更新进度
# 查看更新状态(会阻塞直到完成或超时)kubectl rollout status deploy/nginx# 输出示例:# Waiting for deployment "nginx" rollout to finish: 1 out of 3 new replicas have been updated...# deployment "nginx" successfully rolled out2. 查看历史版本(每个版本对应一个 RS)
kubectl rollouthistorydeploy/nginx# 输出示例:# REVISION CHANGE-CAUSE# 1 <none> # 可通过 --record 记录变更原因# 2 kubectl set image deploy/nginx nginx=nginx:1.21注意:CHANGE-CAUSE来自 Deployment 的metadata.annotations.kubernetes.io/change-cause。K8s v1.26+ 已移除--record参数,需手动添加 annotation:
kubectl annotate deploy/nginx kubernetes.io/change-cause="升级nginx到1.21"3. 暂停与恢复(金丝雀发布的核心手法)
暂停:更新过程中暂停,让新旧版本并存一段时间,验证新版本稳定性后再继续:
# 触发更新后立即暂停kubectlsetimage deploy/nginxnginx=nginx:1.21 kubectl rollout pause deploy/nginx# 此时:RS-v2 创建了 1 个新 Pod 并停止,其余 2 个还在旧版本# 相当于手动控制的金丝雀发布恢复:
kubectl rollout resume deploy/nginx# 继续完成剩余的滚动更新使用场景:先更新 1 个 Pod 验证日志/监控无异常,再恢复继续全量更新。
4. 手动控制更新比例(精细灰度)
通过直接修改maxSurge和maxUnavailable,可以控制每次更新的 Pod 数量:
# 每次只更新 1 个 Podkubectl patch deploy/nginx-p'{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'# 或kubectl edit deploy/nginx# 修改后保存,会自动触发一次滚动更新5. 回滚
# 回滚到上一个版本kubectl rollout undo deploy/nginx# 回滚到指定版本kubectl rollout undo deploy/nginx --to-revision=1# 查看回滚进度kubectl rollout status deploy/nginx回滚原理:让旧 RS 重新扩容、新 RS 缩容,和正向更新的过程完全对称。
五、滚动更新卡住的排查(生产环境最高频问题)
现象
kubectl rollout status长时间停留在:
Waiting for deployment "nginx" rollout to finish: 1 out of 3 new replicas have been updated...排查思路(四步定位)
Step 1:查看 Deployment 状态与事件
kubectl describe deploy/nginx重点看Conditions字段中的Progressing和Available:
Conditions:Type Status Reason----------------Available True MinimumReplicasAvailable Progressing False ProgressDeadlineExceeded# ← 更新超时失败Step 2:查看新旧 RS 的副本分布与状态
kubectl get rs-lapp=nginx# NAME DESIRED CURRENT READY AGE# nginx-abc 3 3 3 1h # 旧 RS# nginx-def 2 2 0 5m # 新 RS,READY=0 → 问题在这里Step 3:查看新 RS 的 Pod 状态与事件
kubectl get pods-lapp=nginx|grepnginx-def kubectl describe pod/nginx-def-xxxxxStep 4:定位根因
| 现象 | 根因 |
|---|---|
Pod 一直ContainerCreating | 镜像拉取失败(私有仓库认证)、存储挂载失败(PVC 未绑) |
PodRunning但READY 0/1 | Readiness Probe 失败(最常见)—— 端口探测不通、健康检查路径返回非 200 |
PodCrashLoopBackOff | 新版本启动即崩溃(环境变量缺失、配置错误、依赖服务未就绪) |
| 新 Pod 一直 Pending | 资源不足、节点亲和性不满足 |
最典型场景:新版本镜像有 bug,启动后 Readiness Probe 一直失败 → 新 Pod 永远不就绪 → 滚动更新永远不会继续缩容旧 RS → 服务保持旧版本可用(这是滚动更新的设计优点:宁可卡住也不中断)。
六、滚动更新故障与应对(运维实战)
场景 1:新版本有严重 Bug,需要立即止损
# 紧急回滚(最快速)kubectl rollout undo deploy/nginx# 或直接改回旧镜像kubectlsetimage deploy/nginxnginx=nginx:1.19场景 2:读不到新版本日志,想先全部切过去再排查
# 将 maxUnavailable 设为 100%(等价于先删后建)kubectl patch deploy/nginx--type='json'\-p='[{"op": "replace", "path": "/spec/strategy/rollingUpdate/maxUnavailable", "value": "100%"}]'# 此时会立即缩容所有旧 Pod,全部切换到新版本# 注意:这会短暂中断服务(等价于 Recreate 策略)场景 3:更新过程中想终止并保持现状
# 暂停滚动更新kubectl rollout pause deploy/nginx# 此时新旧版本并存,保持当前状态不再变化# 适合"先停下来观察一下新版本表现"的场景场景 4:Deployment 更新超时自动标记失败
默认progressDeadlineSeconds=600(10分钟),超过后 Deployment 被标记为Progressing=False,但不会自动回滚,只是停止更新。需要手动介入:
# 查看失败原因kubectl describe deploy/nginx|grep-A5Conditions# 手动回滚kubectl rollout undo deploy/nginx七、面试高频追问(加分要点)
1. 滚动更新期间,服务真的不中断吗?
不完全是。关键取决于:
- Readiness Probe 是否配置正确——新 Pod 就绪后才加入 Service 的 Endpoints,流量才会切过去
- Pod 终止时是否优雅——旧 Pod 收到 SIGTERM 后是否能在宽限期内完成"排空"(处理完当前请求再退出)
面试表述:“滚动更新不中断服务的前提是配置了正确的 Readiness Probe 和 preStop Hook。如果没有 Readiness Probe,新 Pod 刚启动就会被加入 Endpoints,此时容器可能还没真正就绪,会短暂返回 5xx。”
2. 为什么回滚能"一键完成"?
因为每次更新都会留下一个对应的 RS(保留完整的历史 Pod 模板)。回滚只是把目标 RS 重新扩容、当前 RS 缩容——本质也是一次滚动更新,只是方向相反。
3. 如何控制回滚历史的保留数量?
通过spec.revisionHistoryLimit(默认 10)。超过数量的旧 RS 会被 GC 清理,但注意:缩容为 0 的 RS 不会被删除(保留用于回滚),只有超过revisionHistoryLimit的 RS 才会被彻底删除。
4. 滚动更新与 Recreate 策略的区别?
| RollingUpdate(默认) | Recreate | |
|---|---|---|
| 服务中断 | 通常无(前提是配置正确) | 有(先删后建) |
| 资源消耗 | 可能瞬时超出期望副本数(maxSurge) | 无额外消耗 |
| 适用场景 | 无状态服务、微服务 | 有状态服务(数据库)等无法并存的场景 |
| 更新速度 | 较慢(受 maxSurge/maxUnavailable 控制) | 快 |
5. 滚动更新对 etcd 和 apiserver 的压力?
每次滚动更新会触发大量 Pod 创建/删除事件,这些事件会写入 etcd 并广播给所有控制器。大规模集群(数百节点、数千 Pod)同时滚动更新时,需要控制更新速率,否则可能压垮 apiserver。
优化手段:
- 分批更新(先更新部分节点或部分 Deployment)
- 使用
maxSurge: 1, maxUnavailable: 0保守配置 - 结合PDB(PodDisruptionBudget)保护关键服务的副本可用性
八、一个完整的生产级滚动更新示例
apiVersion:apps/v1kind:Deploymentmetadata:name:nginxannotations:kubernetes.io/change-cause:"升级nginx到1.21,修复安全漏洞"spec:replicas:5revisionHistoryLimit:5# 保留最近 5 个历史版本strategy:type:RollingUpdaterollingUpdate:maxUnavailable:1# 最多 1 个副本不可用(保守)maxSurge:2# 最多超出 2 个副本(加快速度)selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:terminationGracePeriodSeconds:30# 优雅终止等待时间containers:-name:nginximage:nginx:1.21ports:-containerPort:80readinessProbe:# 就绪探针(关键!)httpGet:path:/healthzport:80initialDelaySeconds:5periodSeconds:5lifecycle:preStop:# 优雅下线:先摘流量再停止exec:command:["/bin/sh","-c","sleep 5"]执行更新:
# 1. 触发更新kubectl apply-fdeployment.yaml# 2. 监控进度kubectl rollout status deploy/nginx# 3. 确认完成后查看版本kubectl rollouthistorydeploy/nginx# 4. 如果出问题,回滚kubectl rollout undo deploy/nginxpreStop Hook 的作用:当 Pod 被终止时,先执行sleep 5,给 kube-proxy 时间从 Endpoints 中移除该 Pod 的 IP,确保不再有新流量进来,然后才收到 SIGTERM 退出。这是避免"正在处理的请求被中断"的关键。