1、前言
1.1 金丝雀发布
核心思想: 就像矿工下井前会先放一只金丝雀进去探测有毒气体一样,金丝雀发布是指将新版本的软件先发布给一小部分用户(或服务器),让他们作为“金丝雀”来测试新版本的稳定性和性能。如果没有问题,再逐步扩大新版本的流量比例,直到最终完全替换旧版本。
工作流程:
- 部署新版本(V2)到生产环境,但只让一小部分流量(例如 1%)访问 V2。
- 绝大部分流量(99%)仍然访问旧版本(V1)。
- 密切监控 V2 的性能指标(错误率、延迟、CPU/内存使用率等)。
- 如果 V2 表现良好,逐步增加其流量比例(例如 1% -> 5% -> 20% -> 50% -> 100%)。
- 如果在任何一个阶段发现问题,可以立即将所有流量切回 V1,实现快速回滚。
1.2 优缺点
| 特性 | 金丝雀发布 |
|---|---|
| 风险控制 | 非常高。影响范围小,问题可以被控制在极少数用户中,对整体系统影响小。 |
| 回滚速度 | 较快。只需调整流量分配策略,将流量切回旧版本即可。 |
| 用户体验 | 平滑过渡。大部分用户不受影响,只有小部分用户可能遇到新版本的问题。 |
| 真实反馈 | 可以获得真实用户的行为数据。因为新版本是在真实环境中被真实用户使用。 |
| 资源成本 | 较低。不需要双倍的生产环境资源,只需要控制流量分配。 |
| 复杂性 | 高。需要精密的流量控制(如 Service Mesh, Nginx)和强大的监控、自动化系统来逐步调整流量和判断版本健康度。 |
| 数据库/状态 | 挑战较小。新旧版本共享同一个数据库和数据状态,通常不需要特殊处理(但要求数据库 schema 向后兼容)。 |
| 发布速度 | 慢。发布过程是渐进式的,需要分阶段观察和决策,耗时较长。 |
1.3 发布场景
1.对稳定性和风险控制要求极高:例如金融、支付等核心系统,任何微小的故障都可能导致巨大损失。
1.需要验证新功能对用户行为的影响:当你不确定新功能是否受用户欢迎时,可以先给一小部分用户使用,收集反馈和数据。
3.系统复杂,组件众多:对于复杂的微服务架构,金丝雀发布可以更精细地控制每个服务的发布节奏。
4.资源有限,无法承担双倍的服务器成本。
5.拥有成熟的监控、告警和自动化平台:能够自动分析金丝雀版本的健康状况并自动进行流量调整。
2、版本备注
2.1 环境镜像
| 镜像版本 | deployment | 备注 |
|---|---|---|
| harbor.test.com/java-dev/demo:Canary | dev-demo-canary | 假设是灰度版本 |
| harbor.test.com/java-dev/demo:Prod | dev-demo | 假设是正式版本 |
2.2 环境deployment
canary版本
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-demo-canary
namespace: devops
spec:
replicas: 1
selector:
matchLabels:
app: dev-demo-canary
template:
metadata:
labels:
app: dev-demo-canary
spec:
containers:
- name: dev-demo-canary
image: harbor.test.com/java-dev/demo:Canary
volumeMounts: []
volumes: []
restartPolicy: Always
dnsPolicy: ClusterFirst
---
apiVersion: v1
kind: Service
metadata:
name: dev-demo-canary
namespace: devops
spec:
selector:
app: dev-demo-canary
ports:
- name: dev-demo-canary-8080
protocol: TCP
port: 8080
targetPort: 8080
Prod版本
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-demo-prod
namespace: devops
spec:
replicas: 1
selector:
matchLabels:
app: dev-demo-prod
template:
metadata:
labels:
app: dev-demo-prod
spec:
containers:
- name: dev-demo-prod
image: harbor.test.com/java-dev/demo:Prod
volumeMounts: []
volumes: []
restartPolicy: Always
dnsPolicy: ClusterFirst
---
apiVersion: v1
kind: Service
metadata:
name: dev-demo-prod
namespace: devops
spec:
selector:
app: dev-demo-prod
ports:
- name: dev-demo-prod-8080
protocol: TCP
port: 8080
targetPort: 8080
3、Canary灰度发布
3.1 基于Header灰度发布
使用
nginx.ingress.kubernetes.io/canary注解实现灰度流量控制。
3.1.1 prod的ingress-rule
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-prod
namespace: devops
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-prod
port:
number: 8080
3.1.2 canary的ingress-rule
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-canary
namespace: devops
###canary的header注解
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
###以上是header注解
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-canary
port:
number: 8080
注释
| 注解 | 作用 |
|---|---|
| http://nginx.ingress.kubernetes.io/canary: "true" | 表示此 Ingress 为灰度规则 |
| http://nginx.ingress.kubernetes.io/canary-by-header | 指定用于判断的 HTTP Header 名称 |
| http://nginx.ingress.kubernetes.io/canary-by-header-value | 指定 Header 的值,当匹配该值时流量进入 Canary |
3.1.3 验证服务
kubectl -n devops get deploy |grep dev-demo
kubectl -n devops get svc |grep dev-demo
kubectl -n devops get ingress

