通過Ingress-APISIX提供的靈活的路由功能,您可以在不需要修改任何業務代碼的情況下,實現全鏈路灰度能力。本文介紹如何通過Ingress-APISIX實現全鏈路灰度功能。
前提條件
背景信息
微服務架構下,有一些需求開發涉及到微服務調用鏈路上的多個微服務同時改動。通常每個微服務都會有灰度環境或分組來接收灰度流量。您可能希望進入上游灰度環境的流量也能進入下游灰度的環境中,從而能確保1個請求始終在灰度環境中傳遞,即使這個調用鏈路上有一些微服務應用不存在灰度環境,這些微服務應用在請求下游應用的時候依然能夠回到下游應用的灰度環境中。通過將APISIX提供的靈活的路由功能與MSE提供的全鏈路灰度能力結合,您可以在不需要修改任何業務代碼的情況下,輕松實現全鏈路灰度能力。
本文為您介紹基于Ingress-APISIX的全鏈路灰度功能。假設應用的架構由Ingress-APISIX網關以及后端的微服務架構(Spring Cloud)組成,后端調用鏈路有3個:商品中心(A)、交易中心(B)、庫存中心(C),可以通過客戶端或者HTML來訪問后端服務,這些服務之間通過Nacos注冊中心實現服務發現。
準備工作
安裝ingress-apisix組件
安裝Apisix、apisix-ingress-controller和Dashboard組件。具體操作,請參見Apache APISIX Helm Chart。
開啟MSE微服務治理
微服務應用接入
將ACK微服務應用接入MSE治理中心。具體操作,請參見ACK微服務應用接入MSE治理中心。
部署Demo應用程序
登錄容器服務管理控制臺,在左側導航欄選擇集群。
在集群列表頁面,單擊目標集群名稱或者目標集群右側操作列下的詳情。
在集群管理頁左側導航欄,選擇 。
在無狀態頁面,選擇命名空間,然后單擊使用YAML創建資源。
使用如下YAML部署A、B、C三個應用,每個應用分別部署一個基線版本和一個灰度版本,并部署一個Nacos Server應用,用于實現服務發現。
A應用
基線(Base)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a spec: replicas: 2 selector: matchLabels: app: spring-cloud-a template: metadata: labels: msePilotCreateAppName: spring-cloud-a app: spring-cloud-a spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always name: spring-cloud-a ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 10 periodSeconds: 30
灰度(Gray)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-a-new strategy: template: metadata: labels: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-a app: spring-cloud-a-new spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre - name: profiler.micro.service.tag.trace.enable value: "true" image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always name: spring-cloud-a-new ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 10 periodSeconds: 30
B應用
基線(Base)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b spec: replicas: 2 selector: matchLabels: app: spring-cloud-b strategy: template: metadata: labels: msePilotCreateAppName: spring-cloud-b app: spring-cloud-b spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-b:3.0.1 imagePullPolicy: Always name: spring-cloud-b ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 10 periodSeconds: 30
灰度(Gray)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-b-new template: metadata: labels: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-b app: spring-cloud-b-new spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-b:3.0.1 imagePullPolicy: Always name: spring-cloud-b-new ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 10 periodSeconds: 30
C應用
基線(Base)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c spec: replicas: 2 selector: matchLabels: app: spring-cloud-c template: metadata: labels: msePilotCreateAppName: spring-cloud-c app: spring-cloud-c spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always name: spring-cloud-c ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 10 periodSeconds: 30
灰度(Gray)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-c-new template: metadata: labels: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-c app: spring-cloud-c-new spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: IfNotPresent name: spring-cloud-c-new ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 10 periodSeconds: 30
Nacos Server應用YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: nacos-server spec: replicas: 1 selector: matchLabels: app: nacos-server template: metadata: labels: app: nacos-server spec: containers: - env: - name: MODE value: standalone image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/nacos-server:v2.1.2 imagePullPolicy: Always name: nacos-server dnsPolicy: ClusterFirst restartPolicy: Always # Nacos Server Service配置 --- apiVersion: v1 kind: Service metadata: name: nacos-server spec: ports: - port: 8848 protocol: TCP targetPort: 8848 selector: app: nacos-server type: ClusterIP
網絡配置
針對入口應用A ,配置兩個K8s Service。
spring-cloud-a-base
對應A的基線(Base)版本:apiVersion: v1 kind: Service metadata: name: spring-cloud-a-base spec: ports: - name: http port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a
spring-cloud-a-gray
對應A的灰度(Gray)版本:apiVersion: v1 kind: Service metadata: name: spring-cloud-a-gray spec: ports: - name: http port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a-new
登錄APISIX控制臺,左側導航欄單擊上游,在上游列表單擊+創建對步驟1中配置Service進行上游服務配置,配置完成后單擊下一步預覽配置,最后單擊提交即可完成配置。
spring-cloud-a-svc
的上游配置如下:{ "nodes": [ { "host": "spring-cloud-a-svc", "port": 20001, "weight": 1 } ], "timeout": { "connect": 6, "send": 6, "read": 6 }, "type": "roundrobin", "scheme": "http", "pass_host": "pass", "name": "spring-cloud-a-svc", "keepalive_pool": { "idle_timeout": 60, "requests": 1000, "size": 320 } }
spring-cloud-a-gray-svc
的上游配置如下:{ "nodes": [ { "host": "spring-cloud-a-gray-svc", "port": 20001, "weight": 1 } ], "timeout": { "connect": 6, "send": 6, "read": 6 }, "type": "roundrobin", "scheme": "http", "pass_host": "pass", "name": "spring-cloud-a-gray-svc", "keepalive_pool": { "idle_timeout": 60, "requests": 1000, "size": 320 } }
返回上游列表頁面可查看已創建的上游服務。
場景一:按照域名路由,實現全鏈路灰度
如果您想通過不同的域名來區分線上基線環境和灰度環境,灰度環境有單獨的域名可以配置。例如想通過訪問www.example.com
來請求灰度環境,訪問www.aliyundoc.com
走基線環境,如下圖所示:
調用鏈路為Ingress-APISIX > A > B > C ,其中A可以是一個Spring Boot的應用。
配置APISIX路由規則
登錄APISIX控制臺,單擊左側導航欄路由,然后單擊+創建。
在創建路由頁面配置路由信息分為4步,依次是 。
在設置路由信息頁面配置路由基本信息,單擊下一步。
分別配置如下路由:
Base對應的路由配置
{ "uri": "/*", "name": "spring-cloud-a", "methods": [ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE" ], "host": "www.aliyundoc.com", "upstream_id": "40115245*****54748", "labels": { "API_VERSION": "0.0.1" }, "status": 1 }
Gray對應的路由配置
{ "uri": "/*", "name": "spring-cloud-a-gray", "priority": 1, "methods": [ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE" ], "host": "www.example.com", "upstream_id": "40116333*****15388", "labels": { "API_VERSION": "0.0.1" }, "status": 1 }
在設置上游服務頁面,單擊選擇上游服務下拉框,選擇所需的上游服務,然后單擊下一步。
設置路由信息頁面的域名Host為www.aliyundoc.com時,路由到ID為40115245*****54748所對應的上游,即spring-cloud-a-svc;當Host為www.example.com時,路由到ID為40116333*****15388所對應的上游,即spring-cloud-a-gray-svc。
在插件配置頁面單擊下一步。
在預覽頁面查看路由配置,最后單擊提交即可完成配置。
配置MSE全鏈路灰度
登錄MSE治理中心控制臺,并在頂部菜單欄選擇地域。
在左側導航欄,選擇治理中心 > 全鏈路灰度。
在全鏈路灰度頁面,創建泳道組。在創建泳道組面板,輸入泳道組名稱,并添加spring-cloud-a、spring-cloud-b和spring-cloud-c應用進入泳道組,然后單擊確定。
如果您選擇的微服務空間沒有創建過泳道組,單擊創建泳道組及泳道。
如果您選擇的微服務空間已經創建過泳道組,單擊+創建泳道組。
在全鏈路灰度頁面下方,單擊點擊創建第一個分流泳道。在創建泳道面板中選擇泳道標簽gary并設置相關參數,然后單擊確定。
結果驗證
下方代碼中47.97.xx.xx為APISIX的公網IP。
執行如下代碼訪問www.aliyundoc.com,路由到基線環境驗證結果。
curl -H"Host:www.aliyundoc.com" http://47.97.xx.xx/a A[172.18.xx.xx] -> B[172.18.xx.xx] -> C[172.18.xx.xx]%
執行如下代碼訪問www.example.com,路由到灰度環境驗證結果。
curl -H"Host:www.example.com" http://47.97.xx.xx/a Agray[172.18.xx.xx] -> Bgray[172.18.xx.xx] -> Cgray[172.18.xx.xx]%
場景二:按照指定請求參數進行路由,實現全鏈路灰度
若您無法改寫域名,但希望能訪問www.example.com
通過傳入不同的參數來路由到灰度環境。例如想通過env=gray這個請求參數,來訪問灰度環境,如下圖所示:
調用鏈路為Ingress-APISIX > A > B > C ,其中A可以是一個Spring Boot的應用。
配置APISIX路由規則
登錄APISIX控制臺,單擊左側導航欄路由,然后單擊+創建。
創建路由頁面配置路由信息分為4步,依次是 。
在設置路由信息頁面配置路由基本信息,單擊下一步。
分別配置如下路由:
Base對應的路由配置
{ "uri": "/*", "name": "spring-cloud-a", "methods": [ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE" ], "host": "www.example.com", "upstream_id": "40115245*****54748", "labels": { "API_VERSION": "0.0.1" }, "status": 1 }
Gray對應的路由配置。
{ "uri": "/*", "name": "spring-cloud-a-gray", "priority": 1, "methods": [ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE" ], "host": "www.example.com", "vars": [ [ "arg_env", "==", "gray" ] ], "upstream_id": "40116333*****15388", "labels": { "API_VERSION": "0.0.1" }, "status": 1 }
在設置上游服務頁面,單擊選擇上游服務下拉框,去選擇所需的上游服務,然后單擊下一步。
設置路由信息頁面的域名Host為www.example.com時,請求參數
env=gray
時,路由優先匹配到ID為40116333*****15388所對應的上游,即spring-cloud-a-gray-svc;否則,路由到ID為40115245*****54748所對應的上游,即spring-cloud-a-svc。在插件配置頁面單擊下一步。
在預覽頁面查看路由配置,最后單擊提交即可完成配置。
配置MSE全鏈路灰度
配置MSE全鏈路灰度的操作步驟和場景一:按照域名路由,實現全鏈路灰度相同。
結果驗證
下方代碼中的47.97.xx.xx為APISIX的公網IP。
執行下方代碼訪問www.example.com,路由到基線環境。
curl -H"Host:www.example.com" http://47.97.xx.xx/a A[172.18.xx.xx] -> B[172.18.xx.xx] -> C[172.18.xx.xx]%
執行下方代碼訪問www.example.com,同時env=gray時路由到灰度環境。
curl -H"Host:www.example.com" http://47.97.xx.xx/a?env=gray Agray[172.18.xx.xx] -> Bgray[172.18.xx.xx] -> Cgray[172.18.xx.xx]%