Kubernetes灰度发布与蓝绿部署实战指南:从原理到生产落地
导读 :在生产环境中,应用升级发布是最常见的运维操作之一,也是最容易引发故障的环节。一次不当的发布可能导致线上服务大面积中断,甚至造成严重的业务损失。本文将深入讲解Kubernetes中两种最主流的发布策略------灰度发布(金丝雀发布)和蓝绿部署,从设计思路、YAML编写、逐步实操到生产环境选型建议,带你从入门到精通。
一、为什么需要发布策略?
在企业级生产环境中,直接将旧版本"一刀切"替换为新版本是极其危险的做法。假设你的应用每天有100万次请求,新版本存在一个隐藏的Bug导致5%的请求失败,那么直接发布后瞬间就会有5万次失败请求------这对用户体验和业务声誉的打击是巨大的。
因此,业界发展出了多种渐进式发布策略,核心目标只有一个:
在不影响用户体验的前提下,安全、可控地将新版本推向全量。
1.1 三种主流发布策略对比
| 策略 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 灰度发布(金丝雀) | 新旧版本按比例共存,逐步调大新版本、缩小旧版本 | 风险可控,资源利用率高,回滚简单 | 流量分配粒度受副本数限制 | 常规功能迭代、Bug修复 |
| 蓝绿部署 | 同时运行两套完整环境,通过切换Service流量实现发布 | 切换瞬间完成,回滚即时 | 需要双倍资源,成本较高 | 重大版本升级、大促保障 |
| A/B测试 | 基于用户特征精确路由到不同版本 | 精细化控制,可量化对比 | 实现复杂,需要额外的流量网关支持 | 功能实验、用户行为分析 |
二、灰度发布实战
2.1 设计思路
灰度发布(也叫金丝雀发布)的核心思想是:让新旧版本短期共存,通过逐步调整副本数,让新版本逐渐"蚕食"旧版本的流量。
发布流程:
1. 旧版本 Deployment 部署 3 个副本(承载全部流量)
2. 新版本 Deployment 部署 0 个副本(待命)
3. Service 同时匹配新旧版本的 Pod(通过共享标签 apps=xiuxian)
4. 逐步调整:旧 3→2→1→0,新 0→1→2→3
5. 验证无误后,删除旧版本 Deployment
关键点 :Service 的 selector 使用的是新旧版本 共享的公共标签 (如 apps: xiuxian),而不是版本标签。这样才能让流量同时分配到新旧版本的 Pod。
2.2 环境准备
首先部署旧版本(v1):
yaml
# 01-deploy-apps-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-xiuxian-old
spec:
replicas: 3
selector:
matchLabels:
apps: xiuxian
version: v1
template:
metadata:
labels:
apps: xiuxian
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/eci_open/nginx:latest
创建 Service,注意 selector 只使用公共标签:
yaml
# 02-svc-xiuxian.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-xiuxian
spec:
type: ClusterIP
selector:
apps: xiuxian # 关键:只用公共标签,不区分版本
ports:
- port: 80
部署并验证:
bash
# 部署旧版本
kubectl apply -f 01-deploy-apps-v1.yaml
kubectl apply -f 02-svc-xiuxian.yaml
# 确认Pod运行正常
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
deploy-xiuxian-old-568cf47956-fpx84 1/1 Running 0 4s 10.100.2.231 worker233
deploy-xiuxian-old-568cf47956-qlww5 1/1 Running 0 4s 10.100.2.230 worker233
deploy-xiuxian-old-568cf47956-tgqnx 1/1 Running 0 4s 10.100.1.145 worker232
# 确认Service已关联
kubectl get svc svc-xiuxian -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-xiuxian ClusterIP 10.200.64.17 <none> 80/TCP 4m34s apps=xiuxian
此时访问 Service IP,所有请求都由 v1 版本处理。
2.3 开始灰度:部署新版本
yaml
# 03-deploy-apps-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-xiuxian-new
spec:
replicas: 1 # 先只启动1个新版本Pod
selector:
matchLabels:
apps: xiuxian
version: v2
template:
metadata:
labels:
apps: xiuxian
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/acs/nginx:1.21
bash
kubectl apply -f 03-deploy-apps-v2.yaml
此时集群中有 3个旧版本Pod + 1个新版本Pod,Service 会将流量轮询分配到这4个Pod,新版本自然承担了约25%的流量。
2.4 逐步灰度:调整副本比例
这就是灰度发布最核心的操作------通过 kubectl scale 命令逐步调整新旧版本的副本数:
bash
# 第一步:旧3→2,新1→2(各50%流量)
kubectl scale deployment deploy-xiuxian-old --replicas=2
kubectl scale deployment deploy-xiuxian-new --replicas=2
# 验证Pod状态
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP
deploy-xiuxian-new-845ffc675b-4zf2z 1/1 Running 0 2s 10.100.1.146
deploy-xiuxian-new-845ffc675b-9jt5c 1/1 Running 0 80s 10.100.2.232
deploy-xiuxian-old-568cf47956-fpx84 1/1 Running 0 8m14s 10.100.2.231
deploy-xiuxian-old-568cf47956-tgqnx 1/1 Running 0 8m14s 10.100.1.145
# 第二步:旧2→1,新2→3(新版本占75%流量)
kubectl scale deployment deploy-xiuxian-new --replicas=3
kubectl scale deployment deploy-xiuxian-old --replicas=1
# 验证Pod状态
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP
deploy-xiuxian-new-845ffc675b-4zf2z 1/1 Running 0 63s 10.100.1.146
deploy-xiuxian-new-845ffc675b-9jt5c 1/1 Running 0 2m21s 10.100.2.232
deploy-xiuxian-new-845ffc675b-fsnwv 1/1 Running 0 14s 10.100.2.233
deploy-xiuxian-old-568cf47956-fpx84 1/1 Running 0 9m15s 10.100.2.231
# 第三步:旧1→0,完成全量切换
kubectl scale deployment deploy-xiuxian-old --replicas=0
# 确认只剩新版本
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP
deploy-xiuxian-new-845ffc675b-4zf2z 1/1 Running 0 89s 10.100.1.146
deploy-xiuxian-new-845ffc675b-9jt5c 1/1 Running 0 2m47s 10.100.2.232
deploy-xiuxian-new-845ffc675b-fsnwv 1/1 Running 0 40s 10.100.2.233
提示 :在整个灰度过程中,可以使用
while true; do curl <Service-IP>; sleep 0.1; done持续观察响应内容,确认新版本是否正常工作。
2.5 灰度发布流程图解
时间轴 →
阶段1 [全量v1] 阶段2 [灰度25%] 阶段3 [灰度50%] 阶段4 [灰度75%] 阶段5 [全量v2]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
旧版本Pod: [●][●][●] [●][●][●] [●][●] [●] (已清除)
[●] new [●][●] new [●][●][●] new
新版本Pod: (未创建) ↑ 25% ↑↑ 50% ↑↑↑ 75% ↑↑↑ 100%
Service: selector: selector: selector: selector: selector:
apps:xiuxian apps:xiuxian apps:xiuxian apps:xiuxian apps:xiuxian
关键操作: 初始状态 部署new(1副本) scale old→2 new→2 scale old→1 new→3 scale old→0
2.6 灰度发布回滚
如果在灰度过程中发现新版本有问题,回滚非常简单:
bash
# 一键回滚:直接把新版本副本缩为0,旧版本恢复到3
kubectl scale deployment deploy-xiuxian-new --replicas=0
kubectl scale deployment deploy-xiuxian-old --replicas=3
# 确认回滚成功
kubectl get pods -o wide
这就是灰度发布最大的优势:回滚成本极低,秒级恢复。
三、蓝绿部署实战
3.1 设计思路
蓝绿部署与灰度发布的关键区别在于:两套环境始终各自运行完整的副本数 ,通过修改 Service 的 selector 来决定流量指向哪套环境。
发布流程:
1. 旧版本(蓝环境)Deployment 部署 3 个副本
2. 新版本(绿环境)Deployment 部署 3 个副本
3. Service selector 指向旧版本标签(version: v1)
4. 验证新版本Pod正常运行后,修改 Service selector 指向新版本标签(version: v2)
5. 流量瞬间全部切换到新版本
3.2 同时部署两套环境
蓝环境(旧版本 v1):
yaml
# 01-deploy-apps-v1-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-xiuxian-blue
spec:
replicas: 3
selector:
matchLabels:
apps: xiuxian
version: v1
template:
metadata:
labels:
apps: xiuxian
version: v1
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/eci_open/nginx:latest
绿环境(新版本 v2):
yaml
# 02-deploy-apps-v2-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-xiuxian-green
spec:
replicas: 3
selector:
matchLabels:
apps: xiuxian
version: v2
template:
metadata:
labels:
apps: xiuxian
version: v2
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/acs/nginx:1.21
部署两套环境:
bash
kubectl apply -f 01-deploy-apps-v1-blue.yaml -f 02-deploy-apps-v2-green.yaml
# 确认两套环境都正常运行
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP
deploy-xiuxian-blue-568cf47956-fff9l 1/1 Running 0 5s 10.100.2.238 worker233
deploy-xiuxian-blue-568cf47956-ptvxm 1/1 Running 0 5s 10.100.2.237 worker233
deploy-xiuxian-blue-568cf47956-x7nwn 1/1 Running 0 5s 10.100.1.148 worker232
deploy-xiuxian-green-845ffc675b-jmq78 1/1 Running 0 5s 10.100.1.149 worker232
deploy-xiuxian-green-845ffc675b-nh2js 1/1 Running 0 5s 10.100.2.239 worker233
deploy-xiuxian-green-845ffc675b-zdbsq 1/1 Running 0 5s 10.100.2.236 worker233
# 查看标签确认版本区分
kubectl get pods --show-labels
NAME READY LABELS
deploy-xiuxian-blue-568cf47956-fff9l 1/1 apps=xiuxian,pod-template-hash=568cf47956,version=v1
deploy-xiuxian-blue-568cf47956-ptvxm 1/1 apps=xiuxian,pod-template-hash=568cf47956,version=v1
deploy-xiuxian-blue-568cf47956-x7nwn 1/1 apps=xiuxian,pod-template-hash=568cf47956,version=v1
deploy-xiuxian-green-845ffc675b-jmq78 1/1 apps=xiuxian,pod-template-hash=845ffc675b,version=v2
deploy-xiuxian-green-845ffc675b-nh2js 1/1 apps=xiuxian,pod-template-hash=845ffc675b,version=v2
deploy-xiuxian-green-845ffc675b-zdbsq 1/1 apps=xiuxian,pod-template-hash=845ffc675b,version=v2
3.3 创建 Service 并指向蓝环境
注意:蓝绿部署中 Service 的 selector 使用的是 version 标签来区分版本! 这与灰度发布使用公共标签的方式完全不同。
yaml
# 03-svc-xiuxian.yaml (初始指向蓝环境)
apiVersion: v1
kind: Service
metadata:
name: svc-xiuxian
spec:
type: ClusterIP
selector:
version: v1 # 指向蓝环境(旧版本)
ports:
- port: 80
bash
kubectl apply -f 03-svc-xiuxian.yaml
kubectl get svc svc-xiuxian -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-xiuxian ClusterIP 10.200.88.148 <none> 80/TCP 7s version=v1
此时所有流量都指向蓝环境(v1),绿环境(v2)虽然运行着3个Pod,但不接收任何流量------这正是蓝绿部署"空跑"的含义。
3.4 切换流量:一键发布
确认绿环境Pod运行正常后,只需修改 Service 的 selector:
yaml
# 03-svc-xiuxian.yaml (修改后指向绿环境)
apiVersion: v1
kind: Service
metadata:
name: svc-xiuxian
spec:
type: ClusterIP
selector:
version: v2 # 切换到绿环境(新版本)
ports:
- port: 80
bash
kubectl apply -f 03-svc-xiuxian.yaml
kubectl get svc svc-xiuxian -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-xiuxian ClusterIP 10.200.88.148 <none> 80/TCP 72s version=v2
流量切换瞬间完成! 从这一刻起,所有请求都由绿环境(v2)处理,蓝环境变为空跑状态。
3.5 蓝绿部署回滚
如果发现问题,回滚同样简单:
bash
# 把selector改回v1即可
# 修改 03-svc-xiuxian.yaml 中 selector.version 为 v1
kubectl apply -f 03-svc-xiuxian.yaml
一键切换,秒级回滚。 这就是蓝绿部署的核心优势。
3.6 蓝绿部署流程图解
时间轴 →
阶段1 [蓝环境上线] 阶段2 [绿环境就绪] 阶段3 [流量切换] 阶段4 [清理蓝环境]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
蓝环境(v1): [●][●][●] [●][●][●] [●][●][●] (删除)
↑ 承载流量 ↑ 承载流量 (空跑)
绿环境(v2): (未创建) [●][●][●] [●][●][●] [●][●][●]
(空跑) ↑ 承载流量 ↑ 承载流量
Service: selector: selector: selector: selector:
version:v1 version:v1 version:v2 version:v2
关键操作: 部署蓝环境 部署绿环境 修改selector 删除蓝环境
四、灰度发布 vs 蓝绿部署:核心区别
通过前面的实战,我们来对比两种策略的关键差异:
4.1 Service Selector 使用方式对比
这是两种策略最本质的技术差异:
| 对比项 | 灰度发布 | 蓝绿部署 |
|---|---|---|
| Service selector | 使用公共标签 (如 apps: xiuxian) |
使用版本标签 (如 version: v1/v2) |
| 流量分配方式 | 按Pod数量比例自动分配 | Service只指向一套环境,100%流量 |
| 新旧版本关系 | 新旧版本共存于同一个Service | 新旧版本各自独立,Service只选其一 |
| 资源消耗 | 渐进式,峰值约2倍 | 始终2倍资源 |
| 切换方式 | 调整副本数(kubectl scale) |
修改selector标签(kubectl apply) |
4.2 适用场景决策
你的发布需求是什么?
│
├── 需要精确控制流量比例(如先放5%试试)
│ └── ✅ 选择灰度发布
│ (通过副本数比例控制流量分配)
│
├── 需要瞬间完成切换,不能有过渡期
│ └── ✅ 选择蓝绿部署
│ (修改selector即可,切换即时生效)
│
├── 集群资源紧张,不能双倍部署
│ └── ✅ 选择灰度发布
│ (总副本数保持不变,逐步替换)
│
├── 发布风险极高,需要随时秒级回滚
│ └── ✅ 选择蓝绿部署
│ (新旧环境都在运行,回滚就是改回selector)
│
└── 常规迭代,风险可控
└── ✅ 选择灰度发布(更省资源,更常用)
五、生产环境进阶实践
5.1 结合 ReadinessProbe 确保安全发布
无论是灰度还是蓝绿,都应该为新版本Pod配置就绪探针,确保只有真正就绪的Pod才会被Service纳入后端:
yaml
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/acs/nginx:1.21
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3
failureThreshold: 3
为什么这很重要? 如果新版本Pod启动后虽然处于Running状态,但应用内部尚未完成初始化(比如加载缓存、连接数据库等),没有ReadinessProbe的话Service会立即将流量发给它,导致大量请求失败。
5.2 使用 kubectl rollout 管理发布版本
Kubernetes 的 Deployment 控制器本身就内置了发布管理能力:
bash
# 查看发布历史
kubectl rollout history deployment deploy-xiuxian-new
# 查看某个版本的详细信息
kubectl rollout history deployment deploy-xiuxian-new --revision=2
# 回滚到上一版本
kubectl rollout undo deployment deploy-xiuxian-new
# 回滚到指定版本
kubectl rollout undo deployment deploy-xiuxian-new --to-revision=1
# 查看发布状态
kubectl rollout status deployment deploy-xiuxian-new
5.3 灰度发布的比例控制优化
在基础灰度方案中,流量比例受限于副本总数(如3个副本只能实现0%/33%/67%/100%的比例)。在生产环境中,可以结合以下方式实现更精细的控制:
方案一:增加副本总数
将总副本数从3增加到10,就可以实现10%为步进的精细化灰度:
bash
# 10%灰度:v1=9, v2=1
kubectl scale deployment deploy-old --replicas=9
kubectl scale deployment deploy-new --replicas=1
# 30%灰度:v1=7, v2=3
kubectl scale deployment deploy-old --replicas=7
kubectl scale deployment deploy-new --replicas=3
# 50%灰度:v1=5, v2=5
kubectl scale deployment deploy-old --replicas=5
kubectl scale deployment deploy-new --replicas=5
方案二:使用 Ingress 控制器实现权重路由
更高级的做法是借助 Nginx Ingress、Traefik 等网关,通过注解实现基于权重的流量分配:
yaml
# Nginx Ingress Canary 方式(需安装 nginx-ingress-controller)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: xiuxian-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "30" # 30%流量到新版本
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-xiuxian-new
port:
number: 80
5.4 蓝绿部署的资源优化
蓝绿部署最大的痛点是资源占用翻倍。在生产环境中可以采用以下策略优化:
- 降配空跑环境:将空跑环境的副本数降低到最小可用数(如1个),而非与线上保持一致
- 使用 HorizontalPodAutoscaler:让非活跃环境自动缩容到最低副本数
- 结合 PodDisruptionBudget:确保切换过程中始终有足够的Pod可用
bash
# 蓝绿切换后,将旧环境缩到1个副本备用
kubectl scale deployment deploy-xiuxian-blue --replicas=1
# 观察24小时无问题后,彻底删除
kubectl delete deployment deploy-xiuxian-blue
5.5 自动化发布流水线
将发布策略集成到CI/CD流水线中,可以实现自动化的灰度或蓝绿发布:
groovy
// Jenkinsfile - 灰度发布示例
pipeline {
agent any
parameters {
string(name: 'IMAGE_TAG', defaultValue: 'v2', description: '镜像版本')
}
stages {
stage('灰度部署 - 10%') {
steps {
sh 'kubectl scale deployment deploy-old --replicas=9'
sh 'kubectl scale deployment deploy-new --replicas=1'
sh 'sleep 60' // 观察60秒
}
}
stage('灰度部署 - 50%') {
steps {
sh 'kubectl scale deployment deploy-old --replicas=5'
sh 'kubectl scale deployment deploy-new --replicas=5'
sh 'sleep 120' // 观察120秒
}
}
stage('全量发布') {
steps {
sh 'kubectl scale deployment deploy-new --replicas=10'
sh 'kubectl scale deployment deploy-old --replicas=0'
}
}
}
}
六、发布策略速查表
| 操作场景 | 灰度发布命令 | 蓝绿部署命令 |
|---|---|---|
| 部署新版本 | kubectl apply -f deploy-v2.yaml |
kubectl apply -f deploy-v2.yaml |
| 开始灰度/发布 | kubectl scale deploy-new --replicas=1 |
kubectl apply -f svc-v2.yaml |
| 扩大新版本比例 | kubectl scale deploy-new --replicas=N |
N/A(蓝绿是瞬间切换) |
| 回滚 | kubectl scale deploy-new --replicas=0 && kubectl scale deploy-old --replicas=3 |
修改 svc selector: version: v1 后 kubectl apply |
| 查看流量分布 | kubectl get pods --show-labels |
kubectl get svc -o wide |
| 清理旧版本 | kubectl delete deployment deploy-old |
kubectl delete deployment deploy-blue |
七、总结
本文通过完整的实战操作,深入讲解了Kubernetes中两种最主流的发布策略:
-
灰度发布:通过共享标签 + 逐步调整副本数实现渐进式发布,资源利用率高,是最常用的发布策略。核心是 Service selector 使用公共标签,让K8S自动按Pod数量比例分配流量。
-
蓝绿部署:通过版本标签 + 切换 Service selector 实现瞬间切换,适合高风险发布场景。核心是两套完整环境同时运行,切换只是改一个标签值。
实践建议:在实际工作中,推荐以灰度发布为主、蓝绿部署为辅的组合策略。日常迭代使用灰度发布节省资源,重大版本升级时使用蓝绿部署确保安全。同时务必配合 ReadinessProbe 和健康检查机制,让发布更加安全可控。
环境信息:
- Kubernetes: v1.23.17
- 部署方式: kubeadm
- 容器运行时: Docker
- CNI: Flannel