Kubernetes / Linux Note / 探知未来 / 运维笔记

深入剖析全链路灰度技术内幕

Einic Yeo · 12月19日 · 2021年 · · ·

当服务有新版本要发布上线时,通过引流一小部分流量到新版本,可以及时发现程序问题,有效阻止大面积故障的发生。业界上已经有比较成熟的服务发布策略,比如蓝绿发布、A/B 测试以及金丝雀发布,这些发布策略主要专注于如何对单个服务进行发布。在微服务体系架构中,服务之间的依赖关系错综复杂,有时某个功能发版依赖多个服务同时升级上线。我们希望可以对这些服务的新版本同时进行小流量灰度验证,这就是微服务架构中特有的全链路灰度场景,通过构建从网关到整个后端服务的环境隔离来对多个不同版本的服务进行灰度验证。

本文将会揭开全链路灰度的神秘面纱,深入剖析全链路灰度技术内幕,引出两种不同的实现方案,并对实现方案的技术细节进行深入探讨,最后通过实践环节来展示全链路灰度在实际业务中的使用版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!场景。

一、微服务架构带来的挑战

为了满足业务的迭代速度,开发者开始对原来的单体架构进行细粒度的拆分,将单体应用中的服务模块拆分成一个个独版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!立部署运行的微服务,并且这些微服务的生命周期由对应的业务团队独自负责,有效的解决了单体架构中存在的敏捷性不足、灵活性不强的问题。常见的做法是根据业务域或者功能域进行服务拆分,如下图:

其中,流量网关是四层代理,主要功能有负载均衡、TLS 卸载以及一些安全防护功能;微服务网关是七层代理,主要用来暴露后端服务、流量治理、访问控制和流量监控。以”高内聚、低耦合”作为设计理念的微服务架构为开发者带来了前所未有的开发体验,每个业务团队专注于自身业务的代码逻辑,并通过 API 形式对外发布。服务依赖方只需引入服务提供方的 API 定义,即可完成服务之间通信,无需关心服务提供方的部署形态和内部实现。


但任何架构都不是银弹,在解决旧问题同时势必会引入一些新的问题。微服务体系中最令人头疼的问题,是如何对众多微服务进行高效、便捷的治理,主要表现在可见性、连接性和安全性这三个方面。进一步细化,微服务架构带来了以下的挑战:

本文的重点主要关注服务发布这一子领域,如何保证微服务体系中服务新版本升级过程中平滑无损,以及如何低成本的为多个微服务构建流量隔离环境,方便开发者同时对多个服务新版本进行充分的灰度验证,避免故障的发生。

二、什么是全链路灰度

2.1、单体架构下的服务发布

首先,我们先看一下在单体架构中,如何对应用中某个服务模块进行新版本发布。如下图,应用中的 Cart 服务模块有新版本迭代:

由于 Cart 服务是应用的一部分,所以新版本上线时需要对整个应用进行编译、打包以及部署。服务级别发布问题变成了应用级别的发布问题,我们需要对应用的新版本而不是服务来实施有效的发布策略。

目前,业界已经有非常成熟的服务发布方案,例如蓝绿发布和灰度发布。蓝绿发布需要对服务的新版本进行冗余部署,一般新版本的机器规格和数量与旧版本保持一致,相当于该服务有两套完全相同的部署环境,只不过此时只有旧版本在对外提供服务,新版本作为热备。当服务进行版本升级时,我们只需将流量全部切换到新版本即可,旧版本作为热备。我们的例子使用蓝绿发布的示意图如下,流量切换基于四层代理的流量网关即可完成。

在蓝绿发布中,由于存在流量整体切换,所以需要按照原服务占用的机器规模为新版本克隆一套环境,相当于要求原来1倍的机器资源。灰度发布的核心思想是根据请求内容或者请求流量的比例将线上流量的一小部分转发至新版本,待灰度验证通过后,逐步调大新版本的请求流量,是一种循序渐进的发布方式。我们的例子使用灰度发布的示意图如下,基于内容或比例的流量控制需要借助于一个七层代理的微服务网关来完成。

