1. 灰度发布
1.1 什么是灰度发布
灰度发布 Gray Release(又名金丝雀发布 Canary Release)。比如不停机旧版本,部署新版本,高比例流量(例如:95%)走旧版本,低比例流量(例如:5%)切换到新版本,通过监控观察无问题,逐步扩大范围,最终把所有流量都迁移到新版本上。
常见使用场景
-
切分一定比例的流量到新版本
-
灰度新版本到部分用户,通常基于 Header 或 Cookie 进行流量的策略来实现
1.2 什么是微服务全链路灰度
微服务体系架构中,服务之间的依赖关系错综复杂,有时某个功能发布依赖多个服务同时升级上线。我们希望可以对这些服务的新版本同时进行小流量灰度验证,这就是微服务架构中特有的全链路灰度场景,通过构建从网关到整个后端服务的环境隔离来对多个不同版本的服务进行灰度验证。在发布过程中,我们只需部署服务的灰度版本,流量在调用链路上流转时,由流经的网关、各个中间件以及各个微服务来识别灰度流量,并动态转发至对应服务的灰度版本。如下图:
上图可以很好展示这种方案的效果,我们用不同的颜色来表示不同版本的灰度流量,可以看出无论是微服务网关还是微服务本身都需要识别流量,根据治理规则做出动态决策。当服务版本发生变化时,这个调用链路的转发也会实时改变。相比于利用机器搭建的灰度环境,这种方案不仅可以节省大量的机器成本和运维人力,而且可以帮助开发者实时快速的对线上流量进行精细化的全链路控制。
2. 全链路灰度设计思路
如何在实际业务场景中去快速落地全链路灰度呢?目前,主要有两种解决思路,基于完整环境隔离和基于服务流量路由。
2.1 基于完整环境隔离
这种方案需要为要灰度的服务搭建⼀套网络隔离、资源独立的环境,在其中部署服务的灰度版本。由于与正式环境隔离,正式环境中的其他服务无法访问到需要灰度的服务,所以需要在灰度环境中冗余部署这些线上服务,以便整个调用链路正常进行流量转发。
这个方案一般用于企业的测试、预发开发环境的搭建,对于线上灰度发布引流的场景来说其灵活性不够。况且,微服务多版本的存在在微服务架构中是家常便饭,需要为这些业务场景采用堆机器的方式来 维护多套灰度环境。如果您的应用数目过多的情况下,会造成运维、机器成本过大,成本和代价远超收益;如果应用数目很小,就两三个应用,这个方式还是很方便的,可以接受的。
2.2 基于服务流量路由
我们只需部署服务的灰度版本,流量在调用链路上流转时,由流经的网关、各个中间件以及各个微服务来识别灰度流量,并动态转发到对应服务的灰度版本。
全链路流量路由目前有两种主流实现:
-
基于服务发现组件:通过支持为服务设置元数据的服务注册中心,如 Nacos,可以标记服务实例的特征,例如灰度版本。每个服务可以通过注册中心获取其他服务实例的版本信息,并通过修改代码逻辑或 Java Agent 实现流量路由。
-
基于 Istio:采用 Istio 这个开源 Service Mesh 组件,通过在每个服务的容器中部署 Envoy 透明代理,拦截服务之间的网络通信并按指定规则转发,从而实现了全链路流量路由,无需对现有代码进行修改。
要想实现全链路灰度,我们需要解决以下问题:
1.链路上各个组件和服务能够根据请求流量特征进行动态路由。
2.需要对服务下的所有节点进行分组,能够区分版本。
3.需要对流量进行灰度标识、版本标识。
4.需要识别出不同版本的灰度流量。
接下来,会介绍解决上述问题需要用到的技术
标签路由
标签路由通过对服务下所有节点按照标签名和标签值不同进行分组,使得订阅该服务节点信息的服务消费端可以按需访问该服务的某个分组,即所有节点的一个子集。服务消费端可以使用服务提供者节点上的任何标签信息,根据所选标签的实际含义,消费端可以将标签路由应用到更多的业务场景中。
节点打标
那么如何给服务节点添加不同的标签呢?
基于Nacos注册中心服务发现
在使用Nacos作为服务发现的业务系统中,一般是需要业务根据其使用的微服务框架来决定打标方式。如果Java应用使用的Spring Cloud微服务开发框架,我们可以为业务容器添加对应的环境变量来完成标签的添加操作。比如我们希望为节点添加版本灰度标,那么为业务容器添加`spring.cloud.nacos.discovery.metadata.version=gray`,这样框架向Nacos注册该节点时会为其添加一个标签`verison=gray`。
基于Kubernetes Service服务发现
在使用Kubernetes Service作为服务发现的业务系统中,服务提供者通过向ApiServer提交Service资源完成服务暴露,服务消费端监听与该Service资源下关联的Endpoint资源,从Endpoint资源中获取关联的业务Pod 资源,读取上面的Labels数据并作为该节点的元数据信息。所以,我们只要在业务应用描述资源Deployment中的Pod模板中为节点添加标签即可。
可以在K8S的部署配置中,在template配置中,添加labels的方式完成版本打标。
spec:
template:
metadata:
labels:
app: user
version: 2.0.0
流量染色
请求链路上各个组件如何识别出不同的灰度流量?
答案就是流量染色,为请求流量添加不同灰度标识来方便区分。我们可以在请求的源头上对流量进行染色,前端在发起请求时根据用户信息或者平台信息的不同对流量进行打标。如果前端无法做到,我们也可以在微服务网关上对匹配特定路由规则的请求动态添加流量标识。此外,流量在链路中流经灰度节点时,如果请求信息中不含有灰度标识,需要自动为其染色,接下来流量就可以在后续的流转过程中优先访问服务的灰度版本。
打标灰度标签透传
如何保证灰度标识能够在链路中一直传递下去呢?
借助于分布式链路追踪思想,我们也可以传递一些自定义信息,比如灰度标识。业界常见的分布式链路追踪产品都支持链路传递用户自定义的数据,其数据处理流程如下图所示:
3.总结
首先,需要支持动态路由功能,对于Spring Cloud、Dubbo开发框架,可以对出口流量实现自定义Filter,在该Filter中完成流量识别以及标签路由。同时需要借助分布式链路追踪技术完成流量标识链路传递以及流量自动染色。此外,需要引入一个中心化的流量治理平台,方便各个业务线的开发者定义自己的全链路灰度规则。
实现全链路灰度的能力,无论是成本还是技术复杂度都是比较高的,以及后期的维护、扩展都是非常大的成本。
如果觉得这篇文章对你有所帮助,欢迎点个 “在看” 或分享给更多的小伙伴!
关注公众号「Fox爱分享」,解锁更多精彩内容!