普通用户场景访问
#-k忽略证书校验
curl -k https://mydevweb.local
#或
curl -k -H "Host: mydevweb.local" https://mydevweb.local

灰度用户访问
curl -k -H "Host: mydevweb.local" -H "Canary: true" https://mydevweb.local

3.2 基于Cookie灰度发布
3.2.1 prod的ingress-rule
不变
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-prod
namespace: devops
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-prod
port:
number: 8080
3.2.2 canary的ingress-rule
注解内容有变
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-canary
namespace: devops
###canary的cookie注解
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: "Canary"
###以上是cookie注解
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-canary
port:
number: 8080
注释
| 注解 | 含义 |
|---|---|
| nginx.ingress.kubernetes.io/canary | 标记为灰度规则 |
| nginx.ingress.kubernetes.io/canary-by-cookie | 指定 Cookie 名(这里是 Canary) |
3.2.3 验证服务
普通用户场景访问
#-k忽略证书校验
curl -k https://mydevweb.local
#或
curl -k -H "Host: mydevweb.local" https://mydevweb.local

灰度用户场景访问
curl -k -H "Host: mydevweb.local" --cookie "Canary=always" https://mydevweb.local

3.3 基于权重百分比灰度发布
3.3.1 prod的ingress-rule
不变
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-prod
namespace: devops
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-prod
port:
number: 8080
3.3.2 canary的ingress-rule
注解内容有变
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-ingress-canary
namespace: devops
###canary的weight注解
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
###以上是weight注解
spec:
ingressClassName: nginx
tls:
- hosts:
- mydevweb.local
secretName: mydevweb-secret
rules:
- host: mydevweb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev-demo-canary
port:
number: 8080
注释
| 注解 | 含义 |
|---|---|
| nginx.ingress.kubernetes.io/canary | 标记为灰度规则 |
| nginx.ingress.kubernetes.io/canary-weight | 权重百分比 |
3.3.3 验证服务
10次curl请求有2次灰度响应,符合预期
#-k忽略证书校验
for i in {1..10}; do curl -k -H "Host: mydevweb.local" https://mydevweb.local ;sleep 1;echo -e;done

3.3.4 快速调整权重
#填入实际的命名空间和ingress名称
kubectl -n devops annotate ingress dev-ingress-canary nginx.ingress.kubernetes.io/canary-weight="80" --overwrite
调整80后,8次灰度响应,符合预期

3.3.5 全流量切换
全流量切换,通常符合最终版本上线了,优雅几乎无感知
kubectl -n devops annotate ingress dev-ingress-canary nginx.ingress.kubernetes.io/canary-weight="100" --overwrite

评论区