深色模式
Deployment 滚动更新与回滚
摘要:本文面向负责生产发布的 SRE,讲解 Deployment 如何通过 ReplicaSet 实现声明式滚动更新,调优
maxSurge/maxUnavailable,以及安全回滚与灰度。覆盖 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+
- 工具:
kubectl - 前提:理解 ReplicaSet 与 Pod 模板概念
背景与问题
Deployment 是管理无状态应用最常用的工作负载。它的核心能力是:把“期望状态”(Pod 模板)与“实际状态”逐步对齐,且这个过程可控、可暂停、可回滚。真正难的不是“发布”,而是“出问题能秒级回退”以及“发布期间不丢流量、不超卖资源”。
核心机制:Deployment → ReplicaSet → Pod
Deployment 不直接管理 Pod,而是创建一个 ReplicaSet(RS),由 RS 维持副本数。每次修改 .spec.template,Deployment 会新建一个 RS(新哈希),逐步把旧 RS 缩容、新 RS 扩容,从而完成滚动更新。每个 RS 带有 pod-template-hash 标签,保证不重叠。
注意
不要手动管理 Deployment 创建的 ReplicaSet,也不要修改 pod-template-hash 标签,否则会导致选择器冲突与不可预期行为。多个控制器(Deployment 之间、与 StatefulSet)的 selector/labels 也不能重叠。
滚动更新策略
默认 strategy.type: RollingUpdate,由两个关键参数控制:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 4
selector:
matchLabels:
app: nginx
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新期最多超出期望副本数(绝对数或百分比)
maxUnavailable: 0 # 更新期最多不可用副本数
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
maxSurge:允许临时多出的 Pod 数。设 0 会拉长发布时间但最省资源。maxUnavailable:允许临时不可用的 Pod 数。设 0 保证容量不降,但需maxSurge>0。
建议
追求“零停机发布”用 maxSurge=1, maxUnavailable=0(容量始终 ≥ replicas,但集群需有余量);追求“省资源”用 maxSurge=0, maxUnavailable=1(发布期容量临时 -1)。生产推荐前者并预留节点余量。
Recreate 策略会先杀掉所有旧 Pod 再起新的,会造成停机,仅用于不支持多版本共存的场景。
发布操作与回滚
bash
# 更新镜像,触发滚动更新
kubectl set image deployment/nginx nginx=nginx:1.28
# 观察滚动进度
kubectl rollout status deployment/nginx
# 查看发布历史
kubectl rollout history deployment/nginx
# 回滚到上一版本
kubectl rollout undo deployment/nginx
# 回滚到指定版本
kubectl rollout undo deployment/nginx --to-revision=2
# 暂停/恢复,便于分批修改后一次性发布
kubectl rollout pause deployment/nginx
kubectl rollout resume deployment/nginx1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
生产危险
kubectl rollout undo 是生产最常用的“止血”操作,但回滚只恢复 Pod 模板,不会恢复变更期间同时修改的 HPA、Service、ConfigMap 等。涉及多资源变更时,应整体回滚或先备份。
生产实践:发布前先备份
bash
# 备份当前 Deployment 清单,便于极端情况下比对/恢复
kubectl get deployment nginx -o yaml > /backup/nginx-deploy-$(date +%F).yaml1
2
2
结合探针(见《健康检查三大探针》)确保新副本 Ready 后才纳入流量,是滚动更新不丢请求的前提。
灰度与金丝雀
Deployment 原生只支持滚动更新,精细灰度(如 5% 流量)需要额外手段:
- 蓝绿:两套 Deployment + Service 切换 selector,瞬间切换、易回滚。
- 金丝雀:用两个 Deployment(stable/canary)按副本比例分配,配合 Ingress/Service Mesh 按权重分流。
- Argo Rollouts / Flagger:提供基于指标的渐进式交付(analysis + 自动回滚)。
版本相关
revisionHistoryLimit 控制保留的历史 RS 数量(默认 10),太多会占用 etcd。生产可设为 3–5,但保留至少 1 个旧 RS 才能保证即时回滚。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
ProgressDeadlineExceeded | 新副本长时间未 Ready | kubectl describe deployment;查 Pod 事件/探针 |
| 旧 RS 不缩容 | 新 RS 未达可用 | 检查 Readiness 与资源配额 |
| 回滚无效 | 仅改了 ConfigMap 而非模板 | 改模板才会触发新 RS;ConfigMap 需手动滚动重启 |
| 发布卡住 | maxUnavailable=0 且集群无余量 | 放开 maxSurge 或扩容节点 |
常见坑
- 只改环境变量/ConfigMap 未改 Pod 模板,不会触发滚动更新(需
kubectl rollout restart强制)。 - 镜像 tag 用
latest且无imagePullPolicy: IfNotPresent缓存,导致节点拉到不同内容,行为不一致。 progressDeadlineSeconds默认 600s,发布慢服务可能误报超时,需调大。
替代方案与权衡
- 有状态、需稳定网络标识与存储的应用用 StatefulSet(见《StatefulSet 有状态应用编排》)。
- 需节点级常驻的用 DaemonSet。
- 一次性任务用 Job/CronJob。
- 需要高级发布策略(金丝雀、分析驱动回滚)用 Argo Rollouts 等,而非原生 Deployment。
FAQ
Q:回滚会创建新 RS 还是复用旧的? A:kubectl rollout undo 会把模板恢复到指定历史版本,Deployment 据此创建(或缩放回)对应的旧 RS,作为新的“当前”版本。
Q:如何保证发布期间容量不下降? A:设 maxUnavailable: 0 且 maxSurge ≥ 1,并要求集群有足够可调度余量。
参考资料
- Deployments - Kubernetes 官方文档,访问日期:2026-10-08。
- Rolling Back a Deployment - Kubernetes 官方文档,访问日期:2026-10-08。
- ReplicaSet - Kubernetes 官方文档,访问日期:2026-10-08。