其中,Traffic Routing 是基于内容的灰度方式,比如请求中含有头部 stag=gray 的流量路由到应用 v2 版本;Traffic Shifting 是基于比例的灰度方式,以无差别的方式对线上流量按比重进行分流。相比蓝绿发布,灰度发布在机器资源成本以及流量控制能力上更胜一筹,但缺点就是发布周期过长,对运维基础设施要求较高。

2.2、微服务架构下的服务发布

在分布式微服务架构中,应用中被拆分出来的子服务都是独立部署、运行和迭代的。单个服务新版本上线时,我们再也不需要对应用整体进行发版,只需关注每个微服务自身的发布流程即可,如下:

为了验证服务 Cart 的新版本,流量在整个调用链路上能够通过某种方式有选择的路由到 Cart 的灰度版本,这属于微服务治理领域中流量治理问题。常见的治理策略包括基于 Provider 和基于 Consumer 的方式。

  • 基于 Provider 的治理策略。配置 Cart 的流量流入规则,User 路由到 Cart 时使用 Cart 的流量流入规则。
  • 基于 Consumer 的治理策略。配置 User 的流量流出规则, User 路由到 Cart 时使用 User 的流量流出规则。

此外,使用这些治理策略时可以结合上面介绍的蓝绿发布和灰度发布方案来实施真正的服务级别的版本发布。

2.3、全链路灰度

继续考虑上面微服务体系中对服务 Cart 进行发布的场景,如果此时服务 Order 也需要发布新版本,由于本次新功能涉及到服务 Cart 和 Order 的共同变动,所以要求在灰度验证时能够使得灰度流量同时经过服务 Cart 和 Order 的灰度版本。如下图:

按照上一小节提出的两种治理策略,我们需要额外配置服务 Order 的治理规则,确保来自灰度环境的服务 Cart 的流量转发至服务 Order 的灰度版本。这样的做法看似符合正常的操作逻辑,但在真实业务场景中,业务的微服务规模和数量远超我们的例子,其中一条请求链路可能经过数十个微服务,新功能发布时也可能会涉及到多个微服务同时变更,并且业务的服务之间依赖错综复杂,频繁的服务发布、以及服务多版本并行开发导致流量治理规则日益膨胀,给整个系统的维护性和稳定性带来了不利因素。

对于以上的问题,开发者结合实际业务场景和生产实践经验,提出了一种端到端的灰度发布方案,即全链路灰度。全链路灰度治理策略主要专注于整个调用链,它不关心链路上经过具体哪些微服务,流量控制视角从服务转移至请求链路上,仅需要少量的治理规则即可构建出从网关到整个后端服务的多个流量隔离环境,有效保证了多个亲密关系的服务顺利安全发布以及服务多版本并行开发,进一步促进业务的快速发展。

三、全链路灰度的解决方案

如何在实际业务场景中去快速落地全链路灰度呢?目前,主要有两种解决思路,基于物理环境隔离和基于逻辑环境隔离。

3.1、物理环境隔离

物理环境隔离,顾名思义,通过增加机器的方式来搭建真正意义上的流量隔离。



这种方案需要为要灰度的服务搭建一套网络隔离、资源独立的环境,在其中部署服务的灰度版本。由于与正式环境隔离,正式环境中的其他服务无法访问到需要灰度的服务,所以需要在灰度环境中冗余部署这些线上服务,以便整个调用链路正常进行流量转发。此外,注册中心等一些其他依赖的中间件组件也需要冗余部署在灰度环境中,保证微服务之间的可见性问题,确保获取的节点 IP 地址只属于当前的网络环境。

这个方案一般用于企业的测试、预发开发环境的搭建,对于线上灰度发布引流的场景来说其灵活性不够。况且,微服务多版本的存在在微服务架构中是家常便饭,需要为这些业务场景采用堆机器的方式来维护多套灰度环境。如果您的应用数目过多的情况下,会造成运维、机器成本过大,成本和代价远超收益;如果应用数目很小,就两三个应用,这个方式还是很方便的,可以接受的。

