一分钟精华速览
全链路灰度发布是指在微服务体系架构中,应用的新、旧版本间平滑过渡的一种发布方式。由于微服务之间依赖关系错综复杂,一次发布可能会涉及多个服务升级,所以在发布前进行小规模的生产环境验证,让新版本的应用实例来承接、处理限定规模或范围的业务流量,能最大限度控制发布上线风险,保证业务连续性。 本文详细解读微盟全链路灰度平台实践难点、解决思路及使用场景,还原其服务百万商家客户的全过程。
作者介绍
微盟基础架构团队技术专家——戴明智 TakinTalks社区专家团成员,SpringFramework、Apache Skywalking社区Contributor,个人博客阅读量100w+。2019年入职微盟,基础架构团队技术专家,参与并负责微盟全链路灰度平台的建设,经历了整个全链路灰度平台从0到1 的全过程。
温馨提醒:本文约6000字,预计花费12分钟阅读。
「TakinTalks稳定性社区」公众号后台回复 “交流” 进入读者交流群;回复“0412”获取课件资料;
背景
随着微盟业务的高速发展,商家系统的迭代频率和质量要求也在变高。在多环境推出之前,业务研发团队进行产品并行迭代开发时,发布系统存在两个非常显著的问题——
1)只有一套标准的QA测试环境,多版本并行测试困难。
即当两个版本涉及到同一个应用,则第二个版本就会因为环境占用,而无法开始测试。这样就严重影响了测试和发布计划。
2)微盟业务线众多且存在相互依赖,发布过程中出现问题时需要回滚,灵活度和掌控力需进一步提升。
微盟业务线众多且存在相互依赖,而微盟所有应用均采用微服务模式。以上导致微盟一次发布迭代涉及的应用数众多,需要上下游人力协调进行发布,且发布过程中出现问题时需要挨个回滚。灵活度和掌控力需进一步提升。 微盟最终选择引入全链路灰度发布来解决以上发布问题。当然,在近两年的实践和落地中,我们也遇到了不少挑战。我将详细分享微盟全链路灰度在落地过程中的一些难点和解决思路。
一、什么是全链路灰度?
1.1 全链路灰度
微服务全链路灰度是单体架构下灰度发布的衍生物,它的实施成本及复杂度更高。在单体架构下,一次迭代只会涉及到一个服务。但在微服务架构下,则需要链路上的多个服务同时进入灰度环境。
如图所示,单体架构下灰度发布只涉及服务A,则只需要对服务A进行灰度。而微服务架构下,一个用户请求链路上会涉及多个服务,此时则需要服务A和服务C同时进入灰度环境。因此,微服务架构下的灰度发布也被称之为“全链路灰度”。
1.2 全链路灰度有哪些挑战
全链路灰度我认为需要克服以下问题。
1)资源隔离:要做独立的灰度环境,而这个独立的灰度环境会涉及到很多资源,包括K8S资源、消息队列、注册中心等,如何对这些资源制定合适的隔离方案?
2)流量标签传递:我们要保证灰度流量在整个灰度环境里流转,那么灰度流量标签如何做到全链路的传递?
3)多组件支持:全链路灰度会涉及到大量组件和应用,尤其微盟的组件多、每个组件的版本多、框架也很多,且没有统一标准。如何用一个合理的方式支持这些组件快速接入?
4)数据一致性:不管什么样的灰度方案,都需要确保业务数据一致性,以及灰度策略一致性。这些数据一致性如何得到保证?
二、如何应对全链路灰度的挑战?
2.1 挑战1:资源如何隔离
2.1.1 资源隔离需要关注的指标
资源隔离要考虑很多方面,包括稳定性、实施复杂度、成本和性能。
稳定性是做隔离的一个重要指标,灰度须对生产环境的影响越小越好。且实施灰度方案时,不能影响生产调用的性能。复杂度高代表着其维护成本高,也越容易出错,因此复杂度是越低越好。成本方面亦然。
2.1.2 K8S资源隔离
一般会有三种方案,独立集群部署、同一集群标签区分、同一集群node隔离。
1)独立集群部署,不推荐。因为独立集群部署相当于再部署了一套环境,尽管稳定性可能会高一些,但它的复杂度很高,成本也很高。除此之外还要解决跨集群访问的问题。整体性价比不高。
2)同一套物理集群,通过标签区分容器里的资源。它的稳定性相对来说会低一点,但是整体比较简单,成本也低,性能也没有影响。导致其稳定性低的原因,是灰度的所有POD和生产的所有POD共享同一套物理集群,当灰度和生产同时拉起时,就会导致Node的CPU和内存都飙高,从而影响生产。我们曾经就出现过,所有灰度同时拉起时,被调度到了同一台物理机上,影响到了那台Node机器上的生产环境的应用。
3)同一套物理集群,Node做隔离。即在同一套K8S集群里,Node机器通过标签、POD亲和性等机制,来保证灰度的POD优先调度到灰度的机器上,稳定性也因此得到提升。因为独立出来的Node机器,需要设置冗余机器,所以其成本是居于方案一和方案二之间。
以上是K8S资源隔离后的一张架构图。从这里可以看到Ingress、Service、POD都是通过灰度的标签,做了逻辑上的隔离。至于Node上是否要做隔离,可以根据具体的情况而定。 我个人推荐Node做隔离,灰度的规模足够大是需要隔离的。虽然会冗余20%~25%的Node机器,但是它带来的稳定性收益是很高的。
2.1.3 注册中⼼隔离
注册中心的隔离主要考虑RPC的调用,即要保证RPC调用的稳定性,以及能够区分RPC的流量。
注册中心的隔离方式也有两种,独立集群部署和不区分集群。
1)独立集群部署。如果注册中心用的是ZK,就单独为灰度去部署一套ZK。这样它的稳定性会高一些,但复杂度也会相应较高。因为需要同时监听两套注册中心上的服务提供方,还需要识别注册中心是灰度的还是生产的,再去做调用,导致它的成本也会比较高。
(独立集群模式,生产环境和灰度环境分开注册) 2)不区分集群。稳定性相对会低一些。因为如果使用同一套集群,那么在做灰度的过程中,到某一阶段时,作业中心的负载会比较高。比如,如果用ZK,那么因为多了一些灰度的生产者和消费者,就会导致Z-node数量上升,节点激增,CPU也会上涨。此时就很可能会影响到生产上正常的服务订阅和注册。当然,若不区分集群,则复杂度和成本会比较低。
(使用同一集群模式,用标签区分实例) 微盟使用了单一集群的方案,为了避免ZK负载过高,对ZK做了扩容。
2.1.4 消息队列隔离
全链路灰度涉及到核心三个调用,正常的Http调用,还有RPC和消息,所以消息的隔离也很重要。
消息队列的隔离也有两种方式。第一种是Topic级别的隔离。第二种是不隔离,消费方自行过滤。
1)Topic级别的隔离:也可以认为是一种物理上的隔离。因为每一个Topic对应的就是物理的Partition,不会因为生产环境的延迟导致灰度环境的延迟,而灰度环境也不会影响到生产环境,所以它的稳定性会比较高。为什么复杂度也会比较高?因为对一个Topic进行灰度,需要生产方把消息同时生产到两个Topic,消费方也需要同时消费两个Topic中的消息,这样就比较复杂。
同时,一个Topic的生产方会有多个,当一个生产方需要灰度时,其他不必要做灰度的生产方,也必须把自己的消息同时发布到灰度环境中去,这样就会对其他生产方带来额外的负担。而新建一个Topic就意味着要多占一些分区,所以其成本也相应会比较高。
2)不做Topic级别的隔离:只在消息生产时携带灰度标签,消费方在消费时,根据消息里的标签自行过滤,消费方只消费其对应环境的消息。比如,当生产环境的消费方拉到一条灰度消息时,直接不处理然后回应ZK就结束。同理,当灰度环境的消费方拉到生产消息,也不做处理。这样不会带来额外的物理成本,所以成本也相对较低,性能不会受影响。
基于稳定性及监控考虑,微盟目前采用了独立Topic的方案。
2.2 挑战2:流量标签如何传递
之前我们介绍过微盟在分布式链路的场景下,整个链路的Trace ID传递(支撑百万商户、千亿级调用:微盟如何通过链路设计降本40%?)。流量标签的传递基本上可以借鉴这套流程。
全链路灰度主要传递的场景有两个,一个是跨线程,一个是跨进程。
跨线程的传递:可以借助阿里开源的TTL完成传递。也可以借助Skywalking提供的SDK对线程进行封装,这样也可以完成。
跨进程的传递:跨进程的传递主要是找到流量标签传递用的载体。Http的请求可以通过Header设定一个固定的Key来传递流量标签。RPC可以通Double本身提供的RPC Contest来传递。而消息的传递,可以通过在消息中添加Attachment,或者设计一个完整的消息协议,在消息的Header中添加流量标签,以此来完成流量标签的传递。
2.3 挑战3:如何快速支持多组件
我们需要对很多SDK做灰度能力的支持,而微盟的SDK特别多,且同一个SDK在不同的业务部门还有不同的版本,所以在全链路灰度时碰到了比较多的问题。以Double为例,Double 2.7中提供了标签路由的功能,基于这个功能去做灰度调用会很简单,但是微盟只有部分业务组用了这个版本,大部分还停留在2.6的版本,此时,如何快速让这些组件都拥有灰度的能力就是一个很大的问题。
一般会有两种方案,一种是SDK开发封装,一种是JavaAgent。
1)SDK开发封装:基于每一个组件不同的SDK,不同的版本,去做一层封装,然后去提供灰度能力的支持。它的优点是开发比较简单,排查问题也比较方便。因为在本地就可以调试,研发可以自行排查问题。然而,因为微盟的组件实在太多,这意味着需要开发的SDK会特别多,且后期还要去维护升级每一个SDK,推广速度也会很慢,因此并不适合微盟。
2)JavaAgent:这种方式的好处是无侵入,可以把所有的SDK的增强逻辑都维护在同一个Java中,推广也会比较容易。Agent在微盟应用比较广泛,因此有一个专门的管理平台,在管理平台中可以对Agent做灰度推广,比如先推广到200个应用,然后逐步递增到300个、400个等等,逐步覆盖到所有应用。这样的好处是维护方便,但相对于SDK的开发,Agent的开发难度更高,且因为它难以调试,导致一旦灰度链路中Agent出问题,就必须我们协同业务组去定位和解决。同时,还会有很多隐藏问题,比如,两个或者多个Agent之间可能会触发相互干扰,此时定位会比较难,这也是其缺点之一。
2.4 挑战4:如何克服数据一致性
数据一致性是灰度场景下所有人都会碰到的共性问题,而且是比较麻烦的问题。
2.4.1 什么是数据一致性
假设在灰度环境中,有服务A和服务C两个应用,当前执行的策略是“店铺ID=a,b,c”。流量进入灰度环境,假设此时通过中心化的策略平台下发了一个新的策略,那么服务A和服务C之间可能会有延迟,即两个应用不一定能同时接到新下发的策略,且这个延时的值是不确定的。此时会导致两个服务执行两个不同的策略,服务A执行的策略还是“店铺ID=a,b,c”,而服务C执行的策略变成了“店铺ID=a,b”。假设此时有一股流量“店铺ID=c”进来,此时流量会进入服务A的灰度环境,以及服务C的基准环境,这时流量就会出现问题。这个就是策略一致性问题。
2.4.2 如何解决数据一致性问题
1)方案一:下发策略时添加生效时间戳,减少网络延迟带来的影响。
也就是,让服务A和服务C约定到达某个时间后同时生效。但是这种方式并不能保障强一致性,因为即使是统一的生效时间戳,不同机器上的时间也可能是不一致的。且会受到计时策略的影响,比如到期计时是每一毫秒判断一次,还是每秒判断一次。所以这种方案并不能保证数据的强一致性。
2)方案二:先下发策略,策略带有版本号,确认所有应用接收到策略后,通过入口应用启用指定版本的策略。
如图所示,在做策略调整时,服务A和服务C会同时存在两个版本的策略。假设这个灰度环境的流量入口应用是服务A,就可以通知服务A启用V2版本的流量,确保服务A这条链路上涉及到的应用都收到V2版本的策略,这样就能解决策略一致性问题。而业务数据一致性的问题,则需要业务部门自行解决,全链路灰度平台很难解决应用中业务数据的一致性,所以在这里不做展开。
三、全链路灰度在微盟的落地效果如何?
3.1 整体架构
基于对微盟业务的思考,我们做了业务关键字的能力,这里不在于技术的实现,而在于这个诉求本身如何满足。微盟全链路灰度平台的整体架构分为 4 部分,交互层、业务层、策略层和路由层。
交互层主要就是UI界面,即产品入口。通过交互层可以进行创建环境、开始灰度、调整策略、灰度转正等等一系列的操作。所有的请求都会被业务层,也就是灰度调度平台接收到,然后在灰度调度平台中做一系列操作。最核心的就是灰度策略和灰度转正。所有的灰度策略都会被策略层处理,再按照特定的格式推送到路由层,最后由路由层去完成整个灰度流量的路由。
3.2 灰度生命周期
微盟设计了一个比较完整且复杂的灰度生命周期。如图所示。
一个灰度环境在平台上从创建到结束,要经过以上几个流程。初始状态下,需要把这个灰度环境内涉及的所有应用加进来后去创建环境。确认准备工作完成后,可以开始灰度的操作,此时环境就会流转到灰度状态。在灰度状态下,可以做策略编辑,通过策略编辑去实现逐步放大流量的功能。对应文章片头提到的,在微盟SaaS业务中,头部商家的流量占了大部分,通过这种策略编辑就可以先让少量商家承接灰度版本,然后再逐步放大流量,等流量到一定比例后再逐步引入头部商家,这样就保证了发布是足够安全的。灰度状态下,在流量逐步划拨的过程中如果完全没有问题,那么就可以发起一次转正,把灰度版本完全转换成基准版本。如果验证有问题且不可修复的,也可以快速下线,把灰度占用的资源释放掉。
3.3 实践场景
3.3.1 实践场景1:快速创建灰度环境
图中展示的是一个已创建完毕的环境。在创建环境过程中,只需要关注两点,一个是哪些应用需要参与到这次灰度中,另一个是灰度的策略是什么。录入这些信息后,即可开始灰度操作。
3.3.2 实践场景2:配置流量策略
配置流量策略目前已经支持比较复杂的流量策略。按条件灰度,包含Http的Header、域名的Host、Per-stream,以及域名中某些特定值的灰度,这些都已能实现。按比例灰度,对某一个应用控制固定比例(如20%、30%)的流量进入灰度,这个目前也支持。
在配置流量策略的过程中,有一个需要大家关注的问题。流量调整过程中,需要对底层所用的资源做扩容,其中很核心的是需要对POD做扩容,且POD扩容应该要发生在把流量调度到灰度环境之前。因此平台需要允许在每一次配置策略时,同步设置扩容比例,以此来保证它不会被突然的流量增长打崩。
3.3.3 实践场景3:灰度推进到蓝绿状态
在微盟有一个强制要求,无论哪一次灰度,都必须在蓝绿状态做停留。
蓝绿状态在平台上的体现,就是在策略编辑时,要进入全流量灰度的状态,也就是所有的流量都进入灰度的状态。之所以这么设计,是因为很多问题在20%、50%甚至80%流量状态下,是无法得到完全验证的。灰度必须要经历一次完整的流量高峰并验证没有问题,才可以认为是安全的。而至于停留多久,平台不做限制,由业务部门视业务情况自行决定。
3.3.4 实践场景4:灰度转正,灰度发布完成
蓝绿状态验证完成后,此时可以发起灰度转正。前面我们提到了资源隔离,灰度转正的作用就是释放这部分隔离的资源,把灰度版本发布到生产环境中去。
而此前,业务部门去执行一次大版本的发布,是需要考虑流量损失、服务编排等等一系列问题的。通过这个灰度转正流程,可以确保流量无损,也无需业务方考虑服务编排。灰度转正后才会把流量切回生产,以此来保证整个过程中对业务不造成影响。
3.3.5 实践场景5:流量回切,灰度下线
在灰度的过程中可能会碰到一些异常的问题,如果问题比较严重,则可以利用流量回切能力,一键把流量快速切到生产环境。然后再选择灰度下线,或者在灰度环境下继续做验证和修复。
3.4 落地效果
QA测试环境从1套到80+套,支持多部门并行测试。各业务团队可以快速在平台上隔离出一套独立的环境,来解决开篇提到的并行测试环境占用的问题。闲时会有40+环境并行,忙时有高达80+的环境同步运行。
内部覆盖率超过85%。目前应用基本完成接入灰度,覆盖率超过85%。
发布效率大幅提升。使用灰度发布后,没有出现过因为发布导致线上流量受损。同时发布效率大幅提升,即使在大规模迭代的情况下,也没有出现过通宵发布。
四、未来规划
4.1 监控能力提升
目前微盟全链路灰度平台已经能完全区分灰度链路和生产链路,但其监控能力与公司现有的监控能力相比,还有一些短板,比如POD的监控、指标、告警等,接下来都要重点去加强。
(微盟灰度平台链路监控页面)
4.2 开放能力
随着全链路灰度平台的推广,我们收到了越来越多的诉求,比如小程序发布平台、APP管理平台、微盟特有生态系统盟链等等,也希望接入到灰度平台中来,以此来降低对商家造成的影响。
因此我们需要去做扩展能力,来支撑这些诉求。目前我们已经提供了两套扩展机制,帮助外部生态系统接入平台,当然这部分工作也正在优化迭代中。(全文完)
Q&A
1、灰度环境是不是单独一套更好还是怎么样? 2、消息队列隔离,为什么没有考虑不同的消费组? 3、Redis怎么进行灰度? 4、怎么控制灰度的影响范围? 更多详细内容,欢迎点击“阅读全文”,观看完整版解答!
本文由博客一文多发平台 OpenWrite 发布!