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

前提条件

背景信息

在微服务场景中,当您部署的Spring Cloud应用或Dubbo应用存在升级版本时,由于应用间的调用是随机的,会导致无法将具有一定特征的流量路由到应用的目标版本。全链路流量控制功能将应用的相关版本隔离成一个独立的运行环境(即泳道),通过设置Ingress路由规则,将满足规则的请求流量路由到目标版本应用。

本文以电商架构中的下单场景为例介绍从Ingress网关到微服务的全链路流控功能。假设应用的架构由Ingress-nginx网关以及后端的微服务架构(Spring Cloud)组成,后端调用链路有3个:交易中心(A)、商品中心(B)、库存中心(C),可以通过客户端或者是HTML来访问后端服务,这些服务之间通过Nacos注册中心实现服务发现。

客户下单后流量从Ingress网关进来,调用交易中心,交易中心再调用商品中心,商品中心调用下游的库存中心。

交易中心和商品中心各有两个新版本(1和2)在运行,需要对这两个新版本进行灰度验证。此时通过Ingress网关将满足特定流控规则的请求流量路由到新版本,其余流量全部路由到线上(正式)版本。示例图

使用限制

由于全链路灰度功能整合了标签路由功能,因此不推荐已经加入全链路流量控制的应用同时配置金丝雀发布、标签路由规则。
限制项限制值备注
Spring Cloud版本Spring Cloud Edgware及以上版本。无。
Dubbo版本2.5.3~2.7.8Dubbo 3.0+版本支持当前处于灰度中,如有场景需求,请加入钉群(钉群号:34754806)联系技术支持。
客户端类型
  • Resttemplate
  • Spring Cloud OpenFeign
无。
Java应用JDK版本目前支持JDK 1.6、1.7、1.8版本应用接入JDK 11版本当前处于灰度中,如有场景需求,请加入钉群(钉群号:34754806)联系技术支持。
负载均衡类型
  • Ribbon 2.0.x+
  • LoadBalancer 3.0.x+
无。
Spring Cloud Gateway版本Spring Cloud Gateway 2.1.0.RELEASE+无。
Spring Cloud Zuul版本1.3.x无。
注册中心类型
  • Nacos
  • Eureka
  • ZooKeeper
微服务治理能力无关注册中心,可以是MSE托管注册中心,也可以是自建注册中心。

名词解释

  • 泳道

    为相同版本应用定义的一套隔离环境。只有满足了流控路由规则的请求流量才会路由到对应泳道里的打标应用。一个应用可以属于多个泳道,一个泳道可以包含多个应用,应用和泳道是多对多的关系。

  • 泳道组

    泳道的集合。泳道组的作用主要是为了区分不同团队或不同场景。

准备工作

安装Ingress-nginx组件

  1. 登录ack-ingress-nginx应用市场,在页面右上方单击一键部署,在创建面板中选择集群命名空间,设置组件发布名称,然后单击下一步
    说明 推荐使用默认命名空间kube-system
  2. 参数配置向导中确认组件参数信息,然后单击确定
    安装完成后,在命名空间kube-system中出现ack-ingress-nginx-default应用,表示安装成功。

开启MSE微服务治理

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

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

      关于微服务治理的计费详情,请参见计费概述

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

    1. 容器服务控制台左侧导航栏中,选择市场 > 应用市场
    2. 应用市场页面单击应用目录页签,然后搜索并单击ack-onepilot组件。
    3. ack-onepilot页面右上方单击一键部署,在创建面板中选择集群命名空间,设置组件发布名称,然后单击下一步
      说明 推荐使用默认的命名空间ack-onepilot
    4. 参数配置向导中确认组件参数信息,然后单击确定

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

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

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

部署Demo应用程序

  1. 容器服务控制台左侧导航栏中,单击集群
  2. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  3. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  4. 无状态页面选择命名空间,然后单击使用YAML创建资源
  5. 对模板进行相关配置,完成配置后单击创建
    本文示例中部署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
                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:latest
              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                            
  6. 针对入口应用A,配置两个K8s Service。
    1. 在集群详情页面左侧导航栏选择网络 > 服务
    2. 服务页面选择命名空间,然后单击使用YAML创建资源
    3. 对模板进行相关配置,完成配置后单击创建
      • 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