3.2、逻辑环境隔离

另一种方案是构建逻辑上的环境隔离,我们只需部署服务的灰度版本,流量在调用链路上流转时,由流经的网关、各个中间件以及各个微服务来识别灰度流量,并动态转发至对应服务的灰度版本。如下图:

上图可以很好展示这种方案的效果,我们用不同的颜色来表示不同版本的灰度流量,可以看出无论是微服务网关还是微服务本身都需要识别流量,根据治理规则做出动态决策。当服务版本发生变化时,这个调用链路的转发也会实时改变。相比于利用机器搭建的灰度环境,这种方案不仅可以节省大量的机器成本和运维人力,而且可以帮助开发者实时快速的对线上流量进行精细化的全链路控制。

那么全链路灰度具体是如何实现呢?通过上面的讨论,我们需要解决以下问题:

  1. 链路上各个组件和服务能够根据请求流量特征进行动态路由
  2. 需要对服务下的所有节点进行分组,能够区分版本
  3. 需要对流量进行灰度标识、版本标识
  4. 需要识别出不同版本的灰度流量

接下来,会介绍解决上述问题需要用到的技术。

标签路由

标签路由通过对服务下所有节点按照标签名和标签值不同进行分组,使得订阅该服务节点信息的服务消费端可以按需访问该服务的某个分组,即所有节点的一个子集。服务消费端可以使用服务提供者节点上的任何标签信息,根据所选标签的实际含义,消费端可以将标签路由应用到更多的业务场景中。

节点打标

那么如何给服务节点添加不同的标签呢?在如今火热的云原生技术推动下,大多数业务都在积极进行容器化改造之旅。这里,我就以容器化的应用为例,介绍在使用 Kubernetes Service 作为服务发现和使用比较流行的 Nacos 注册中心这两种场景下如何对服务 Workload 进行节点打标。

在使用 Kubernetes Service 作为服务发现的业务系统中,服务提供者通过向 ApiServer 提交 Service 资源完成服务暴露,服务消费端监听与该 Service 资源下关联的 Endpoint 资源,从 Endpoint 资源中获取关联的业务 Pod 资源,读取上面的 Labels 数据并作为该节点的元数据信息。所以,我们只要在业务应用描述资源 Deployment 中的 Pod 模板中为节点添加标签即可。

在使用 Nacos 作为服务发现的业务系统中,一般是需要业务根据其使用的微服务框架来决定打标方式。如果 Java 应用使用的 Spring Cloud 微服务开发框架,我们可以为业务容器添加对应的环境变量来完成标签的添加操作。比如我们希望为节点添加版本灰度标,那么为业务容器添加`spring.cloud.nacos.discovery.metadata.version=gray`,这样框架向Nacos注册该节点时会为其添加一个标签`verison=gray`。

流量染色

请求链路上各个组件如何识别出不同的灰度流量?答案就是流量染色,为请求流量添加不同灰度标识来方便区分。我们可以在请求的源头上对流量进行染色,前端在发起请求时根据用户信息或者平台信息的不同对流量进行打标。如果前端无法做到,我们也可以在微服务网关上对匹配特定路由规则的请求动态添加流量标识。此外,流量在链路中流经灰度节点时,如果请求信息中不含有灰度标识,需要自动为其染色,接下来流量就可以在后续的流转过程中优先访问服务的灰度版本。

分布式链路追踪

还有一个很重要的问题是如何保证灰度标识能够在链路中一直传递下去呢?如果在请求源头染色,那么请求经过网关时,网关作为代理会将请求原封不动的转发给入口服务,除非开发者在网关的路由策略中实施请求内容修改策略。接着,请求流量会从入口服务开始调用下一个微服务,会根据业务代码逻辑形成新的调用请求,那么我们如何将灰度标识添加到这个新的调用请求,从而可以在链路中传递下去呢?


