本文主要從使用背景、使用前提和實現原理等方面介紹了ACK Nginx Ingress 灰度發布,以及如何快速配置灰度發布流水線。
使用背景
灰度及藍綠發布:為新版本創建一個與老版本完全一致的生產環境,在不影響老版本的前提下,按照一定的規則把部分流量切換到新版本,當新版本試運行一段時間沒有問題后,將用戶的全量流量從老版本遷移至新版本。其中A/B測試就是一種灰度發布方式,一部分用戶繼續使用老版本的服務,將一部分用戶的流量切換到新版本,如果新版本運行穩定,則逐步將所有用戶遷移到新版本。
Ingress灰度發布使用背景:控制新版本流量分配權重,以小部分線上流量對服務進行驗證通過cookie或者header使得部分受控用戶在線上對發布進行驗證。當發布驗證失敗后,可以快速回滾到舊版本。
使用前提
使用由阿里云容器服務Kubernetes版本提供的Kubernetes集群。
Deployment的labels內包含有Service的全部selector labels,如:
--- apiVersion: apps/v1 kind: Deployment metadata: labels: run: spring-boot-sample name: spring-boot-sample spec: replicas: 2 selector: matchLabels: run: spring-boot-sample template: metadata: labels: run: spring-boot-sample spec: containers: - image: ${IMAGE} name: app --- apiVersion: v1 kind: Service metadata: name: spring-boot-service labels: test: test labele2: label spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080 - name: https port: 443 protocol: TCP targetPort: 443 selector: run: spring-boot-sample sessionAffinity: None type: ClusterIP
實現原理
如下圖所示,當采用ACK Nginx Ingress灰度發布時,假定當前運行版為primary,將要發布版本為canary。
發布過程:
發布前檢查:預檢查當前Ingress是否有且只關聯了唯一的Service實例,且Service實例下有且只有唯一版本的Deployment。
生成Canary版本:克隆primary版本的Service以及Deployment生成canary版本的Service和Deployment,同時修改canary版本Deployment的鏡像到新版本。
修改Ingress流量規則:根據發布配置調整Ingress配置,開始執行灰度。
人工驗證:通過cookie或者header對灰度版本進行驗證,根據結果選擇完成發布或者回滾。
完成灰度:修改Ingress配置以及流量規則,下線Primary版本的Service以及Deployment實例。
回滾發布:修改Ingress配置以及流量規則,下線Canary版本的Service以及Deployment。
快速開始
初始化應用部署
通過以下配置快速初始化應用部署:
apiVersion: apps/v1 kind: Deployment metadata: name: old-nginx labels: run: old-nginx spec: replicas: 2 selector: matchLabels: run: old-nginx template: metadata: labels: run: old-nginx spec: containers: - image: nginx imagePullPolicy: Always name: 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 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: gray-release spec: rules: - host: www.example.com http: paths: # 老版本服務 - path: / backend: serviceName: old-nginx servicePort: 80
保存以上內容到,gray-release.yaml,使用kubectl或者控制臺完成應用初始化:
kubectl apply -f gray-release.yaml
配置流水線
如下所示,配置如下信息:
配置說明:
配置項
說明
命名空間
當前服務所在Kubernetes集群命名空間, 示例中為default
Ingress名稱
發布的目標Ingress實例名稱,示例中為gray-release
服務端口
Ingress后端Service 實例對外暴露的端口,示例中為80
應用端口
鏡像對外暴露的端口,示例中為80
容器名稱
應用發布時需要更新鏡像的容器名,示例中為nginx
鏡像
通過前序任務鏡像構建產生的鏡像,或者是特定的鏡像名稱
灰度方式
選擇使用header或者cookie方式進行灰度驗證,并設置匹配的Key/Value
灰度初始化流量權重
默認灰度版本上線后的流量權重
啟動等待時間
灰度版本發布后等待該時間后再修改Ingress配置
下線等待時間
Ingress配置調整后等待該時長后再移除應用實例
運行流水線
保存并運行流水線,當新版本發布后,流水線的任務將處于灰度發布中,等待人工驗證以確定后續操作。
灰度中查看Kubernetes資源狀態如下所示:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"nginx.ingress.kubernetes.io/service-match":"gray-release-v20201231112858: cookie(\"foo\", /^bar$/)","nginx.ingress.kubernetes.io/service-weight":"gray-release-v20201231112858: 0, old-nginx: 100"},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"old-nginx","servicePort":80},"path":"/","pathType":"ImplementationSpecific"},{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}} nginx.ingress.kubernetes.io/service-match: 'gray-release-v20201231112858: cookie("foo", /^bar$/)' nginx.ingress.kubernetes.io/service-weight: 'gray-release-v20201231112858: 0, old-nginx: 100' name: gray-release namespace: default spec: rules: - host: www.example.com http: paths: - backend: serviceName: old-nginx servicePort: 80 path: / pathType: ImplementationSpecific - backend: serviceName: gray-release-v20201231112858 servicePort: 80 path: / pathType: ImplementationSpecific
在灰度中云效自動創建了灰度版本的服務,命名規則為:
<ingressname>-<version>
,查看灰度版本的Service以及Deployment詳情:apiVersion: v1 kind: Service metadata: labels: run: old-nginx version: v20201231112858 name: gray-release-v20201231112858 namespace: default spec: clusterIP: 10.0.6.88 ports: - port: 80 protocol: TCP targetPort: 80 selector: run: old-nginx version: v20201231112858 type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: generation: 1 labels: run: old-nginx version: v20201231112858 name: gray-release-v20201231112858 namespace: default spec: progressDeadlineSeconds: 600 replicas: 2 revisionHistoryLimit: 10 selector: matchLabels: run: old-nginx version: v20201231112858 strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: creationTimestamp: null labels: run: old-nginx version: v20201231112858 spec: containers: - image: nginx:latest imagePullPolicy: Always name: nginx ports: - containerPort: 80 protocol: TCP
用戶可以通過在瀏覽器設置cookie的后來訪問新版本的應用:
document.cookie="foo=bar"
確認發布完成
在人工驗證成功后,在卡片上單擊完成按鈕后,流水線將會自動完成ingress的配置調整,以及老版本的應用下線操作:
此時查看線上應用Ingress信息,如下所示:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"gray-release","namespace":"default"},"spec":{"rules":[{"host":"www.example.com","http":{"paths":[{"backend":{"serviceName":"gray-release-v20201231112858","servicePort":80},"path":"/","pathType":"ImplementationSpecific"}]}}]}} name: gray-release namespace: default? spec: rules: - host: www.example.com http: paths: - backend: serviceName: gray-release-v20201231112858 servicePort: 80 path: / pathType: ImplementationSpecific ---?
常見發布失敗的問題
當Ingress關聯了多個Service實例時發布失敗。
當Service關聯了多個Deployment實例時發布失敗,Service的LabelSelector需要確保與Deployment的labels保持匹配。
線上版本鏡像和當前發布鏡像未變化時,發布失敗。
而發布配置中的容器名稱無法匹配到容器定義時發布失敗。