CODING DevOps + Nginx-ingress 實現自動化灰度釋出

CODING_DevOps發表於2020-07-23

作者:王煒,CODING DevOps 後端開發工程師,擁有多年研發經驗,雲原生、DevOps、Kubernetes 資深愛好者,Servicemesher 服務網格中文社群成員。獲得 Kubernetes CKA、CKAD 認證。

前言

在 Kubernetes 上的應用實現灰度釋出,最簡單的方案是引入官方的 Nginx-ingress 來實現。

我們通過部署兩套 deployment 和 services,分別代表灰度環境和生產環境,通過負載均衡演算法,實現對兩套環境的按照灰度比例進行分流,進而實現灰度釋出。

通常的做法是當專案打包新映象後,通過修改 yaml 檔案的映象版本,執行 kubectl apply 的方式來更新服務。如果釋出流程還需要進行灰度釋出,那麼可以通過調整兩套服務的配置檔案權重來控制灰度釋出,這種方式離不開人工執行。如果專案數量多,灰度的時間跨度過長,人為誤操作的概率將大大增加,過於依賴於人工執行,這對於 DevOps 工程實踐是不能忍受的。

那麼,有沒有一種方式能夠實現無需人工干預的自動化灰度呢?例如在程式碼更新後,自動釋出到預釋出和灰度環境,並在一天的時間內自動將灰度比例從 10% 權重提高到 100%,且能夠隨時終止,灰度通過後自動釋出到生產環境?

答案是肯定的,利用 CODING DevOps 就能夠滿足此類需求。

Nginx-ingress 架構和原理

迅速回顧一下 Nginx-ingress 的架構和實現原理:

Nginx-ingress 架構

Nginx-ingress 通過前置的 Loadbalancer 型別的 Service 接收叢集流量,將流量轉發至 Nginx-ingress Pod 內並對配置的策略進行檢查,再轉發至目標 Service,最終將流量轉發至業務容器。

傳統的 Nginx 需要我們配置 conf 檔案策略。但 Nginx-ingress 通過實現 Nginx-ingress-Controller 將原生 conf 配置檔案和 yaml 配置檔案進行了轉化,當我們配置 yaml 檔案的策略後,Nginx-ingress-Controller 將對其進行轉化,並且動態更新策略,動態 Reload Nginx Pod,實現自動管理。

那麼 Nginx-ingress-Controller 如何能夠動態感知叢集的策略變化呢?方法有很多種,可以通過 webhook admission 攔截器,也可以通過 ServiceAccount 與 Kubernetes Api 進行互動,動態獲取。Nginx-ingress-Controller 使用後者來實現。所以在部署 Nginx-ingress 我們會發現 Deployment 內指定了 Pod 的 ServiceAccount,以及實現了 RoleBinding ,最終達到 Pod 能夠與 Kubernetes Api 互動的目的。

實現方案預覽

為了實現以上目標,我們設計了以下持續部署流水線。

持續部署流水線案例

此持續部署流水線主要實現了以下幾個步驟:

1、自動部署到預釋出環境
2、是否進行 A/B 測試
3、自動灰度釋出(自動進行3次逐漸提升灰度比例)
4、釋出到生產環境

同時,本文案例還演示了從 Git 提交程式碼到自動觸發持續整合的步驟:

1、提交程式碼後觸發持續整合,自動構建映象
2、映象構建完成後,自動推送映象到製品庫
3、觸發持續部署

1、提交程式碼後觸發持續整合,自動構建映象並推送到製品庫

2、觸發持續部署,併發布到預釋出環境

3、人工確認:進行 A/B 測試(或跳過直接進入自動灰度)

進行 A/B 測試時,只有 Header 包含 location=shenzhen 可以訪問新版本,其他使用者訪問生產環境仍然為舊版本。

4、人工確認:是否自動灰度釋出(自動進行 3 輪逐漸提升灰度比例,每輪間隔 30s)

第一次灰度:新版本 30% 的灰度比例,此時訪問生產環境大約有 30% 的流量進入新版本灰度環境:

30s 後自動進行第二輪灰度:新版本 60% 的灰度比例:

60s 後自動進行第三輪灰度:新版本 90% 的灰度比例:

本案例中,我們配置了自動化灰度釋出將會以 3 次漸進式進行,每次提高 30% 的比例,每次持續 30s 後自動進入下一個灰度階段。在不同的灰度階段,會發現請求新版本出現的概率越來越高。漸進式的灰度可根據業務需要進行任意配置,例如持續 1 天時間分 10 次自動進行灰度,直至釋出到生產環境而無需人工值守。

5、灰度完成,30s 後釋出到生產環境

專案原始碼和原理分析

專案原始碼地址:https://wangweicoding.coding.net/public/nginx-ingress-gray/nginx-ingress-gray/git

├── Jenkinsfile  # 持續整合指令碼
├── deployment
│   ├── canary
│   │   └── deploy.yaml   # 灰度釋出部署檔案
│   ├── dev
│   │   └── deploy.yaml   # 預釋出部署檔案
│   └── pro
│       └── deploy.yaml   # 生產部署檔案
├── docker
│   ├── Dockerfile
│   └── html
│       └── index.html
├── nginx-ingress-init
│   ├── nginx-ingress-deployment  # nginx-ingress 部署檔案
│   │   ├── ClusterRoleBinding.yaml
│   │   ├── RoleBinding.yaml
│   │   ├── clusterRole.yaml
│   │   ├── defaultBackendService.yaml
│   │   ├── defaultBackendServiceaccount.yaml
│   │   ├── deployment.yaml
│   │   ├── nginxDefaultBackendDeploy.yaml
│   │   ├── roles.yaml
│   │   ├── service.yaml
│   │   └── serviceAccount.yaml
│   └── nginx-ingress-helm   # nginx-ingress Helm 包
│       └── nginx-ingress-1.36.3.tgz
└── pipeline   # 持續部署流水線模板
    ├── gray-deploy.json  # 灰度釋出流水線
    ├── gray-init.json    # 灰度釋出初始化(首次執行)
    └── nginx-ingress-init.json  # nginx-ingress 初始化(首次執行)

