背景信息
當對服務進行版本更新升級時,需要使用到滾動升級、分批暫停發布、藍綠發布以及灰度發布等發布方式。本文將介紹在Serverless集群中如何通過Nginx Ingress Controller來實現應用服務的灰度發布。
當對服務進行版本更新升級時,需要使用到滾動升級、藍綠發布以及灰度發布等發布方式。
- 滾動更新:依次進行新舊替換,直到舊的全部被替換為止。
- 藍綠發布:兩套獨立的系統,對外提供服務的稱為綠系統,待上線的服務稱為藍系統,當藍系統里面的應用測試完成后,用戶流量接入藍系統,藍系統將稱為綠系統,以前的綠系統就可以銷毀。
- 灰度發布:在一套集群中存在穩定和灰度兩個版本,灰度版本可以限制只針對部分人員可用,待灰度版本測試完成后,可以將灰度版本升級為穩定版本,舊的穩定版本就可以下線了,也稱之為金絲雀發布。
前提條件
- 確保您已經創建Serverless集群集群,具體操作請參閱創建Serverless集群。
- 在集群中安裝nginx-ingress-controller插件,作為Ingress Controller,并通過Nginx對外暴露統一的流量入口。詳細操作可參考安裝插件。
實現原理
nginx-ingress是Kubernetes官方推薦的ingress controller,它是基于nginx實現的,增加了一組用于實現額外功能的Lua插件。
為了實現灰度發布,ingress-nginx通過定義annotation來實現不同場景的灰度發布,其支持的規則如下:
- nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,適用于灰度發布以及 A/B 測試。當 Request Header 設置為 always時,則將請求切分到Canary Ingress定義的Service上;當 Request Header 設置為 never時,請求不會被發送到 Canary 入口,會將請求轉發到常規Ingress定義的Service上;對于任何其他 Header 值,將忽略 Header,并通過優先級將請求與其他金絲雀規則進行優先級的比較。
- nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當 Request Header 設置為此值時,它將被路由到 Canary 入口,將請求切分到Canary Ingress定義的Service上。該規則允許用戶自定義 Request Header 的值,必須與上一個 annotation (即:canary-by-header)一起使用。
- nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,適用于灰度發布與 A/B 測試。用于通知 Ingress 將請求路由到 Canary Ingress 中指定的服務的cookie。Cookie 值僅支持“always”和“never”。當 cookie 值設置為 always時,它將被路由到 Canary 入口;當 cookie 值設置為 never時,請求不會被發送到 Canary 入口;對于任何其他值,將忽略 cookie 并將請求與其他金絲雀規則進行優先級的比較。
- nginx.ingress.kubernetes.io/canary-weight:基于服務權重的流量切分,適用于藍綠部署,權重取值范圍為[0-100],表示Canary Ingress所切分到的流量百分比。權重為 0 意味著該金絲雀規則不會向 Canary 入口的服務發送任何請求。權重為 100 意味著所有請求都將被發送到 Canary 入口。
以上策略的優先級順序為: canary-by-header > canary-by-cookie > canary-weight 。
基于以上annotation的發布思路如下:
- 在集群中部署新舊兩套應用版本,一套是stable版本,一套是canary版本,兩個版本都有自己的service。
- 定義兩個Ingress配置,一個正常提供服務,一個增加canary的annotation。
- 待canary版本無誤后,將其切換成stable版本,并且將舊的版本下線,流量全部接入新的stable版本。
應用場景
場景一:基于用戶請求將匹配的業務流量切分到新版本
假設在當前線上環境中,您已經有一套服務Service v1對外提供7層服務,此時開發了一些新的功能,現需發布新版本Service v2服務。但又不想直接替換Service v1服務,而是希望將請求頭包含 “foo=bar” 或者Cookie包含 “foo=bar” 的客戶端請求轉發到Service v2服務中,驗證一下新版本功能是否正常,待穩定運行后,再逐步全量切到Service v2服務,平滑下線Service v1服務。示意圖如下:
場景二:基于服務權重將業務流量切分到新版本
假設當前線上環境,您已經有一套服務Service v1對外提供7層服務,此時修復了一些問題,需要發布上線一個新的版本Service v2。但又不想將所有客戶端流量切換到新版本Service v2中,而是希望將20%的流量灰度到Service v2,待穩定運行后,逐步全量切到Service v2,平滑下線Service v1。示意圖如下:
操作步驟
場景一:基于用戶請求將匹配的業務流量切分到新版本
步驟一:部署舊版本Service v1和常規Ingress
這里使用Ingress作為service v1應用服務,并且為方便觀測流量切分的效果,將nginx歡迎頁設置為“v1”。
- 創建配置configmap,key為index.html,value為v1。
- 創建nginx工作負載,配置數據卷為剛才創建的configmap;配置鏡像和掛載卷,掛載容器路徑為:/usr/share/nginx/html;配置訪問設置,選擇虛擬集群IP類型,容器端口80,服務端口30080。
- 創建舊版本service v1的常規ingress。灰度ingress一欄選擇否,在域名路徑規則一欄填寫域名,指定服務名稱以及端口等。
- 檢查通過Ingress域名能正常訪問舊版本service v1服務。
步驟二:部署新版本Service v2
這里同樣使用Ingress作為service v2應用服務,并且為方便觀測流量切分的效果,將nginx歡迎頁設置為“v2”。
- 創建配置configmap,key為index.html,value為v2。
- 創建Ingress工作負載,配置數據卷為剛才創建的configmap;配置鏡像和掛載卷,掛載容器路徑為:/usr/share/nginx/html;配置訪問設置,選擇虛擬集群IP類型,容器端口80,服務端口30081。
步驟三:創建灰度ingress,在灰度發布新版本
- 基于Header創建新版本service v2的Ingress。
- 在灰度Ingress一欄選擇是;在生產ingress一欄選擇舊版本service v1的常規Ingress;在流量切換方式一欄選擇灰度,基于Header的區分方式,填寫Header key為foo,Header value為bar,精確匹配;在域名路徑規則一欄填寫域名,指定服務名稱和端口等。
執行命令進行訪問測試,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP:
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' -H 'foo: bar'
v2
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' -H 'foo: bar'
v2
可以看出,僅當Header中包含foo且值為bar的流量才會切分到新版本service v2服務。
- 基于Cookie創建新版本service v2的Ingress。
在灰度Ingress一欄選擇是;在生產Ingress一欄選擇舊版本service v1的常規Ingress;在流量切換方式一欄選擇灰度,基于Cookie的區分方式,填寫Cookie key為foo,Cookie value為always,精確匹配;在域名路徑規則一欄填寫域名,指定服務名稱和端口等。
執行命令進行訪問測試,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP:
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' --cookie 'foo=bar'
v2
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' --cookie 'foo=bar'
v2
可以看出,僅當Cookie中包含foo且值為bar的流量才會切分到新版本service v2服務。
步驟四:下線舊版本Service v1服務
- 將service v1的常規Ingress的服務名稱改為service v2服務。
- 刪除service v2的Ingress。
- 刪除舊版本service v1的無狀態工作負載和配置項。
平滑下線舊版本后,通過原來的常規Ingress請求的流量都會切分到新版本Service v2服務了。
場景二:基于服務權重將業務流量切分到新版本
步驟一:部署舊版本Service v1和常規Ingress
同“場景一:基于用戶請求將匹配的業務流量切分到新版本”。
步驟二:部署新版本Service v2
同“場景一:基于用戶請求將匹配的業務流量切分到新版本”。
步驟三:創建灰度Ingress,在灰度發布新版本
- 基于服務權重新版本service v2的Ingress。
- 在灰度Ingress一欄選擇是;在生產ingress一欄選擇舊版本service v1的常規Ingress;在流量切換方式一欄選擇藍綠,配置全部切到灰度的權重百分比;在域名路徑規則一欄填寫域名,指定服務名稱和端口等。
執行命令進行訪問測試,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP:
$ for i in {1..10}; do curl //<EXTERNAL_IP> -H 'Host: test-gray.com'; done;
v2
v2
v2
v2
v1
v1
v1
v2
v1
v2
可以看出,有近50%的流量切分到新版本service v2服務,當請求的數量越多時比例會越接近50%。
步驟四:下線舊版本Service v1服務
- 將service v1的常規Ingress的服務名稱改為service v2服務。
- 刪除service v2的Ingress。
- 刪除舊版本service v1的無狀態工作負載和配置項。
平滑下線舊版本后,通過原來的常規Ingress請求的流量都會切分到新版本Service v2服務了。