通过MSE云原生网关提供的全链路灰度能力,可以在不需要修改任何您的业务代码的情况下,实现全链路流量控制。本文介绍通过MSE云原生网关实现全链路灰度功能。

前提条件

背景信息

微服务架构下,有些开发需求,微服务调用链路上的多个微服务同时发生了改动,通常每个微服务都会有灰度环境或分组来接收灰度流量。此时希望通过进入上游灰度环境的流量,也能进入下游灰度的环境中,确保1个请求始终在灰度环境中传递,即使这个调用链路上有一些微服务没有灰度环境,这些应用请求在下游的时候依然能够回到灰度环境中。通过MSE提供的全链路灰度能力,可以在不需要修改任何您的业务代码的情况下,能够轻松实现上述能力。

本文通过示例为您演示MSE云原生网关全链路灰度功能。假设应用的架构由MSE云原生网关以及后端的微服务架构(Spring Cloud)组成,后端调用链路有3个:购物车(A),交易中心(B),库存中心(C),可以通过客户端或者是HTML来访问后端服务,这些服务之间通过Nacos注册中心实现服务发现。

准备工作

开启MSE微服务治理

  1. 开通微服务治理专业版:

    1. 单击开通MSE微服务治理
    2. 微服务治理版本选择专业版,选中服务协议,然后单击立即开通

      关于微服务治理的计费详情,请参见价格说明

  2. 安装MSE微服务治理组件:

    1. 容器服务控制台左侧导航栏中,选择市场 > 应用目录
    2. 应用目录页面搜索框中输入ack-mse-pilot,单击搜索图标,然后单击组件。
    3. 详情页面选择开通该组件的集群,然后单击创建

      安装完成后,在命名空间mse-pilot中出现mse-pilot-ack-mse-pilot应用,表示安装成功。

  3. 为应用开启微服务治理:

    1. 登录MSE治理中心控制台
    2. 在左侧导航栏选择微服务治理中心 > K8s集群列表
    3. K8s集群列表页面搜索目标集群,单击搜索图标图标,然后单击目标集群操作列下方的管理
    4. 集群详情页面命名空间列表区域,单击目标命名空间操作列下方的开启微服务治理
    5. 开启微服务治理对话框中单击确认

部署Demo应用程序

  1. 容器服务控制台左侧导航栏中,单击集群
  2. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  3. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  4. 无状态页面单击使用YAML创建资源
  5. 对模板进行相关配置,完成配置后单击创建
    本文示例中部署A、B、C三个应用,每个应用分别部署一个基线版本和一个灰度版本。
    说明
    • 因为需要使用MSE Nacos注册中心,所以需要将spring.cloud.nacos.discovery.server-addr换成实际业务中的Nacos注册中心地址。
    • 接入MSE云原生网关的服务,如果需要使用灰度发布,需要在发布服务时在元数据信息中增加版本标签。在本文示例中,服务A是需要暴露给网关,所以为基线版本添加spring.cloud.nacos.discovery.metadata.version=base,为灰度版本添加spring.cloud.nacos.discovery.metadata.version=gray
    • A应用
      • 基线(base)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-a
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                - name: spring.cloud.nacos.discovery.metadata.version
                  value: base
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-a
                ports:
                - containerPort: 20001
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi
      • 灰度(gray)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-a-new
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: profiler.micro.service.tag.trace.enable
                  value: "true"
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                - name: spring.cloud.nacos.discovery.metadata.version
                  value: gray
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-a-new
                ports:
                - containerPort: 20001
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi
    • B应用
      • 基线(base)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-b
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.2-demo-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-b
                ports:
                - containerPort: 8080
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi
      • 灰度(gray)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-b-new
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.2-demo-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-b-new
                ports:
                - containerPort: 8080
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi
    • C应用
      • 基线(base)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-c
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.2-demo-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-c
                ports:
                - containerPort: 8080
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi
      • 灰度(gray)版本YAML:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          labels:
            app: spring-cloud-c-new
          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: LANG
                  value: C.UTF-8
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.2-demo-SNAPSHOT
                imagePullPolicy: Always
                name: spring-cloud-c-new
                ports:
                - containerPort: 8080
                  protocol: TCP
                resources:
                  requests:
                    cpu: 250m
                    memory: 512Mi

配置MSE云原生网关

  1. 登录MSE网关管理控制台
  2. 为云原生网关添加MSE Nacos服务来源。具体操作,请参见新建服务来源
  3. 为云原生网关添加服务,服务来源选择MSE Nacos,服务选择sc-A。具体操作,请参见添加服务
    添加服务A
  4. 为入口服务A创建基线(base)版本和灰度(gray)版本。具体操作,请参见添加服务版本
    说明 版本划分依据服务注册时所带的元数据信息:version,可以是任意区分服务版本的标签值,取决于您在注册服务时所采用的元数据信息。
    为服务A设置服务版本
  5. 为云原生网关创建base.example.comgray.example.com的域名。具体操作,请参见创建域名
    关联域名列表