灰度環境和生產環境主要由 deployment/canary/deploy.yamldeployment/pro/deploy.yaml 來實現,主要是實現了兩套環境的:

  • Deployment
  • Service
  • Ingress

A/B 測試和灰度由配置的 Ingress 進行控制:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx  # nginx=nginx-ingress| qcloud=CLB ingress
    nginx.ingress.kubernetes.io/canary: "true"  # 開啟灰度
    nginx.ingress.kubernetes.io/canary-by-header: "location"  # A/B 測試用例 Header key
    nginx.ingress.kubernetes.io/canary-by-header-value: "shenzhen"  # A/B 測試用例 Header value
  name: my-ingress
  namespace: pro
spec:
  rules:
  - host: nginx-ingress.coding.pro
    http:
      paths:
      - backend:
          serviceName: nginx-canary
          servicePort: 80
        path: /

A/B 測試主要由註解 nginx.ingress.kubernetes.io/canary-by-headernginx.ingress.kubernetes.io/canary-by-header-value 進行控制,來匹配請求 Header 的 Key 和 Value。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx  # nginx=nginx-ingress| qcloud=CLB ingress
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: 30
  name: my-ingress
  namespace: pro
spec:
  rules:
  - host: nginx-ingress.coding.pro
    http:
      paths:
      - backend:
          serviceName: nginx-canary
          servicePort: 80
        path: /

而灰度則由註解 nginx.ingress.kubernetes.io/canary-weight 控制,值範圍可以是 0-100,對應灰度權重比例。在 Nginx-ingress ,負載均衡演算法主要由加權輪詢的演算法來實現分流。

整體架構圖如所示:

環境準備

1、K8S 叢集,推薦使用騰訊雲容器服務
2、開通 CODING DevOps,提供映象構建和流水線的部署能力。

實踐步驟

1、克隆原始碼並推送至自己的 CODING Git 倉庫

```
$ git clone https://e.coding.net/wangweicoding/nginx-ingress-gray/nginx-ingress-gray.git
$ git remote set-url origin https://you coding git
$ git add .
$ git commit -a -m 'first commit'
$ git push -u origin master
```

注意,推送前請將 deployment/devdeployment/canarydeployment/pro 資料夾的 deploy.yaml image 修改為自己的製品庫映象地址。

2、建立持續整合流水線
使用“自定義構建過程”建立構建計劃,並選擇使用程式碼倉庫的 Jenkinsfile

3、新增雲賬號並建立持續部署流水線,複製專案的 pipeline Json 模板到建立的流水線內(3 個)

為了便於使用模板,建立持續部署流水線應用名為:nginx-ingress

建立繼續建立空白部署流程,複製 Json 模板到持續部署流水線中,一共建立三條流水線:

  • nginx-ingress-init - 用於初始化 nginx-ingress
  • gray-init - 用於首次初始化環境
  • gray-deploy - 用於演示灰度釋出
    注意:請將以上流水線的雲賬號選擇為自己的雲賬號,另外 gray-deploy 流水線中,請重新配置“啟動所需製品”和“觸發器”。

4、初始化 nginx-ingress(首次執行)
首次執行 nginx-ingress 流水線將自動為您部署nginx-ingress。部署成功後,執行 kubectl get svc | grep nginx-ingress-controller 獲取 Ningx-ingressEXTERNAL-IP,此 IP 為叢集請求入口 IP 。併為本機配置 Host ,便於訪問。

5、初始化灰度釋出(首次執行)
首次執行 gray-init 流水線將自動部署一套完整的環境,否則自動化灰度流水線將會失敗。

6、自動觸發灰度釋出
現在,您可以嘗試修改專案 docker/html/index.html 檔案,推送後將自動觸發構建和持續部署,觸發後,進入“持續部署”頁面,檢視部署詳情和流程。

總結

我們主要利用了 CODING 持續部署等待階段,通過對不同灰度比例的階段設定等待時間,自動化逐一執行灰度階段,最終實現無人工值守的自動化灰度釋出。

利用等待階段,可以實現平滑的釋出流程,只有當釋出出現問題,才需要人工介入。配合持續部署通知功能,可以很方便的將當前釋出狀態推送到企業微信、釘釘等協作工具。

為了方便展示,案例中對灰度比例和等待時間進行了硬編碼,你也可以使用階段的“自定義引數”來實現對灰度比例和等待實現進行動態控制,針對當前的釋出等級動態輸入灰度比例和流程控制,使得釋出更加靈活。

生產建議

本文的 Nginx-ingress 採用 deployment 的部署方式來實現。Nginx-ingress 作為 Kubernetes 叢集的邊緣閘道器,承擔著所有入口流量,其高可用性直接決定了 Kubernetes 叢集的高可用性。

在生產環境,部署 Nginx-ingress 建議遵循以下幾點:

  • 推薦使用 DaemonSet 的方式部署,避免節點故障。
  • 通過標籤選擇器,將 Nginx-ingress-controller 部署在獨立的 Node 節點(如高主頻、高網路、高 IO 節點)或者低負載的節點。
  • 如果採用 Deployment 的方式部署,可以為 Nginx-ingress 配置 HPA 水平伸縮。

關於 CODING,瞭解更多

相關文章