在微服務場景中,應用間的調用是隨機的。當您部署的Spring Cloud應用或Dubbo應用存在升級版本時,可能會導致無法將具有一定特征的流量路由到應用的目標版本。通過MSE提供的全鏈路灰度能力,您無需修改業務代碼,就可以實現端到端的全鏈路流量控制。泳道可以將應用的相關版本隔離成一個獨立的運行環境。通過設置泳道規則,可以將滿足規則的請求流量路由到目標版本的應用。本文介紹如何通過配置MSE云原生網關實現端到端的全鏈路灰度。
全鏈路灰度實現流程
使用限制
由于全鏈路灰度功能整合了標簽路由功能(此處指的是MSE微服務治理的標簽路由,與MSE云原生網關的標簽路由無關),因此不推薦已經加入全鏈路流量控制的應用配置金絲雀發布和標簽路由規則。
全鏈路灰度能力,對于Java應用的限制詳見微服務治理支持的Java框架。
最新版本的全鏈路灰度能力,MSE云原生網關版本需要大于等于2.0.6版本,升級網關版本詳見升級MSE云原生網關。
場景示例
本文以電商架構中的下單場景為例,介紹從MSE云原生網關到微服務的全鏈路流控功能。假設應用的架構由MSE云原生網關以及后端的微服務架構(Spring Cloud)組成,后端調用鏈路有3個:交易中心(A)、商品中心(B)、庫存中心(C),可以通過客戶端或者HTML來訪問后端服務,這些服務之間通過MSE Nacos注冊中心實現服務發現。
客戶下單后流量從MSE云原生網關進來,調用交易中心(A),交易中心(A)再調用商品中心(B),商品中心(B)調用下游的庫存中心(C)。總之,調用鏈路為:客戶>MSE云原生網關>A>B>C。
隨著業務不斷迭代,新功能需要上線,涉及到應用A和應用C同時發布新版本。在新版本正式上線之前,需要同時對應用A和應用C進行灰度驗證,確認無誤后方可正式上線。
通過MSE云原生網關和MSE微服務治理提供的全鏈路灰度能力,您可以端到端構建從網關到多個后端服務的全鏈路灰度,控制具有一定特征的灰度流量始終路由到應用對應的灰度環境,滿足您同時灰度驗證多個服務的訴求。此外,在應用無目標灰度版本時,自動容災到正式環境中。
名詞解釋
MSE云原生網關:MSE云原生網關是兼容K8s Ingress標準的下一代網關產品,支持ACK容器和Nacos等多種服務發現方式,支持多種認證登錄方式快速構建安全防線。更多信息,請參見云原生網關概述。
泳道:為相同版本應用定義的一套隔離環境。只有滿足了流控路由規則的請求流量才會路由到對應泳道里的打標應用。一個應用可以屬于多個泳道,一個泳道可以包含多個應用,應用和泳道是多對多的關系。
泳道組:泳道的集合。泳道組的作用主要是為了區分不同團隊或不同場景。
基線環境:未打標的應用屬于基線環境,是其他環境的兜底環境。
準備工作
創建Kubernetes集群
開啟MSE微服務治理
請確保MSE Java 探針的版本3.2.3及以上,否則會影響業務運行。
在MSE微服務治理開通頁面,開通微服務治理專業版。
將ACK微服務應用接入MSE治理中心,您可以選擇您需要的方式實現應用接入。更多信息,請參見ACK微服務應用接入MSE治理中心。
創建MSE云原生網關
具體操作,請參見創建MSE云原生網關。
為云原生網關關聯服務來源
具體操作,請參見創建服務來源。
基于MSE云原生網關的全鏈路灰度目前暫只支持容器服務和MSE Nacos兩種來源。
MSE云原生網關與容器服務的Kubernetes集群/MSE Nacos需要處于同一VPC。
步驟一:搭建業務應用的基線環境
步驟1:部署后端業務應用的基線版本
登錄容器服務控制臺。
在左側導航欄,單擊集群,然后單擊目標集群名稱。
在左側導航欄,選擇 。
在頁面上方,選擇集群的命名空間,然后單擊右上角的使用YAML創建資源。
部署基線應用。
服務來源為MSE Nacos
復制如下YAML文件內容,部署A、B、C三個應用的基線版本。
說明代碼中的{nacos server address}需要替換成您創建的MSE Nacos的內網域名,同時需要去掉大括號
{}
。# 應用A的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a namespace: default spec: selector: matchLabels: app: spring-cloud-a template: metadata: labels: app: spring-cloud-a msePilotCreateAppName: spring-cloud-a msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-a image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 30 periodSeconds: 60 env: - name: spring.cloud.nacos.discovery.server-addr value: {nacos server address} - name: dubbo.registry.address value: 'nacos://{nacos server address}:8848' --- # 應用B的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b namespace: default spec: selector: matchLabels: app: spring-cloud-b template: metadata: labels: app: spring-cloud-b msePilotCreateAppName: spring-cloud-b msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-b image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-b:3.0.1 imagePullPolicy: Always ports: - containerPort: 20002 livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 30 periodSeconds: 60 env: - name: spring.cloud.nacos.discovery.server-addr value: {nacos server address} - name: dubbo.registry.address value: 'nacos://{nacos server address}:8848' --- # 應用C的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c namespace: default spec: selector: matchLabels: app: spring-cloud-c template: metadata: labels: app: spring-cloud-c msePilotCreateAppName: spring-cloud-c msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-c image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always ports: - containerPort: 20003 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 30 periodSeconds: 60 env: - name: spring.cloud.nacos.discovery.server-addr value: {nacos server address} - name: dubbo.registry.address value: 'nacos://{nacos server address}:8848'
服務來源為容器服務
復制如下YAML文件,部署Nacos,模擬自建服務注冊中心的場景。
apiVersion: apps/v1 kind: Deployment metadata: name: nacos-server spec: replicas: 1 selector: matchLabels: app: nacos-server template: metadata: labels: msePilotAutoEnable: "off" app: nacos-server spec: containers: - name: nacos-server image: 'registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/nacos-server:v2.1.2' env: - name: MODE value: standalone - name: JVM_XMS value: 512M - name: JVM_XMX value: 512M - name: JVM_XMN value: 256M imagePullPolicy: Always livenessProbe: failureThreshold: 3 initialDelaySeconds: 15 periodSeconds: 10 successThreshold: 1 tcpSocket: port: 8848 timeoutSeconds: 3 readinessProbe: failureThreshold: 5 initialDelaySeconds: 15 periodSeconds: 15 successThreshold: 1 tcpSocket: port: 8848 timeoutSeconds: 3 resources: requests: cpu: '1' memory: 2Gi dnsPolicy: ClusterFirst restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: nacos-server spec: type: ClusterIP ports: - name: nacos-server-8848-8848 port: 8848 protocol: TCP targetPort: 8848 - name: nacos-server-9848-9848 port: 9848 protocol: TCP targetPort: 9848 selector: app: nacos-server
復制如下YAML文件,部署A、B、C三個應用的基線版本。
# 應用A的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a namespace: default spec: selector: matchLabels: app: spring-cloud-a template: metadata: labels: app: spring-cloud-a msePilotCreateAppName: spring-cloud-a msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-a image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 30 periodSeconds: 60 # 通過Nacos的Service訪問自建Nacos env: - name: spring.cloud.nacos.discovery.server-addr value: nacos-server - name: dubbo.registry.address value: 'nacos://nacos-server:8848' --- # 應用B的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b namespace: default spec: selector: matchLabels: app: spring-cloud-b template: metadata: labels: app: spring-cloud-b msePilotCreateAppName: spring-cloud-b msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-b image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-b:3.0.1 imagePullPolicy: Always ports: - containerPort: 20002 livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 30 periodSeconds: 60 # 通過Nacos的Service訪問自建Nacos env: - name: spring.cloud.nacos.discovery.server-addr value: nacos-server - name: dubbo.registry.address value: 'nacos://nacos-server:8848' --- # 應用C的基線版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c namespace: default spec: selector: matchLabels: app: spring-cloud-c template: metadata: labels: app: spring-cloud-c msePilotCreateAppName: spring-cloud-c msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-c image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always ports: - containerPort: 20003 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 30 periodSeconds: 60 # 通過Nacos的Service訪問自建Nacos env: - name: spring.cloud.nacos.discovery.server-addr value: nacos-server - name: dubbo.registry.address value: 'nacos://nacos-server:8848'
復制如下YAML文件,為入口應用A創建Service。
# 暴露應用A apiVersion: v1 kind: Service metadata: name: sc-a namespace: default spec: ports: - port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a type: ClusterIP
步驟2:通過MSE云原生網關暴露應用A
情況一:新服務
如果您未在云原生網關上添加過應用A,可以通過如下操作暴露應用A。
情況二:線上已有服務
如果您的實際場景是針對已導入的服務,可通過如下操作暴露應用A。
登錄MSE管理控制臺。選擇云原生網關 > 網關列表,選擇已創建的云原生網關實例。在左側導航欄路由管理頁面,選擇路由頁簽。編輯已有的路由,以便暴露服務。
配置項 | 描述 |
路徑(Path) | 選擇匹配條件為前綴是,路徑為/a。 |
路由指向 | 選擇單服務。 |
后端服務 | 選擇 sc-A |
步驟3:測試基線版本流量
登錄MSE網關管理控制臺,并在頂部菜單欄選擇地域。
在左側導航欄,選擇云原生網關 > 網關列表,單擊目標網關名稱。
在左側導航欄,單擊基本概覽。
在網關入口頁簽,查看SLB的入口地址。
使用curl命令,測試基線版本流量,發現流量經過了A、B和C的基線版本。如下所示:
# 測試命令 curl x.x.1.1/a # 測試結果 A[10.0.3.178][config=base] -> B[10.0.3.195] -> C[10.0.3.201]
如測試結果所示,B和C輸出結果的應用名后面沒有版本,則代表訪問對應應用的基線版本。
步驟二:搭建業務應用的灰度環境
如果業務應用要發布新功能,新功能要求應用A和應用C同時發版,這時就需要借助全鏈路灰度來同時驗證不同應用的灰度版本。
步驟1:部署后端業務應用的灰度版本
登錄容器服務控制臺。
在左側導航欄,單擊集群,然后單擊目標集群名稱。
在左側導航欄,選擇 。
在頁面上方,選擇集群的命名空間,然后單擊右上角的使用YAML創建資源。
復制以下YAML文件內容,部署A、C兩個應用的灰度版本。
服務來源為MSE Nacos
說明代碼中的{nacos server address}需要替換成您創建的MSE Nacos的內網域名,同時需要去掉大括號
{}
。# 應用A的灰度版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a-gray namespace: default spec: selector: matchLabels: app: spring-cloud-a-gray template: metadata: labels: alicloud.service.tag: gray app: spring-cloud-a-gray msePilotCreateAppName: spring-cloud-a msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-a image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 30 periodSeconds: 60 env: - name: spring.cloud.nacos.discovery.server-addr value: {nacos server address} - name: dubbo.registry.address value: 'nacos://{nacos server address}:8848' --- # 應用C的灰度版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c-gray namespace: default spec: selector: matchLabels: app: spring-cloud-c-gray template: metadata: labels: alicloud.service.tag: gray app: spring-cloud-c-gray msePilotCreateAppName: spring-cloud-c msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-c image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always ports: - containerPort: 20003 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 30 periodSeconds: 60 env: - name: spring.cloud.nacos.discovery.server-addr value: {nacos server address} - name: dubbo.registry.address value: 'nacos://{nacos server address}:8848'
服務來源為容器服務
# 應用A的灰度版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a-gray namespace: default spec: selector: matchLabels: app: spring-cloud-a template: metadata: labels: alicloud.service.tag: gray app: spring-cloud-a msePilotCreateAppName: spring-cloud-a msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-a image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 30 periodSeconds: 60 # 通過Nacos的Service訪問自建Nacos env: - name: spring.cloud.nacos.discovery.server-addr value: nacos-server - name: dubbo.registry.address value: 'nacos://nacos-server:8848' --- # 應用C的灰度版本 apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c-gray namespace: default spec: selector: matchLabels: app: spring-cloud-c template: metadata: labels: alicloud.service.tag: gray app: spring-cloud-c msePilotCreateAppName: spring-cloud-c msePilotAutoEnable: 'on' spec: containers: - name: spring-cloud-c image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always ports: - containerPort: 20003 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 30 periodSeconds: 60 # 通過Nacos的Service訪問自建Nacos env: - name: spring.cloud.nacos.discovery.server-addr value: nacos-server - name: dubbo.registry.address value: 'nacos://nacos-server:8848'
步驟2:創建灰度環境泳道組
登錄MSE治理中心控制臺,并在頂部菜單欄選擇地域。
在左側導航欄,選擇 。
如果您選擇的微服務命名空間內沒有創建過泳道組,則單擊創建泳道組及泳道。如果您選擇的微服務命名空間內已經創建過泳道組,則單擊+創建泳道組。
在創建泳道組面板,設置如下相關配置,然后單擊確定。
配置項
說明
泳道組名稱
自定義泳道組的名稱。
入口類型
選擇MSE 云原生網關。
入口網關
選擇目標云原生網關。
泳道組涉及應用
選擇spring-cloud-a、spring-cloud-b和spring-cloud-c。
泳道組創建完成后,在全鏈路灰度頁面的泳道組區域,可以查看您創建的泳道組。如需變更泳道組信息,單擊圖標,可在頁面自行修改相關信息。
步驟3:創建灰度環境泳道
使用全鏈路灰度功能時,需要您給灰度應用添加一個特殊的
tag
標記,以便將這些節點和其他節點區分開來。容器環境下需要您在spec.template.metadata.labels
下增加alicloud.service.tag: ${tag}
信息;在ECS環境下需要添加Java啟動參數-Dalicloud.service.tag=${tag}
。以云原生網關作為全鏈路灰度入口時,MSE支持兩種泳道灰度模式。
按請求內容灰度:當請求內容可以用于灰度識別時,建議采用此種灰度模式。如果不可以,也強烈建議您通過系統改造增加此類灰度標識,以便獲得更好的灰度效果。比如,可以保持灰度請求前后的一致性。
按比例路由灰度:當請求內容無法用于灰度識別且遺留系統也無法進行改造時,可以采用此種退化的灰度模式。該模式有一定弊端,可能導致同一來源的請求進入不同泳道,而造成灰度請求前后行為的不一致。
泳道路由模式在一個泳道組中需要保持一致。您只有在創建泳道組中第一條泳道時可以調整泳道路由的灰度模式。
在全鏈路灰度頁面底部,如果您選擇的微服務命名空間內沒有創建過泳道,則單擊點擊創建第一個分流泳道。如果您選擇的微服務命名空間內已經創建過泳道,則單擊+創建泳道。
在創建泳道面板,設置流控泳道相關配置,然后單擊確定。
創建按請求內容路由的泳道
配置項
說明
配置節點標簽
需要您給灰度應用節點打上標簽,以便與正常節點做區分。
填寫泳道信息
泳道名稱:輸入便于理解和識別的泳道名稱。
泳道標簽:該泳道內的匹配的流量去往的目標標簽。
確認匹配關系:檢查您配置了該標簽的應用節點數是否符合預期。
泳道狀態:選擇開啟。
配置路由和灰度規則
設置流量進入該泳道的規則。
灰度模式:選擇按內容灰度。
灰度條件:選擇以下條件同時滿足。
本示例灰度條件配置如下:
參數類型:Header。
參數:canary。
條件:==。
值:gray。
泳道路由模式說明
灰度條件
效果
以下條件同時滿足
需要所有條件內容滿足的流量才會去往對應的泳道。
以下條件任意滿足
滿足如下任意一個條件內容的流量就會去往對應的泳道。
條件內容說明
條件
描述
全鏈路灰度配置示例
云原生網關請求示例
==
精確匹配,流量值與條件值需要完全相等。
!=
不等匹配,流量值與條件值不相等時滿足條件。
in
包含匹配,流量值需要在指定的列表中時滿足條件。
百分比
百分比匹配,原理:滿足`hash(get(key)) % 100 < value` 成立時滿足條件。
正則匹配
正則表達式匹配,按照正則表達式規則匹配時滿足條件。
創建按比例路由的泳道
說明創建按比例路由的泳道,需要ack-onepilot版本為3.0.18及以上,且探針版本為3.2.3及以上。
配置項
說明
配置節點標簽
需要您手工給您的灰度應用節點打上標簽,以便與正常節點做區分。
填寫泳道信息
泳道名稱:輸入便于理解和識別的泳道名稱。
泳道標簽:該泳道內的匹配的流量去往的目標標簽。
確認匹配關系:檢查您配置了該標簽的應用節點數是否符合預期。
泳道狀態:選擇開啟。
配置路由和灰度規則
設置流量進入該泳道的規則。
灰度模式:選擇按比例路由。
流量比例:30%。
說明您還可以為每條網關基線路由分別設置不同的流量比例,開啟該功能時,您需要注意該網關基線路由當前在所有泳道組中配置的流量比例總和不應超過100%。
完成泳道創建后,在全鏈路灰度的流量分配區域,可以查看泳道詳情,還可以進行如下操作。
在操作列,選擇開啟,創建的泳道將會生效,即流量會按照泳道方式進行流轉,滿足規則的流量會優先流向標記有當前泳道對應標簽的應用版本,如果沒有對應標簽的應用版本則流向未打標的應用版本。
在操作列,選擇關閉,關閉創建的泳道,即該應用往后的流量會流向未打標的應用版本。
單擊圖標,可以查看該泳道的流量比例。
單擊圖標,可以設置該泳道上應用的狀態。
步驟4:測試灰度版本流量
測試按請求內容路由的泳道
使用curl命令測試灰度流量,如測試結果所示,流量經過了A、C的灰度環境,由于B沒有gray環境,所以流量自動容災到基線版本。
# 測試命令
curl -H "canary: gray" x.x.x.x/a
# 測試結果
Agray[10.0.3.177][config=base] -> B[10.0.3.195] -> Cgray[10.0.3.180]
測試按比例路由的泳道
可以通過如下Python腳本測試按比例路由的分流情況(需要安裝requests包),并將“x.x.x.x”替換為云原生網關的入口SLB地址。
# pip3 install requests
# python3 traffic.py
import requests
TOTAL_REQUEST = 100
ENTRY_URL = 'http://x.x.x.x/a'
def parse_tag(text:str):
'''
A[10.0.23.64][config=base] -> B[10.0.23.65] -> C[10.0.23.61]
Agray[10.0.23.64][config=base] -> B[10.0.23.65] -> Cgray[10.0.23.61]
Ablue[10.0.23.64][config=base] -> B[10.0.23.65] -> Cblue[10.0.23.61]
'''
print(text)
app_parts = text.split(' -> ')
# tag_app: C[10.0.23.61] / Cgray[10.0.23.61]
tag_app = app_parts[-1]
splits = tag_app.split('[')
# tag_part: C / Cgray
tag_part = splits[0]
tag = tag_part[1:]
return tag if len(tag) > 0 else 'base'
def get_tag(url:str):
resp = requests.get(url)
resp.encoding = resp.apparent_encoding
return parse_tag(resp.text)
def cal_tag_count(url:str, total_request:int):
count_map = {}
for i in range(total_request):
tag = get_tag(url)
if tag not in count_map:
count_map[tag] = 1
else:
count_map[tag] += 1
print()
print('Total Request:', total_request)
print('Traffic Distribution:', count_map)
if __name__ == '__main__':
cal_tag_count(ENTRY_URL, TOTAL_REQUEST)
從結果可以看到,約有30%比例的流量去往了灰度環境。
(可選)步驟三:可觀測
若應用出現異常,您可以通過MSE提供的可觀測能力查看異常數據,幫助您快速定位問題。
云原生網關可觀測
在MSE云原生網關的路由管理頁面,選擇服務頁簽。單擊目標服務,在監控區域查看服務的流量情況。
微服務治理可觀測
在MSE微服務治理的全鏈路灰度頁面,單擊目標應用,在應用 QPS 監控區域,可查看對應泳道基線版本(未打標)和灰度版本的流量情況。
總QPS:該應用總的QPS。
異常QPS:該應用出錯的請求數。
GrayQPS:該應用gray版本的QPS。
Arms 可觀測
如果您的應用同時接入了 Arms,您可以在 Arms 全鏈路灰度場景化頁面觀察灰度環境業務情況,觀察灰度大盤業務情況,決定回滾還是繼續發布。