首页 微服务引擎 MSE 最佳实践 微服务治理 全链路灰度 基于Ingress-APISIX网关实现全链路灰度

基于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组件

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

开启MSE微服务治理

  1. MSE微服务治理开通页面,开通微服务治理专业版。
    关于微服务治理的计费详情,请参见计费概述
  2. 安装MSE微服务治理组件。
    1. 登录容器服务控制台
    2. 在左侧导航栏,选择市场 > 应用市场,在搜索框中输入ack-onepilot,单击该组件。
    3. ack-onepilot页面右上方,单击一键部署,在创建面板中选择集群和命名空间,设置组件发布名称,然后单击下一步
      说明 推荐使用默认的命名空间ack-onepilot
    4. 参数配置页面,确认组件参数信息,然后单击确定
      安装完成后,在命名空间ack-onepilot中出现ack-onepilot应用,表示安装成功。
  3. 为应用开启微服务治理。
    1. 登录MSE治理中心控制台
    2. 在左侧导航栏,选择治理中心 > 运维中心 > K8s集群列表。在目标集群操作列,单击管理
    3. 集群详情页面,在目标命名空间操作列,单击开启微服务治理。然后单击确定

部署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
                - 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                    

网络配置

  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. 在左侧导航栏选择微服务治理中心 > 全链路灰度

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

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

  4. 创建泳道组页面,创建泳道组名称,并添加spring-cloud-a、spring-cloud-b和spring-cloud-c应用进入泳道组。然后单击确定。

  5. 全链路灰度页面单击点击创建第一个分流泳道,在创建泳道面板中设置流控泳道相关参数,选择标签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路由规则

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

阿里云首页 微服务引擎 相关技术圈