Nginx Ingress可以通過業務流量切分的方式,實現應用的灰度/藍綠發布。Nginx Ingress支持三種流量切分策略,分別是基于Header、基于Cookie和基于服務權重。這三種策略可以歸納為兩種場景:
- 將匹配的業務流量切分到新版本
- 將指定比例的流量切分到新版本
接下來本文將按照Nginx Ingress配置說明 、前提條件、應用場景和操作步驟的順序進行介紹。
Nginx Ingress配置說明
為實現灰度/藍綠發布,應用前提:
- 配置兩個Ingress資源:一個為常規Ingress,一個為Canary Ingress。這兩個Ingress通過域名(host)和路徑(path)關聯。Canary Ingress需帶有注解nginx.ingress.kubernetes.io/canary: "true"。
- 通過注解為Canary Ingress配置流量切分策略,將流量路由到Canary Ingress定義的Service上,即可實現灰度發布、藍綠發布、A/B測試等各種業務場景。
Nginx Ingress支持的流量切分策略注解如下:
- nginx.ingress.kubernetes.io/canary-by-header
基于Header切分流量,用于灰度發布。如果請求頭中包含指定的header key,且值為“always”,則將請求切分到Canary Ingress定義的Service上。如果值為“never”,則將請求轉發到常規Ingress定義的Service上。如果為其他值,則忽略該注解規則,并通過優先級將請求流量分配到其他規則。
- nginx.ingress.kubernetes.io/canary-by-header-value
與 canary-by-header 一起使用,可自定義header value。如果請求頭命中自定義值,則將請求切分到Canary Ingress定義的Service上。如果為其他值,則忽略該注解規則,并通過優先級將請求流量分配到其他規則。
- nginx.ingress.kubernetes.io/canary-by-header-pattern
與 canary-by-header-value 工作方式類似,支持通過正則表達式匹配header value。請注意,當設置了 canary-by-header-value 時,此注解規則將被忽略。
- nginx.ingress.kubernetes.io/canary-by-cookie
基于Cookie切分流量,用于灰度發布,與canary-by-header相似。Cookie value僅支持“always”和“never”。
- nginx.ingress.kubernetes.io/canary-weight
基于服務權重切分流量,用于灰度發布或藍綠發布。權重取值范圍為[0-100],表示Canary Ingress所切分到的流量百分比。
以上策略的優先級順序為: canary-by-header > canary-by-cookie > canary-weight 。更多內容請參閱官方文檔Annotations。
前提條件
1、在集群中安裝Nginx Ingress插件,作為Ingress Controller,并通過Nginx對外暴露統一的流量入口。詳細操作可參考 安裝插件。
2、上傳Nginx鏡像至容器鏡像服務,使用Nginx作為demo應用。
3、使用Nginx部署應用Service v1,為方便觀測流量切分的效果,將歡迎頁設置為“v1”。關鍵配置見下步。
應用場景和操作步驟
場景一:將匹配的業務流量切分到新版本
應用運行了一套對外提供7層服務的Service v1,現需發布新版本Service v2。應用希望將header包含“version=v2”或Cookie包含“v2=always”的流量灰度到Service v2,待穩定運行后,逐步全量切到Service v2,平滑下線Service v1。示意圖如下:
步驟1:部署舊版本Service v1和常規Ingresss
- 創建配置項(ConfigMap)gray-v1
- 部署無狀態應用(Deployment)gray-v1-deploy
配置數據卷:
配置鏡像和掛載卷:
訪問配置:
- 創建常規Ingress gray-v1-ing
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata:
annotations:
kubernetes.io/ingress.class: "nginx-ingress-controller" # 使用Nginx型Ingress
name: "gray-v1-ing"
namespace: "gray-test"
spec:
rules:
- host: "test-gray.com" # 域名
http:
paths:
- backend:
service:
name: "gray-v1-deploy" # 指定后端服務為gray-v1-deploy
port:
number: 80
path: "/" # 路徑
pathType: "Prefix"
步驟2:部署新版本Service v2
- 創建配置項(ConfigMap)gray-v2
- 部署無狀態應用(Deployment)gray-v2-deploy
配置數據卷:
配置鏡像和掛載卷:
訪問配置:
步驟3:灰度發布新版本
- 基于Header創建Canary Ingress gray-v2-ing
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata:
annotations:
kubernetes.io/ingress.class: "nginx-ingress-controller"
nginx.ingress.kubernetes.io/canary: "true" # 啟用Canary
nginx.ingress.kubernetes.io/canary-by-header: "version"
nginx.ingress.kubernetes.io/canary-by-header-value: "v2" # Header中包含version且值為v2的請求轉發到此Canary Ingress
labels:
ingressName: "gray-v1-ing"
ingressNamespace: "gray-test"
name: "gray-v2-ing"
namespace: "gray-test"
spec:
rules:
- host: "test-gray.com" # 域名
http:
paths:
- backend:
service:
name: "gray-v2-deploy" # 指定后端服務為gray-v2-deploy
port:
number: 80
path: "/" # 路徑
pathType: "Prefix"
執行命令進行訪問測試:
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' -H 'version: v2'
v2
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' -H 'version: v2'
v2
其中,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP。可以看出,僅當Header中包含version且值為v2的流量已切分到新版本服務。
- 基于Cookie創建Canary Ingress gray-v2-ing
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata:
annotations:
kubernetes.io/ingress.class: "nginx-ingress-controller"
nginx.ingress.kubernetes.io/canary: "true" # 啟用Canary
nginx.ingress.kubernetes.io/canary-by-cookie: "version" # Cookie中包含version且值為alway的請求轉發到此Canary Ingress
labels:
ingressName: "gray-v1-ing"
ingressNamespace: "gray-test"
name: "gray-v2-ing"
namespace: "gray-test"
spec:
rules:
- host: "test-gray.com" # 域名
http:
paths:
- backend:
service:
name: "gray-v2-deploy" # 指定后端服務為gray-v2-deploy
port:
number: 80
path: "/" # 路徑
pathType: "Prefix"
執行命令進行訪問測試:
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' --cookie 'version=always'
v2
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com'
v1
# curl //<EXTERNAL_IP> -H 'Host: test-gray.com' --cookie 'version=always'
v2
其中,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP。可以看出,僅當Cookie中包含version且值為always的流量已切分到新版本服務。
步驟4:下線舊版本Service v1
- 將常規Ingress gray-v1-ing 的服務名稱改為gray-v2-deploy
- 刪除Canary Ingress gray-v2-ing
- 刪除舊無狀態應用 gray-v1-deploy 和配置項 gray-v1
場景二:將一定比例的流量切分到新版本
應用運行了一套對外提供7層服務的Service v1,現需發布新版本Service v2。應用希望將20%的流量灰度到Service v2,待穩定運行后,逐步全量切到Service v2,平滑下線Service v1。示意圖如下:
步驟1:部署舊版本Service v1和常規Ingresss
同 “場景一:將匹配的業務流量切分到新版本”
步驟2:部署新版本Service v2
同 “場景一:將匹配的業務流量切分到新版本”
步驟3:灰度發布新版本
基于服務權重創建Canary Ingress gray-v2-ing
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata:
annotations:
kubernetes.io/ingress.class: "nginx-ingress-controller"
nginx.ingress.kubernetes.io/canary: "true" # 啟用Canary
nginx.ingress.kubernetes.io/canary-weight: "50" # 50%的請求轉發到此Canary Ingress
labels:
ingressName: "gray-v1-ing"
ingressNamespace: "gray-test"
name: "gray-v2-ing"
namespace: "gray-test"
spec:
rules:
- host: "test-gray.com" # 域名
http:
paths:
- backend:
service:
name: "gray-v2-deploy" # 指定后端服務為gray-v2-deploy
port:
number: 80
path: "/" # 路徑
pathType: "Prefix"
執行命令進行訪問測試:
# 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
其中,<EXTERNAL_IP>為Nginx Ingress對外暴露的IP。可以看出,有近50%的流量切分到新版本服務,當請求的數量越多時比例會越接近50%。
步驟4:下線舊版本Service v1
- 將常規Ingress gray-v1-ing 的服務名稱改為gray-v2-deploy
- 刪除Canary Ingress gray-v2-ing
- 刪除舊無狀態應用 gray-v1-deploy 和配置項 gray-v1