CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
Deployment 与滚动更新 副本集、滚动发布、回滚。
Deployment 与滚动更新
这一课你将能做到
写出带 replicas、selector、Pod 模板的 Deployment,并验证「删 Pod 自动补齐」的自愈能力
用 kubectl set image 或改 YAML 完成一次滚动更新,用 rollout status/history 观察全程
执行 rollout undo 回滚到上一版本,并说清 maxUnavailable / maxSurge 的含义
为什么要学
无状态 API(本站 backend、platform 前端)在 K8s 里的标准载体是 Deployment :你声明「要 N 个这样的 Pod」,控制器负责维持健康副本,并内置滚动更新与回滚。
对照现网:以前换 jar 要停服、传包、重启,用户感知中断;K8s 的滚动更新是「起新的、验就绪、停旧的」逐步替换,全程服务不断,出问题一条命令回滚。这是 K8s 最打动业务方的能力,也是面试最高频的考点(「滚动更新怎么实现」「发布如何不中断」)。
先建立图像
Deployment(你操作的层)
└─ ReplicaSet(版本世代:v1 一组、v2 一组)
└─ Pod × N(同一模板的实例)
发布 v1 → v2 的滚动过程:
新建 RS(v2) → 逐步加 v2 Pod、减 v1 Pod → 旧 RS 缩到 0(保留以便回滚)
出问题 → rollout undo → 旧 RS 重新扩容、新 RS 缩到 0
你的操作 实际发生 apply Deployment 创建/更新 ReplicaSet 与 Pod 删一个 Pod 控制器立刻补一个 改 template 里的镜像/探针 生成新 RS,触发滚动替换 只改 replicas 仅扩缩容,不算发版
跟着做
步骤 1:写 Deployment YAML
deploy-backend.yaml(先用 nginx 占位,逻辑与本站 backend 完全一致):
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
labels:
app: backend
spec:
replicas: 2
revisionHistoryLimit: 5 # 保留最近 5 个版本供回滚
selector:
matchLabels:
app: backend # 必须与 template.labels 匹配
template:
metadata:
labels:
app: backend
spec:
containers:
- name: api
image: nginx:1.27-alpine
ports:
- containerPort: 80
硬规则 :spec.selector.matchLabels 必须与 template.metadata.labels 匹配,且创建后 selector 不可再改——写错直接 apply 失败或抓错 Pod。
步骤 2:apply 并验证自愈
kubectl apply -f deploy-backend.yaml
kubectl get deploy backend
kubectl get rs -l app=backend
kubectl get pods -l app=backend
看到 1 个 Deployment、1 个 ReplicaSet、2 个 Pod(名字形如 backend-7d9f8b6c5-xxxxx)。现在杀掉一个 Pod:
kubectl delete pod <某个pod名>
kubectl get pods -l app=backend -w
几秒内会出现一个新 Pod 接替它——ReplicaSet 控制器发现副本数低于期望,自动重建。这就是「自愈」。
步骤 3:扩缩容 kubectl scale deployment/backend --replicas=4
kubectl get pods -l app=backend
改回 2 也一样。工程习惯:改 YAML 文件里的 replicas 再 apply (声明式、可追溯),scale 命令适合应急。
步骤 4:滚动更新镜像 kubectl set image deployment/backend api=nginx:1.25-alpine
kubectl rollout status deployment/backend
rollout status 会实时打印 Waiting for deployment "backend" rollout to finish... 直到完成。期间另开终端 kubectl get pods -l app=backend -w,能看到新旧 Pod 此消彼长:新 Pod 先起、Ready 后旧 Pod 才退。对本站即:your-registry/backend:1.0.0 → 1.1.0,用户全程无感。
kubectl rollout history deployment/backend
应看到 REVISION 列表——每次模板变更生成一个修订版本。
步骤 5:回滚 kubectl rollout undo deployment/backend
kubectl rollout status deployment/backend
kubectl get rs -l app=backend
应回到上一版本(旧 RS 重新扩容,新 RS 缩到 0)。指定回到某一版:
kubectl rollout undo deployment/backend --to-revision=1
人话:发布前确认能 undo,比「祈祷不出事」可靠一万倍。
步骤 6:理解更新策略参数 在 Deployment 里显式写策略(默认就是 RollingUpdate,默认值 25%/25%):
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # 滚动期间最多允许几个副本不可用:0 = 不牺牲容量
maxSurge: 1 # 滚动期间最多允许多起几个临时副本
小集群/低副本数建议 maxUnavailable: 0, maxSurge: 1——先补齐再替换,容量不缩水。注意:必须配合 readiness 探针 ,否则「新 Pod 起来了」不等于「新 Pod 能接流量」,策略形同虚设。改回文件里的镜像 tag 后 kubectl apply -f deploy-backend.yaml 收尾,保持「文件是真相」。
讲清楚要点
Deployment 管 RS、RS 管 Pod :日常别手搓 ReplicaSet——会和控制器打架;也解释了为什么删 Deployment 才是「真卸载」。
只有改 Pod 模板才触发滚动 :改镜像、环境变量、探针算发版;只改 replicas 是扩缩。版本历史按「模板变更」计。
滚动更新靠 readiness 保障不中断 :新 Pod 未 Ready 就不摘旧 Pod。没配 readiness 的滚动发布 = 「用户打到启动中的实例」,是发布事故最常见根因。
maxUnavailable 与 maxSurge 决定节奏 :不可用上限保护容量,超出上限控制资源消耗;两者都以「期望副本数」的百分比或绝对数表示。
镜像 tag 必须可追溯 :本站镜像来自 CI 固定 tag;用 :latest 滚动可能「以为更新了其实没变」(本地缓存旧层),也回滚不了明确版本。
回滚依赖 revisionHistoryLimit :默认保留 10 个;设 0 会失去回滚能力。频繁无意义的改动会挤掉历史。
你可能会懵的地方
spec.selector 能改吗?
创建后不可变。要换标签体系通常得新建 Deployment 迁移——所以命名空间和标签规范要一开始就想好。
滚动更新卡住不动?
rollout status 超时常见于:镜像拉不下来(describe 新 Pod 看 Events)、探针一直失败、节点资源不足。处理完根因滚动会自动继续。
和 Compose 换镜像比差在哪?
docker compose up -d 常常整容器重建,中间有真空期;K8s 按策略逐步替换并保留回滚点,且由 readiness 保证流量只打健康实例。
有状态应用也用 Deployment?
数据库类优先考虑托管服务或 StatefulSet(第 8 课);Deployment 擅长无状态服务。MySQL 的多个副本绝不该用一个 Deployment 硬凑。
k8s 里还有别的发布方式吗?
蓝绿、金丝雀可以在 Deployment 层面做(双 Deployment 切流量),更标准的做法靠 Ingress/服务网格按权重分流(第 9 课铺垫)。本课先把「滚动 + undo」练成肌肉记忆。
错法对照 错法 问题 正解 手动删 ReplicaSet「清理」 破坏版本历史,回滚失效 用 rollout undo / 改 Deployment selector 与 template 标签不一致 apply 被拒或 Pod 失管 两处严格一致 无 readiness 就滚动 流量进未就绪实例 探针必配(上一课) 生产直接改集群 live 对象、不入 Git 无法复现、无法审计 YAML 进仓库,apply 都有据可查 把一次性批处理写成 Deployment 任务永不结束或被反复重启 用 Job/CronJob(按需自查)
卡点急救
滚动更新卡住,rollout status 一直等 :kubectl describe pod -l app=backend 看新 Pod 的 Events——多数是镜像拉取失败或探针不通过;kubectl rollout undo 可立即止损。
undo 提示没有可回滚版本 :revisionHistoryLimit 设成了 0,或这是第一个版本。预防:保留默认 10,别乱设 0。
副本数与期望不符 :kubectl describe deployment backend 看 Conditions;Pod 起不来多因资源不足(Pending,第 10 课)或镜像错误,个别旧 Pod 处于 Terminating 属正常。
面试常问
滚动更新的原理是什么?
修改 Pod 模板后 Deployment 创建新 ReplicaSet,按 maxSurge 逐步扩新 RS、按 maxUnavailable 逐步缩旧 RS,此消彼长直至新 RS 达到期望副本、旧 RS 缩到 0(但保留对象以备回滚)。每个新 Pod 必须 readiness 通过才继续下一步,配合探针实现发布全程服务不中断。rolling update 的每一步都可被 rollout pause/resume 暂停,用于灰度观察。
maxUnavailable 和 maxSurge 怎么理解?
都是相对期望副本数的比例或绝对数。maxUnavailable:滚动过程中允许同时有多少个副本「不可用」,控制容量下限;maxSurge:允许超出期望副本数多少个,控制资源上限。例:期望 4、maxUnavailable=0、maxSurge=1,则滚动期间始终 ≥4 个可用副本,最多瞬时 5 个 Pod。要求「容量永不缩水」就设 maxUnavailable: 0。
线上发新版本发现异常,怎么回滚?
kubectl rollout undo deployment/<名> 回到上一 revision(或 --to-revision=N 指定版本),随后 rollout status 确认完成。前提:镜像 tag 可追溯、revisionHistoryLimit 未被清零。更完善的方案把「回滚」做成 CI/CD 里的标准动作(重新发布上一个稳定 tag)。
自测三问
删一个 Pod 和删 Deployment 的后果分别是什么?
如何确认一次滚动更新真正完成(而不是「看起来完成」)?
本站 backend 发版时,镜像 tag 与 rollout undo 各扮演什么角色?
小结一句话 Deployment 用「副本维持 + 滚动策略 + 版本历史」把上线变成可观察、可回滚的常规操作——下一课用 Service 给这些随时会变的 Pod 一个稳定访问入口。
延伸