灰度發布和藍綠發布
當對服務進行版本更新升級時,需要使用到滾動升級、分批暫停發布、藍綠發布以及灰度發布等發布方式。本文將介紹在云容器引擎集群中如何通過Nginx Ingress Controller來實現應用服務的灰度發布。
背景信息
灰度及藍綠發布是為新版本創建一個與老版本完全一致的生產環境,在不影響老版本的前提下,按照一定的規則把部分流量切換到新版本,當新版本試運行一段時間沒有問題后,將用戶的全量流量從老版本遷移至新版本。
其中AB測試就是一種灰度發布方式,一部分用戶繼續使用老版本的服務,將一部分用戶的流量切換到新版本,如果新版本運行穩定,則逐步將所有用戶遷移到新版本。
云容器引擎控制臺灰度發布功能的用法如下。
canary-注解方式:使用canary- Annotation配置藍綠發布與灰度發布,canary-* Annotation是社區官方實現的灰度發布方式。
應用場景
基于客戶端請求的流量切分場景:假設當前線上環境,您已經有一套服務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服務下線。




針對以上多種不同的應用發布需求,天翼云容器云服務引擎Ingress Controller支持了多種流量切分方式:基于Request Header的流量切分,適用于灰度發布以及AB測試場景。基于Cookie的流量切分,適用于灰度發布以及AB測試場景。基于Query Param的流量切分,適用于灰度發布以及AB測試場景。基于服務權重的流量切分,適用于藍綠發布場景。
canary-注解方式
注解說明
Nginx Ingress Controller通過下列canary-* Annotation來支持應用服務的灰度發布機制。
nnotation 說明 適用的容器?Nginx Ingress Controller版本 nginx.ingress.kubernetes.io/canary 必須設置該Annotation值為?true,否則其它規則將不會生效。
取值: true:啟用canary功能。 false:不啟用canary功能。≥v1.3.1 nginx.ingress.kubernetes.io/canary-by-header 表示基于請求頭的名稱進行灰度發布。
請求頭名稱的特殊取值:always:無論什么情況下,流量均會進入灰度服務。never:無論什么情況下,流量均不會進入灰度服務。
若沒有指定請求頭名稱的值,則只要該頭存在,都會進行流量轉發。≥v1.3.1 nginx.ingress.kubernetes.io/canary-by-header-value 表示基于請求頭的值進行灰度發布。需要與canary-by-header頭配合使用。 ≥v1.3.1 nginx.ingress.kubernetes.io/canary-by-cookie 表示基于Cookie進行灰度發布。
Cookie名稱的特殊取值:always:無論什么情況下,流量均會進入灰度服務。never:無論什么情況下,流量均不會進入灰度服務。
只要存在該Cookie名稱,都會進行流量轉發。≥v1.3.1 nginx.ingress.kubernetes.io/canary-weight 表示基于權重進行灰度發布。
取值范圍:0~權重總值。
若未設定總值,默認總值為100。≥v1.3.1
操作方式
步驟一:部署服務。部署Nginx服務并通過Nginx Ingress Controller對外提供7層域名訪問。創建Deployment和Service。創建nginx.yaml。
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: registry- nm6b -crs.ctyun.com/ccse-sample/old-nginx 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
執行以下命令,創建Deployment和Service。
kubectl apply -f nginx.yaml
部署Ingress,創建ingress.yaml。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release spec: rules: - host: www.ctyun.com http: paths: #老版本服務。 - path: / backend: service: name: old-nginx port: number: 80 pathType: ImplementationSpecific
執行以下命令,部署Ingress。
kubectl apply -f ingress.yaml
測試訪問情況。執行以下命令,獲取外部IP。
kubectl get ingress
執行以下命令,查看路由訪問情況。
curl -H"Host: www.ctyun.com" //<EXTERNAL_IP>
期望輸出:
old
步驟二:灰度發布新版本服務
發布一個新版本的Nginx服務并配置路由規則。部署新版本的Deployment和Service。創建nginx1.yaml。
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: registry-nm6b-crs.ctyun.com/ccse-sample/new-nginx 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
執行以下命令,創建Deployment和Service。
kubectl apply -f nginx.yaml
設置訪問新版本服務的路由規則。容器支持設置以下三種路由規則,您可以根據實際情況選擇路由規則。設置滿足特定規則的客戶端才能訪問新版本服務。以下示例僅請求頭中滿足foo=bar的客戶端請求才能路由到新版本服務。按照以上條件,創建新的Ingress資源gray-release-canary。
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: www.ctyun.com http: paths: #新版本服務。 - path: / backend: service: name: new-nginx port: number: 80 pathType: ImplementationSpecific
查看路由訪問情況。執行以下命令,訪問服務。
curl -H"Host: www.ctyun.com" //<EXTERNAL_IP>
預期輸出:
old
執行以下命令,請求頭中滿足foo=bar的客戶端請求訪問服務。
curl -H"Host: www.ctyun.com"-H"foo: bar"//<EXTERNAL_IP>
預期輸出:
new
在滿足特定規則的基礎上設置一定比例的請求被路由到新版本服務中。以下示例要求請求頭中滿足foo=bar的客戶端請求,且僅允許其中50%的流量被路由到新版本服務中。按照以下內容,修改步驟2中創建的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" #在滿足上述匹配規則的基礎上僅允許50%的流量會被路由到新版本服務new-nginx中。 nginx.ingress.kubernetes.io/canary-weight: "50" spec: rules: - host: www.ctyun.com http: paths: #新版本服務。 - path: / backend: service: name: new-nginx port: number: 80 pathType: ImplementationSpecific
查看路由訪問情況。執行以下命令,訪問服務。
curl -H"Host: www.ctyun.com" //<EXTERNAL_IP>
預期輸出:
old
執行以下命令,請求頭中滿足foo=bar的客戶端請求訪問服務。
curl -H"Host: www.ctyun.com"-H"foo: bar"//<EXTERNAL_IP>
預期輸出:
new
重復執行以上命令。可以看到,僅請求頭中滿足foo=bar的客戶端請求,且只有50%的流量才能路由到新版本服務。重復執行以上命令。可以看到,僅請求頭中滿足foo=bar的客戶端請求,且只有50%的流量才能路由到新版本服務。按照以下內容,修改步驟2創建的Ingress。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release-canary annotations: #開啟Canary。 nginx.ingress.kubernetes.io/canary: "true" #僅允許50%的流量會被路由到新版本服務new-nginx中。 #默認總值為100。 nginx.ingress.kubernetes.io/canary-weight: "50" spec: rules: - host: www.ctyun.com http: paths: #新版本服務。 - path: / backend: service: name: new-nginx port: number: 80 pathType: ImplementationSpecific
執行以下命令,查看路由訪問情況。
curl -H"Host: www.ctyun.com"//<EXTERNAL_IP>
重復執行以上命令,可以看到僅50%的流量路由到新版本服務。
步驟三:刪除老版本服務基于Helm的發布管理
系統運行一段時間后,當新版本服務已經穩定并且符合預期后,需要下線老版本的服務,僅保留新版本服務在線上運行。為了達到該目標,需要將舊版本的Service指向新版本服務的Deployment,并且刪除舊版本的Deployment和新版本的Service。修改舊版本Service,使其指向新版本服務。
apiVersion:v1 kind: Service metadata: name:old-nginx spec: ports: -port:80 protocol:TCP targetPort:80 selector: #指向新版本服務。 run:new-nginx sessionAffinity:None type:NodePort
執行以下命令,請求頭中滿足foo=bar的客戶端請求訪問服務。
curl -H"Host: www.ctyun.com"//<EXTERNAL_IP>
預期輸出:
new
重復執行以上命令,可以看到請求全部被路由到了新版本的服務。執行以下命令,刪除Canary Ingress資源gray-release-canary。
kubectldeleteingress gray-release-canary
刪除舊版本的Deployment和新版本的Service。執行以下命令,刪除舊版本的Deployment。
kubectldeletedeployold-nginx
執行以下命令,刪除新版本的Service。
kubectldeletesvcnew-nginx