前提条件
使用ECK应用管理功能安装 eck-ingress-nginx Helm Chart。
基本概念
灰度及蓝绿发布是为新版本创建一个与老版本完全一致的生产环境,在不影响老版本的前提下,按照一定的规则把部分流量切换到新版本,当新版本试运行一段时间没有问题后,将用户的全量流量从老版本迁移至新版本。
其中蓝绿发布就是一种灰度发布方式,一部分用户继续使用老版本的服务,将一部分用户的流量切换到新版本,如果新版本运行稳定,则逐步将所有用户迁移到新版本。
场景说明
基于客户端请求的流量切分场景
假设当前线上环境,您已经有一套服务Service A对外提供7层服务,此时上线了一些新的特性,需要发布上线一个新的版本Service A'。但又不想直接替换Service A服务,而是希望将请求头中包含foo=bar或者Cookie中包含foo=bar的客户端请求转发到Service A'服务中。待运行一段时间稳定后,可将所有的流量从Service A切换到Service A'服务中,再平滑地将Service A服务下线。
基于服务权重的流量切分场景
假设当前线上环境,您已经有一套服务Service B对外提供7层服务,此时修复了一些问题,需要发布上线一个新的版本Service B'。但又不想将所有客户端流量切换到新版本Service B'中,而是希望将20%的流量切换到新版本Service B'中。待运行一段时间稳定后,再将所有的流量从Service B切换到Service B'服务中,再平滑地将Service B服务下线。
针对以上的应用发布场景,ECK的Ingress Controller支持下面的流量切分方式:
- 基于Request Header的流量切分,适用于灰度发布。
- 基于Cookie的流量切分,适用于灰度发布。
- 基于服务权重的流量切分,适用于蓝绿发布。
使用canary-*注解实现灰度发布和蓝绿发布
注解说明
Annotation | 说明 |
---|---|
nginx.ingress.kubernetes.io/canary | 是否开启灰度发布机制。取值:true :启用 canary 功能 false :关闭 canary 功能 |
nginx.ingress.kubernetes.io/canary-by-header | 表示基于请求头的名称进行灰度发布,取值:always :无论什么情况下,流量均会进入灰度服务。 never :无论什么情况下,流量均不会进入灰度服务 |
nginx.ingress.kubernetes.io/canary-by-header-value | 表示基于请求头的值进行灰度发布。需要与canary-by-header头配合使用。 |
nginx.ingress.kubernetes.io/canary-by-header-pattern | 表示基于请求头的值进行灰度发布,并对请求头的值进行正则匹配。需要与canary-by-header头配合使用。取值为用于匹配请求头的值的正则表达式。 |
nginx.ingress.kubernetes.io/canary-by-cookie | 表示基于Cookie进行灰度发布。例如,nginx.ingress.kubernetes.io/canary-by-cookie: foo 。Cookie内容的取值:* always :当 foo=always ,流量会进入灰度服务。* never :当 foo=never ,流量不会进入灰度服务。 |
nginx.ingress.kubernetes.io/canary-weight | 表示基于权重进行灰度发布,权限取值:0~100。 |
不同灰度方式的优先级由高到低为:
canary-by-header
>canary-by-cookie
>canary-weight
步骤一:部署旧版本服务
- 创建Deployment和Service。
apiVersion: apps/v1
kind: Deployment
metadata:
name: old-nginx
spec:
replicas: 2
selector:
matchLabels:
run: old-nginx
template:
metadata:
labels:
run: old-nginx
spec:
containers:
- image: harbor.ctyuncdn.cn/eecdn/nginx:old
imagePullPolicy: Always
name: old-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
- 创建Ingress。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gray-release
spec:
rules:
- host: a.ctyun.cn
http:
paths:
# 老版本服务。
- path: /
backend:
service:
name: old-nginx
port:
number: 80
pathType: ImplementationSpecific
步骤二:部署新版本服务
- 创建新版本的Deployment和Service。
apiVersion: apps/v1
kind: Deployment
metadata:
name: new-nginx
spec:
replicas: 1
selector:
matchLabels:
run: new-nginx
template:
metadata:
labels:
run: new-nginx
spec:
containers:
- image: harbor.ctyuncdn.cn/eecdn/nginx:new
imagePullPolicy: Always
name: new-nginx
ports:
- containerPort: 80
protocol: TCP
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: new-nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: new-nginx
sessionAffinity: None
type: NodePort
步骤三:灰度发布验证
以设置满足特定规则request header的请求才能访问新版本服务为例(以下示例仅请求头中满足foo=bar的客户端请求才能路由到新版本服务)来验证:
- 创建新的Ingress。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gray-release-canary
annotations:
# 开启Canary。
nginx.ingress.kubernetes.io/canary: "true"
# 请求头为foo。
nginx.ingress.kubernetes.io/canary-by-header: "foo"
# 请求头foo的值为bar时,请求才会被路由到新版本服务new-nginx中。
nginx.ingress.kubernetes.io/canary-by-header-value: "bar"
spec:
rules:
- host: a.ctyun.cn
http:
paths:
# 新版本服务。
- path: /
backend:
service:
name: new-nginx
port:
number: 80
pathType: ImplementationSpecific
- 不携带特殊请求头验证。
curl -H "Host: a.ctyun.cn" http://
期望返回:old
- 携带特殊请求头验证。
curl -H "Host: a.ctyun.cn" -H "foo: bar" http://
期望返回:new