从单体架构演进到分布式微服务架构,服务之间调用从同一个线程中方法调用变为从本地进程的服务调用远端进程中服务,并且远端服务可能以多副本形式部署,以至于一条请求流经的节点是不可预知的、不确定的,而且其中每一跳的调用都有可能因为网络故障或服务故障而出错。分布式链路追踪技术对大型分布式系统中请求调用链路进行详细记录,核心思想就是通过一个全局唯一的 traceid 和每一条的 spanid 来记录请求链路所经过的节点以及请求耗时,其中 traceid 是需要整个链路传递的。

借助于分布式链路追踪思想,我们也可以传递一些自定义信息,比如灰度标识。业界常见的分布式链路追踪产品都支持链路传递用户自定义的数据,其数据处理流程如下图所示:

逻辑环境隔离——基于 SDK

上面我们详细介绍了实现全链路灰度所需要的几种技术,如果想为现有的业务接入全链路灰度能力,不可避免的需要为业务使用的开发框架 SDK 进行改造。首先,需要支持动态路由功能,对于 Spring Cloud、Dubbo 开发框架,可以对出口流量实现自定义 Filter,在该 Filter 中完成流量识别以及标签路由。同时需要借助分布式链路追踪技术完成流量标识链路传递以及流量自动染色。此外,需要引入一个中心化的流量治理平台,方便各个业务线的开发者定义自己的全链路灰度规则。基于 SDK 实现方式的图例如下:

逻辑环境隔离——基于 Java Agent

基于 SDK 方式的弊端在于需要业务进行 SDK 版本升级,甚至会涉及到业务代码的变动。企业内部各个微服务虽然使用同一种开发框架,但很难保证框架版本是一致的,所以不得不为每一个版本维护一份全链路灰度的代码。业务代码与 SDK 代码紧耦合,SDK 版本迭代会触发业务不必要的发版变更,对业务的侵入性比较强。

另一种比较流行的方式是基于字节码增强技术在编译时对开发框架进行功能拓展,这种方案业务无感知,以无侵入方式为业务引入全链路灰度能力。基于 Java Agent 的实现方式的图例如下:

但仍然无法避免是开发者需要为业务使用版本不一致的开发框架维护对应的 Java Agent 的版本。如果您比较倾向于这种无侵入的方案但又不想自己来维护,您可以选择阿里云 MSE 服务治理产品,该产品就是一款基于 Java Agent 实现的无侵入式企业生产级服务治理产品,您不需要修改任何一行业务代码,即可拥有不限于全链路灰度的治理能力,并且支持近 5 年内所有的 Spring Boot、Spring Cloud 和 Dubbo。

逻辑环境隔离——基于 Service Mesh

在业务系统的微服务架构中,如果存在大量使用不同的技术栈、语言栈的微服务,Java Agent 的方式就无能为力了。我们可能需要为每一个语言的 SDK 编写和维护全链路灰度代码,不仅需要不同语言栈的开发者,而且涉及到语言无关的 bug 修复时需要全语言版本的 SDK 共同升级,这种代价不见得比基于物理环境隔离方案小。

那有没有一种与语言无关的方案呢?有,下一代微服务架构服务网格,Service Mesh。它将分布式服务的通信层抽象为单独的一层,在这一层中实现负载均衡、服务发现、认证授权、监控追踪、流量控制等分布式系统所需要的功能。显然,我们所需的全链路灰度能力也可以在这个流量治理基础设施层来实现。幸运的是,服务网格明星产品Istio以声明式 API 资源对流量治理进行了统一抽象,借助于 VirtualService 和 DestinationRule 治理规则可以很容易实现全链路灰度的效果,并且Istio集成了各种主流的分布式链路追踪框架。基于 Service Mesh 的实现方式的图例如下:

