ACK Nginx Ingress 灰度发布
灰度及蓝绿发布是为新版本创建一个与老版本完全一致的生产环境,在不影响老版本的前提下,按照一定的规则把部分流量切换到新版本,当新版本试运行一段时间没有问题后,将用户的全量流量从老版本迁移至新版本。
其中 A/B 测试就是一种灰度发布方式,一部分用户继续使用老版本的服务,将一部分用户的流量切换到新版本,如果新版本运行稳定,则逐步将所有用户迁移到新版本。
采用 Ingress 灰度发布方式用户可以:
控制新版本流量分配权重,以小部分线上流量对服务进行验证
通过 cookie 或者 header 使得部分受控用户在线上对发布进行验证
当发布验证失败后,可以快速回滚到旧版本
使用前提
使用由阿里云容器服务 Kubernetes 版本提供的 Kubernetes 集群
Deployment 的 labels 内包含有 Service 的全部 selector labels,如:
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: spring-boot-sample
name: spring-boot-sample
spec:
replicas: 2
selector:
matchLabels:
run: spring-boot-sample
template:
metadata:
labels:
run: spring-boot-sample
spec:
containers:
- image: ${IMAGE}
name: app
---
apiVersion: v1
kind: Service
metadata:
name: spring-boot-service
labels:
test: test
labele2: label
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
run: spring-boot-sample
sessionAffinity: None
type: ClusterIP
实现原理
如下图所示,当采用 ACK Nginx Ingress 灰度发布时,假定当前运行版本为 primary,将要发布版本为 canary。

发布过程:
发布前检查:预检查当前 Ingress 是否有且只关联了唯一的 Service 实例,且 Service 实例下有且只有唯一版本的 Deployment。
生成 Canary 版本:克隆 primary 版本的 Service 以及 Deployment 生成 canary 版本的 Service 和 Deployment,同时修改 canary 版本 Deployment 的镜像到新版本。
修改 Ingress 流量规则:根据发布配置调整 Ingress 配置,开始执行灰度。
人工验证:通过 cookie 或者 header 对灰度版本进行验证,根据结果选择完成发布或者回滚。
完成灰度:修改 Ingress 配置以及流量规则,下线 Primary 版本的 Service 以及 Deployment 实例。
回滚发布:修改 Ingress 配置以及流量规则,下线 Canary 版本的 Service 以及 Deployment。
快速开始
1. 初始化应用部署
通过以下配置快速初始化应用部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: old-nginx
labels:
run: old-nginx
spec:
replicas: 2
selector:
matchLabels:
run: old-nginx
template:
metadata:
labels:
run: old-nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx # 容器名称
ports:
- containerPort: 80
protocol: TCP
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: old-nginx
spec:
ports:
- port: 80 #服务端口
protocol: TCP
targetPort: 80 #应用端口
selector:
run: old-nginx
sessionAffinity: None
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gray-release
spec:
rules:
- host: www.example.com
http:
paths:
# 老版本服务
- path: /
backend:
serviceName: old-nginx
servicePort: 80
保存以上内容到,gray-release.yaml,使用 kubectl 或者控制台完成应用初始化:
kubectl apply -f gray-release.yaml
2. 配置流水线
如下所示,配置如下信息:

配置说明:
配置项 | 说明 |
命名空间 | 当前服务所在 Kubernetes 集群命名空间, 示例中为 default |
Ingress名称 | 发布的目标 Ingress 实例名称,示例中为 gray-release |
服务端口 | Ingress 后端 Service 实例对外暴露的端口,示例中为 80 |
应用端口 | 镜像对外暴露的端口,示例中为 80 |
容器名称 | 应用发布时需要更新镜像的容器名,示例中为 nginx |
镜像 | 通过前序任务镜像构建产生的镜像,或者是特定的镜像名称 |
灰度方式 | 选择使用 header 或者 cookie 方式进行灰度验证,并设置匹配的 Key/Value |
灰度初始化流量权重 | 默认灰度版本上线后的流量权重 |
启动等待时间 | 灰度版本发布后等待该时间后再修改 Ingress 配置 |
下线等待时间 | Ingress 配置调整后等待该时长后再移除应用实例 |
3. 运行流水线
保存并运行流水线,当新版本发布后,流水线将任务将处于灰度发布中,等待人工验证以确定后续操作。
灰度中查看Kubernetes资源状态如下所示:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"nginx.ingress.kubernetes.io/service-match":"gray-release-v20201231112858: cookie(\"foo\", /^bar$/)","nginx.ingress.kubernetes.io/service-weight":"gray-release-v20201231112858: 0, old-nginx: 100"},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"old-nginx","servicePort":80},"path":"/","pathType":"ImplementationSpecific"},{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}}
nginx.ingress.kubernetes.io/service-match: 'gray-release-v20201231112858: cookie("foo", /^bar$/)'
nginx.ingress.kubernetes.io/service-weight: 'gray-release-v20201231112858: 0, old-nginx: 100'
name: gray-release
namespace: default
spec:
rules:
- host: www.example.com
http:
paths:
- backend:
serviceName: old-nginx
servicePort: 80
path: /
pathType: ImplementationSpecific
- backend:
serviceName: gray-release-v20201231112858
servicePort: 80
path: /
pathType: ImplementationSpecific
在灰度中云效自动创建了灰度版本的服务,命名规则未:<ingressname>-<version>
查看灰度版本的 Service 以及 Deployment 详情:
apiVersion: v1
kind: Service
metadata:
labels:
run: old-nginx
version: v20201231112858
name: gray-release-v20201231112858
namespace: default
spec:
clusterIP: 10.0.6.88
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: old-nginx
version: v20201231112858
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
generation: 1
labels:
run: old-nginx
version: v20201231112858
name: gray-release-v20201231112858
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
run: old-nginx
version: v20201231112858
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
run: old-nginx
version: v20201231112858
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
protocol: TCP
用户可以通过在浏览器设置 cookie 的后来访问新版本的应用:
document.cookie="foo=bar"
4. 确认发布完成
在人工验证成功后,在卡片上点击“完成”按钮,后流水线将会自动完成ingress的配置调整,以及老版本的应用下线操作:

此时查看线上应用 Ingress 信息,如下所示:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}}
name: gray-release
namespace: default
spec:
rules:
- host: www.example.com
http:
paths:
- backend:
serviceName: gray-release-v20201231112858
servicePort: 80
path: /
pathType: ImplementationSpecific
---
常见发布失败的问题:
当 Ingress 关联了多个 Service 实例时发布失败。
当 Service 关联了多个 Deployment 实例时发布失败,Service 的 LabelSelector 需要确保与 Deployment 的 labels 保持匹配。
线上版本镜像和当前发布镜像未变化时,发布失败。
而发布配置中的容器名称无法匹配到容器定义时发布失败。