全链路灰度实现流程

使用限制
- 全链路灰度能力,对于Java应用的限制详见微服务治理支持的Java框架。 
- 全链路灰度能力,云原生API网关版本需要大于等于2.0.1版本。 
- 基于云原生API网关实现全链路灰度,仅支持云原生API网关的开服地域,详情可见开服地域。 
场景示例
本文以电商架构中的下单场景为例,介绍从云原生API网关到微服务的全链路流控功能。假设应用的架构由云原生API网关以及后端的微服务架构组成,后端调用链路有3个:交易中心(A,使用Spring Cloud框架)、商品中心(B,使用Dubbo框架)、库存中心(C),可以通过客户端或者HTML来访问后端服务,这些服务之间通过MSE Nacos注册中心实现服务发现。
客户下单后流量从云原生API网关进入,调用交易中心(A),交易中心(A)再调用商品中心(B),商品中心(B)调用下游的库存中心(C)。总之,调用链路为:客户->云原生API网关->A->B->C。
随着业务不断迭代,新功能需要上线,涉及到应用A和应用C同时发布新版本。在新版本正式上线之前,需要同时对应用A和应用C进行灰度验证,确认无误后方可正式上线。
通过云原生API网关和MSE微服务治理提供的全链路灰度能力,您可以端到端构建从网关到多个后端服务的全链路灰度,控制具有一定特征的灰度流量始终路由到应用对应的灰度环境,满足您同时灰度验证多个服务的诉求。此外,在应用无目标灰度版本时,自动容灾到正式环境中。

名词解释
- 云原生API网关:云原生API网关将流量网关、微服务网关、安全网关和AI 网关四合一,实现碎片化网关的架构统一,提供服务暴露及流量管控、AI应用流量入口与集成、API全生命周期管理等能力,具有性能更强劲、稳定更可靠、多重安全防御、扩展性强,是高性能、安全、AI友好的统一型网关。更多信息,请参见什么是云原生API网关。 
- 泳道:为相同版本应用定义的一套隔离环境。只有满足了流控路由规则的请求流量才会路由到对应泳道里的打标应用。一个应用可以属于多个泳道,一个泳道可以包含多个应用,应用和泳道是多对多的关系。 
- 泳道组:泳道的集合。泳道组的作用主要是为了区分不同团队或不同场景。 
- 基线环境:未打标的应用属于基线环境,是其他环境的兜底环境。 
准备工作
开启MSE微服务治理
重要 请确保MSE Java 探针的版本在3.2.3及以上,ack-onepilot的版本在3.0.18及以上,否则会影响业务运行。
 为云原生API网关关联服务来源
具体操作,请参见创建服务来源。
步骤一:搭建业务应用的基线环境
步骤1:部署后端业务应用的基线版本
- 登录容器服务控制台。 
- 在左侧导航栏,单击集群列表,然后单击目标集群名称。 
- 在左侧导航栏,选择。 
- 在页面上方,选择集群的命名空间,然后单击右上角的使用YAML创建资源。 
- 部署基线应用。 - 服务来源为MSE Nacos- 复制如下YAML文件内容,部署A、B、C三个应用的基线版本。 
 - 说明 - 代码中的{nacos server address}需要替换成您创建的MSE Nacos的内网域名,同时需要去掉大括号- {}。
 
 
- 展开查看YAML文件 - # 应用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,模拟自建服务注册中心的场景。 - 展开查看YAML文件 - 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三个应用的基线版本。 - 展开查看YAML文件 - # 应用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。 - 展开查看YAML文件 - # 暴露应用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:通过云原生API网关暴露应用A
如果您未在云原生API网关上添加过应用A,可以通过如下操作暴露应用A。
- 登录云原生API网关控制台。 
- 在左侧导航栏,选择实例,并在顶部菜单栏选择地域。 
- 在实例页面,单击目标网关实例名称。 
- 在左侧导航栏,选择服务,并单击服务页签。 
- 在服务页签下,单击创建服务。具体操作,请参见创建服务。 -  来源为容器服务- 服务来源:选择容器服务。 
- 命名空间:选择default。 
- 服务列表:选择sc-a。 
 - 来源为MSE Nacos- 服务来源:选择MSE Nacos。 
