文章目录
- 一、理论:实现灰度发布的几种场景
- 1、场景一:将新版本灰度给部分用户
- 2、场景二:按照比例流程给新版本
- 3、实现灰度发布字段解释
- 二、实践:
- 1、实验前提环境
- 2、基于Request Header(请求头)进行流量分割
- 3、基于Cookie进行流量切分
- 4、基于服务权重进行流量切分
一、理论:实现灰度发布的几种场景
1、场景一:将新版本灰度给部分用户
假设线上运行了一套对外提供 7 层服务的 Service A 服务,后来开发了个新版本 Service AA需要上线,但不想直接替换掉原来的 Service A,希望先灰度一小部分用户,等运行一段时间足够稳定了再逐渐全量上线新版本,最后平滑下线旧版本。
这个时候就可以利用 Nginx Ingress 基于 Header 或 Cookie 进行流量切分的策略来发布,业务使用 Header 或 Cookie 来标识不同类型的用户,我们通过配置 Ingress 来实现让带有指定 Header 或 Cookie 的请求被转发到新版本,其它的仍然转发到旧版本,从而实现将新版本灰度给部分用户。
2、场景二:按照比例流程给新版本
假设线上运行了一套对外提供 7 层服务的 Service B 服务,后来修复了一些问题,需要灰度上线一个新版本 Service BB,但又不想直接替换掉原来的 Service B而是让先切 10% 的流量到新版本。
等观察一段时间稳定后再逐渐加大新版本的流量比例直至完全替换旧版本,最后再滑下线旧版本,从而实现切一定比例的流量给新版本。
3、实现灰度发布字段解释
Ingress-Nginx是一个K8S ingress工具,支持配置Ingress Annotations来实现不同场景下的灰度发布和测试。 Nginx Annotations 支持以下几种Canary规则:
假设我们现在部署了两个版本的服务,老版本和canary版本
-
nginx.ingress.kubernetes.io/canary-by-header:基于Request Header的流量切分,适用于灰度发布以及 A/B 测试。当Request Header 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口。
-
nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。
-
nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为60意味着60%流量转到canary。权重为 100 意味着所有请求都将被发送到 Canary 入口。
-
nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的cookie。当 cookie 值设置为 always时,它将被路由到 Canary 入口;当 cookie 值设置为 never时,请求不会被发送到 Canary 入口。
二、实践:
1、实验前提环境
1、增加V1环境:
cat v1.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
imagePullPolicy: IfNotPresent
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts: # 挂载卷
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes: # 定义卷,引用configMap
- name: config
configMap:
name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v1
name: nginx-v1
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v1")
';
}
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-v1
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
version: v1
2、增加V2环境:
cat v2.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v2
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
imagePullPolicy: IfNotPresent
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts:
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v2
name: nginx-v2
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v2")
';
}
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-v2
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
version: v2
3、执行YAML文件
kubectl apply -f v1.yaml
kubectl apply -f v2.yaml
4、验证Pod是启动
kubectl get pods -o wide
5、验证,请求Pod内容
curl http://10.244.247.2
curl http://10.244.84.134
如上图,请求不同版本的环境,返回不同版本号,表示无误。
2、基于Request Header(请求头)进行流量分割
1、创建v1版本的ingress规则
cat v1-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-v1
spec:
ingressClassName: nginx
rules:
- host: qinzt.ingress.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v1
port:
number: 80
执行YAML文件:
kubectl apply -f v1-ingress.yaml
访问验证一下:16.32.15.201:30080
是我ingress-nginx访问地址
curl -H "Host: qinzt.ingress.com" http://16.32.15.201:30080
nginx-v1
2、创建ingress规则,基于Request Header进行流量分割
cat v2-request.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "Region" #基于Request Header的流量切分
nginx.ingress.kubernetes.io/canary-by-header-pattern: "cd|sz" #Header信息中带有 Region=cd,Region=sz的转发到此ingress
name: nginx-request-v2
spec:
ingressClassName: nginx
rules:
- host: qinzt.ingress.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v2
port:
number: 80
执行YAML文件:
kubectl apply -f v2-request.yaml
访问验证一下:16.32.15.201:30080
是我ingress-nginx访问地址
curl -H "Host: qinzt.ingress.com" -H "Region: cd" http://16.32.15.201:30080
curl -H "Host: qinzt.ingress.com" -H "Region: sz" http://16.32.15.201:30080
OK,可以看到上图,携带Region: cd
、Region: sz
请求头消息,会转发到V2环境。
3、基于Cookie进行流量切分
与前面 Header 类似,不过使用 Cookie 就无法自定义 value 了,这里以模拟灰度成都地域用户为例,仅将带有名为 user_from_cd
的 cookie 的请求转发给当前V2环境 。
1、先删除前面基于 Header 的流量切分的 Ingress
kubectl delete -f v2-request.yaml
2、创建基于cookie 转发的ingress规则
cat v2-cookie.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: "user_from_cd" # 匹配cookie携带user_from_cd,使用此ingress规则
name: nginx-cookie-v2
spec:
ingressClassName: nginx
rules:
- host: qinzt.ingress.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v2
port:
number: 80
执行YAML文件
kubectl apply -f v2-cookie.yaml
3、测试验证,http://16.32.15.201:30080
是我ingress nginx地址
curl -s -H "Host: qinzt.ingress.com" --cookie "user_from_cd=always" http://16.32.15.201:30080
nginx-v2
curl -s -H "Host: qinzt.ingress.com" --cookie "user_from_bj=always" http://16.32.15.201:30080
nginx-v1
可以看到只有cookie=user_from_cd
的才会转发到V2的ingress
4、基于服务权重进行流量切分
基于服务权重的直接定义需要导入的流量比例,这里以导入 10% 流量到 v2 环境版本为。
1、删除上面基于cookie的ingress规则
kubectl delete -f v2-cookie.yaml
2、创建基于权重流量分配的ingress规则
cat v2-weight.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
name: nginx-weight-v2
spec:
ingressClassName: nginx
rules:
- host: qinzt.ingress.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v2
port:
number: 80
执行YAML文件:
kubectl apply -f v2-weight.yaml
3、测试验证
for i in {1..10}; do curl -H "Host: qinzt.ingress.com" http://16.32.15.201:30080; done;
OK,如上图大约10%的记录会请求到V2环境上面