4.2 滚动更新与回滚:声明的版本演进


4.2 滚动更新与回滚:声明的版本演进

滚动更新是 Deployment 的内置升级策略:新旧两个代际的 ReplicaSet 此消彼长,新副本通过就绪探针后逐步接入流量,旧副本同步退出,全程服务不中断。回滚则是把账本里的代际指针拨回历史版本,旧 ReplicaSet 重新扩容。升级的节奏由 maxSurge 与 maxUnavailable 两个参数精确控制。

副本守恒让服务"活着",这一节让它"换代"。orders-api 要从 1.4.2 升到 1.5.0——在手工时代这是一场深夜战役,在声明式世界里它是改一个字段加一条命令的事。但"能自动"不等于"无脑安全",节奏参数与探针配合不当照样会断流量。本节把换代的全过程、节奏旋钮与回滚原理一次讲透。

一次不中断的换防

升级的触发点只有一个:声明里的镜像字段变了。部署控制器发现模板与当前代际不符,随即启动换防:

  1. 按新模板创建新代际 ReplicaSet(1.5.0),初始副本数为 0;
  2. 新代际扩容 1 个副本,等它通过 readiness 探针(3.4 节的闸门在此生效);
  3. 新副本就绪后,旧代际(1.4.2)缩容 1 个副本;
  4. 交替执行,直至新代际满额、旧代际清零;
  5. 旧 ReplicaSet 保留 0 副本不删除——它是回滚的存档点。

全过程用一条命令盯着走:

# 触发升级:只改镜像字段(等价于改 YAML 后 apply) kubectl set image deployment/orders-api orders-api=orders-api:1.5.0 -n production # deployment.apps/orders-api image updated # 观察换防进度(此命令会持续刷新直到完成) kubectl rollout status deployment/orders-api -n production # Waiting for deployment "orders-api" rollout to finish: 2 of 3 updated replicas are available... # deployment "orders-api" successfully rolled out

换防期间流量如何做到不断?关键在 3.4 节埋的伏笔:Service 的端点名单只收 READY 副本。新副本体检通过才进名单,旧副本退出前先被摘出名单——流量切换发生在端点名单层,与容器启停完全解耦

节奏的两个旋钮

换防的激进度由 strategy 段的两个参数控制,先看声明写法:

spec: strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # 允许临时超出期望副本数 1 个(先建新的) maxUnavailable: 0 # 任何时刻不允许副本缺口(旧的必须等新的就绪再退)

两个旋钮的组合决定风格,四种典型搭配的取舍:

搭配 行为 代价 适用
surge 1 + unavailable 0 先补后撤,容量只增不减 升级期间资源占用偏高 容量敏感的核心服务(本例)
surge 0 + unavailable 1 先撤后补,资源不超额 升级期间容量打八折 资源紧张、可容忍短暂降容
surge 25% + unavailable 25% 默认折中 中等 大副本数的一般业务
surge 0 + unavailable 100% 一次性全换(接近重建) 全程中断风险 仅停机窗口内使用

⚠️ 常见坑:副本数 2 的小服务配默认 25% 会被取整成 surge 1 与 unavailable 1,升级瞬间可能出现"1 个新副本未就绪、1 个旧副本已摘除"的空窗。小副本数服务建议显式写 maxUnavailable: 0。

回滚的账本原理

新版本有问题时,回滚不是重发旧镜像,而是把账本里的活跃代际指针拨回去——旧 ReplicaSet 还在,只是 0 副本:

# 查看代际存档:CHANGE-CAUSE 需要在声明里加注解才会显示 kubectl rollout history deployment/orders-api -n production # deployment.apps/orders-api # REVISION CHANGE-CAUSE # 3 升级到 1.5.0 # 2 升级到 1.4.2 # 1 初始上线 1.3.9 # 一条命令回滚到上一代 kubectl rollout undo deployment/orders-api -n production # deployment.apps/orders-api rolled back # 回到指定代际(跳回多版) kubectl rollout undo deployment/orders-api -n production --to-revision=1

回滚的执行路径与升级完全对称:旧 ReplicaSet 扩容、新 ReplicaSet 缩容,探针把门、名单切换。这也解释了 revisionHistoryLimit 的意义——默认保留 10 个历史 ReplicaSet,超出才清理;把代际档案清光的集群,回滚就只能重新 apply 旧 YAML 了。

案例:一次带内存泄漏的版本上线与十分钟回滚

背景:1.5.0 上线两小时后,监控显示内存曲线持续爬升且不回落(4 个副本全部如此),疑似新版本连接池泄漏,决定回滚。

操作

# 第一步:暂停换防惯性——先冻结升级(若还有滚动中副本) kubectl rollout pause deployment/orders-api -n production # deployment.apps/orders-api paused # 第二步:确认健康证据后回滚并恢复 kubectl rollout undo deployment/orders-api -n production kubectl rollout resume deployment/orders-api -n production # 第三步:验证代际与镜像 kubectl get deployment orders-api -n production -o jsonpath='{.spec.template.spec.containers[0].image}' # orders-api:1.4.2 kubectl rollout status deployment/orders-api -n production # deployment "orders-api" successfully rolled out

结果:回滚全程约十分钟,期间错误率与延迟无异常波动;内存曲线在旧版本上恢复平稳锯齿状。

解读:回滚快的前提有两个——历史 ReplicaSet 还在(没被 revisionHistoryLimit 清掉)、镜像 1.4.2 还在仓库里(没被版本策略删除)。前者靠集群默认,后者靠镜像仓库的保留策略,两者都是发布体系设计时就要写进规范的项。

变式:更稳妥的发布形态是金丝雀:先放一个小流量比例给新版本(例如用两个 Deployment 加不同副本数、由 Service 按就绪副本自然分流),观察指标后再全量。声明的世界不阻止你组织更复杂的发布,只是把"复杂"从运维脚本转移进了可见的声明结构里。

本节要点回顾

  • 换防五步:建新代际、扩新、过探针、缩旧、清零保留存档;触发点只是镜像字段变更;
  • 流量切换在名单层:新副本过体检进名单、旧副本先摘后撤,启停与分流解耦;
  • 两个节奏旋钮:maxSurge 管临时超额、maxUnavailable 管允许缺口,小副本数务必显式声明;
  • 回滚是拨指针:历史 ReplicaSet 就是存档点,CHANGE-CAUSE 注解让存档可读;
  • 回滚快的前提:代际档案未清理、旧镜像仍在仓库,发布规范要同时保住两者。

无状态负载的换代已经收放自如,但数据库、日志代理、批处理任务各有各的脾气。下一节把调和循环套到这三类特殊负载上——选对工作负载类型,声明的承诺才不会落空。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U