应用场景一:对经过节点的流量进行自动染色,实现全链路灰度

可以通过不同的域名来区分线上基线环境和灰度环境,灰度环境有单独的域名可以配置,例如通过gray.example.com访问灰度环境,通过base.example.com访问基线环境。应用场景一(云原生网关)

调用链路为:云原生网关->A->B->C。

说明 入口应用A的基线(base)和灰度(gray)环境,需要增加profiler.micro.service.tag.trace.enable=true环境变量,表示开启向后透传当前环境的标签功能。这样,当云原生网关路由A的灰度版本后,即使请求中没有携带任何Header,往后调用时会自动添加x-mse-tag:gray这个Header,其中的Header的值gray来自于A应用配置的标签信息。如果原来的请求中带有x-mse-tag:gray则会以原来请求中的标签优先。
  1. 登录MSE网关管理控制台
  2. 在左侧导航栏选择云原生网关 > 网关列表
  3. 在云原生网关管理控制台上分别创建两条路由规则:
    • 关联域名base.example.com,路由到sc-A服务的基线(base)版本中。路由sc-a服务基线版本
    • 关联域名gray.example.com,路由到sc-A服务的灰度(gray)版本中。路由sc-a服务gray版本

    关于创建路由规则的具体操作,请参见新建路由规则

    创建的网关路由规则如下:

    路由规则列表
结果验证
  • 访问base.example.com路由到基线(base)环境。
    • Curl命令:
      curl -H "Host: base.example.com" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> B[172.21.XX.XX] -> C[172.21.XX.XX]
  • 访问gray.example.com路由到灰度(gray)环境。
    • Curl命令:
      curl -H "Host: gray.example.com" http://118.31.XX.XX/a
    • 执行结果:
      Agray[172.21.XX.XX] -> Bgray[172.21.XX.XX] -> Cgray[172.21.XX.XX]
  • 如果入口应用A没有灰度(gray)环境,访问到A的基线(base)环境,又需要在A->B的时候进入灰度环境,则可以通过增加一个特殊的 Header:x-mse-tag来实现,Header的值是流量走向的环境的标签,例如gray
    • Curl命令:
      curl -H "Host: base.example.com" -H "x-mse-tag: gray" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> Bgray[172.21.XX.XX] -> Cgray[172.21.XX.XX]

      可以看到,首先进入了A的基线(base)环境,但是A->B的时候又重新回到了灰度(gray)环境。

      说明 您在MSE云原生网关中配置好规则,当某个应用需要灰度发布时,只需要在灰度环境中部署好应用,灰度流量会进入到灰度节点中。如果验证没问题,则将灰度的镜像发布到基线环境中;如果一次变更中有多个应用需要灰度发布,则把他们都加入到灰度环境中即可。

场景二:通过给流量带上特定的Header,实现全链路灰度

有些客户端没法改写域名,希望能访问base.example.com通过传入不同的Header来路由到灰度环境。例如下图中,通过添加x-mse-tag:gray这个Header,来访问灰度(gray)环境。应用场景二(云原生网关)
  1. 登录MSE网关管理控制台
  2. 在左侧导航栏选择云原生网关 > 网关列表
  3. 在云原生网关管理控制台上分别创建两条路由规则:
    • 关联域名base.example.com,路由到sc-A服务的基线(base)版本中。路由sc-a服务基线版本
    • 关联域名base.example.com,路由到sc-A服务的灰度(gray)版本中,添加请求头Header:x-mse-tag: graysc-a-gray路由

    关于创建路由规则的具体操作,请参见新建路由规则

    创建的网关路由规则如下:

    路由规则列表2
结果验证
  • 访问base.example.com路由到基线(base)环境。
    • Curl命令:
      curl -H "Host: base.example.com" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> B[172.21.XX.XX] -> C[172.21.XX.XX]
  • 如果想访问灰度环境,只需要在请求中增加一个Header:x-mse-tag:gray
    • Curl命令:
      curl -H "Host: base.example.com" -H "x-mse-tag: gray" http://118.31.XX.XX/a
    • 执行结果:
      Agray[172.21.XX.XX] -> Bgray[172.21.XX.XX] -> Cgray[172.21.XX.XX]

      可以看到云原生网关根据这个Header直接路由到了A的灰度(gray)环境中。

