发布策略
这一课你将能做到
- 区分 Rolling Update、Blue-Green、Canary 三种策略各自改变什么(Pod 集合 / 入口指向 / 流量比例),并能画出各自的流量示意
- 在 Helm values 中正确暴露策略旋钮(
maxSurge/maxUnavailable、activeColor、canary.weight),并说清 Helm 本身不实现流量分割 这个边界
- 写出 GitOps 下「PR 改 tag → 同步 → 观察 → 全量/回滚」的发布五步,标出每一步的观察点与回退动作
为什么要学
helm upgrade 换了镜像 tag,默认触发 Deployment 的 RollingUpdate——在探针正确时这已经能零停机,是 80% 场景的正确答案。但业务要「先放 5% 流量观察」的金丝雀、要「双栈秒切」的蓝绿时,需要 Ingress 能力 / Service Mesh / Argo Rollouts 等数据面配合;Helm 的角色是 把策略参数做成可评审的 values,GitOps 的角色是 让每次放量都可追溯。混淆这两层会导致两个经典错误:以为装了 Helm 就自动有金丝雀;或者生产要 10% 放量时,在 values 里翻不到该改哪个旋钮。本课把三层(Deployment 策略 / 入口切换 / 流量权重)的坐标系钉死。
先建立图像
Rolling Update(默认)
同一个 Deployment,新 Pod 逐步替换旧 Pod
旋钮:maxSurge / maxUnavailable + 就绪探针
流量:始终只有一套 Pod,旧新共存过渡
Blue-Green
两套完整栈同时存在(backend-blue / backend-green)
切换:改 Service/Ingress 的指向(selector 或 weight)
特点:切换秒级、回切秒级,代价是双倍资源窗口
Canary
新版本先接小比例流量,观察指标再逐步放大
旋钮:weight(5% → 25% → 100%)
前提:数据面支持按权重分流 + 有指标可看
| 策略 | 主要改什么 | Helm values 常暴露 | 还需要什么 |
|---|
| Rolling | Pod 模板(镜像) | image.tag、surge/unavailable | 正确的就绪探针 |
| Blue-Green | 入口选哪套 | activeColor、两套镜像 tag | Service selector/Ingress 切换 |
| Canary | 流量百分比 | canary.weight、双版本 tag | Ingress 权重/Mesh/Argo Rollouts |
GitOps 发布节奏(金丝雀示例)
PR1: image.tag 1.0.0→1.1.0 且 canary.weight: 10 → merge → sync
观察错误率/延迟/P99 指标仪表
PR2: canary.weight: 50 → merge → sync → 观察
PR3: canary.weight: 100(或合并双版本回单版本) → 全量
任一阶段异常:revert 对应 PR → sync → 流量回落
跟着做
步骤 1:把 Rolling 参数做进 values 与模板
values.yaml:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 允许比期望副本多 1 个(先起新的)
maxUnavailable: 0 # 期间不允许可用副本减少(更稳,需资源冗余)
对应位置:
templates/deployment.yaml
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: {{ .Values.strategy.type }}
rollingUpdate:
maxSurge: {{ .Values.strategy.rollingUpdate.maxSurge }}
maxUnavailable: {{ .Values.strategy.rollingUpdate.maxUnavailable }}
执行一次仅改 image.tag 的升级,kubectl get pods -w 观察新旧交替顺序;再故意把探针路径改错,观察滚动 卡住不动——这是保护机制,不是故障。
步骤 2:纸笔推演 Blue-Green 的 values 设计
假设 Chart 渲染两套 Deployment,Service 靠颜色标签选边:
# values 示意
activeColor: blue # 入口当前指向 blue
image:
blue:
tag: "1.0.0" # 线上版本
green:
tag: "1.1.0" # 已部署未接流量的新版本
# templates/service.yaml 的 selector 逻辑(示意)
# selector:
# color: {{ .Values.activeColor }}
在笔记画箭头:发布=把 green 部署起来观察健康 → 切 activeColor: green 并 sync(秒级接流)→ 异常则改回 blue 回切(秒级)。理解关键:切换是「入口指向」的一次变更,不是再 install 一个 Release;用两个 Release(backend-blue/backend-green)+ 上层入口切换是另一种等价实现,团队选一种写进规范。
步骤 3:Canary 只定义权重旋钮,明确边界
canary:
enabled: true
weight: 10 # 期望:10% 流量进新版本
imageTag: "1.1.0"
stable:
imageTag: "1.0.0"
在笔记写死这个因果链:Helm 渲染出带权重的 Ingress 规则 / Argo Rollout 对象 → 真正分流的是 Ingress 控制器 / Mesh / Rollouts 控制器。若你的集群既没有权重型 Ingress 也没有 Mesh/Rollouts,canary.weight 就只是一行死配置——写了也不会有金丝雀发生。
步骤 4:认识 Argo Rollouts(渐进交付的标配拼图)
- Argo CD:GitOps 同步(管 desired 的落地)
- Argo Rollouts:渐进交付控制器(管 canary 步骤、暂停、分析)
- 组合方式:Argo CD 同步一个
Rollout 对象(替代 Deployment),Rollouts 按策略逐步放量
# Rollout 意示(结构)
spec:
strategy:
canary:
steps:
- setWeight: 10
- pause: { duration: 5m } # 观察 5 分钟
- setWeight: 50
- analysis: {} # 挂指标分析
步骤 5:设计本站「仅 Rolling」的 GitOps 发布清单
- CI 测试 + 构建 + 推镜像
1.1.0(github-actions-essentials 范畴)
- PR 只改
deploy/env/prod/backend-values.yaml 的 image.tag(GitOps 层)
- 评审 merge → Argo CD 自动 sync(GitOps 层)
- 观察:Pod Ready、
/actuator/health、错误率与延迟指标(可观测层)
- 异常则
git revert → sync 回滚;极端情况集群侧 helm rollback 兜底但必须回写 Git
步骤 6:口头验收
一分钟说清:「默认 upgrade 走 Rolling,靠探针与 surge 参数保证零停机;蓝绿是两套栈+入口一次切,代价是双倍资源;金丝雀按权重放量,必须有数据面与指标支持;Helm 只负责把这些策略参数做成可评审的 values,不做流量本身。」
讲清楚要点
- 因为 Deployment 默认滚动更新,所以先把探针与 surge 参数做对再谈高级策略——readiness 不对,任何策略都是放大事故的舞台。
- 因为 Helm 不管流量,所以 values 里的 weight 必须有对应的 Ingress/Mesh/Rollouts 实现承接——没有数据面就没有策略,只有配置文本。
- 因为蓝绿的切换点是入口,所以要有秒级回切路径与双倍资源预算——切换窗口内写
activeColor 的 PR 要有明确 owner 与评审通道。
- 因为 GitOps 以 Git 为真相源,所以发布=改 values 的 PR,而不是 kubectl set image——放量比例、观察结论都在 PR 里留痕,审计链完整。
- 因为金丝雀依赖可观测性,所以没有错误率/延迟仪表就不要假装放量——「看不见的 10% 失败」比 100% 翻车更隐蔽。
- 因为策略与打包正交,所以同一 Chart 可按环境选不同策略参数——dev 直接滚动全量,prod 才开 canary 开关;开关本身也是 values。
你可能会懵的地方
-
Argo Rollouts 和 Argo CD 是一回事吗?
不是。Argo CD 做 GitOps 同步;Rollouts 是渐进式交付控制器,用 Rollout CRD 替代 Deployment 实现 canary 步骤与指标分析。常见组合:CD 同步 Rollout,Rollouts 管放量。
-
装两个 Helm Release 算蓝绿吗?
是实现手法之一(backend-blue/backend-green),但还需入口层决定谁接流量、谁先升级。「Release 多」不等于「蓝绿」,核心是入口切换能力与资源预算。
-
--atomic 是不是金丝雀?
不是。atomic 是「这次 upgrade 失败就整体回滚」,粒度是发布动作;金丝雀是「先放一部分真实流量」,粒度是流量比例。二者解决不同风险。
-
maxUnavailable: 0 一定更好吗?
更稳但更慢更费资源:要求容量冗余、新 Pod 全部 Ready 后旧 Pod 才逐个退。资源紧张或副本数少(1-2 副本)时可接受 maxUnavailable: 1,用可用性换速度——参数没有银弹,看 SLA。
-
必须上 Service Mesh 吗?
不必须。Rolling + 正确探针 + GitOps 已覆盖大多数团队;有明确「按比例放量+自动分析」需求且指标体系成熟时,再评估 Mesh 或 Rollouts。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 以为 helm upgrade = 金丝雀 | 实际是全量滚动 | 分清策略层,需要时接权重组件 |
| readiness 探针没配就收紧 maxUnavailable=0 | 滚动卡死或旧 Pod 提前被杀 | 先修探针,再调策略参数 |
| kubectl 改 Service selector「临时切绿」 | 与 Git 漂移,下轮被拉回 | 改 activeColor 走 PR 再 sync |
| canary.weight 写了但数据面不支持 | 无效安慰剂,全量直接上 | 先确认 Ingress/Mesh/Rollouts 能力 |
| 无指标就做 10% 放量 | 失败不可见 | 先建错误率/延迟监控再放量 |
| 回滚只 rollback 集群不改 Git | 下轮 sync 又推回新版 | git revert 为第一回滚动作 |
卡点急救
- 现象:滚动更新卡住,新旧 Pod 共存不动——先查新 Pod 是否 Ready 失败(探针路径/端口/依赖):
kubectl describe pod;探针是保护阀,修根因而不是绕过策略;确认后旧 Pod 会继续逐批替换。
- 现象:金丝雀值改了但流量比例没变——按链路查:values 是否真的渲染进 Ingress/Rollout(
helm template/Argo diff)→ 数据面是否支持权重 → 权重对象是否被控制器接受;任何一环断开都是「死配置」。
- 现象:蓝绿切换后新版本全量 5xx——立刻切回(revert PR 或改回 activeColor),秒级止损;再排镜像/配置差异。蓝绿的价值就在这个「秒级回切」,平时必须演练回切路径。
面试常问
-
Rolling / Blue-Green / Canary 的区别与选型?
Rolling:同一 Deployment 逐步替换 Pod,零额外资源、依赖探针,适合绝大多数常规发布;Blue-Green:两套完整栈,入口一次切换,秒级发布与回切,代价是双倍资源窗口,适合要求快速回退的大版本;Canary:新版本先接小比例真实流量,逐步放大并观察指标,风险最小,但需要数据面权重能力与可观测体系。选型口诀:默认 Rolling,要秒回退选蓝绿,要灰度验证选金丝雀。
-
Helm 在发布策略中扮演什么角色?
打包与参数化:把 strategy、双版本 tag、activeColor、canary.weight 等做成 values 旋钮,渲染出对应的 Deployment/Service/Ingress/Rollout 对象。Helm 不执行流量分割、不做暂停观察——这些属于数据面与渐进交付控制器。
-
GitOps 下如何做一次金丝雀发布?
每一步都是一次 values 变更 PR:PR1 设新 tag 且 weight=10 → merge 后控制器 sync → 观察指标仪表 → PR2 放大 weight=50 → 观察 → PR3 weight=100 全量;任一阶段异常 revert 该 PR 即回落。整个放量过程在 Git 历史里完整可审计,这是 GitOps 对渐进交付的直接增益。
自测三问
- Rolling、Blue-Green、Canary 分别改变系统的哪一部分?Helm 在其中的职责边界是什么?
maxSurge 与 maxUnavailable 组合为 1/0 和 0/1 各意味着什么?分别适合什么场景?
- 写出仅改
image.tag 的 GitOps 发布五步,并标出每一步的观察点与回退动作。
小结一句话
默认滚动靠 Deployment 策略与探针,蓝绿靠入口切换,金丝雀靠流量权重组件;Helm 把策略参数做成可评审的 values、GitOps 让每次放量可追溯——发布策略三层的坐标系就此钉死。
延伸