开发者学堂课程【微服务治理之全链路灰度:微服务治理之全链路灰度】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/973/detail/14894
微服务治理之全链路灰度
方案二涉及的技术:标签路由
如下图所示:
标签路由是consumer端对provider下所有的节点根据它们所携带的标签信息进行分组,因为标签信息在每个节点上面都有分组信息并且很多,所以consumer可以根据这些标签信息来做不限于全链路灰度的一些问题。
标签路由是打多个自定义标来表示不同节点有不同的意义;
金丝雀发布更倾向于一些版本标,例如v1,v2等;
全链路流控针对线上的某些流量进行控制,让流量带有这些标带有队伍,然后到链路上的每个服务时进行判断,如果这个流量带标就会判断这个流量有没有触发对应的流控配置,如果触发,就会进行限流;
同AZ优先路由就是节点,有时会带一些地域标机房标这些信息,consumer端就可以基于这些信息对provider下面限定分组,来就近选择一个离自己比较近的一个节点;
全链路压测主要是对线上流量用线上环境进行压测,但是数据库需要进行区分,一般会有一个影子消息队列和影子消息库。当流量带有全链路压测标时就会去访问一个影子的数据库进行落盘;
场景链路就是跟业务比较耦合的一个场景,例如在购物中现有一个大促针对女装,我们需要进行识别流量来源是否为女装,所以需要保障该流量是可用的,不是女装的其他流量会被转发到其他版本中;
容灾路由基于region标,当最近的机房没有节点时希望将流量转到哪个机房。
方案二涉及的技术:节点打标
在全链路灰度中,流量在动态路由时首先会判断本次流量的回路标是什么,同时判断需要调用的provider服务有没有对应的标签。所以在某个服务灰度发布时,需要向注册中心显示这些标签信息,这就为节点打标。
这里主要是以容器化的业务运用举例,服务发现使用NACOS注册中心。
例如图上现有一个service,左边蓝色是正式版本的Deployment,右边是灰度版本的deployment,它们需要在pod启动时向注册中心注册一些信息,左侧注册version=base,右侧注册version=gray,那么在真实的业务场景中如何操作?
图右代码为一个使用spring cloud的服务治理框架,我们在业务容器的环境变量中添加这样的一个标:
-name:spring.cloud.nacos.discovery.metadata.version
value:gray
version是业务的一个自定义的元素性的key,值为gray
那么pod在注册到nacos上面的节点时,下面的元素信息就会自动添加一条version等于base或者是gray的一条信息。
以容器化的业务应用举例,服务发现使用k8s Service:
利用业务pod标签来操作。需要额外的apiserver去apply一个service资源。在kps中服务注册跟服务发布是结偶的,服务业务deployment可以独立部署,但是如果被其他的组件发现,就要通过一个service资源,然后再发布,才能真正完成服务发布。
真正的consumer端只需要去订阅service资源对应的endpoint资源,endpoint资源是api,为自动生成,监听到endpoint资源时再进一步监听跟endpoint有关的pod资源,再从pod资源上面读取这些label。
案例:
apiVersion: apps/v1
kind: Service
metadata :
name: httpbin
spec:
ports:
-port: 8080
protocol: TCP
selector:
app: httpbin
template :
metadata :
labels:
app: httpbin
version: v1 ,
spec:
containers :
-image: xxxxx
imagePullPolicy: Always
ports :
-containerPort: 8080
方案二涉及的技术:流量染色
如何给流量打上标识使得这些标识还能被流经各个服务组件的时候被它们识别呢?流量染色的作用是什么呢?
流量染色其实就是用来区分不同的流量。此处与刚才例子相同:现要搞一个女装活动的一个大卖活动。这些ABCD对于家电或者女装来说,都提供一样的服务,但要在女装活动大促时确保这个流量都是高可用的,不会出现问题。因为流量比较大,所以会额外去进行扩容,扩容之后达到若是线上的这些购物的流量是女装的这种场景就去访问扩容后的这些业务的节点。家电这种非女装流量去访问之前的业务节点。通过这样的流量隔离,可以确保大促的平稳运行。
方案二涉及的技术:分布式链路追踪
我们需要确保请求流量标识是怎么在整个链路上面传递的。如果是在请求源头染色,那么请求经过网关时,网关作为代理,会将请求原封不动的转发给入口服务。即若对流量打上了一个自定义的一个灰度标识,那么经过网关时,会自动带上请求的灰度标识。那么当网关访问入口服务A时,服务A在调服务B时需要去创建一个新的请求。在创建一个新的请求同时不会携带灰度图标。所以需要通过某种方案来确保该灰度标识可以从头带到尾。此处就使用到分布式链路追踪技术。
分布式链路追踪解决的是分布式应用中请求链路的一个可视化。因为在单体架构中,服务之间的调用是方法调用,在同一个线程里再来回调动。
但是在微服务架构中,因为服务都是独立部署,所以之前同一进程,同一线程内的方法调用变为了不同节点,不同进程之间的调用,还涉及到网络通信,并且每个服务的实力数也是不一样的即ip不是固定的。所以整个调用链路是不可预见,不可知的。
所以需要请求经过网关时,生成一个全局的TraceID,它是唯一标识的一条请求链。然后创建一个SpanID,这些信息会被发送到请求的头部或者是上下文中,然后到达服服务A,服务A就会通过Extract组件,去请求中把TraceID,SpanID提取出来,然后存到Treadlocal存储,该存储是线程相关的存储。
当服务A到服务B时,会从本地线程存储中取出这两个信息,并且在服务A即将向服务B发起请求时将这两个信息注入,TraceID保持不变,SpanID会自动往后增一个数字,1.1就代表这个请求经过了两跳。
当服务A调用服务C时,服务A涉及到两个调用,先调服务B再调服务C。那么spanID的第二个值就会自动增加一,可以看到请求是先访问服务B然后再访问服务C。
方案二实现的方式:基于SDK
第一种方式是基于sdk的方式,需要去修改业务架构,现在使用的开发框架有spring cloud或者dubbo,能够使得它们在流量流经各个组件和各个服务时做一些动态路由。对于spring cloud跟dubbo来说,需要去实现一个自定义的filter来实现以上逻辑。例如标签路由、分布式链路,都需要在filter里做逻辑,同时还要做一些容灾机制,确保服务。
例如服务A访服务B时,灰度环境为1,但是服务B去访问服务C时会发现服务环境1中没有该节点,此时需要回到服务C的正式环境。它们都需要去引入一个分布式链路追踪技术,确保一些灰度标识的传递。dubboe现在已经支持了一部分这样的能力,但是dubbo各个版本实现不一致。
总结sdk主要的逻辑:
1. 每个服务都需要订阅一些流量规则,这些流量规则需要在sdk filter里动态更新,而且是实时生效;
2. sdk需要对每个服务的所有的出口流量应用这些流量治理规则,确保服务A可访问服务B时,可以根据一些标签进行一些路由;
3. 在filter中需要依托分布式链路的追踪技术,然后透传一些自定义标签;
4. 高可用,需要做一些自动容灾机制,确保流转在灰度环境1或灰度环境2时没有对应版本的服务可以回退到正式环境。
优点是业务代码逻辑比较熟悉,改造比较灵活。
缺点是开发者需要去维护sdk,在微服务架构中,每个服务使用的版本不一致,需要对每个版本的框架去维护sdk的逻辑。如果出现了bug就需要对全网的sdk进行升级,代价很高。
方案二涉及的技术:基于Java Agent
基于java agent的方式主要是通过一种无侵入方式为微服务开发框架来增添一些功能,是一种基于字解码增强的技术实现的。同时也需要开发人员去维护agent代码。企业中微服务架构可能采用的框架版本不一致,所以需要对不同的版本去维护agent的代码逻辑。当出现bug时,也需要全网升级。
还需要与基于sdk一样,额外再去部署一个流量规则Ops。ops方便开发者去发布一些流量治理规则,agent跟流量规则ops进行联动,如果自建,开发代价比较大。
阿里云MSE服务治理,是一款基于java agent实现的无侵入式企业级服务治理产品,可以做到不需要修改任何一行业务代码,即可拥有不限于全灰度的服务治理能力。并且支持五年内所有版本SpringBoot spring cloud和dubbo。
优点是它是一种无侵入的方式,所以业务无感知。
缺点是比较局限于java技术的微服务框架,对于一些多语言的环境,例如在一些大的企业,因为微服务采用的框架可能是不一致的。业务团队一可能用spring cloud,业务团队二可能用dubbo,所以在多语言场景下进行全链路灰度需要另一种技术Service Mesh
方案二涉及的技术:基于Service Mesh
Service Mesh,号称下一代微服务架构,是将分布式服务的通信程抽象为单独的一程,然后在这一层中实现负载均衡。需要全面度灰度的能力也可以在流量治理基础设施层来实现。
如果用开源的产品,对于存在新的灰度版本要发布时需要去更新配置。
配置的路由需要感知到环境标是什么?如果服务B有多个版本在发布,服务C也有版本在并行发布。那么就会导致service的数量指数级增长,变得不可控制。
并且Service Mesh没有容灾的机制,当指定服务A访问服务B灰度环境v1的版本时,如果服务B没有,就不会自动去容灾到服务B的正式环境上,它会仍然访问不可用的节点,就会导致404这些错误。
阿里云服务网格ASM产品,针对全链路灰度做了很大优化。引入了TrafficLabel,对于全链路灰度的整个使用体验有了很大提升,不需要再配很多配置,再通过占位符实现一个配置动态,它会随着灰度环境的数量,进行自动的扩展。并且引入了一个函数,可以去实现一些复杂场景的达标诉求。
ASM总体是一个统一管理微服务应用的流量平台,并且兼容lstio,是托管式的。
总结:
1. 引入TrafficLabel资源,优化全链路使用体验
2. 引入占位符,实现配置动态
3. 引入函数,应对复杂场景的打标诉求
4. 自动容灭,自动降级
方案二的3种实现方式对比
方案 |
客户体验 |
稳定性 |
支持成本 |
客户白盒程度 |
性能 |
多语言 |
SDK |
差:客户需要理解SDK里的代码并修改业务代码 |
低:重新编写新的路由逻辑 |
低:编写代码提供SDK即可 |
高:代码透明,自主可控 |
高:SPI覆盖原有逻辑 |
差:只支持Java |
Java Agent |
好:客户0感知,只需要在控制台上配置规则 |
低:重新编写新的路由逻辑 |
中:发布系统or Pilot挂载Java Agent |
低3:代码对客户不透明,黑盒 |
高:Java AOP切面拦截用户代码 |
差:只支持Java |
Mesh/Sidecar |
好:客户0感知,只需要在控制台上配置规则 |
高:基于开源lstio做部分改造 |
高:sidecar是一个单独的container,需要资源,也需要发布系统or Pilot管控Sidecar |
低:代码对客户不透明,黑盒 |
低:流量劫持多了2跳 |
好:语言无关 |