上集我们搞懂了 Deployment 的三级结构、滚动更新策略调优、pause/resume 灰度验证和回滚全流程。但生产环境有时候滚动更新还不够——比如重大版本升级,你想先全量验证再切流量;或者高风险变更,你想只让一小部分用户先踩坑。这时候蓝绿部署和灰度发布就派上用场了。
下集聚焦三种发布策略的进阶对比、蓝绿/灰度手操全流程,以及扩缩容和常见排坑。文章末尾会给出贯穿上下集的 10 条落地清单。
一、蓝绿部署(Blue/Green Deployment)
1.原理
滚动更新是"渐进替换",蓝绿部署是"全量切换"——同时跑两套完整环境(蓝=旧版,绿=新版),新版验证通过后,一把切流量。
┌─────────────┐ 用户 ──→ │ Service │ ──→ app: blue ──→ 蓝环境(3个旧Pod) └─────────────┘ │ ↓ 切换 selector ┌─────────────┐ 用户 ──→ │ Service │ ──→ app: green ──→ 绿环境(3个新Pod) └─────────────┘优点:零停机,风险可控——出问题切回蓝环境就行。
缺点:需要双倍资源;切换是全量的,新版本有问题影响全部用户。
2.实战操作
Step 1:部署蓝环境(当前版本 v1)
# 01-blue.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:app-bluespec:replicas:3selector:matchLabels:app:bluetemplate:metadata:labels:app:bluespec:containers:-name:appimage:nginx:1.22.1---apiVersion:v1kind:Servicemetadata:name:app-svcspec:type:NodePortselector:app:blue# ← 指向蓝环境ports:-port:80targetPort:80nodePort:30000kubectl apply-f01-blue.yamlStep 2:部署绿环境(新版本 v2)——不接入流量
# 02-green.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:app-greenspec:replicas:3selector:matchLabels:app:greentemplate:metadata:labels:app:greenspec:containers:-name:appimage:nginx:1.30.1kubectl apply-f02-green.yaml# 此时绿环境 Pod 在跑,但没有 Service 指过来——不接收流量Step 3:切换流量到绿环境
# 改 Service selector:blue → greenkubectl patchserviceapp-svc-p'{"spec":{"selector":{"app":"green"}}}'# 流量瞬间切到 v2!whiletrue;docurl-sIhttp://192.168.91.21:30000|head-2;sleep1;done验证思路:切之前while true; do curl <nodeIP>:30000; sleep 0.5; done,切的一瞬间返回内容从 v1 变成 v2。出问题马上patch回app: blue即可。
二、灰度发布(金丝雀发布/Canary)
1.原理
蓝绿是全量切换,灰度是逐步导流——旧版本和新版本同时在线,先让一小部分流量打到新版本,确认没问题后逐步放大。
核心思路:两个 Deployment 用同一个 label,Service 同时选中它们,靠副本数比例控制流量分配。
┌─────────────┐ 用户 ───→ │ Service │ ──→ app: web └─────────────┘ │ ┌────────┴────────┐ ↓ ↓ 旧 Deployment 新 Deployment replicas: 3 → 0 replicas: 1 → 3 (v1, 旧版) (v2, 新版)2.实战操作
Step 1:部署旧版本(3副本,100% 流量)
# 01-old.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:app-oldspec:replicas:3selector:matchLabels:app:webtemplate:metadata:labels:app:web# ← 同一个 labelspec:containers:-name:appimage:nginx:1.22.1---apiVersion:v1kind:Servicemetadata:name:app-svcspec:type:NodePortselector:app:web# ← 匹配两个 Deployment 的 Podports:-port:80targetPort:80nodePort:30000kubectl apply-f01-old.yamlStep 2:部署新版本(1副本,~25% 流量)
# 02-new.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:app-newspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:web# ← 同一个 labelspec:containers:-name:appimage:nginx:1.30.1kubectl apply-f02-new.yaml# 现在 4个 Pod:3个old + 1个new,Service 轮询负载,约 75%/25% 分流Step 3:观察 → 逐步切流
# 观察新版本无异常后,逐步调副本数kubectl scale deployment app-old--replicas=2# 旧:3→2,流量比约 67%/33%kubectl scale deployment app-old--replicas=1# 旧:2→1,流量比约 50%/50%kubectl scale deployment app-new--replicas=3# 新:1→3kubectl scale deployment app-old--replicas=0# 旧:1→0,100% 流量到新版灰度 vs 蓝绿 vs 滚动更新对比:
| 方式 | 切换方式 | 资源占用 | 回滚难度 | 适用场景 |
|---|---|---|---|---|
| 滚动更新 | 渐进替换 | 正常 | 秒级(rollout undo) | 日常发布首选 |
| 蓝绿部署 | 全量切换 | 双倍 | 秒级(切 selector) | 重要版本升级 |
| 灰度发布 | 逐步导流 | 递增 | 秒级(scale 回旧) | 高风险变更 |
三、扩缩容
Deployment 的副本数可以随时调整,两种方式:
1. 手动扩缩容
# 扩容到 5 个副本kubectl scale deployment nginx--replicas=5# 缩容到 2 个kubectl scale deployment nginx--replicas=2底层就是改 RS 的replicas字段,K8s 自动调度或回收 Pod。
2. 自动扩缩容(HPA)
Horizontal Pod Autoscaler 根据 CPU/内存指标自动调整副本数:
# 前置条件:集群已安装 metrics-serverkubectl autoscale deployment nginx--min=2--max=10--cpu-percent=80- 当 Pod 平均 CPU 使用率 > 80% → 自动增加副本
- 当 Pod 平均 CPU 使用率 < 80% → 自动减少副本
- 副本数始终在
--min和--max之间
# 查看 HPA 状态kubectl get hpa注意:HPA 和手动 scale 会冲突。如果手动改了 replicas,HPA 可能立刻改回来——统一用一个方式管理副本数。
四、故障排查
1.常见问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| Pod 始终 Pending | 资源不足 / 节点选择器不匹配 | kubectl describe pod看 Events |
| 更新卡在 Progressing | 镜像拉不下来 / 探针失败 / 资源不足 | kubectl describe pod <新Pod>看 Events |
| 回滚报 revision not found | revisionHistoryLimit清理了旧 RS | kubectl rollout history确认保留范围 |
| 滚动更新太慢 | maxSurge 和 maxUnavailable 都是 0,一次只能更新 1 个 Pod | 适当调大 maxSurge |
| Recreate 导致服务中断 | Recreate先杀所有旧 Pod | 只有不兼容版本才用 Recreate |
2.详细排查
2.1 回滚失败:revision 已被清理
kubectl rollout undo deployment/nginx --to-revision=1# error: revision "1" not found in deployment "nginx"原因:revisionHistoryLimit清理了旧 RS。预防:重要 Deployment 适当调大。
spec:revisionHistoryLimit:20# 保留20个历史版本2.2 更新卡住:progressDeadlineSeconds 超时
kubectl get deployment nginx# NAME READY UP-TO-DATE AVAILABLE AGE# nginx 2/3 2 2 5mkubectl describe deployment nginx# Conditions:# Type Status Reason# Progressing False ProgressDeadlineExceeded原因:新 Pod 一直不 Ready(镜像拉不下来、探针失败、资源不足)。排查:
# 看 Pod 日志kubectl logs-f<新Pod名称># 看 Pod Eventskubectl describe pod<新Pod名称>2.3 滚动更新太慢
maxSurge: 0, maxUnavailable: 0→ 一次只更新1个Pod,3副本要3轮。适当增大maxSurge或maxUnavailable。
2.4 Recreate 策略导致服务中断
Recreate会先杀所有旧Pod再建新Pod,中间所有Pod不可用。只有在新旧版本真的不兼容时才用 Recreate,否则永远用 RollingUpdate。
五、总结
一句话总结:Deployment 滚动更新的本质是新 RS 扩容 + 旧 RS 缩容,maxSurge/maxUnavailable 控制节奏,旧 RS 保留是为了秒级回滚。日常发布首选滚动更新,重要版本可用蓝绿或灰度兜底。
落地清单:
- 核心业务 Deployment 设置
maxSurge: 1, maxUnavailable: 0 - 设置
minReadySeconds: 30,防止 Pod 刚 Ready 就被计入可用 - 重要 Deployment 调大
revisionHistoryLimit: 20 - 用
kubectl annotate记录每次变更原因,不要依赖已弃用的--record - 灰度场景用
rollout pause+rollout resume手动控制节奏 - 回滚前先
rollout history确认目标 revision 还在 - 重大版本升级考虑蓝绿部署:先部署新版 → 验证 → 切 Service selector
- 高风险变更用灰度发布:两个 Deployment 同 label,靠副本数比例控流
- Recreate 策略只在新旧版本不兼容时使用
- HPA 和手动 scale 二选一,不要混用