作者:王炜,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
通過前置的
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.yaml
和
deployment/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-header
nginx.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/dev
、
deployment/canary
deployment/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-ingress
的
EXTERNAL-IP
,此 IP 為叢集請求入口 IP 。并為本機配置
Host
,便于通路。
5、初始化灰階釋出(首次運作)
gray-init
流水線将自動部署一套完整的環境,否則自動化灰階流水線将會失敗。
6、自動觸發灰階釋出
現在,您可以嘗試修改項目
docker/html/index.html
檔案,推送後将自動觸發建構和持續部署,觸發後,進入“持續部署”頁面,檢視部署詳情和流程。
總結
我們主要利用了
CODING 持續部署
等待
階段,通過對不同灰階比例的階段設定等待時間,自動化逐一運作灰階階段,最終實作無人工值守的自動化灰階釋出。
利用
等待
階段,可以實作平滑的釋出流程,隻有當釋出出現問題,才需要人工介入。配合持續部署通知功能,可以很友善的将目前釋出狀态推送到企業微信、釘釘等協作工具。
為了友善展示,案例中對灰階比例和等待時間進行了寫死,你也可以使用階段的“自定義參數”來實作對灰階比例和等待實作進行動态控制,針對目前的釋出等級動态輸入灰階比例和流程控制,使得釋出更加靈活。
生産建議
本文的
Nginx-ingress
采用
deployment
的部署方式來實作。
Nginx-ingress
作為
Kubernetes
叢集的邊緣網關,承擔着所有入口流量,其高可用性直接決定了
Kubernetes
叢集的高可用性。
在生産環境,部署
Nginx-ingress
建議遵循以下幾點:
- 推薦使用 DaemonSet 的方式部署,避免節點故障。
- 通過标簽選擇器,将
部署在獨立的 Node 節點(如高主頻、高網絡、高 IO 節點)或者低負載的節點。Nginx-ingress-controller
- 如果采用
的方式部署,可以為Deployment
配置 HPA 水準伸縮。Nginx-ingress
關于 CODING,了解更多