灰度/滚动发布
灰度发布是金丝雀发布的延伸,是将发布分成不同的阶段/批次,每个阶段/批次的用户数量逐级增加。如果新版本在当前阶段没有发现问题,就再增加用户数量进入下一个阶段,直至扩展到全部用户。
灰度发布可以减小发布风险,是一种零宕机时间的发布策略。它通过切换线上并存版本之间的路由权重,逐步从一个版本切换为另一个版本。整个发布过程会持续比较长的时间, 在这段时间内,新旧代码共存,所以在开发过程中,需要考虑版本之间的兼容性,新旧代码共存不能影响功能可用性和用户体验。当新版本代码出现问题时,灰度发布能够比较快的回滚到老版本的代码上。
结合特性开关等技术,灰度发布可以实现更复杂灵活的发布策略。
优势:
-
用户体验影响比较小, 不需要停机发布
-
能够控制发布风险
劣势:
-
发布时间会比较长
-
需要复杂的发布系统和负载均衡器
-
需要考虑新旧版本共存时的兼容性
适用场景:
-
适合可用性较高的生产环境发布
通过注解来实现流量的分发
灰度也是有一个线上的环境,现在要发灰度的时候,要让一小部分用户去收到这个新版本,这一小部分用户可以通过cooki或者header,或者通过ingress权重的方式去分配。
- V1 旧版本正常运行中, 用户请求流量都在V1
- V2 新版本开始发布, ingress控制, 通过header/cookie/weight 将一部分流量引到V2 10%
- 更新Ingress , 流量扩大到30%
- 更新Ingress , 流量扩大到60%
- 更新Ingress , 流量扩大到90%
- 更新Ingress , 流量扩大到100%
- 此时测试没问题, 用V2 helm chart 部署替换V1旧版本。
[root@master devops]# kubectl get svc -n devops
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginxapp ClusterIP 10.233.34.137 <none> 80/TCP 46h
nginxnpm ClusterIP 10.233.18.40 <none> 80/TCP 46h
[root@master devops]# kubectl get pod -n devops
NAME READY STATUS RESTARTS AGE
nginx-npm-blue-6c5ddc9ffd-jl25q 1/1 Running 1 46h
nginx-npm-blue-6c5ddc9ffd-qx2kw 1/1 Running 1 46h
nginx-npm-blue-6c5ddc9ffd-vhtnx 1/1 Running 1 46h
nginxapp-1.20-green-5668c79ff7-9l7kl 1/1 Running 1 46h
nginxapp-1.20-green-5668c79ff7-fckpc 1/1 Running 1 46h
nginxapp-1.20-green-5668c79ff7-vmvq9 1/1 Running 1 46h
[root@master ~]# curl 10.233.18.40
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>vuedemo</title><link href=/static/css/app.30790115300ab27614ce176899523b62.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.0aad2172a75b8ed4f46c.js></script><script type=text/javascript src=/static/js/app.2f2e5edd9af2c59aa514.js></script></body></html>[root@master ~]#
[root@master ~]#
[root@master ~]# curl 10.233.34.137
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
现在生产
[root@master devops]# cat ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxnpm-ingress
namespace: devops
spec:
rules:
- host: nginxnpm.devops.com
http:
paths:
- path: /
backend:
serviceName: nginxapp
servicePort: 80
基于权重的发布
[root@master devops]# cat ingress-weight.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxapp-canary
namespace: devops
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "50"
spec:
rules:
- host: nginxnpm.devops.com
http:
paths:
- path: /
backend:
serviceName: nginxnpm
servicePort: 80
访问同一个页面两次,分别请求到了不同的版本。
Header报头流量分发
在访问的时候可以通过请求头进行区分的,比如来自于北京的,那么可以先让来自北京的用户先看到,那么就可以根据请求头里面的信息来匹配。
下面就是北京来的请求访问新版本。
[root@master devops]# cat ingress-header.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxapp-canary
namespace: devops
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "Region"
nginx.ingress.kubernetes.io/canary-by-header-value: "bj"
spec:
rules:
- host: nginxnpm.devops.com
http:
paths:
- path: /
backend:
serviceName: nginxnpm
servicePort: 80
杭州来的用户访问,流量用的还是老版本
[root@master devops]# for i in {1..2};do curl -H "region: hz" nginxnpm.devops.com && sleep 5s;done
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
北京来的用户访问,用的流量是新版本
[root@master devops]# for i in {1..2};do curl -H "region: bj" nginxnpm.devops.com && sleep 5s;done
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>vuedemo</title><link href=/static/css/app.30790115300ab27614ce176899523b62.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.0aad2172a75b8ed4f46c.js></script><script type=text/javascript src=/static/js/app.2f2e5edd9af2c59aa514.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>vuedemo</title><link href=/static/css/app.30790115300ab27614ce176899523b62.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.0aad2172a75b8ed4f46c.js></script><script type=text/javascript src=/static/js/app.2f2e5edd9af2c59aa514.js></script></body></html>[root@master devops]#
根据cookie流量分发
cookie只有两个值,一个是always(新版本),还有一个是never
[root@master devops]# cat ingress-cookie.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxapp-canary
namespace: devops
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: "from"
spec:
rules:
- host: nginxnpm.devops.com
http:
paths:
- path: /
backend:
serviceName: nginxnpm
servicePort: 80
[root@master devops]# curl nginxnpm.devops.com --cookie "from=always"
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>vuedemo</title><link href=/static/css/app.30790115300ab27614ce176899523b62.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.0aad2172a75b8ed4f46c.js></script><script type=text/javascript src=/static/js/app.2f2e5edd9af2c59aa514.js></script></body></html>[root@master devops]#
[root@master devops]#
[root@master devops]#
[root@master devops]#
[root@master devops]# curl nginxnpm.devops.com --cookie "from=a"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>