从 2010 年 Netflix 上线 Chaos Mokey 的第一个版本到如今,虽然混沌工程发展已历时十年,但其实只在少数大厂里面有较成熟的落地,对绝大部分研发同窗来讲,混沌工程仍是一个比较陌生的领域。git
分布式和微服务化已经成为主流的系统架构设计方案,大规模分布式系统的可用性保障能力愈来愈成为关注的重点。混沌工程也开始如雨后春笋般在各大企业内部萌芽生长,但大部分还处于初期的探索阶段,在实践过程当中也遇到了这样或那样的问题,有技术上也有认知层面上的,这些问题不免会对混沌工程的快速落地产生阻力。github
下面介绍一下字节跳动在混沌工程实践过程当中的一个关键阶段:场景化主动实验。但愿本文能够帮助你们加深对混沌工程价值的了解,对设计混沌工程实验、落地混沌工程建设提供更多的思路。算法
混沌工程的高级原则要求可以在生产环境自动的运行实验,这个目标并非一蹴而就的。数据库
根据混沌工程成熟度模型(CMM)[4]说明,要分别从“熟练度”和“应用度”两个维度同时进行建设。其中,“熟练度”体现了混沌工程系统的有效性和安全性,“应用度”衡量了混沌工程实验覆盖的广度和深度。在混沌工程建设的中前期,这两点都是混沌工程成功落地的关键路径。缓存
在混沌工程的初级阶段,一般都会建设一个故障注入测试平台(FIT,Fault Inject Testing),集成一些常见的故障场景或异常事件的模拟能力,由业务或 QA 同窗设计并执行实验来验证系统的韧性能力。安全
在这个阶段,基础架构和业务系统的实现均可能处于比较粗放状态,混沌工程平台的故障注入能力须要兼容各类业务架构的实现方案和软硬件环境,执行实验时,业务同窗不只要设计实验的故障场景(机房网络故障、下游服务宕机等)、配置演练环境(目标服务、实验集群等控制实验的爆炸半径),还要找到可以描述实验时服务状态的稳定性的指标(如 metrics、日志或告警等),而后手动启动实验,执行人还要不停的观察稳定性指标的变化,判断系统的容灾逻辑或弹性策略是否被正确触发、业务系统的表现是否符合预期等等。若是在执行过程当中发现异常,须要马上终止实验,收敛实验影响。markdown
整个实验过程的人力成本较高,实验的操做门槛也较高,再加上这个阶段业务同窗对混沌工程价值和理念的认知还处于较初级水平,很难会主动对本身的服务设计实验,更没法保证明验的常态化执行。所以,混沌工程实验的时效性和业务系统的弹性容灾策略持续有效就比较难以保证了。网络
如何突破这个阶段、成功抵达混沌工程的终极目标呢?架构
经过不断的思考,咱们认为混沌工程建设须要一个过渡阶段,即场景化主动演练。框架
所谓场景化主动演练,就是在明确混沌工程的终极建设目标的前提下,以终为始,分阶段去设计混沌工程的实验标准、定义技术规范,搭配工程化能力,逐步将人和业务引导到混沌工程建设的高速公路上,共同推动 CMM 模型的熟练度和应用度。
因此,场景化主动实验是通向混沌工程自动化建设的关键路径。
混沌工程演变图
首先须要明确混沌工程的最终目标,以终为始,反推当前阶段应该建设什么样的技术规范和标准能力。
而后,根据业务当前的基础架构现状和实验诉求构建一个通用的实验场景,由混沌工程平台方在保证明验风险可控的条件下主动对业务系统进行实验。这样,在知足业务须要的同时又能够推进相关技术规范和基础能力的建设,并且对业务同窗的资源依赖较少。
以字节跳动为例,要实如今生产环境自动的执行可控的混沌工程实验,当前阶段应该具有的能力包括:
场景主动实验能力拓扑图
FIT 平台须要业务同窗分析业务容灾场景并制定实验,而后执行实验进行验证,混沌工程平台方只是给予一些技术支持和建议。在业务同窗对混沌工程认知度不高状况下,实验的主动性、覆盖率、时效性都很难保证,若是再想让业务同窗去配合建设一些混沌工程的基础能力难度就可想而知。
转换一下思路,将业务主动改成平台主动!由混沌平台针对业务系统的某个场景主动执行混动实验来验证服务的弹性能力,业务同窗只需关注实验结果。只要实验场景契合业务痛点、实验结果对业务构建弹性系统有意义,业务同窗天然会承认混沌工程的价值,也会更加积极的参与混沌工程实验和混沌工程的基础建设。
混沌工程的价值是发现业务系统中潜在薄弱环节,提高业务系统韧性能力,即服务可用性和稳定性,因此主动实验场景也应该知足这个前提。
在字节跳动,主动实验落地的第一个场景是验证服务调用链的强弱依赖关系。所谓强弱依赖关系,就是当访问的下游服务异常(服务宕机、响应超时、接口返回失败等)时,调用方的稳定性和可用性是否会受到影响。例如,查询缓存 miss 或失败后能够继续回源访问数据库,缓存的问题并不会影响服务可用性,这个缓存就是个弱依赖。
为何要选择这个场景呢?公司的不少线上运营事故都是由于不合理的依赖关系致使的。字节跳动有不少高 QPS 的海量服务系统,为了保证用户体验,对高可用高稳定性的要求很高,会经过各类缓存、服务兜底、数据兜底等容灾策略来减小强依赖服务的数量。另外,在字节跳动的服务治理体系中,会根据服务间的强弱依赖调用关系为某些故障场景预设自动容灾降级的策略。好比,当服务治理系统检测到系统负载太高时,可以自动对弱依赖的下游执行降级,经过 FailFast 来缓解系统的负载。所以,业务负责人一般都会比较关注服务的强弱依赖关系。
服务依赖调用链
主动设计和执行实验意味着将业务侧的人力成本转嫁给了混沌平台,混沌平台必需要借助通用化、自动化的执行能力来下降人力成本。须要强调的是,这里的通用化和自动化必定是和混沌工程的终极目标一致的,可以承载过渡阶段的职责。
能够从混沌工程的高级原则[2]进行剖析:
混沌工程高级原则
一个公司内部一般会有不少条产品线,每一个产品又会包含不少微服务,还会依赖到中台服务或基础组件,这些不一样模块的开发语言、服务框架、技术栈等可能五花八门。要想实现混沌工程通用化与自动化的能力,就必须制定一些通用的服务标准和技术规范。
混沌工程不只须要了解业务系统当前的技术栈,还要可以预测将来的技术发展趋势,帮助业务提早规划切实可行的技术规范并协助进行建设。
举个例子,如何定义服务的稳定状态?
在字节跳动,咱们以调用方视角定义了一个服务级别的 metrics 指标来描述服务的稳定性状态:
之因此要在响应包中单独定义一个 stability 字段是为了区分开系统错误和业务错误。由于业务错误并不表示服务出现了问题,因此咱们更关注系统错误。举个例子,删除好友时被调方返回好友不存在的错误,这个错误并不表示系统的稳定性出现了问题。
这个规范最开始只在字节跳动的少数核心服务中使用,其余服务一般都有本身的稳定性指标,有些服务可能须要多个 metrics 指标一块儿来描述服务的稳定性。由于混沌工程的通用性要求应该具有一种通用的指标来描述全部服务的稳定性状态,通过评估,咱们将 stability metrics 做为了混沌工程的服务通用稳定性描述指标,并计划将其推广到全公司的业务,不管什么服务框架和开发语言均可以低成本的快速实现。
字节跳动的混沌工程体系主要包括场景化主动实验平台、FIT 平台和红蓝对抗平台。
字节跳动混沌工程总体架构图
场景化主动实验的流程图
同时推动混沌工程建设的“熟练度”和“应用度”,以生产环境自动执行混沌工程实验为目标构建场景化主动实验的流程和标准,实验场景选定为验证服务间的强弱依赖调用关系,可自动化运行实验,具有实验爆炸半径控制能力。
经过验证强弱依赖调用关系的正确性反推服务的稳定性指标是否规范,推进稳定性指标的规范落地。
优先在字节跳动的核心服务落地,树立模范标杆,而后扩展到更大范围。
由于 stability metrics 已经被多数核心服务当作通用的稳定性描述指标,因此场景化主动演练将 stability metrics 做为稳定性描述的核心指标,同时辅助判断接口成功 qps、失败 qps、调用下游成功 qps、失败 qps、pct99 等指标。
自动化执行实验要求混沌平台可以自动检测稳定性指标的变更,由于不一样服务的指标曲线是不同的,同一服务的不一样时刻的指标曲线也是不同的,因此预置曲线波动的阈值上限的效果确定不会太好。所以,在项目启动阶段咱们就直接探索自动化的动态检测 metrics 曲线波动的方案:
因为方案一要求具有更灵活的流量筛选能力和实验环境隔离能力,当前阶段的建设难度和成本较高,因此咱们选择了方案二。
起初咱们参考线上报警的检测方案:在执行主动演练时,先经过机器学习为稳定性指标实时训练检测模型,而后用模型实时检测指标曲线的变化。可是,由于主动实验的时间较短(一个实验节点只有 60~120 秒)、metrics 数据点稀疏(一个节点的实验时间只能采集到 2~4 个数据点)、实验流量较低(爆炸半径控制在 5~10 QPS),因此基于机器学习的检测效果并不理想。
因而,咱们改为组合多种统计规则的检测算法,根据最近一段时间的历史数据动态生成曲线的合理波动范围阈值,而后在实验过程当中实时检测增量数据点的波动范围。若是数据点超出了波动范围阈值,就被断定为不稳定。
通过不断调优,最终把这个场景下的指标检测效果优化到了预期水平。
在实践中,咱们发现单个稳定性指标的曲线会偶现非预期的波动噪音,这些噪音会影响曲线检测结果的准确率。
因而,咱们增长了一个噪音过滤策略:经过对比有相关性的多条稳定性指标曲线的波动类似度来过滤噪音。举个例子,对下游依赖服务注入故障后,调用下游服务失败的 metrics 曲线会上升,若是稳定性指标曲线也上升,并且这两个曲线的变化趋势也类似时,才会认为曲线变化是受实验的影响。这个策略可以比较有效的过滤掉偶现的曲线波动噪音。
具有了稳定状态的检测能力,在场景化主动实验时就能够根据稳定状态的检测结果自动推断下游依赖服务是强依赖仍是弱依赖。
为了保证明验的通用性和下降构造实验流量的成本,咱们选定在生产环境执行实验,所以最小化爆炸半径控制能力就很是重要了。
字节跳动的生产环境有个用于服务灰度上线的金丝雀集群,在服务上线时会先升级这个集群来验证服务的正确性。这个集群的实例比较少,且支持经过修改集群权重来调整进入集群流量。
通过验证,实验流量在 5~10QPS 时就能够保证稳定性 metrics 指标检测的准确率。因此,执行实验时,先从服务的金丝雀集群中随机选择一个实例做为实验目标,计算实例流量与预期流量的误差从新生成权重值,而后经过修改集群权重来调整实例流量。
字节跳动的微服务管理平台经过聚合服务的 Trace 日志生成了服务间的调用拓扑图,经过 OpenAPI 能够查询到某个服务的全部一级下游依赖服务列表。
而后,逐个对下游依赖服务注入一段时间的宕机故障,同时检测服务的稳定性指标是否出现异常波动来推断下游依赖服务是强依赖仍是弱依赖。
须要注意的是,由于是逐个对下游依赖服务执行实验,为了不前一个实验对下一个实验产生干扰,咱们在两次实验间增长了一个间隔时间,具体的间隔时长依赖所使用的指标检测算法。
实验执行结束,平台会将下游依赖服务的强弱依赖推断结果、执行上下文、稳态指标监控视图和检测结果等汇总成一个实验报告发送给服务负责人。
服务负责人确认实验报告,若是发现实验结果不符合预期,能够经过执行上下文和监控视图等信息来辅助定位问题。同时,能够在实验报告中备注不符合预期缘由、问题修复方案和修复进度等,平台会定时跟进修复进度并提醒服务负责人更新结果。
若是实验结果符合预期或问题已完成修复,实验报告会进入结单状态,同时记录服务的强弱依赖推断结果,并输出给服务治理平台,应用于服务在线治理。
业务系统是持续迭代的,下游依赖关系也是动态变化的。若是非预期的引入了强依赖,会给系统增长可用性风险,所以场景化主动实验也须要常态化执行。
已完成的主动实验均可以开启试验结果自动保准,每隔必定时间会自动执行实验验证下游依赖服务的强弱依赖关系,并与历史实验结果进行对比,并将变更的部分经过实验报告发送给服务负责人。
通过服务负责人确认后,新的验证结果会被更新到存储和发送给服务治理平台。
场景化主动实验流程图
咱们的验证范围从核心服务开始、逐渐向外围服务扩张。
核心服务的验证结果基本符合预期,但仍是有少数个案存在稳定性指标不规范、容灾逻辑不符合预期的状况。经过实验结果自动保准机制,也屡次及时发现因代码变动所致使的容灾逻辑失效状况。
外围服务验证出来的问题就比较多了,稳定性指标严重不规范致使没法评估服务的稳定性、容灾逻辑覆盖率低致使过多的强依赖使得服务可用性风险较高等等。须要持续的推进业务进行优化升级,并给予一些高可用弹性系统的建设思路或参考方案。
最近,咱们新上线了在结果保准时自动对弱依赖服务注入随机故障来进一步探索弱依赖的抗风险能力。咱们认为弱依赖服务在任何故障场景下都不该该影响服务的可用性。
另外,场景化主动实验还接入了服务的上线流程,在服务灰度上线时就触发实验,让服务负责人可以更及时的发现不符合预期的系统变动,若是有必要可马上终止或回滚上线变动。
经过场景化主动实验,咱们已具有持续保障字节跳动核心服务的稳定性指标规范性、强弱依赖正确性,以及弱依赖的抗风险能力等等。
接下来,咱们会把场景化主动实验扩展到更大服务范围和更多业务场景,让场景化主动实验发挥出更大的价值。
另外,咱们也在思考打通服务上下游,从点到线再到面,以更高维度的系统视角来探索启发式的智能混沌实验。
欢迎关注「字节跳动技术团队」
投递简历请联系「tech@bytedance.com」