基于ingress做灰度发布

前言

在日常业务中我们会经常对应用进行发版,常用到的发布方式有滚动更新、蓝绿发布、灰度发布。

  • 滚动更新:依次进行旧版替换,直到旧的全部被替换为止。
  • 蓝绿发布:2套系统版本,待上线称为蓝系统。当蓝系统测试完成用户流量接入后,蓝系统将称为绿系统,以前的绿系统就可销毁。这个主要用在早先物理机环境,成本代价高。
  • 灰度发布:1套集群中2个版本,灰度版本只内部及少部分用户使用。待稳定后将灰度升级为稳定版本,旧版本下线,也称为金丝雀发布。

ingress-nginx

ingress-nginx是k8s官方推荐的ingress controller,它基于nginx实现增加了一组用于实现额外功能的Lua组件。

为了实现灰度发布,ingress-nginx通过定义annotation来实现不同场景的灰度发布。

  • nginx.ingress.kubernetes.io/canary-weight: 基于服务权重的流量切分,权重范围0-100按百分比将请求路由到Canary Ingress中指定的服务。
  • nginx.ingress.kubernetes.io/canary-by-header: 基于Request Header的流量切分,当Request Header设置为always时,请求将会被一直发送到Canary版本;设置为never则不会发送到Canary;对于其他任何Header值将忽略。
  • nginx.ingress.kubernetes.io/canary-by-header-value: 匹配Request Header的值,将请求指定到Canary中指定的服务。该规则允许用户自定义Request Header的值,必须与上面annotation(canary-by-header)一起使用。
  • nginx.ingress.kubernetes.io/canary-by-cookie: 基于cookie的流量切分,用于通知ingress将请求路由到cannry指定的服务cookie。当cookie值设置为always时,它将被路由到canary;当设置为never时请求不会路过去;对于任何其他值将忽略cookie并将请求与其他规则进行优先级比较。
1
2
3
4
5
6
通过annotation来实现灰度发布,整体思路如下:
1. 集群中部署2套不同版本系统,一个是stable版,一个是canary版,2个版本都有自己的service。
2. 定义2个ingress配置,一个正常提供服务,一个增加canary的annotation
3. 待canary本本无误后,将其切换成stable版本,并且将旧版本下线,流量全部接入新的stable版本。

金丝雀规则按优先顺序进行如下排序:canary-by-header - > canary-by-cookie - > canary-weight

准备工作

  • 镜像

    1
    mirrorgooglecontainers/echoserver:1.10
  • yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s.kuboard.cn/name: echoserverv1
name: echoserverv1
namespace: default
spec:
replicas: 1
selector:
matchLabels:
k8s.kuboard.cn/name: echoserverv1
template:
metadata:
labels:
k8s.kuboard.cn/name: echoserverv1
spec:
containers:
- image: 'mirrorgooglecontainers/echoserver:1.10'
imagePullPolicy: IfNotPresent
name: echoserverv1
ports:
- containerPort: 8080
name: echoserverv1
protocol: TCP

---
apiVersion: v1
kind: Service
metadata:
labels:
k8s.kuboard.cn/name: echoserverv1
name: echoserverv1
namespace: default
spec:
ports:
- name: echoserverv1
port: 8080
protocol: TCP
targetPort: 8080
selector:
k8s.kuboard.cn/name: echoserverv1
type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
k8s.kuboard.cn/name: echoserverv1
name: echoserverv1
namespace: default
spec:
ingressClassName: liykingress-ng
rules:
- host: test.key1024.cn
http:
paths:
- backend:
service:
name: echoserverv1
port:
number: 8080
path: /
pathType: Prefix

canary版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s.kuboard.cn/name: echoserverv2
name: echoserverv2
namespace: default
spec:
replicas: 1
selector:
matchLabels:
k8s.kuboard.cn/name: echoserverv2
template:
metadata:
labels:
k8s.kuboard.cn/name: echoserverv2
spec:
containers:
- image: 'mirrorgooglecontainers/echoserver:1.10'
imagePullPolicy: IfNotPresent
name: echoserverv1
ports:
- containerPort: 8080
name: echoserverv2
protocol: TCP

---
apiVersion: v1
kind: Service
metadata:
labels:
k8s.kuboard.cn/name: echoserverv2
name: echoserverv2
namespace: default
spec:
ports:
- name: echoserverv2
port: 8080
protocol: TCP
targetPort: 8080
selector:
k8s.kuboard.cn/name: echoserverv2
type: ClusterIP
  • 基于权重
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: 'true'
nginx.ingress.kubernetes.io/canary-weight: '10'
labels:
k8s.kuboard.cn/name: echoserverv2
name: echoserverv2
namespace: default
spec:
ingressClassName: liykingress-ng
rules:
- host: test.key1024.cn
http:
paths:
- backend:
service:
name: echoserverv2
port:
number: 8080
path: /
pathType: Prefix

# 访问测试
[root@master ~]# for i in `seq 10`;do sleep 0.1; curl -s test.key1024.cn:30044 |grep Hostname;done
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
Hostname: echoserverv1-54ccbbbd7f-lkkwr
  • 基于用户请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: 'true'
nginx.ingress.kubernetes.io/canary-by-header: "region"
nginx.ingress.kubernetes.io/canary-by-header-value: "xian"
labels:
k8s.kuboard.cn/name: echoserverv2
name: echoserverv2
namespace: default
spec:
ingressClassName: liykingress-ng
rules:
- host: test.key1024.cn
http:
paths:
- backend:
service:
name: echoserverv2
port:
number: 8080
path: /
pathType: Prefix

# 访问测试
[root@master ~]# for i in `seq 10`;do sleep 0.1; curl -H 'region: xian' -s test.key1024.cn:30044 |grep Hostname;done
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
Hostname: echoserverv2-775cf8cb7b-zwxkp
-------------本文结束感谢您的阅读-------------
原创技术分享,感谢您的支持。