概述
APISIX是動態、實時、高性能的API網關,可以提供豐富的流量管理能力,可以作為K8s Ingress控制器。本文介紹安裝Ingress-APISIX后,通過靈活的路由能力,在無需修改業務代碼的情況下,實現全鏈路灰度能力。
背景信息
在微服務架構下,一次需求可能會同時修改多個微服務應用。在發布應用時,通常將這些應用劃分為同一個分組,使灰度流量始終在灰度應用中流轉。當上游有灰度流量時,會通過引流的方式將灰度流量引導至灰度分組,在此次鏈路調用過程中,如果存在一些微服務沒有灰度環境,那這些請求在下游時依然能回到灰度環境中,以此實現全鏈路灰度。
本文將APISIX提供的靈活的路由能力和微服務治理中心的全鏈路灰度能力相結合,在無需修改業務代碼的情況下,輕松實現全鏈路灰度能力。
本文的演示應用由Ingress-APISIX和Spring Cloud應用組成,Spring Cloud應用有3個,分別是app-a、app-b和app-c,服務之間通過Nacos注冊中心實現服務注冊與發現,部署完成后,通過Ingress-APISIX配置路由規則,來訪問后端服務。

前提條件
- 用戶已開通微服務治理中心企業版。
- 用戶已開通云容器引擎。
- 用戶已開通注冊配置中心Nacos。
準備工作
部署Spring Cloud應用
- 登錄云容器引擎,選擇左側菜單“集群”,再點進目標集群。
- 在集群管理頁面,點擊工作負載->無狀態。
- 選擇命名空間,點擊“新增YAML資源”。
- 本文部署app-a、app-b和app-c三個應用,每個應用分別部署一個基線版本和一個灰度版本,同時開通一個注冊配置中心Nacos,用于實現服務注冊與發現。
部署app-a應用基線版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
image: "鏡像倉庫域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-a應用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-b應用基線版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
image: "鏡像倉庫域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-b應用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-c應用基線版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
image: "鏡像倉庫域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-c應用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
在云容器引擎中安裝Ingress APISIX
自主安裝apisix、apisix-ingress-controller和Dashboard組件。具體安裝步驟可參考:Apache APISIX Helm Chart 。
對app-a進行網絡配置
針對入口應用app-a,分別為基線版本和灰度版本配置兩個K8s Service,用于Ingress APISIX轉發流量。
- 登錄云容器引擎,選擇左側菜單“集群”,再點進目標集群。
- 在集群管理頁面,點擊網絡->服務。
- 選擇命名空間,點擊“新增YAML”,依次創建下面兩個YAML資源。
app-a-base-service對應app-a應用的基線版本:
apiVersion: "v1"
kind: "Service"
metadata:
name: "app-a-base-service"
namespace: "default"
spec:
ports:
- name: "http"
port: 26160
protocol: "TCP"
targetPort: 26160
selector:
name: "app-a"
app-a-base-service對應app-a應用的基線版本:
apiVersion: "v1"
kind: "Service"
metadata:
name: "app-a-gray-service"
namespace: "default"
spec:
ports:
- name: "http"
port: 26160
protocol: "TCP"
targetPort: 26160
selector:
name: "app-a-gray"
- 登錄APISIX控制臺,點擊“上游”,在上游管理頁面中點擊創建,分別為app-a和app-a-gray配置上游服務,配置詳情如下:
app-a:

app-a-gray:

創建全鏈路灰度泳道和泳道組
完成準備工作后,即可在微服務治理中心控制臺創建全鏈路灰度泳道和泳道組。
- 登錄微服務治理中心控制臺,點擊全鏈路灰度。
- 在全鏈路灰度頁面點擊“創建泳道組及泳道”。
- 設置泳道組名稱,選擇入口類型為“Ingress/自建網關”,選擇“泳道組涉及所有應用”:app-a、app-b和app-c。

- 點擊創建第一個分流泳道,設置“泳道名稱”,選擇標簽“gray”,點擊創建泳道。

通過 APISIX配置路由規則, 按照指定請求參數進行路由,實現全鏈路灰度
通過在Ingress-APISIX設置路由規則,將請求攜帶參數tag=gray的流量路由到app-a-gray。根據全鏈路灰度規則,攜帶參數tag=gray的請求路徑是Ingress-APISIX > app-a-gray > app-b-gray > app-c-gray。

配置APISIX路由規則
- 登錄APISIX控制臺,點擊左側菜單“路由”,然后點擊“創建”。
- 在創建路由頁面,首先設置路由信息,分別設置app-a-route和app-a-gray-route的路由名稱、路由版本和高級匹配條件。
app-a-route:設置路由名稱為app-a-route,路由版本1.0.0,設置優先級為0,點擊下一步,選擇上游服務app-a-base,點擊下一步,創建app-a-route路由。
{
"uri": "/*",
"name": "app-a-base",
"methods": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
"CONNECT",
"TRACE",
"PURGE"
],
"upstream_id": "49xxxxxxxxxx937",
"labels": {
"API_VERSION": "1.0.0"
},
"status": 1
}
app-a-gray-route:設置路由名稱為app-a-gray-route,路由版本1.0.0,設置優先級為1,創建高級匹配條件,設置規則請求參數tag==gray,點擊下一步,選擇上游服務app-a-base,點擊下一步,創建app-a-route路由。
{
"uri": "/*",
"name": "app-a-gray-route",
"priority": 1,
"methods": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
"CONNECT",
"TRACE",
"PURGE"
],
"vars": [
[
"arg_tag",
"==",
"gray"
]
],
"upstream_id": "498xxxxxxxxxx009",
"labels": {
"API_VERSION": "1.0.0"
},
"status": 1
}
結果驗證
說明:192.168.xx.xx:9080為Ingress-APISIX的ip和端口。
不攜帶參數訪問接口:curl //192.168.xx.xx:9080/callA,路由到基線環境。
curl //192.168.xx.xx:9080/callA
a__192.168.xx.xx -> b__192.168.xx.xx -> c__192.168.xx.xx
攜帶參數訪問接口:curl //192.168.xx.xx:9080/callA?tag=gray,路由到灰度環境。
curl //192.168.xx.xx:9080/callA?tag=gray
a_gray_192.168.xx.xx -> b_gray_192.168.xx.xx -> c_gray_192.168.xx.xx