一、前言
发布变更是影响稳定性的一个重大因素,为了发布异常时能快速回滚,增加发布期间的稳定性,也为了解决多服务部署时互相依赖而导致的发布时间增长等问题,得物在今年引入一种新的发布模式--蓝绿发布。这种发布模式带来了稳定性和效率的提升,这里我们以SRE的视角来解读下得物的蓝绿发布。
二、常见的发布形式有哪些?分别有什么优势?
全量发布
全量发布是早期企业进行系统升级的一种方式,因为早期的服务大多为大型机,单实例程序为主。并没有形成当下流行的微服务架构,因此当发布时往往需要停机发布。生产环境禁止使用这种方式进行部署!
滚动发布
滚动发布顾名思义,假如生产中16台机器,我们可以分成4批。每批4台机器,每批机器执行更新,从版本V1更新为V2,更新后重新将其投入使用,连续不断的更新其他机器,直到集群中所有的实例都更新为版本B后,结束发布。
这种方式的好处就是更新过程体验影响少,费用开销也少,发布期间无需额外新增机器。但是缺点也同样明显,一旦开始发布后,回滚时长很久,在多个有关联的服务部署时,需要上游服务完全发布后,才能发布下游服务,整体发布时间也很长。
滚动发布流程演示:
图片
图片
蓝绿发布
通常意义上的蓝绿发布一般是将服务分为两组,蓝组和绿组,正常运转的情况下每组承载50%的流量。当准备发布服务时, 将蓝组流量设置为0%,将绿组空闲出来,将服务部署到绿组的机器,然后利用SLB将流量切换到绿组的机器,让绿组来运行业务,没问题的话流量全部导向绿组,把蓝组也进行服务更新。
传统意义上的蓝绿发布优点在于发布策略简单,对于用户几乎无感知,可以实现平滑过度,在发布期间发现问题后也可以快速的回滚。而缺点则是通常需要准备正常业务使资源俩倍以上的服务器,需要投入较大的资源成本。
蓝绿发布流程演示:
切除绿集群流量:
图片
当A组升级完毕,负载均衡重新接入A组,再把B组从负载列表中摘除,进行新版本的部署,A组重新提供服务。
图片
最后,B组也升级完成,负载均衡重新接入B组,此时,AB组版本都已经升级完成,并且都对外提供服务。
灰度发布
灰度发布,也被叫作金丝雀发布。与蓝绿部署、红黑部署不同的是,灰度发布属于增量发布方法。也就是说,服务升级的过程中,新旧版本会同时为用户提供服务。
灰度发布的具体流程是这样的:在集群的一小部分机器上部署新版本,给一部分用户使用,以测试新版本的功能和性能;确认没有问题之后,再对整个集群进行升级。简单地说,灰度发布就是把部署好的服务分批次、逐步暴露给越来越多的用户,直到最终完全上线。
图片
之所以叫作灰度发布,是因为它介于黑与白之间,并不是版本之间的直接切换,而是一个平滑过渡的过程。
AB Test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现并调整问题,以保证其影响度,而我们平常所说的金丝雀部署也就是灰度发布的一种方式。
之所以又被叫作金丝雀发布,是因为金丝雀对瓦斯极其敏感,17 世纪时英国矿井工人会携带金丝雀下井,以便及时发现危险。这就与灰色发布过程中,先发布给一部分用户来测试相似,因而得名。
图片
对于灰度发布来说,它的优点在于如果前期出问题影响范围很小,相对用户体验也少;可以做到及时发现、及时调整问题,影响范围可控。但是采取这种模式对自动化以及运维监控能力的要求非常高。
三、得物的蓝绿发布是如何实现的?
前面讲了“what”,我们现在来说下“how”。
在平时,我们也会保留蓝绿两个集群,在发布时,引入灰度的流量平滑过度,帮助我们完成整个发布过程,下面以SRE的视角大致讲一下蓝绿发布的架构与流程。
蓝绿发布的流程
在这种架构下,整体的发布流程如下:
- 日常流量
在未发布时,我们接入蓝绿发布的服务是平均分成蓝绿俩个集群的。平时通过网关均匀切分流量,平均每个集群50%的流量。
图片
- 开始发布
当进行蓝绿发布时,我们将需要发布的应用创建在一个通道中(这里先说下只有一个通道部署的情况)。
图片
- 蓝集群(右侧)摘流
此时所有流量将只访问绿集群,如下图所示,当蓝集群摘流完成后, 此时集群没有任何流量,即可进行部署。
图片
- 蓝集群(右侧)引流
当蓝集群发布完成后,我们需要对蓝集群发布后的服务进行确认。在确认部署成功后,则梯度的将流量引入更新后代码的蓝集群(右侧),最开始我们会切1%的流量,切流量后,我们可以在线上观察蓝服务的流量、错误率等。以此观测发布的版本是否有异常。之后,我们逐渐将流量切回50%。注意,需要确保相关缺陷都在该环节暴露出来,因为这个环节另一半老版本的、稳定的代码还在Standby,可以随时操作流量比例,进行流量迁移。
图片
发布过程中,蓝、绿节点间流量不会互窜(对比上图,蓝绿集群间斜向箭头没有了)。
此阶段需要注意MQ流量,因为MQ当前无法按比例进行切分,因此一旦开始切流,则MQ流量会恢复为50%/50%。
- 绿集群(左侧)摘流
通过扩大流量比例,流量全部切到蓝集群,这个阶段流量已全部切到新代码,可以让测试同学介入进行新功能验证以及回归测试。
在这个阶段只要绿集群还没发布,发现问题,仍然可以全部切回老代码!
当测试验证完成后,即可进行绿集群发布。发布后则不可以回切了!就算要代码回滚,也得等本次发布结束后,再单独对服务进行回滚。
图片
- 绿集群(左侧)引流
在发布后则开始进入绿集群引流了,此时可以快速引流,因为已经没有可以回滚的、稳定版本的代码了。同样,还在发布阶段,及时流量均衡,也不会出现互相交叉的流量。
图片
- 发布完成
发布完成后,则去除通道,蓝绿集群可以继续进行交互。
图片
如上图所示,使用以上发布流程具备以下好处:
- 整个发布过程是以蓝、绿集群维度并行调度、实施的,通过发布平台统一操作,摘流,无需各业务域各自处理。
- 通过请求蓝绿粘性,让下游应用的新老版本代码可以同时存在,无需阻塞等待下游应用全部升级到新代码,解除了批次依赖。
- 发布过程中有灵活的流量控制能力,可以按1%、50%等阶梯流量验证应用。
- 上述发布流程,可以同时并存若干个,摘流、引流动作互不影响(多发布通道)。
蓝绿发布的架构
应用架构 [1]
图片
1.1 流量规则SDK
在所有需要接入蓝绿发布的程序中,首先需要升级流量规则SDK,流量规则SDK是应用蓝绿发布能力的代码底座,向中间件组件如RPC、MQ、JOB提供了主动查询流量规则和被动接受流量规则变更事件的能力,各中间件组件响应流量规则进行合适的动作,实现各类型流量的动态摘流、动态引流。
1.2 核心能力
- 依赖配置中心做持久化存储与事件推送。
- 所有配置读取都是内存操作,只会在启动时读取一次配置中心的配置,后续配置变更都依赖于配置中心的事件推送。
- 提供了如下的能力:
当前应用所在的发布通道,是否在蓝绿发布中。
指定应用所在的发布通道,是否在蓝绿发布中。
蓝色流量百分比,范围[0, 100]。
- 提供了如下的事件推送:
发布开始事件onStart。
发布结束事件onFinish。
切流事件onFlowChange,切流事件又细分了以下几个事件。
切流事件,蓝色流量标占比为100,绿色流量标占比为0,onEnterAllBlue。
切流事件,蓝色流量标占比从100改为非100,绿色流量标占比从0改为非0,onExitAllBlue。
切流事件,蓝色标流量占比为0,绿色流量标占比为100,onEnterAllGreen。
切流事件,蓝色标流量占比从0改为非0,绿色流量标占比从100改为非100,onExitAllGreen。
流量控制
得物目前的流量分为内部流量及外部流量,大部分流量情况如下:
- 外部流量
通过各类Gateway请求
通过k8s Ingress请求(暂不支持蓝绿发布)
- 内部流量
- 通过Gateway互联
- 通过Dubbo/Feign RPC协议互联
- 通过MQ异步请求
- 通过kafka异步请求(暂不支持)
- JOB类任务发起的流量
- 通过k8s SVC请求(暂不支持蓝绿发布)
其中Gateway也是通过Dubbo或者Feign请求下游服务,因此也可统一为RPC类型,所以得物目前的流量主要包含RPC、MQ、JOB三种。
2.1 RPC
我们RPC流量核心主要依赖注册中心,通过Dubbo的负载均衡策略进行调整。
图片
- 如何实现RPC流量比例控制
RPC场景下应用的流量比例控制,取决于它的上游应用按照流量规则比例向其发起调用。核心是上游应用感知到下游应用实例权重。
当前应用通过流量规则SDK监听到所在通道的流量规则变更时,修改注册中心上的实例权重。
上游应用通过注册中心透明的感知下游应用的实例权重,通过加权负载均衡策略实现流量比例控制。
Dubbo原生的各类负载均衡策略都支持加权,也就是即便上游没有升级蓝绿依赖,下游应用依然可以通过蓝绿实例权重控制自己蓝绿集群被调用的比例。
Feign原生是不支持的,Fusion框架重写了负载均衡策略。
- 如何控制流量比例
蓝色流量比例Rate,蓝色集群实例权重WB,绿色集群实例权重WG。
假设Rate从1调整到99,一共有4个节点。
调整前WB=1,WG=99,调整后WB=99,WG=1,可能出现以下情况:
图片
- 流量规则变更时,只让蓝或绿某一个集群修改自己的权重。
权重值是相对的,只需要保证蓝、绿集群节点权重相对值服从流量比例即可,无需同时修改蓝绿集群所有节点的权重。实例权重初始值设为100,修改权重时,尽可能保证一半集群实例权重保持100不变,只修改另一侧被调整的集群实例的权重。
规则如下:
蓝色流量比例Rate,公式:W/(100+W) = Rate/100。
Rate = 50,蓝色集群实例权重=100,绿色集群实例权重=100。
Rate < 50,蓝色集群实例权重=100 * Rate / (100-Rate),绿色集群实例权重=100。
Rate > 50,蓝色集群实例权重=100,绿色集群实例权重=100 * Rate1 / (100-Rate1),其中Rate1=100 - Rate。
- 只有一个颜色的集群时,忽略权重。
- 如何实现完全摘流
Dubbo框架内置的所有负载均衡策略都会识别下游实例的权重进行加权筛选节点,无需上游升级依赖,下游应用实例权重置0后即可实现摘流。
Feign框架默认不识别实例权重,不进行加权负载均衡,为了避免蓝绿发布项目落地时推动发布链路上下游应用升级的困难,应用摘流时,会将自身注册的所有Feign服务反注册,以保证Feign流量能被彻底摘流。
- 如何实现请求链路蓝绿粘性(一蓝到底或一绿到底)
蓝绿子集群的代码是不一样的,按我们制定的发布流程,蓝集群是新代码,绿集群是老代码,如果不能固定请求链路的颜色,实现请求过程一蓝到底或者一绿到底,那么可能会出现上游新代码调用到下游老代码,出现代码不兼容的异常。
将RPC请求第一次进入每个通道时的蓝绿决策结果以KV形式Append到分布式Trace的baggage中,全链路透传、隔离、复用。
如果Trace中有蓝绿决策结果,则按照蓝绿决策结果筛选节点;
否则按照流量比例筛选节点,并将决策结果(节点集群颜色)Append到Trace。
baggage-key: x-deploy-channel-type。
baggage-value:key=通道标识,value=蓝集群或绿集群标识,多个通道以&分割。
图片
2.2 MQ(RocketMQ)
核心是通过多消费组实现MQ流量隔离和控制。业务上创建的一个业务消费组,会在MQ SDK层面透明的创建2个衍生的颜色消费组。
发送消息时,会在消息头上携带当前节点蓝绿标。3个消费组收到消息时,根据消息颜色和消费组颜色做颜色请和判断,互斥的消费同一个TOPIC上的所有消息。
- 非摘流状态
图片
- 摘流状态
应用感知到通道内蓝集群摘流时,蓝集群节点关闭消费组、绿集群节点启动三种颜色消费组。此时,MQ流量完全由绿集群接管。
图片
通道内绿集群摘流时同理。
- 过程详解
原本一个消费组,拆分成三个消费组。
原始消费组origin-consumer用于消费无(蓝绿)标识的流量。
蓝色消费组blue-consumer用于消费「蓝色」标识流量。
绿色消费组green-consumer用于消费「绿色」标识流量。
图片
2.3 JOB(elasticjob)
图片
elasticjob在运行时会在业务应用集群内利用ZK协调产生一个Master节点,由Master节点来按负载均衡策略将任务分配到各个执行器节点上。这个任务分配关系一经分配就会固定并在后续复用,除非是有应用进程上下线、JOB分片数有变更。
改造elasticjob客户端适配流量规则SDK,正在蓝绿发布的应用,在感知到有集群已经摘流时,会修改ZK上的状态标识,将上述记录的分配关系失效。
失效后,后续JOB执行时,会根据流量规则重新进行任务分配,避让已经摘流的节点,以保证已经摘流的节点上不会有JOB执行。
- 过程详解
图片
蓝绿接入注意事项
因当前技术限制, 服务接入蓝绿需要注意以下事项:
- 流量无法摘除的服务暂时无法接入
未通过网关进入服务流量:例如,通过域名SLB进入服务、通过Ingress进入服务的流量无法摘除。
消费Kafka的流量无法摘除:由于应用使用的原生kafka客户端并全面铺开、无法对切入提供支持。
未使用统一框架/注册中心:未使用统一框架和注册中心的Java应用、以及非Java类应用当前不支持蓝绿发布。
- 使用特别提醒
- 消费消息需幂等:使用消息中间件必须做幂等,这是基本要求,在消费组启停管控中可能产生重复消息。
- 消费组线程数量:由于会有三个消费组、消费线程也会增加两倍,有业务影响时需调低线程数。
- 需要好流量评估:蓝绿发布需一半节点承接线上流量、在应用升级蓝绿集群时做好确认。
- 升级到特定版本:使用蓝绿发布需要应用升级到框架指定版本,详见接入指南。
- Feign/HTTP流量:针对使用框架Feign的HTTP流量,需上下游应用全部升级后方可使用。
- 使用Dubbo流量:使用框架Dubbo的服务只需要自身服务升级版本即可、无需上下游升级。
四、得物SRE团队对蓝绿发布的相关支持
容器集群针对蓝绿的改造
我们容器的workload使用的OpenKruise来进行管理。在进行蓝绿发布之前,我们使用的单个clonesets进行控制。
以下图所示,为我们一个测试非蓝绿集群,这里就是使用单个cloneset进行控制的单实例。
图片
在进行蓝绿改造后,我们将workload分为蓝绿两个clonesets,通过这样,我们可以实现蓝绿发布时候的单边实例发布。同时在我们管理平台界面,任是单个集群界面, 以此来实现单集群下 蓝绿集群的拆分。
图片
蓝绿发布扩容资源优化
前面就说过,蓝绿发布的一大缺点是通常需要准备平常流量2倍的资源,以应对蓝绿发布期间的流量。我们在加入蓝绿发布集群时,也尽量会提醒需要增加资源以应对蓝绿发布。但如果毫无规划的进行扩容,则会带来以下几个问题,比如长期保留扩容资源,则会带来成本的答复增长,而临时的扩容,则代表着对人力的消耗增加,而且临时的扩容也增长了对资源池管理的难度。可能在用户扩容时,出现资源池不足的情况。
为了应对这个问题,我们针对蓝绿发布进行了优化。首先是在发布流程中加入了扩缩容的环节。让平台自动帮助进行服务的扩缩容。其次,在容器层面,我们利用云服务商的弹性实例功能,来弥补常规资源池不足的情况,通过基于Virtual Kubelet技术接入到k8s中,支持秒级启动,按量计费,可快速完成扩缩容,满足业务的实时响应需求。
注意: 在应用加入发布通道时,因蓝绿发布会导致流量减半,请务必对核心服务进行扩容(SRE建议扩容30%以上)。
图片
图片
图片
发布监控
加入灰度的蓝绿发布,因为涉及流量切换过程,因此对监控要求非常高,需要及时观测整个通道中的服务状态,而历史中单应用的监控页面无法满足发布owner有效观测。因此,针对这个问题,我们专门设计了通道级的蓝绿发布大盘,有效的观测流量分布情况,服务的请求情况等。通过该大盘,发布owner能有效掌握本次发布情况,决定是否继续进行切流。
图片
五、蓝绿发布期间可能出现的问题及应急响应策略
资源不足导致的服务异常
- 发布前扩容
根据蓝绿发布原理可知,我们在发布时,只有50%的实例来支撑原先100%的流量, 因此务必在蓝绿发布前勾选发布前临时扩容。扩容量需要评估以下几个数据:
- 服务CPU 水位情况
根据历史经验,如日常水位99值在20%以内的服务,无需进行临时扩容,而99值在20%-30以内的服务,建议扩容20%左右。而99值在30-40%之间的服务,应当扩容30%以上。同时也要考虑发布当天的流量情况。
比如我们在七夕大促期间的发布,因大促流量过高,我们许多服务在蓝绿发布时扩容达到了50%以上的情况,通过此方式,保证在切流期间,服务也能正常。
因为我们的服务大多以JAVA为主,内存大多用固定方式分配给了JVM堆,因此内存不是一个核心的参考指标。
- 服务线程使用情况
除了服务CPU外,服务线程也是一个核心参考指标,特别是Dubbo线程池以及DB/Redis的线程池。比如原先Dubbo线程池,max为 200,10个实例的服务,当日常QPS大于1000的时候,在蓝绿发布时就需要扩容了,否则实例数少了一半,意味着可用线程也少了一半,这个时候就会出现线程拒绝异常了。
- 发布期间的资源不足
有些时候,我们评估不足会导致在开始发布后因为资源不足导致的错误率上升,此时需要我们紧急处理,但为了发布期间的稳定性,一旦我们开启了蓝绿通道,就不允许进行集群的扩容了。此时需要SRE接入在后台进行处理,处理逻辑如下:
- 确认待扩容集群未处于发布状态。
- 手动修改蓝/绿单边集群cloneset的replicas数据。
- 待发布完毕后,手动还原该cloneset的replicas数据。
因该操作非标准操作,且存在风险,请尽量不要使用以上方式进行。
发布中出现流量不均衡的情况
之前的一次测试中,我们在引流后出现了服务流量不均衡的问题,当时因为Fusion框架升级了Dubbo异步的改造中存在逻辑缺陷,导致流量无法均衡分布。之后,通过回退Fusion框架版本后问题恢复。
图片
这是个比较危险的情况,某些节点会在发布时承担日常400%的流量,很容易造成服务雪崩。因此,在蓝绿发布中,负责人和SRE要加强服务的监控和关注力度,及时发现流量不均衡的情况并介入。
发布中出现流量互窜的情况
流量互窜的问题会有许多种情况:有因为切流前的JOB持续运行,导致双边集群依旧有流量;有的因为链路中间节点没有升级蓝绿能力,导致流量错位;有的因为MQ请求下游导致的流量错位。这里不针对问题进行一一分析,问题的解决仅能依靠框架的升级,这里仅说下问题的影响和排查方法。
在蓝绿发布时,原先我们的预计是老代码连老代码,新代码连新代码,但出现异常请求时,可能出现新代码连老代码,或者老代码连下游新代码的情况,这个时候就会出现因为依赖不匹配,导致的服务异常。
图片
要发现此类问题,我们首先要知道,在这种情况下,大多会出现单边集群错误率上升。通过我们的监控页面,我们能很好的发现单边错误率上升的情况。此时,我们就能根据这些错误的情况,在天眼的调用链分析中,查看错误的具体情况。此时需要我们判断链路里是否有出现流量异常的情况,查看节点的Host name,可以判断是蓝或者绿集群节点。
图片
图片
六、历史总结及展望未来
蓝绿发布的效果
自从交易域进行蓝绿发布以来,平均大版本的发布时效较之前得到了较大的提升,同时近期大版本已没有出现故障事件,在升级蓝绿发布后,我们可以提前在切流阶段发现问题,并快速回切进行修复,避免了故障带入生产。因此,现在蓝绿发布相比过去滚动部署,在效率和稳定性上均大有提升。
未来展望
目前我们核心服务都已切换至蓝绿集群,这种为我们的多活打下了优势,已经天然具备了多活的条件。因此,未来我们可以通过这种架构来部署我们的多活,这样,当任何单机房出现异常后,能够快速切换到另外一个机房,我们的抗风险能力也会有巨大的提升。参考引用:
[1] 特别鸣谢,本节蓝绿发布架构及原理部分引用了得物中间件平台 “羊羽”同学的文章。