现如今,越来越多的应用采用了微服务架构,这也导致了应用数量相比传统模式更多,管理更加复杂,发布更加频繁,如果直接将新版本上线发布给全部用户。一旦遇到线上事故(或BUG),对用户的影响极大,解决问题周期较长,甚至有时不得不回滚到前一版本,严重影响了用户体验。为了保证整体系统的稳定,风险降到最低,我们可以采用灰度发布与蓝绿发布等不同的发布方式。
金丝雀发布,又称灰度发布,是指通过让小部份用户流量引入的新版本进行测试,如果一切顺利,则可以增加(可能逐渐增加)百分比,逐步替换旧版本。如在过程中出现任何问题,则可以中止并快速回滚到旧版本。最简单的方式是随机选择百分比请求到金丝雀版本,但在更复杂的方案下,则可以基于请求的内容、特定范围的用户或其他属性等。
蓝绿发布,提供了一种零宕机的部署方式,是一种以可观测的方式发布应用的方式,目的减少发布过程中停止时间。在保留老版本的同时部署新版本,将两个版本同时在线,新版本和老版本相互热备,通过切换路由权重的方式(非0即100)实现应用的不同版本上线或者下线,如果有问题可以快速地回滚到老版本。这样做的好处是无需停机,并且风险较小。
部署示例版本:
$ cat demo.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: demo
imagePullPolicy: Always
image: registry.cn-shanghai.aliyuncs.com/kubesre01/demo:v1
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: demo-svc
spec:
type: ClusterIP
selector:
app: demo
ports:
- port: 8080
targetPort: 8080
$ kubectl apply -f demo.yml
deployment.apps/demo created
部署新版本:
$ cat demo_new.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-new
labels:
app: demo-new
spec:
replicas: 1
selector:
matchLabels:
app: demo-new
template:
metadata:
labels:
app: demo-new
spec:
containers:
- name: demo-new
imagePullPolicy: Always
image: registry.cn-shanghai.aliyuncs.com/kubesre01/demo:v2
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: demo-new-svc
spec:
type: ClusterIP
selector:
app: demo-new
ports:
- port: 8080
targetPort: 8080
$ kubectl apply -f demo_new.yml
deployment.apps/demo_new created
创建Ingress记录:
$ cat demo-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-ingress.yml
ingress.networking.k8s.io/demo-ingress created
# 访问如下内容说明部署成功!
$ curl http://demo.kubesre.com/info
{"message":"云原生运维圈!"}
到此为止,示例应用与新版本已部署完毕!
假设线上已运行了一套对外提供的七层demo应用,此时开发了一些新的功能,需要上线新版本demo应用,但是又不想直接替换成新版本demo应用,而是希望将请求头包含user=kubesre
的客户端请求转发到新版本demo应用中,进行验证测试新版本demo应用,等测试验证通过并稳定后,可将所有流量从老版本demo应用切换到新版本demo应用中,再平滑地将老版本demo应用下线。创建新版本Ingress:
$ cat demo-new-canary.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-new-canary
annotations:
# 开启Canary。
nginx.ingress.kubernetes.io/canary: "true"
# 请求头为user
nginx.ingress.kubernetes.io/canary-by-header: "user"
# 请求头user的值为kubesre时,请求才会被路由到新版本服务new-new中。
nginx.ingress.kubernetes.io/canary-by-header-value: "kubesre"
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-new-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-new-canary.yml
ingress.networking.k8s.io/demo-new-canary created
测试验证:
# 请求头为user: kubesre,访问到新的版本
$ curl -H "user: kubesre" http://demo.kubesre.com/info
{"message":"云原生运维圈!新版本"}
# 其他则访问到老的版本
$ curl http://demo.kubesre.com/info
{"message":"云原生运维圈!"}
假设线上已运行了一套对外提供的七层demo应用,此时开发了一些新的功能,需要上线新版本demo应用,又不想直接替换成新版本demo应用,而是只希望公司内部人员能访问到新版本demo应用中,进行测试验证新版本demo应用,非公司内部人员访问还是访问到老版本应用中。等公司内部人员测试验证通过并稳定后,可将所有流量从老版本demo应用切换到新版本demo应用中,再平滑地将老版本demo应用下线。创建新版本Ingress:
$ cat demo-new-canary.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-new-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Forwarded-For"
# 假设客户端来源IP为123.456.789.123
nginx.ingress.kubernetes.io/canary-by-header-value: "123.456.789.123"
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-new-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-new-canary.yml
ingress.networking.k8s.io/demo-new-canary created
测试验证:
# 通过请求头模拟来源IP,真实环境不需要
$ curl -H "X-Forwarded-For:123.456.789.123" http://demo.kubesre.com/info
{"message":"云原生运维圈!新版本"}
# 其他则访问到老的版本
$ curl http://demo.kubesre.com/info
{"message":"云原生运维圈!"}
假设线上已运行了一套对外提供的七层demo应用,此时修复了一些问题,需要上线新版本demo应用,又不想直接替换成新版本demo应用,而是希望将20%的流量切换新版本。待运行一段时间稳定后,可将所有流量从老版本demo应用切换到新版本demo应用中,再平滑地将老版本demo应用下线。创建新版本Ingress:
$ cat demo-new-canary.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-new-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Forwarded-For"
# 将20%的流量转发到新版本
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-new-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-new-canary.yml
ingress.networking.k8s.io/demo-new-canary created
测试验证:
# 可以看出,有4/20的几率由新版本服务响应,符合20%服务权重的设置。
$ for i in {1..20}; do curl http://demo.kubesre.com/info; done;
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!新版本"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!新版本"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!新版本"}{
"message":"云原生运维圈!新版本"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
{"message":"云原生运维圈!"}
Nginx Ingress支持通过配置注解(Annotations)来实现不同场景下的发布和测试,可以满足灰度发布、蓝绿发布、A/B测试等业务场景。具体实现过程如下:为服务创建两个Ingress,一个为常规Ingress,另一个为带nginx.ingress.kubernetes.io/canary: "true"注解的Ingress,称为Canary Ingress;为Canary Ingress配置流量切分策略Annotation,两个Ingress相互配合,即可实现多种场景的发布和测试。Nginx Ingress的Annotation支持以下几种规则:
以上注解规则会按优先级进行评估,优先级为:canary-by-header -> canary-by-cookie -> canary-weight。
本文介绍了金丝雀发布与蓝绿发布,并以企业案例的方式讲解了不同的场景使用什么样的发布方式,下一章将介绍Ingress 证书管理与双向认证,请敬请期待!