CODING DevOps + Nginx-ingress 實現自動化灰度發佈

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

前言

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

咱們經過部署兩套 deployment 和 services,分別表明灰度環境和生產環境,經過負載均衡算法,實現對兩套環境的按照灰度比例進行分流,進而實現灰度發佈。nginx

一般的作法是當項目打包新鏡像後,經過修改 yaml 文件的鏡像版本,執行 kubectl apply 的方式來更新服務。若是發佈流程還須要進行灰度發佈,那麼能夠經過調整兩套服務的配置文件權重來控制灰度發佈,這種方式離不開人工執行。若是項目數量多,灰度的時間跨度過長,人爲誤操做的機率將大大增長,過於依賴於人工執行,這對於 DevOps 工程實踐是不能忍受的。git

那麼,有沒有一種方式可以實現無需人工干預的自動化灰度呢?例如在代碼更新後,自動發佈到預發佈和灰度環境,並在一天的時間內自動將灰度比例從 10% 權重提升到 100%,且可以隨時終止,灰度經過後自動發佈到生產環境?web

答案是確定的,利用 CODING DevOps 就可以知足此類需求。算法

Nginx-ingress 架構和原理

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

Nginx-ingress 架構

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

傳統的 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 交互的目的。api

實現方案預覽

爲了實現以上目標,咱們設計瞭如下持續部署流水線。微信

持續部署流水線案例

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

一、自動部署到預發佈環境
二、是否進行 A/B 測試
三、自動灰度發佈(自動進行3次逐漸提高灰度比例)
四、發佈到生產環境

同時,本文案例還演示了從 Git 提交代碼到自動觸發持續集成的步驟:

一、提交代碼後觸發持續集成,自動構建鏡像
二、鏡像構建完成後,自動推送鏡像到製品庫
三、觸發持續部署

一、提交代碼後觸發持續集成,自動構建鏡像並推送到製品庫

二、觸發持續部署,併發布到預發佈環境

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

進行 A/B 測試時,只有 Header 包含 location=shenzhen 能夠訪問新版本,其餘用戶訪問生產環境仍然爲舊版本。

四、人工確認:是否自動灰度發佈(自動進行 3 輪逐漸提高灰度比例,每輪間隔 30s)

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

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

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

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

五、灰度完成,30s 後發佈到生產環境

項目源碼和原理分析

項目源碼地址:https://wangweicoding.coding....

├── 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 ,負載均衡算法主要由加權輪詢的算法來實現分流。

總體架構圖如所示:

環境準備

一、K8S 集羣,推薦使用騰訊雲容器服務
二、開通 CODING DevOps,提供鏡像構建和流水線的部署能力。

實踐步驟

一、克隆源碼並推送至本身的 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 修改成本身的製品庫鏡像地址。

二、建立持續集成流水線
使用「自定義構建過程」建立構建計劃,並選擇使用代碼倉庫的 Jenkinsfile

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

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

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

  • nginx-ingress-init - 用於初始化 nginx-ingress
  • gray-init - 用於首次初始化環境
  • gray-deploy - 用於演示灰度發佈

注意:請將以上流水線的雲帳號選擇爲本身的雲帳號,另外 gray-deploy 流水線中,請從新配置「啓動所需製品」和「觸發器」。

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

五、初始化灰度發佈(首次運行)
首次運行 gray-init 流水線將自動部署一套完整的環境,不然自動化灰度流水線將會失敗。

六、自動觸發灰度發佈
如今,您能夠嘗試修改項目 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,瞭解更多
相關文章
相關標籤/搜索