在实际生产环境中,服务多版本并行开发是很常见的事情,而且版本迭代速度非常快。版本每次变更都需要修改 VirtualSerivice 资源中路由匹配规则,另外 VirtualSerivice 资源中并没有提供容灾能力。比如存在一条路由规则访问服务提供方的某个灰度版本,如果目标服务不存在该灰度版本或者不可用,按照目前 Istio 的实现是仍然将流量转发至该版本,缺乏容灾机制。还有一种业务场景,如果我们希望对处于一定 UID 范围的用户流量转发指定灰度环境,是无法通过 Istio 现有的流量治理规则实现的。此时,您可以选择阿里云服务网格产品 ASM,是一个统一管理微服务应用流量、兼容 Istio 的托管式平台。ASM 针对上述两个场景都有应对方案,轻松解决您在多语言场景下的全链路灰度诉求。

三种方式对比

下表是三种方式对比,从多个方面进行了对比。

  • 如果您倾向于使用无侵入式的 Java Agent 的方式,但又担心自建带来的稳定性问题,您可以选择 MSE 微服务治理产品,该产品是阿里巴巴内部多年在微服务治理领域的沉淀的产出,经历了各种大促考验。
  • 如果您倾向于使用语言无关、无侵入式的 Service Mesh 的方式,但又担心自建带来的稳定性问题,您可以选择阿里云 ASM 产品,相比开源 Istio,在功能性、稳定性和安全性都有很大的提升。

3.3、流量入口:网关

在分布式应用中,作为流量入口的网关是不可或缺的。在全链路灰度场景中,就要求微服务网关具备丰富的流量治理能力,支持服务多版本路由,支持对特定路由规则上的请求进行动态打标。对于入口服务可见性问题,网关需要支持多种服务发现方式。安全性问题上,网关作为集群对外的入口可以对所有请求流量进行认证鉴权,保障业务系统不被非法流量入侵。

在虚拟化时期的微服务架构下,业务通常采用流量网关 + 微服务网关的两层架构,流量网关负责南北向流量调度和安全防护,微服务网关负责东西向流量调度和服务治理。在容器和 K8s 主导的云原生时代,Ingress 成为 K8s 生态的网关标准,赋予了网关新的使命,使得流量网关 + 微服务网关合二为一成为可能。阿里云 MSE 发布的云原生网关在能力不打折的情况下,将两层网关变为一层,不仅可以节省 50% 的资源成本,还可以降低运维及使用成本。最重要的是,云原生网关支持与后端微服务治理联动实现端到端的全链路灰度。

四、从 0 到 1 实践全链路灰度

看到这里,相信大多数读者对全链路灰度有了大概版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!的认识,也了解了几种解决方案以及实现细节。接下来,我们会基于文中提到的 MSE 云原生网关和 MSE 服务治理产品,从 0 到 1 对全链路灰度进行实践,一方面加深对全链路灰度的认识,另一方面可以了解下阿里云是如何将阿里巴巴内部的最佳实践输出到云产品中。

我们假设应用的架构由 MSE 云原生网关以及后端的微服务架构(Spring Cloud)来组成,后端调用链路有 3 跳,购物车(a),交易中心(b),库存中心(c),通过客户端或者是 H5 页面来访问后端服务,它们通过 Nacos 注册中心做服务发现。现在希望使用全链路灰度能力构建一个灰度环境,方便对服务 A 和服务 C 同时进行灰度验证。


4.1、前提条件

必备的资源列表

  • 已拥有一个 MSE 云原生网关
  • 已拥有一个 MSE Nacos 注册中心
  • 已拥有一个 ACK 运维集群
  • 已开通 MSE 微服务治理专业版

部署 Demo 应用程序

将下面的文件保存到 ingress-gray.yaml 中,并执行kubectl apply -f ingress-gray.yaml以部署应用,这里我们将要部署 A,B,C 三个应用,A 和 C 应用分别部署一个基线版本和一个灰度版本,B 应用部署一个基线版本。