- 命名空间:选择public。 
- 服务列表:选择sc-A。 
 
- 在云原生API网关控制台,左侧导航栏选择API。为服务sc-a/sc-A创建HTTP API或REST API。具体操作,请参见创建HTTP API或创建REST API并添加接口。 
- 通过路由或者接口暴露应用A。 - 使用HTTP API- 如果您使用HTTP API,请为该API创建路由。进入第6步创建的API,单击创建路由。具体操作,请参见创建路由。 | 配置项 | 描述 |  | 路径 | 选择匹配条件为前缀是,路径为/a。 |  | 所属环境 | 选择目标云原生API网关实例的对应环境 |  | 使用场景 | 选择单服务 |  | 后端服务 | 服务名称选择sc-a |  
 - 使用REST API- 如果您使用REST API,请为该API添加接口。进入第6步创建的API,单击添加接口。 - 添加接口后,请发布API。 | 配置项 | 描述 |  | 所属环境 | 选择目标云原生API网关实例的对应环境 |  | 使用场景 | 选择单服务 |  | 后端服务 | 服务名称选择sc-a |  
 
步骤3:测试基线版本流量
- 登录云原生API网关控制台。 
- 通过路由调试或者接口调试,观察流量情况。 
- 测试基线版本流量,发现流量经过了A、B和C的基线版本,B和C输出结果的应用名后面没有版本,则代表访问对应应用的基线版本。如下所示: - # 测试结果
A[10.0.3.178][config=base] -> B[10.0.3.195] -> C[10.0.3.201]
 
步骤二:搭建业务应用的灰度环境
如果业务应用要发布新功能,新功能要求应用A和应用C同时发版,这时就需要借助全链路灰度来同时验证不同应用的灰度版本。
步骤1:部署后端业务应用的灰度版本
- 登录容器服务控制台。 
- 在左侧导航栏,单击集群列表,然后单击目标集群名称。 
- 在左侧导航栏,选择。 
- 在页面上方,选择集群的命名空间,然后单击右上角的使用YAML创建资源。 
- 复制以下YAML文件内容,部署A、C两个应用的灰度版本。 - 服务来源为MSE Nacos
 - 说明 - 代码中的{nacos server address}需要替换成您创建的MSE Nacos的内网域名,同时需要去掉大括号- {}。
 
 
- 展开查看YAML文件 - # 应用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'
 
- 服务来源为容器服务- 展开查看YAML文件 - # 应用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治理中心控制台,并在顶部菜单栏选择地域。 
- 在左侧导航栏,选择。 
- 如果您选择的微服务命名空间内没有创建过泳道组,则单击创建泳道组及泳道。如果您选择的微服务命名空间内已经创建过泳道组,则单击+创建泳道组。 
- 在创建泳道组面板,设置如下相关配置,然后单击确定。 | 配置项 | 说明 |  | 泳道组名称 | 自定义泳道组的名称。 |  | 入口类型 | 选择云原生API网关。 |  | 入口网关 | 选择目标云原生API网关。 |  | 泳道组涉及应用 | 选择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}。
 
- 以云原生API网关作为全链路灰度入口时,MSE支持两种泳道灰度模式。 
- 泳道路由模式在一个泳道组中需要保持一致。您只有在创建泳道组中第一条泳道时可以调整泳道路由的灰度模式。 
 - 在全链路灰度页面底部,如果您选择的微服务命名空间内没有创建过泳道,则单击点击创建第一个分流泳道。如果您选择的微服务命名空间内已经创建过泳道,则单击+创建泳道。 