步骤一:创建泳道组

  1. 登录MSE治理中心控制台,并在顶部菜单栏选择地域。
  2. 在顶部菜单栏选择地域。
  3. 在左侧导航栏,选择治理中心 > 全链路灰度
  4. 全链路灰度页面,单击创建泳道组及泳道。如果您选择的微服务空间内已经创建过泳道组,则单击+创建泳道组
  5. 创建泳道组面板中设置泳道组相关参数,然后单击确定
    表 1. 泳道组配置参数说明
    参数描述
    泳道组名称自定义设置泳道组的名称。支持大小写字母、数字、短划线(-)和下划线(_),长度不超过64个字符。
    入口类型选择ingress/自建网关
    泳道组涉及所有应用单击+添加灰度链路涉及应用,选择您的入口应用或入口网关所涉及的所有相关服务。
    泳道组创建完成后,在全链路灰度页面的泳道组涉及应用区域出现您所创建的泳道组。请检查入口应用和所涉及的应用是否正确,如需变更泳道组信息,请单击右侧的编辑图标并修改相关信息。

步骤二:创建泳道

  1. 全链路灰度页面上方选择创建和泳道组时相同的微服务空间,然后底部单击点击创建第一个分流泳道

    如果您选择的微服务空间内已经创建过泳道,则单击创建泳道

    重要 加入全链路流量控制的应用,将不再支持金丝雀发布、标签路由等功能。
  2. 创建泳道面板中设置流控泳道相关参数,然后单击确定
    重要 如果您的网关应用是Ingress网关,需要去容器控制台配置Ingress路由规则。
    表 2. 泳道配置参数说明
    参数描述
    泳道名称自定义设置流控泳道的名称。支持大小写字母、数字、短划线(-)和下划线(_),长度不超过64个字符。
    配置应用标签配置方式:在容器ACK控制台中去应用YAML的spec.template.metadata.annotations下增加alicloud.service.tag:{tag}
    添加应用当STPE 2配置好后,单击刷新按钮,下拉框中会出现相应的标签列表,选择对应的标签,就会自动添加相应的应用。
  3. 全链路灰度页面的流量分配面板查看泳道,有以下两种展现形式:
    • 单击查看泳道1图标,您可以查看该泳道的流量比例。
    • 单击查看泳道2图标,您可以设置该泳道上应用的状态。
      操作列的开启关闭有如下含义:
      • 开启:创建的泳道将会生效,即流量会按照泳道方式进行流转,会优先流向标记有当前泳道对应标签的应用版本,如果没有对应标签的应用版本则流向未打标的应用版本。
      • 关闭:关闭创建的泳道,即该应用往后的流量会流向未打标的应用版本。
  4. 配置流量入口的Ingress规则,访问www.base.com路由到A应用的base版本,访问www.gray.com路由到A应用的gray版本。
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      name: spring-cloud-a-base
    spec:
      rules:
      - host: www.base.com
        http:
          paths:
          - backend:
              serviceName: spring-cloud-a-base
              servicePort: 20001
            path: /
    
    ---
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      name: spring-cloud-a-gray
    spec:
      rules:
      - host: www.gray.com
        http:
          paths:
          - backend:
              serviceName: spring-cloud-a-gray
              servicePort: 20001
            path: /

验证特征流量路由到目标应用

  • 结果验证

    • 访问www.base.com路由到基线环境
      • Curl命令:
        curl -H"Host:www.base.com" http://106.14.XX.XX/a
      • 返回结果:
        A[172.18.XX.XX] -> B[172.18.XX.XX] -> C[172.18.XX.XX]%
    • 访问www.gray.com路由到灰度环境
      • Curl命令:
        curl -H"Host:www.gray.com" http://106.14.XX.XX/a
      • 返回结果:
        Agray[172.18.XX.XX] -> Bgray[172.18.XX.XX] -> Cgray[172.18.XX.XX]%
  • 查看打标应用的流量监控图

    1. 全链路灰度页面单击目标泳道组页签。
    2. 泳道组涉及应用区域单击目标应用名称,即可在右侧出现相应的QPS监控图
  • 查看所有应用监控图

    您除了查看单个应用的监控图外,您还可以查看泳道组内所有应用的监控图。通过比对分析所有应用的监控图,可以分析出更多有用信息。

    QPS监控图右侧单击查看所有应用监控(流量逃逸观测),您可以查看该泳道所有应用的流量监控视图。

    • 您可以查看同一时刻,调用的应用概览信息。
    • 您可以分析流量逃逸问题,判断逃逸对象。