基于Nginx Ingress实现灰度发布
什么是灰度发布和蓝绿发布?
简单来说,就是不要一次性把所有流量都切到新版本,而是先让一小部分用户试用新版本,观察一段时间没问题了再逐步放量。
- 灰度发布:按照一定规则(比如特定用户、特定地区、或者按百分比)把部分流量导到新版本
- 蓝绿发布:其实是灰度发布的一种特殊形式,通过权重控制流量分配,比如先给新版本10%流量,稳定后再调到50%,最后100%。与灰度发布不同的是,蓝绿发布通常是指同时存在两个完整的生产环境(蓝色和绿色),通过切换流量来实现无缝发布。
这样做的好处很明显:
- 新版本有问题可以快速回滚,影响范围小
- 可以在真实环境中验证新功能
- 用户无感知,体验更好
Nginx Ingress的Canary机制
Nginx Ingress Controller提供了一套Canary(金丝雀)机制来实现灰度发布,主要通过几个annotation来控制:
nginx.ingress.kubernetes.io/canary: "true"- 开启Canary模式nginx.ingress.kubernetes.io/canary-by-header- 基于请求头匹配nginx.ingress.kubernetes.io/canary-by-header-value- 请求头的具体值nginx.ingress.kubernetes.io/canary-by-cookie- 基于cookie匹配nginx.ingress.kubernetes.io/canary-weight- 基于权重分配流量(0-100)
这几种方式的优先级是:canary-by-header > canary-by-cookie > canary-weight
需要注意的是,一个Ingress规则只能对应一个Canary Ingress,配置多个的话只有第一个会生效。另外,Canary Ingress必须与普通Ingress具有相同的host和path规则才能生效。
灰度发布流程图
我画了一张图展示了基于Nginx Ingress实现灰度发布的完整流程:

从图中可以看出,灰度发布主要包括以下几个关键步骤:
- 部署老版本服务并创建普通Ingress规则
- 部署新版本服务
- 创建Canary Ingress规则,配置灰度策略(基于请求头、cookie或权重)
- 逐步调整流量分配比例
- 完全切换到新版本并清理资源
实战
下面我们用一个实际例子来演示整个流程。假设我们有一个Nginx服务要从老版本升级到新版本。
第一步:部署老版本服务
首先把现有的老版本服务部署起来,这个比较简单。
创建 old-nginx-deployment-and-service.yaml:
1 | apiVersion: apps/v1 |
然后创建Ingress规则 ingress.yaml:
1 | apiVersion: networking.k8s.io/v1 |
执行部署命令:
1 | kubectl apply -f old-nginx-deployment-and-service.yaml |
测试一下是否正常:
1 | # 获取Ingress的外部IP |
如果能看到nginx的欢迎页面,说明老版本服务已经正常运行了。
第二步:部署新版本服务
现在我们要上线新版本了,先把新版本的服务部署起来。
创建 new-nginx-deployment-and-service.yaml:
1 | apiVersion: apps/v1 |
部署新版本:
1 | kubectl apply -f new-nginx-deployment-and-service.yaml |
这时候新老版本都在运行,但是流量还都在老版本上。
第三步:配置灰度规则
接下来是关键步骤,我们要配置灰度规则。这里有两种常用的方式:
方式一:基于请求头的灰度(适合灰度发布以及AB测试场景)
这种方式适合给特定用户开放新功能,比如内部测试人员或者VIP用户。
创建 ingress-canary-rule1.yaml:
1 | apiVersion: networking.k8s.io/v1 |
应用配置:
1 | kubectl apply -f ingress-canary-rule1.yaml |
测试效果:
1 | # 普通请求,还是访问老版本 |
这样就实现了精准的流量控制,只有带特定请求头的请求才会路由到新版本。实际使用中,可以在客户端给测试用户的请求加上这个header,或者用 X-Canary-Version 这种更语义化的header名。
除了基于请求头,还可以使用基于cookie的方式,只需将annotation改为:
1 | nginx.ingress.kubernetes.io/canary-by-cookie: "canary" |
这样当请求中包含名为canary的cookie且值为always时,请求会被路由到Canary版本。
方式二:基于权重的灰度(适合蓝绿发布)
这种方式更常用,按百分比逐步放量。
创建 ingress-canary-rule2.yaml:
1 | apiVersion: networking.k8s.io/v1 |
应用配置:
1 | kubectl apply -f ingress-canary-rule2.yaml |
测试效果:
1 | # 多次执行这个命令 |
你会发现大概一半的请求会路由到新版本,一半还在老版本,这就是50%的流量分配。
实际操作中,可以这样逐步放量:
- 先设置
canary-weight: "10"给新版本10%流量,观察一段时间 - 没问题就改成
canary-weight: "30",继续观察 - 再改成
canary-weight: "50" - 最后改成
canary-weight: "100",或者直接进入下一步
第四步:完全切换到新版本
经过一段时间的观察,新版本运行稳定,没有出现问题,现在可以完全切换到新版本了。
这里有个小技巧:不是直接删除老版本,而是让老版本的Service指向新版本的Deployment。这样做的好处是,如果后面发现问题,可以快速回滚。
修改 old-nginx-deployment-and-service.yaml 中的Service部分:
1 | apiVersion: v1 |
应用修改:
1 | kubectl apply -f old-nginx-deployment-and-service.yaml |
验证一下:
1 | curl -H "Host: www.example.com" http://<EXTERNAL_IP> |
确认没问题后,就可以清理资源了:
1 | # 删除Canary Ingress |
最佳实践提示:在生产环境中,建议保留老版本的Deployment一段时间(比如一周),并将副本数设置为0,这样如果新版本出现严重问题,可以快速恢复老版本,而不需要重新构建镜像。
至此,整个灰度发布流程就完成了。
实际使用中的一些注意事项
监控很重要:在灰度过程中,一定要密切关注监控指标,比如错误率、响应时间、CPU和内存使用情况等。
逐步放量:不要一上来就给50%流量,建议从5%或10%开始,每次翻倍,比如 5% -> 10% -> 25% -> 50% -> 100%。
设置观察期:每次调整权重后,至少观察15-30分钟,确保没有问题再继续。
准备回滚方案:虽然灰度发布已经很安全了,但还是要准备好快速回滚的方案。最简单的方法就是把Canary Ingress删掉,流量就全部回到老版本了。
日志和告警:配置好日志收集和告警规则,一旦出现异常能第一时间发现。
Canary规则注意事项:
- Canary Ingress必须与普通Ingress具有相同的host和path规则
- 一个普通Ingress只能对应一个Canary Ingress
- Canary Ingress的创建时间必须晚于普通Ingress
- 建议给Canary Ingress添加明确的标签,方便管理
总结
使用Nginx Ingress Controller实现灰度发布和蓝绿发布其实并不复杂,核心就是利用Canary机制:
- 基于请求头的方式适合AB测试和内部测试
- 基于权重的方式适合逐步放量的蓝绿发布
通过合理配置Canary规则,我们可以实现平滑的应用升级,最大程度降低新版本上线风险,提升用户体验。在实际生产环境中,建议结合监控、日志和告警系统,形成完整的发布流程,确保应用的高可用性和稳定性。