更进一步,还可以借助MSE云原生网关实现更复杂的路由,例如客户端已经带上了某个Header,想要利用现成的Header来实现路由,而不用新增一个Header,如下图所示,假设我们想要x-user-id为100的请求进入灰度环境。应用场景2-1(云原生网关)
  1. 确保入口的A应用添加了profiler.micro.service.tag.trace.enable=true配置。
  2. MSE网关管理控制台上修改灰度的路由配置(sc-a-gray)中的请求头部。
    编辑路由

    关于编辑MSE云原生网关路由规则的具体操作,请参见变更路由规则

结果验证
  • 访问的时候带上特殊的Header:x-user-id:100,满足条件进入灰度(gray)环境。
    • Curl命令:
      curl -H "Host: base.example.com" -H "x-user-id:100" http://118.31.XX.XX/a
    • 执行结果:
      Agray[172.21.XX.XX] -> Bgray[172.21.XX.XX] -> Cgray[172.21.XX.XX]
  • 不满足条件的请求,进入基线(base)环境:
    • Curl命令:
      curl -H "Host: base.example.com" -H "x-user-id:101" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> B[172.21.XX.XX] -> C[172.21.XX.XX]

    这样的好处是,客户端的域名不变,只需要通过请求来进行区分。

场景三:通过自定义路由规则来进行全链路灰度

有时候,我们不想要自动透传且自动路由,而是希望微服务调用链上下游的每个应用能自定义灰度规则,例如B应用希望控制只有满足自定义规则的请求才会路由到B应用这里,而C应用有可能希望定义和B应用不同的灰度规则,如下图所示:应用场景三(云原生网关)
  • 对于入口服务A,我们希望x-user-id:100的请求进入灰度(gray)版本,其他进入基线(base)版本。
  • 对于后端服务B,我们希望x-user-id:100的请求进入灰度(gray)版本,其他进入基线(base)版本。
  • 对于后端服务C,我们希望x-user-id:200的请求进入灰度(gray)版本,其他进入基线(base)版本
  1. 在入口应用A处(最好是所有的入口应用都增加该环境变量,包括基线环境和灰度环境) 增加一个环境变量:alicloud.service.header=x-user-id,其中x-user-id是需要透传的Header,它的作用是识别该Header并做自动透传。
    说明
    • 建议把场景一和场景二中配置的环境变量profiler.micro.service.tag.trace.enable=true删除。
    • 不要使用x-mse-tag,它是系统默认的一Header,有特殊的逻辑。
  2. MSE治理中心控制台给B应用(即sc-B)配置标签路由规则,设置只有x-user-id:100的请求进入灰度(gray)版本。

    关于给应用配置标签路由的具体操作,请参见配置标签路由

    配置路由规则
  3. MSE治理中心控制台给C应用(即sc-C)配置标签路由规则,设置只有x-user-id:200的请求进入灰度(gray)版本。
    配置标签路由2
  4. 确认路由规则。
    路由规则
结果验证
  • 带上满足入口服务的灰度条件的Header访问网关,整条访问链路会经过A的灰度(gray)环境、B的灰度(gray)环境和C的基线(base)环境。
    • Curl命令:
      curl -v -H "Host: base.example.com" -H "x-user-id: 100" http://118.31.XX.XX/a
    • 执行结果:
      Agray[10.43.XX.XX] -> Bgray[10.43.XX.XX] -> C[10.43.XX.XX]
  • 如果仅仅需要灰度对应的应用,不需要通过过MSE云原生网关实现Header路由,那么可以去掉sc-a-gray配置。

    访问基线(base)环境A应用(基线环境入口应用需要加上alicloud.service.header环境变量),带上满足条件的Header,路由到B应用的灰度(gray)环境,C应用的基线(base)环境中。

    • Curl命令:
      curl -v -H "Host: base.example.com" -H "x-user-id: 100" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> Bgray[172.21.XX.XX] -> C[172.21.XX.XX]
  • 访问基线(base)环境A应用(基线环境入口应用需要加上alicloud.service.header环境变量),带上满足条件的Header,路由到B应用的基线(base)环境,C应用的灰度(gray)环境中。
    • Curl命令:
      curl -v -H "Host: base.example.com" -H "x-user-id: 200" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> B[172.21.XX.XX] -> Cgray[172.21.XX.XX]
  • 访问基线(base)环境A应用(基线环境入口应用需要加上alicloud.service.header环境变量),带上不满足B、C灰度条件的Header,路由到B应用的基线(base)环境,C应用的基线(gray)环境中。
    • Curl命令:
      curl -v -H "Host: base.example.com" -H "x-user-id: 101" http://118.31.XX.XX/a
    • 执行结果:
      A[172.21.XX.XX] -> B[172.21.XX.XX] -> C[172.21.XX.XX]