有以下注意点:

  1. 全链路灰度能力是与注册中心无关的,本文用例暂以 MSE Nacos 作为注册中心,所以需要将 spring.cloud.nacos.discovery.server-addr 换成业务自己的 Nacos 注册中心地址
  2. 接入云原生网关的服务,如果需要使用灰度发布,需要在发布服务时在元数据信息增加版本标。在我们的例子,服务 A 是需要暴露给网关,所以发布时为基线版本添加spring.cloud.nacos.discovery.metadata.version=base,为灰度版本添加 spring.cloud.nacos.discovery.metadata.version=gray。
# A 应用 base 版本
---
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
      
# A 应用 gray 版本
---            
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 版本
---
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
            
# C 应用 base 版本
---
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
            
# C 应用 gray 版本
---
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

完成云原生网关初步配置

第一步,为云原生网关添加 Nacos 服务来源,服务管理,来源管理,点击创建来源,

选择 MSE Nacos 服务来源,选择需要关联的 Nacos 注册中心,点击确定。


第二步,导入要通过云原生网关暴露给外部的服务。选择服务管理,服务列表,点击创建服务。


选择服务来源为 MSE Nacos,选择服务 sc-A。

点击服务 A 的策略配置,为入口服务 A 创建多版本,版本划分依据服务注册时所带的元数据信息 version(注意,这里可以是任意可以区分服务版本的标签值,取决于用户注册服务时所采用的元数据信息),创建以下两个版本 base 和 gray。

4.2、路由配置

创建基线环境的路由匹配规则,关联域名 base.example.com,路由到服务 sc-A 的 base 版本中。

创建灰度环境的路由匹配规则,关联的域名与基线环境保持一致,注意此处增加了请求头相关的配置,并且路由的目标服务为服务 sc-A 的 gray 版本。

这时,我们有了如下两条路由规则,



此时,访问base.example.com路由到基线环境

curl -H "Host: base.example.com" http://118.31.118.69/aA[172.21.240.105] -> B[172.21.240.106] -> C[172.21.240.46]

如何访问灰度环境呢?只需要在请求中增加一个 header x-mse-tag: gray 即可。

curl -H "Host: base.example.com" -H "x-mse-tag: gray" http://118.31.118.69/aAgray[172.21.240.44] -> B[172.21.240.146] -> Cgray[172.21.240.147]

可以看到 云原生网关 将灰度流量路由到了 A 和 C 的灰度版本,由于B没有指定的灰度版本,所以流量自动回退到基线版本。

4.3、分析

从上面可以看出,我们只需要开通 MSE 微服务治理专业版,在云原生网关配置入口服务的路由规则,并且对入口流量进行灰度染色,即可满足我们对 A 和C 全链路灰度的要求。另外还有一个很重要的点是,业务需要自行对节点打标并对入口服务开启链路传递。为 Pod 模板的 Annotations添加 alicloud.service.tag 键值对完成节点打标,Java Agent 会在业务向注册中心时登记节点时自动为其添加这个元数据信息,同时需要为入口服务的业务容器添加环境变量profiler.micro.service.tag.trace.enable=true开启链路传递灰度标识。MSE 服务治理组件默认使用 x-mse-tag 来标识流量,并在整个调用链路中传递。

另外,您可以设置其他自定义或业务已有的字段来标识流量,更多详情操作、场景展示参考文档:http版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!s://help.aliyun.com/document_detail/359851.html

五、总结

本文从单体架构向微服务架构演进过程中带来的挑战展开,着重对其子领域服务发布在单体架构和微服务架构体系下的形态进行分析,引出了分布式应用场景中特有的全链路灰度问题。针对业务对全链路能力的要求,介绍了基于物理环境隔离和基于逻辑环境隔离两种方案,其中对基于逻辑环境隔离方案进行详细分析,对涉及到的各个技术点也进行了很好的解释版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!,接着提出了三种基于逻辑环境隔离的落地方案,并进行了简单的对比分析,最后引出了阿里云 MSE 云原生、MSE 服务治理和服务网格 ASM 是如何提供不限于全链路灰度的流量治理能力。

参考文献

https://mp.weixin.qq.com/s/JklS0ZBNRCEBvLWUOo-UrQ

0 条回应