基于Ingress-APISIX网关实现全链路灰度
通过Ingress-APISIX提供的灵活的路由功能,您可以在不需要修改任何业务代码的情况下,实现全链路灰度能力。本文介绍如何通过Ingress-APISIX实现全链路灰度功能。
前提条件
创建Kubernetes集群,您可按需选择创建Kubernetes托管版集群、创建ASK集群、创建Kubernetes专有版集群。
背景信息
微服务架构下,有一些需求开发涉及到微服务调用链路上的多个微服务同时改动。通常每个微服务都会有灰度环境或分组来接收灰度流量。您可能希望进入上游灰度环境的流量也能进入下游灰度的环境中,从而能确保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微服务治理
- 在MSE微服务治理开通页面,开通微服务治理专业版。关于微服务治理的计费详情,请参见计费概述。
- 安装MSE微服务治理组件。
- 登录容器服务控制台。
- 在左侧导航栏,选择 ,在搜索框中输入ack-onepilot,单击该组件。
- 在ack-onepilot页面右上方,单击一键部署,在创建面板中选择集群和命名空间,设置组件发布名称,然后单击下一步。说明 推荐使用默认的命名空间ack-onepilot。
- 在参数配置页面,确认组件参数信息,然后单击确定。安装完成后,在命名空间ack-onepilot中出现ack-onepilot应用,表示安装成功。
- 为应用开启微服务治理。
- 登录MSE治理中心控制台。
- 在左侧导航栏,选择 。在目标集群操作列,单击管理。
- 在集群详情页面,在目标命名空间操作列,单击开启微服务治理。然后单击确定。
部署Demo应用程序
在容器服务控制台左侧导航栏中,单击集群。
- 在集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情。
- 在集群管理页左侧导航栏,选择 。
在无状态页面选择命名空间,然后单击使用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: annotations: msePilotCreateAppName: spring-cloud-a labels: app: spring-cloud-a spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT 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: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-a labels: 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-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT 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: annotations: msePilotCreateAppName: spring-cloud-b labels: app: spring-cloud-b spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.1-SNAPSHOT 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: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-b labels: app: spring-cloud-b-new spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.1-SNAPSHOT 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: annotations: msePilotCreateAppName: spring-cloud-c labels: app: spring-cloud-c spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.1-SNAPSHOT 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: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-c labels: app: spring-cloud-c-new spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.1-SNAPSHOT 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: nacos/nacos-server:v2.2.0 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,单击确定
gray
泳道即可创建成功。
结果验证
下方代码中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]%