字节跳动混沌工程实践总结 | IDCF
内容来源:字节跳动技术团队 作者:基础架构团队
(图片来源于网络)
本文选自“字节跳动基础架构实践”系列文章。 混沌工程是通过故障注入的方式帮助系统寻找薄弱点,从而提高系统的稳定性。随着微服务、云原生相关技术的发展,分布式系统已经流行在业界各处,但因此也带来了复杂度急剧上升、故障发生难以预测后果、难以避免与验证等挑战。而混沌工程正是通过故障注入等方式为切入点,帮助解决以上问题。本文讨论了字节跳动引入混沌工程以来的相关实践,希望能提供一些参考。
一、背景:什么是混沌工程
二、业内实践
Netflix 最早系统化地提出了混沌工程的概念,并出版了混沌工程领域内的首部书籍《混沌工程:Netflix 系统稳定性之道》[1],在本书中提出了混沌工程成熟度模型与应用度模型,并总结了五条高级原则,对于混沌工程的发展具有指导性意义。另外 Netflix 开源了其混沌工程项目 - Chaos Monkey[3]。 阿里巴巴是国内较早开始探索混沌工程并做出开源的公司,其开源项目 ChaosBlade[4]可以结合阿里云进行 chaos 实验。 PingCap 作为国内优秀的数据库领域开源公司,其在混沌工程领域一直有投入,并在最近开源了内部混沌工程实践平台 - Chaos Mesh[5]。 Gremlin 为一家混沌工程商业化公司,该公司提供了一个混沌工程实验平台,通过将其 agent 安装在云主机上触发故障。同时提出了 chaos gameday[2] 的概念。
三、字节跳动如何实践
故障注入 自动化指标分析 活动实践落地
降低实验的时间成本,我们可以依赖自动化指标分析,帮助我们进行辅助判断,从而寻找更大的产出。 降低实验的风险成本,我们可以依赖自动化指标分析,进行稳定性判断,作为 chaos 实验自动化停止的决策依据。
在故障注入方面,设计可扩展的故障中心,实现精准可控的故障。 在实践活动方面,确立混沌工程规范,探索最佳实践。 解耦故障实现与 chaos 实践活动管理。
所有故障的发生,都会间接或直接影响某个微服务。而我们的最终目的,是观察该服务在各外部依赖异常时,服务本身的 resilience 能力如何。
Target - 即上文提及的目标微服务,在开始 chaos 实验之前,需要明确,对什么服务注入故障,该服务为主要观察目标。 Scope Filter - 对应混沌工程概念中的爆炸半径,为了降低实验风险,我们不会令服务全流量受影响。通常会过滤出某一部署单元,该单元或为某一机房,或为某一集群,甚至精确到实例级别乃至流量级别。 Dependency - 依赖,我们认为所谓服务被故障影响,实际是其依赖有异常。该异常可能来自中间件,可能来自某下游服务,也可能来自所依赖的 cpu,磁盘,网络等。 Action - 故障事件,即描述该服务的外部依赖究竟发生了何种故障,比如下游服务返回了拒绝,发生了丢包,或者延时;又比如磁盘发生了写异常,读繁忙,写满等意外情况。
spec. //微服务application A 的 cluster1集群内10%的实例cpu突然满载
tareget("application A").
cluster_scope_filter("cluster1").
percent_scope_filter("10%").
dependency("cpu").
action("cpu_burn").
end_at("2020-04-19 13:36:23")
spec. //服务application B 的 cluster2集群所依赖的下游application C突然延时增加100ms
tareget("application B").
cluster_scope_filter("cluster2").
dependency("application C").
action("delay, 200ms").
end_at("2020-04-19 13:36:23")
3.2.3 故障中心设计
我们在以上故障模型基础上设计了一套声明式接口。当注入某一故障时,只需按上述模型添加故障声明即生效;若想终止故障,只需删除该声明。
故障中心在收到上述类似声明后,便开始向内部研发体系平台寻找符合条件的实例,并自动安装故障 agent,通过将相关指令下发给 agent,实现故障注入的目的。故障中心适当借鉴了 Kubernetes 的架构设计与理念,其架构设计如下:
故障中心架构图
故障中心由三个核心组件 API Server, Scheduler, Controller,外加一个核心存储 etcd 组成。其中
API Server负责包装 etcd 并对外提供声明式接口;
Scheduler 则负责将故障声明解析,并根据声明持续寻找 Target 对应的实例,以及 Dependency 对应的下游实例/中间件/物理设备;
在这之后,Controller 将 action 故障解析为可执行指令,下发至对应实例的 agent,或者调用对应中间件的 API,达到精准的故障注入。
3.2.4 实验选择的原则
在 chaos 实验中,我们考虑到风险以及各业务不同特点对 chaos 理念的接受程度不同,所以定义了实验选择的原则,可供各业务线根据实际情况自行决定,其原则如下:
从线下到生产
从小到大
从面向过去到面向未来
从工作日到休息日
1)从线下到生产
此条指的是环境的选择。
一般认为,混沌工程只有在生产环境实验才有意义;但我们认为一种比较温和的实验步骤是从线下逐渐走到生产。这也是综合考虑,从线下开始着手会让各方都比较放心。不过对于分布式系统而言,部署不同、流量不同都会带来不一样的结果,唯有在生产进行实验才能真正验证。一条比较好的路径是:
测试环境-> 预发布环境 -> 预览环境特定流量 -> 生产集群生产流量
2)从小到大
此条指的是故障范围的选择。
我们推荐故障应该从小范围,较温和的开始。当建立了足够的信心之后,再进一步扩大故障范围。一条比较好的路径是:
可控流量 -> 单个接口 -> 单机 -> 单集群 -> 单机房 -> 全链路
3)从面向过去到面向未来
此条指的是故障类型的选择。
我们认为曾发生过的故障是实验优先级最高的。而人类历史告诉我们,人们总是会在一个地方反复跌倒;生产发生过的故障,很有可能再次发生;且同样可能会在其他链路上发生类似故障。因此一条较好的路径是:
重现历史事故的故障 -> 来自历史事故的故障类型 & 相似链路 -> 各种随机故障 & 全链路。
4)从工作日到休息日
此条指的是混沌工程实验时间的选择。
休息日代指任意时间。我们推荐实验时间从工作日开始尝试,最优的是工作日下午 3 点左右(各业务根据自身高低峰期再行考虑)。这个时间段,相关人员一般都在工作岗位上,有任何情况都能及时处理。混沌工程的早期目标就是为了在可控的环境中提前暴露问题。当然,随着混沌工程不断走向成熟,我们将会慢慢开始尝试在任意时间进行实验。一条比较好的路径是:
工作日下午 -> 工作日晚上 -> 休息日 -> 随机时间
3.2.5 实验过程设计
在该阶段,我们为业务系统设计了 chaos 实验过程的最佳实践。按此过程,chaos 实验将会更有目的,观察到的内容亦更有意义。
实验前
0)⚠️ 在开始你的第一个混沌实验之前,请确保你的服务已经应用了弹性模式,并准备好处理可能出现的错误,否则不要随意尝试。
1)准备故障注入的能力
字节跳动混沌工程实验平台的故障模拟能力
联系各依赖方手动制造故障
2)选定本次实验的假设,如:
不会因为某个下游服务挂了而影响业务。
不会因为 redis 网络抖动而影响业务。
不会因为某个 pod 突然被杀而影响业务。
当某个核心下游依赖挂了之后,降级方案必须有效,且副作用可接受。
3)选定能体现实验假设的指标,并观察。
4)选定能反应服务损失的指标,并设定底线。
5)在组织内沟通到位。
实验中
1)执行期间要密切关注相关指标,因为可能需要随时终止实验。
2)牢记实验的假定,收集相关指标稍后可辅助分析实验结果。
3)在实验过程中,可能会根据指标的波动情况,随时调整实验参数(故障范围与烈度),多尝试几次,会有更好的效果。
实验后
根据指标和业务表现,分析本次实验所能带来的成果。根据经验反馈,一般会获得以下相关成果:
找到了脆弱点,并获得改进
验证了降级/预案,增强信心
找到了系统性能拐点
梳理出一波无效告警,优化告警效率
3.2.6 总结
在这一阶段,我们对故障中心完全重构,在架构上使得故障注入更加简单可控;在模型抽象上,故障注入的扩展性更强。在这一阶段,我们梳理了 chaos 实验选择与流程的最佳实践。在下一代产品中除了继续丰富故障能力外,将会着眼于补齐指标分析能力,以及进一步沉淀有更大产出的实践活动。
3.3 第三代
在完成了初步阶段的实践之后,chaos 的核心能力-故障注入已经具备,同时字节跳动各业务线也陆续开始了 chaos 之旅。于是该阶段,我们的目标是:
在自动化指标分析方面,补齐指标分析能力。
在故障注入方面,丰富故障的类型。
在实践活动方面,沉淀上一阶段总结的实践活动,并进一步探索实践形式,挖掘更大的价值产出。
3.3.1 系统设计
基于以上目的,整体设计如下:
成熟阶段系统设计
在原子能力层,添加自动化指标观察能力。通过引入机器学习,我们做到了基于指标历史规律的无阈值异常检测。在平台层,添加自动化强弱依赖梳理,与红蓝对抗模块。
3.3.2 自动化指标观察
在 chaos 实验中,相关指标的梳理与收集是一件繁琐且耗费心力的活动。我们观察了 chaos 实验过程之后,总结了三类指标,如下:
故障指标 - 确定故障是否注入成功 。
止损指标 - 确保系统不会因故障无法承受而造成过大损失。
观察指标 - 观察细节,观察故障导致了哪些关联异常。
1)故障指标
指标定义 - 故障指标源于故障,因故障触发了指标波动。比如我们注入了故障:redis 延时增加 30ms,那么该指标则为目标服务 -> redis 之间的均值延时 pct99 延时等。该指标可帮助用户直观看到故障的产生与结束。
如何处理 - 对于此类指标,仅做展示即可,可以保证用户清晰看到故障何时开始,何时结束。
获得途径 - 来自故障,平台制造故障时,平台知道会受影响的直接指标是什么。
2)止损指标
指标定义 - 止损指标对于目标服务/目标业务至关重要,表示该次演练所能承受的最大限度,指标可能来自服务本身相关(比如对外错误率),亦可能来自关联较远的业务指标(比如每分钟点播量),甚至可能来自兜底服务的指标(比如兜底服务的最大承受指标);也可能是以上提到的各类指标某种组合。
如何处理 - 对于此类指标,需要非常精准地标识关键阈值,一旦波动到阈值时,表示到达了损失底线,任何操作必须马上停止,故障需要恢复。
3)观察指标
指标定义 - 观察指标,对于在 chaos 实验的时候发现新的问题,有很大的辅助作用。观察指标应该是一切与服务和故障相关的指标。比如服务本身的 SRE 四项黄金指标(latency, traffic, errors, and saturation),比如故障可能影响的关联指标(redis 延时故障,是否会导致 redis 其他指标的变化?redis qps,reids errors,降级服务的 qps 变化),比如关联告警记录,关联日志。
如何处理 - 此类指标,没有明确的阈值,但往往此类指标在事后分析的时候最容易发现各种潜在问题,我们在处理此类指标的时候引入了机器学习方法,与指标历史规律进行对比,可做到自动化异常检测。
因此指标观察的核心是智能指标筛选和无阈值异常检测。另外配合一套基于经验的人工规则,我们可以面向不同的实践活动做各类自动化判断或者辅助决策。
3.3.3 红蓝对抗实践
字节跳动的红蓝对抗实践,吸收自 Gremlin 介绍的 chaos gameday[2]。在字节跳动内部多次实践中,我们也不断因地制宜调整,最终发展成为字节跳动特色的红蓝对抗实践。红蓝对抗的实施目标是帮助业务系统进行全面摸底,也可认为是对业务系统的稳定性建设目标的一次集中验证。
目前红蓝对抗已多次帮助字节跳动推荐中台进行全面摸底,发现了从监控、告警到兜底、降级、熔断策略等各方面的问题。
1)流程设计
在开启红蓝对抗之前,红蓝双方的沟通特别关键。红军(即防守方)需要进行诸多决策,比如评估有信心参与对抗的服务与范围,比如评估近期业务迭代节奏,权衡业务迭代与稳定性建设。我们遵循如下流程进行实践对抗前的活动正在完成平台化沉淀:
红蓝对抗执行前流程图
红蓝对抗一旦开启后,主要操作将由蓝方主导,除非发生预期外情况(这一般也意味着防守失败)或者需要操作预案开关,否则红方在此过程中,基本处于 stand by 状态。主要流程如下:
红蓝对抗执行中流程图
在对抗结束后的复盘回顾是关键环节,通过将红蓝对抗过程中所记录的数据汇总。可清晰地看到对抗整体效果,一目了然地了解此次计划中目标业务系统的稳定性建设情况。
单场红蓝对抗数据汇总
另外,我们将过程中发现的问题汇总记录,并保留对抗时候的完整记录。这使得发现问题可追踪,剖析问题有现场。
单场红蓝对抗结果汇总
3.3.4 强弱依赖自动化梳理实践
服务的强弱依赖信息对于服务治理、容灾体系的设计都至关重要,而强弱依赖的真实情况只能在故障发生时才能得到验证。故我们开启了强弱依赖的验证工作,并随着实践打磨,不断提高强弱依赖梳理的自动化程度。在通过引入机器学习帮助我们进行无阈值指标异常检测之后,强弱依赖梳理过程已基本实现全自动化。目前强弱依赖梳理已基本覆盖抖音与火山的核心场景,为其服务治理与容灾体系设计都提供了巨大的输入。强弱依赖自动化梳理的整体流程如下:
强弱依赖自动化梳理流程
3.3.5 总结
在该阶段,我们补齐了指标分析能力,通过引入机器学习,很大程度降低了指标分析成本。
基于自动化指标分析能力,我们尝试结合新的实践活动挖掘了更多的产出。红蓝对抗活动帮助业务系统对自身稳定性有个更全面的了解。而强弱依赖分析则帮助业务系统对自身的稳定性细节有了更深的认识。
四、未来阶段:面向基础设施的混沌工程
五、结尾
《混沌工程:Netflix 系统稳定性之道》:https://www.oreilly.com/library/view/chaos-engineering/9781491988459/
《How To Run a GameDay》:https://www.gremlin.com/community/tutorials/how-to-run-a-gameday/
Netflix 混沌工程开源项目 - Chaos Monkey:https://github.com/Netflix/chaosmonkey
阿里巴巴 混沌工程开源项目 - ChaosBlade:https://github.com/chaosblade-io/chaosblade
PingCAP 混沌工程开源项目 - Chaos Mesh:https://github.com/pingcap/chaos-mesh
分布式一致性测试框架 - Jepsen:https://jepsen.io/
Zhou, Xiang, et al. "Latent error prediction and fault localization for microservice applications by learning from system trace logs." Proceedings of the 2019 27th ACM Joint Meeting on European Software Engineering Conference and Symposium on the Foundations of Software Engineering. 2019. :https://dl.acm.org/doi/10.1145/3338906.3338961