- 在创建泳道面板,设置流控泳道相关配置,然后单击确定。 - 创建按请求内容路由的泳道| 配置项 | 说明 |  | 配置节点标签 | 需要您给灰度应用节点打上标签,以便与正常节点做区分。 |  | 填写泳道信息 | 泳道名称:输入便于理解和识别的泳道名称。 泳道标签:该泳道内的匹配的流量去往的目标标签。 确认匹配关系:检查您配置了该标签的应用节点数是否符合预期。 泳道状态:选择开启。 |  | 配置路由和灰度规则 | 设置流量进入该泳道的规则。 灰度模式:选择按内容灰度。灰度条件:选择以下条件同时满足。 本示例灰度条件配置如下: 参数类型:Header。参数:canary。条件:==。值:gray。
 |  
 - 泳道路由模式说明| 灰度条件 | 效果 |  | 以下条件同时满足 | 需要所有条件内容满足的流量才会去往对应的泳道。 |  | 以下条件任意满足 | 满足如下任意一个条件内容的流量就会去往对应的泳道。 |  
 - 条件内容说明| 条件 | 描述 | 全链路灰度配置示例 |  | == | 精确匹配,流量值与条件值需要完全相等。 | 
 |  | != | 不等匹配,流量值与条件值不相等时满足条件。 | 
 |  | in | 包含匹配,流量值需要在指定的列表中满足条件。 | 
 |  | 百分比 | 百分比匹配,原理:满足`hash(get(key)) % 100 < value` 成立时满足条件。 | 
 |  | 正则匹配 | 正则表达式匹配,按照正则表达式规则匹配时满足条件。 | 
 |  | 前缀匹配 | 前缀匹配,指定值是实际值的前缀时满足条件。 | 
 |  
 - 创建按比例路由的泳道
 - 说明 - 创建按比例路由的泳道,需要ack-onepilot版本为3.0.18及以上,且探针版本为3.2.3及以上。 
 
| 配置项 | 说明 |  | 配置节点标签 | 需要您手工给您的灰度应用节点打上标签,以便与正常节点做区分。 |  | 填写泳道信息 | 泳道名称:输入便于理解和识别的泳道名称。 泳道标签:该泳道内的匹配的流量去往的目标标签。 确认匹配关系:检查您配置了该标签的应用节点数是否符合预期。 泳道状态:选择开启。 |  | 配置路由和灰度规则 | 设置流量进入该泳道的规则。 |  
 
 - 说明 - 您还可以为每条网关基线路由分别设置不同的流量比例,开启该功能时,您需要注意该网关基线路由当前在所有泳道组中配置的流量比例总和不应超过100%。 
 
完成泳道创建后,在全链路灰度的流量分配区域,可以查看泳道详情,还可以进行如下操作。
步骤4:测试灰度版本流量
- 您可以通过云原生API网关路由或者接口调试处复制URL。  
  
 
- 测试路由。 - 测试按请求内容路由的泳道- 通过云原生API网关测试灰度流量,如测试结果所示,流量经过了A、C的灰度环境,由于B没有gray环境,所以流量自动容灾到基线版本。 - # 测试结果
Agray[10.0.3.177][config=base] -> B[10.0.3.195] -> Cgray[10.0.3.180]
 - 测试按比例路由的泳道- 您可使用性能测试PTS工具发起测试, 您可以参考如何在一分钟内发起压测?。如图所示,可以观察到有30%的流量进行了灰度。  
 
 - 说明 - 设置您的QPS大小,快速使用小流量进行测试。您可在MSE微服务治理的全链路灰度页面,单击目标应用,在应用QPS 监控区域,可查看对应泳道基线版本(未打标)和灰度版本的流量情况。 
 
(可选)步骤三:可观测
您可以通过MSE提供的可观测大盘,帮助您观察全链路灰度情况,快速定位问题。
微服务治理可观测
在MSE微服务治理的全链路灰度页面,单击目标应用,在应用 QPS 监控区域,可查看对应泳道基线版本(未打标)和灰度版本的流量情况。

- 总QPS:该应用总的QPS。 
- 异常QPS:该应用出错的请求数。 
- GrayQPS:该应用gray版本的QPS。 
Arms 可观测
如果您应用同时接入了 Arms,您可以在 Arms 全链路灰度场景化页面观察灰度环境业务情况,观察灰度大盘业务情况,决定回滚还是继续发布。
