稳定性全系列(三)——放火&降级演练
于是我们在想,能不能提前“放火”——人为制造故障?就好比人类在为了抵御病毒的攻击,制造了疫苗,而疫苗的本质其实就是被虚弱的病毒,我们为什么不能在系统中注入一些弱化的故障,来看系统的反应,以方便我们更有针对性的去做措施?
Netflix把自己在云上的实践提炼出来,写了《混沌工程(Chaos Enginnering)》一书,相信里面的一些体系化的思路对大伙会很有启发。所谓混沌工程,其实一种应用经验探索来学习系统行为的方法,按照Netflix的说法,混沌工程是一门学科。
▌3.1 变被动为主动
就像前面提到的,之前我们大多数稳定性工作都是被动的为未来的发生的紧急事件做准备,我们认为放火演练将改变这一局势,我们能人为提前注入故障,来看看我们系统的反应是否符合我们预期。如果系统的反应没有达到预期,故障带来的影响面也比我们预估的要大,那我们就应该好好分析到底问题出在哪里,该如何去修复。就应该是这样,只有我们清晰的看到系统的稳定性问题,才能更有动力去解决该问题。有了放火演练,我们能将更多的“假想敌”活生生构造出来。
关于放火演练,还有一点非常重要,那就是降级预案(更通俗的说就是紧急预案),降级预案按照生效方式,分为自动预案和手动预案两种,当然,预案的建设得依靠工具,在自动实施的预案中,只要我们定好策略,降级工具(例如 https://github.com/didi/sds)会在系统达到某种预先设定的条件下自动处理,而在手动预案的实施过程中,我们可能需要进行业务开关、系统开关、服务降级、切流等操作,较成熟的预案会将这些操作收口,可能只需要点一次按钮就能完成整个预案的实施。理想的情况应该是,线上80%的预案是自动预案。
我们常常把放火和预案结合在一起来演练,一方面我们要检测在紧急事件发生时,系统是否有超出我们预期的行为,另一方面我们也需要不断找机会来验证我们预案的有效性。介于此,我们也希望放火&降级演练能成为工程师了解自己系统行为的一个桥梁,来驱动和帮助工程师构建更具“弹性”的系统。
▌3.2 前提条件
我们前面已经提到了“放火”和“混沌工程”的概念,在滴滴内部,已经有定期的“盲测”演练,其实也属于放火的范畴,但“盲测”的主要目标是使因机房问题导致的故障能在X分钟内发现和资损,例如我们会定期随机停掉一台交换机,看下网络设施是否做了足够的容错。看似简单明了的举措,实际上前期已经做了充分的准备,需要记住的是,并不是任何系统都能做放火&降级演练,必须要具备如下条件才行:
能使用线上环境:如果最终不能在线上或类线上的环境进行放火,那么放火发现的行为和得出结论将没有实际的价值,所以请务必能申请到在线上环境实施。
完善的监控与告警机制:如果我们连线上系统的基本指标和行为都不清楚的话,何来预期的结果?这需要监控系统帮我们完成。另一方面,我们总不能一直盯着监控大盘来看指标,需要报警系统来保障我们能及时发现系统异常。
可预估的燃烧半径:在放火体系中,我们把它对系统的影响面形象的称之为燃烧半径,放火的首要前提一定是对业务和系统的影响控制在预期范围之内(例如XXX放火后发单接口的avg耗时涨幅不超过20%、发单量波动小于2%),前期的放火当然难以较准确的评估燃烧半径,所以我们要先“放小火”,后面再逐渐加大马力。
有对应的降级预案:在具体实施时,我们要对放火的内容进行归类和沉淀,每种放火我们要提前想好对应的降级预案是什么以及预案的触发条件。初步的降级预案是放火的前提条件,而放火最终也会促进降级预案的不断完善和进化,这是一个良性循环。
▌3.3 方案实施
首先我们必须承认一个事实,哪怕是经验再丰富的工程师,也无法穷举未来可能发生的紧急事件,更不要说这些紧急事件的组合了。想要真正开始放火其实并不容易,目前我们是按照如下步骤展开:
选择一个放火事件
其实最先做的应该是放火事件的归类,由于我们更关注服务端的系统稳定性,所以主要把放火事件类型分为五类:
存储放火:包括Redis、MongoDB、ES、MySQL等错误量和耗时的放火。
依赖服务放火:包括下游调用超时、抛异常等。
消息放火:主要是针对MQ的放火,包括减少Consumer、增加Producer发送耗时等。
服务器放火:主要包括CPU、内存等服务器资源放火。
网络放火:主要包括网络带宽、网络节点的放火,例如前面提到的盲测。
由于放火&降级演练是定期举行的,所以每种类型的放火事件我们都有机会尝试。例如,我们可以选择依赖服务放火类型,即线上通过经纬度反查城市ID的服务耗时比平常翻一倍这一放火事件。
确定燃烧半径
燃烧半径即放火范围,燃烧半径的选择一定要十分小心,不能过小,否则效果不明显,不能过大,否则将影响线上业务和用户,当然还是建议从小到大,逐步调整,比如这次我们选择燃烧半径为线上10%的经纬度反查城市ID的服务。
制定预案
确定计划
开始放火
图的右侧是业务系统集群,每个模块的机器上都会安装一个放火Agent,用来接收和实施放火指令,并收集必要的数据,放火Agent如何实施放火指令呢?答案就在ChaosBlade中,内部实际上是使用JVM agent的注入能力(参见:https://blog.csdn.net/manzhizhen/article/details/100178857);而左侧的放火平台提供了放火的操作功能界面以及一些简单的数据大盘。由于我们使用了Dubbo,所以放火平台需要从Dubbo注册中心取服务拓扑,而为了放火平台能做得更通用,所以有个dubbo-fire模块进行服务拓扑的适配和转化等,还提供缓存功能。
可以看到,整体的放火架构还是很简单的,目前滴滴的放火平台还处在快速迭代中。
记住一点,一旦达到终止放火的条件(例如影响的用户数或系统指标超过预先确定的阈值),一定要及时的停止放火。
记录和分析结果
我们需要记录系统在放火过程中的表现、对业务的影响、预案的效果等,然后进行讨论和分析,这是不是我们想要的?怎么做才能让该紧急事件对系统和业务指标的影响更小?
我是易振强,热爱开源,热爱分享,深耕分布式系统和稳定性建设,欢迎关注SDS服务降级系统:https://github.com/didi/sds ;也热爱生活,热爱漫画,一拳超人和海贼王都很好看!!