基于Ingress-APISIX网关实现全链路灰度

通过Ingress-APISIX提供的灵活的路由功能,您可以在不需要修改任何业务代码的情况下,实现全链路灰度能力。本文介绍如何通过Ingress-APISIX实现全链路灰度功能。

前提条件

创建Kubernetes集群。具体操作,请参见创建创建集群创建ACK专有集群

背景信息

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

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

准备工作

安装ingress-apisix组件

安装Apisixapisix-ingress-controllerDashboard组件。具体操作,请参见Apache APISIX Helm Chart

开启MSE微服务治理

开通微服务治理专业版

微服务应用接入

将ACK微服务应用接入MSE治理中心。具体操作,请参见ACK微服务应用接入MSE治理中心

部署Demo应用程序

  1. 登录容器服务管理控制台,在左侧导航栏选择集群

  2. 集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情

  3. 在集群管理页左侧导航栏,选择工作负载 > 无状态

  4. 无状态页面,选择命名空间,然后单击使用YAML创建资源

  5. 使用如下YAML部署A、B、C三个应用,每个应用分别部署一个基线版本和一个灰度版本,并部署一个Nacos Server应用,用于实现服务发现。

    • A应用

      • 基线(Base)版本YAML:

        展开查看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:

        展开查看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:

        展开查看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:

        展开查看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:

        展开查看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:

        展开查看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:

      展开查看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                    

网络配置

  1. 针对入口应用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
  2. 登录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路由规则

  1. 登录APISIX控制台,单击左侧导航栏路由,然后单击+创建

  2. 创建路由页面配置路由信息分为4步,依次是设置路由信息 > 设置上游服务 > 插件配置 > 预览

    1. 设置路由信息页面配置路由基本信息,单击下一步

      分别配置如下路由:

      • 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
        }

      设置路由信息

    2. 设置上游服务页面,单击选择上游服务下拉框,选择所需的上游服务,然后单击下一步

      设置路由信息页面的域名Host为www.aliyundoc.com时,路由到ID为40115245*****54748所对应的上游,即spring-cloud-a-svc;当Host为www.example.com时,路由到ID为40116333*****15388所对应的上游,即spring-cloud-a-gray-svc。

    3. 插件配置页面单击下一步

    4. 预览页面查看路由配置,最后单击提交即可完成配置。

配置MSE全链路灰度

  1. 登录MSE治理中心控制台,并在顶部菜单栏选择地域。

  2. 在左侧导航栏,选择治理中心 > 全链路灰度

  3. 全链路灰度页面,创建泳道组。在创建泳道组面板,输入泳道组名称,并添加spring-cloud-a、spring-cloud-b和spring-cloud-c应用进入泳道组,然后单击确定。

    • 如果您选择的微服务空间没有创建过泳道组,单击创建泳道组及泳道

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

  4. 全链路灰度页面下方,单击点击创建第一个分流泳道。在创建泳道面板中选择泳道标签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路由规则

  1. 登录APISIX控制台,单击左侧导航栏路由,然后单击+创建

  2. 创建路由页面配置路由信息分为4步,依次是设置路由信息 > 设置上游服务 > 插件配置 > 预览

    1. 设置路由信息页面配置路由基本信息,单击下一步

      分别配置如下路由:

      • 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
        }

      设置路由信息

    2. 设置上游服务页面,单击选择上游服务下拉框,去选择所需的上游服务,然后单击下一步

      设置路由信息页面的域名Host为www.example.com时,请求参数env=gray时,路由优先匹配到ID为40116333*****15388所对应的上游,即spring-cloud-a-gray-svc;否则,路由到ID为40115245*****54748所对应的上游,即spring-cloud-a-svc。

    3. 插件配置页面单击下一步

    4. 预览页面查看路由配置,最后单击提交即可完成配置。

配置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]%