Skip to content

混沌工程:Netflix 系统稳定性之道

🏷️ 读书笔记 《混沌工程》

第一部分 混沌工程介绍

只要你有过在生产环境中实际运行一个分布式系统的经历,你就应该清楚,各种不可预期的突发事件是一定会发生的。

混沌工程是一套通过在系统基础设施上进行实验,主动找出系统中的脆弱环节的方法学。

第1章 为什么需要混沌工程

混沌工程是一种通过实验探究的方式来让我们理解系统行为的方法。

混沌工程和测试的区别

混沌工程、故障注入和故障测试在侧重点和工具集的使用上有一些重叠。

混沌工程和其他测试方法的主要区别在于,混沌工程是发现新信息的实践过程,而故障注入则是基于一个特定的条件、变量的实验方法。

实验可以产生新的认知,而且通常还能开辟出一个更广袤的对复杂系统的认知空间。混沌工程是一种帮助我们获得更多的关于系统的新认知的实验方法。

一些混沌工程实验的输入样例:

  • 模拟整个云服务区域或整个数据中心的故障。
  • 跨多实例删除部分Kafka主题(Topic)来重现生产环境中发生过的问题。
  • 挑选一个时间段,针对一部分流量,对其涉及的服务之间的调用注入一些特定的延时。
  • 方法级别的混乱(运行时注入):让方法随机抛出各种异常。
  • 代码插入:在目标程序中插入一些指令,使得故障注人在这些指令之前先运行。
  • 强迫系统节点间的时间彼此不同步。
  • 在驱动程序中执行模拟 I/O 错误的程序。
  • 让一个 Elasticsearch 集群的 CPU 超负荷。。

混沌工程绝不是 Netflix 的专属

书中所描述的基本原则并不针对任何特定的组织,所介绍的实验设计指南也没有基于任何特定的架构或者工具集。

混沌工程也同样适用于传统行业,如大型金融机构、制造业和医疗机构。

实施混沌工程的前提条件

  1. 系统已经具备一定的弹性来应对真实环境中的一些异常事件,像某个服务异常、网络闪断或瞬间延迟提高这样的事件。
  2. 系统需要有配套的监控系统,你需要用它来判断系统当前的各项状态。

第2章 管理复杂性

复杂性对工程师来说既是挑战也是机遇。

软件工程师通常会对这三方面进行优化:性能、可用性、容错能力。

  1. 性能:特指使延迟或资源成本最小化。
  2. 可用性:系统正常响应和避免宕机的能力。
  3. 容错能力:系统从非正常状态中恢复的能力。
  4. 新功能开发的速度:工程师可以把新功能、创新功能提供给用户的速度。

在充分考虑了这些方面之后,Netflix 选择采用微服务架构。这个架构策略以沟通协调为代价来提高新功能的开发速度。小团队之间的有效沟通是成功实施微服务架构的关键。混沌工程通过适时地验证系统弹性,来支持快速的功能开发、实验,以及增强团队对系统的信心。

康威定律:任何组织所做的系统设计(广义的系统)的结构,将不可避免地复制这个组织信息沟通的组织结构。

理解复杂系统

微服务架构给我们带来了开发速度和灵活性的提升,代价却是牺牲了我们的掌控性和可理解性。这个缺失恰好为混沌工程创造了机会。

系统复杂性的例子

从例子中学到了什么

“牛鞭效用”:输入的一点扰动便会触发一个自我强化的循环,最终导致输出结果的剧烈波动。

期待架构师能理解这些组件和组件的交互模式,从而能充分预测这些预料之外的系统效应,是不合理的。而混沌工程提供了可以让这些效应浮出水面的工具从而让我们建立对复杂分布式系统的信心。

第二部分 混沌工程原则

优化一个复杂系统的性能通常需要在混乱的边缘进行,即在系统行为即将开始变得混乱、无迹可寻之前。
-- Sydney Dekker, Drift Into Failure

混沌工程是一门原则性很强的学科,特别是一门实验性的学科。这些原则建立在真正实施混沌工程的大规模系统之上。在实施混沌工程的过程中,并不是所有高级原则都必须用到。但我们发现,运用的原则越多,你对系统弹性的信心就越充足。

实验

软件系统缺乏理论的预测模型,因此就不得不通过实验方法来了解在不同的情况下我们的系统会如何表现。我们通过在系统上进行各种各样的实验来了解系统的表现。我们尝试给系统制造各种麻烦,看它会发生什么状况。

故障注入测试(FIT)

2014年初,Netflix 开发了一个名为 FIT 的工具,意思是故障注入测试( Failure Injection Testing )。这个工具允许工程师在访问服务的一类请求的请求头中注入一些失败场景。这些被注入失败场景的请求在系统中流转的过程中,微服务中被注入的故障锚点会根据不同的失败场景触发相应的逻辑。

高级原则

  • 建立稳定状态的假设
  • 用多样的现实世界事件做验证
  • 在生产环境中进行实验
  • 自动化实验以持续运行
  • 最小化爆炸半径

第3章 建立稳定状态的假设

我们将系统正常运行时的状态定义为系统的“稳定状态”。

如何定义服务的稳定状态?

先搜集和系统健康有关的数据。

  • 系统指标有助于帮助我们诊断性能问题,有时也能帮助我们发现功能缺陷。
  • 业务指标与系统指标形成对比,这些指标通常能回答业务问题。

通常来说,让你的系统有能力抓取业务级别的指标比抓取系统级别的指标更难。然而花精力采集业务级别的指标是值得的,因为它们才能真实地反应系统地健康状况。这些指标获取的延迟越低越好。

在选择指标时,你需要平衡以下几点:

  • 指标和底层架构的关系。
  • 收集相关数据需要的工作量。
  • 指标和系统接下来的行为之间的时间延迟。

如何描述稳定状态

牢记我们的目标:期望通过一个模型,基于所期望的业务指标来描述系统的稳定状态。

你所处的行业决定了你的指标是否以一种可以预测的方式随时间波动。

建立假设

当你进行混沌工程实验的时候,应该首先在心里对实验结果有一个假设。思考一下,当你向系统注入不同类型的事件时,稳定状态的行为会发生什么变化。

最后,思考一下,如何衡量稳定状态行为的变化。当偏离稳定状态行为发生时如何测量这个偏差。

金丝雀分析
在 Netflix ,我们使用金丝雀发布:我们首先把新代码发布到一个只接受一小部分生产流量的小型集群中,然后进行验证以确保新发布的版本的健康性,最后再进行全面发布。

第4章 用多样的现实世界事件做验证

通常有以下几类不可预测的事件或条件:

  • 硬件故障
  • 功能缺陷
  • 状态转换异常(例如发送方和接收方的状态不一致)
  • 网络延迟和分区
  • 上行或下行输入的大幅波动以及重试风暴
  • 资源耗尽
  • 服务之间不正常的或者预料之外的组合调用
  • 拜占庭故障(例如性能差或有异常的节点发出有错误的响应、异常的行为、对调用者随机地返回不同的响应等)
  • 资源竞争条件
  • 下游依赖故障

也许最复杂的情况是上述事件的各类组合导致系统发生异常行为。

在决定引入哪些事件的时候,我们应当估算这些事件发生的频率和影响范围,权衡引入它们的成本和复杂度。

文化因素也是一种成本。

虽然硬件故障并不是导致线上事故最常见的原因,但是注入硬件故障是在组织中引入混沌工程并获益的一个较简单的途径。

和硬件故障一样,一些现实世界的故障也可以被直接注入。例如每台机器的负载增加、通信延迟、网络分区、证书失效、时间偏差、数据膨胀等。

通常来说,故障隔离既可以是物理隔离,也可以是逻辑隔离,隔离是容错的必要但不充分条件。要获得一个可以接收的结果,还需要某种形式的冗余或者优雅降级。一个故障的影响范围和隔离范围被称为这个故障的故障域。

发现和验证故障域以确保满足产品的可用性预期是工程师团队的责任。

实施混沌工程时采用故障域的概念具有一定的乘数效应。

我们应该对系统注入引发故障的根因事件( root-cause event )。向系统注入根因事件会暴露出这些因为资源共享形成的故障域。

我们不需要穷举所有可能对系统造成改变的事件,只需要注入那些频繁发生且影响较大的事件,同时要足够了解会被影响的故障域。

再强调一次,注入的事件一定是你认为系统能处理的。同时,注入的事件应该是所有可能的真实事件的事件,而不仅仅是故障或延迟。

第5章 在生产环境中进行实验

经典测试的一般信条是,寻找软件缺陷要离生产环境越远越好。但是在混沌工程领域,整个策略却要反过来。在生产环境越近的地方进行实验越好。理想的实践就是直接在生产环境中进行实验。

状态和服务

总有一些意想不到的状态会伤害到你。

生产环境中的输入

对于软件工程师来说,最难的一课莫过于,用户永远不会如你预期的那样与系统进行交互。

生产环境总是会收到测试所覆盖不到的组合数据。

真正对系统建立信心的唯一方法就是,在生产环境中对真实的输入数据进行验证并实验。

第三方系统

分布系统就是,其中有一台你根本不知道的机器发生故障了,有可能会让你自己机器上的服务也发生故障。
-- Leslie Lamport

我们虽然可以预见所有在控制范围内的系统的状态,但也总是会依赖于外部系统,这些外部系统的行为我们不可能全都知道。

第三方系统在自己的生产环境中的行为总是和在它与其他环境集成的大环境中的行为有所不同。这进一步强调了在生产环境中进行实验的必要性,生产环境才是你的系统与第三方系统进行真实交互的唯一场所。

生产环境变更

在 Netflix ,我们的系统一直在不断更新。工程师的自动脚本每天都在通过不同的方式更新着系统。

如果将系统的概念扩展,使其包括这些生产环境中的变更,那么很明显在测试环境中想要模拟这些系统行为是非常困难的。

外部有效性

“外部有效性”:这个实验的结果是否能概括我们真正感兴趣的现象?或者我们测量结果的产品运行环境是否是专门为测试而准备的?

如果不直接在生产环境中运行混沌工程实验,那么我们在本章讨论的问题(状态、输入、第三方系统、生产环境变更)就都是混沌工程实验的外部有效性的潜在威胁。

不愿意实践混沌工程的说辞

  • 我们确定它会宕机

    你应该对系统的弹性具备一定信心的时候再进行混沌工程实验。混沌工程的一个主要目的是识别系统中的薄弱环节。如果你已经看到了明显的薄弱环节,那么你应该首先专注于提高系统在这一点上的弹性。当你确信系统有足够的弹性时,就可以开始进行混沌工程实验了。

  • 如果真的宕机了,我们就会有大麻烦

    通过下面两个策略来将潜在的影响范围最小化:

    • 允许快速终止实验
    • 将实验造成的爆炸半径最小化

离生产环境越近越好

即使你不能在生产环境中进行实验,也要尽可能地在离生产环境最近地环境中进行。越接近生产环境,对实验外部有效性地威胁就越少,对实验结果的信心就越足。

记住:为了保障系统在未来不会大规模中断,冒一点可控的风险是值得的。

第6章 自动化实验以持续运行

自动化是最长的杠杆。

自动执行实验

手动执行一次性的实验是非常好的第一步。

在成功执行实验后,下一步就是将这个实验自动化,让其持续运行。

如果一个实验不是自动化的,那么就可以将这个实验废弃。

当今系统的复杂性意味着,我们无法先验地知道,生产环境的哪些变动会改变混沌工程实验的结果。基于这个原因,我们必须假设所以变动都会改变实验结果。生产环境实际上处在一个无时不变的状态,这导致的结果就是,对实验结果的信心是随着时间而衰减的。

在理想情况下,实验应该随着每次的变化而执行,这有点像混沌金丝雀。

不能也不应该让工程师牺牲开发速度,专门花时间来手动定期执行混沌工程实验。相反地,我们要自己投入精力来开发混沌工程的工具和平台,以期不断降低创建新实验的门槛,并使这些实验能够完全自动运行。

2016年底,Netflix 发布了混沌工程自动化平台( Chaos Automation Platform ),简称 ChAP 。

自动创建实验

我们认为还可以追求一个更好的自动化水平:自动设计实验。

一个值得关注的关于自动创建实验的研究是一项叫做正确路径驱动的故障注入( Lineage-Driven Fault Injection, LDFI )的技术,由加州大学圣克鲁兹分校的 Peter Alvaro 教授开发。LDFI 可以识别出可能导致分布式系统故障的错误事件组合,其工作原理是通过推断系统正常情况下的行为来判断需要注入的候选错误事件。

第7章 最小化爆炸半径

每一次的混沌工程实验的确具备导致生产环境崩溃的风险。混沌工程师的一项专业职责就是要理解和降低生产风险,可以为实验设计良好的系统,以阻止大规模的生产事故,将受影响的用户数量降到最少。

不幸的是,我们经常运行本来只影响一小部分用户的测试,却由于级联故障无意中影响了更多的用户。

能带来最大信心的实验也是风险最大的,是对所有生产流量都有影响的实验。而混沌工程实验应该只承受可以衡量的风险,并采用递进的方式,进行的每一步实验都在前一步的基础之上。这种递进的方式不断增加我们对系统的信心,而不会对用户造成过多不必要的影响。

  • 最小风险的实验只作用于很少的用户。
  • 在自动化实验成功之后,下一步就是运行小规模的扩散实验。
  • 接下来是进行小规模的集中实验,通过修改路由策略将所有实验的用户流量导向特定的节点。
  • 风险最大但最精准的实验是无自定义路由的大规模实验。

除了不断扩大实验范围,在实验造成过多危害时及时终止实验也是必不可少的。

我们强烈建议实施自动终止实验。

为了尽可能高效地应对实验发生不可预料的情况,我们要避免在高风险的时间运行实验。

每次只检查一个可控的故障。

第三部分 混沌工程实践

第8章 设计实验

  1. 选定假设

  2. 设定实验的范围

    两个原则:

    1. 在生产环境中运行实验
    2. 最小化爆炸半径

    我们提倡将第一个实验的范围控制的越小越好。而且在生产环境中执行实验之前,应该先在测试环境中试一试。一旦进入生产环境,就一定要从最小的用户流量开始尝试。

  3. 识别出要监控的指标

    要尽可能基于指标来验证假设。

    一旦实验产生的影响比预料中的严重,你就应该做好立即终止实验的准备。可以预先设定好明确的阈值,这可以帮你快速做出决定。

  4. 在组织内沟通到位

    当你第一次在生产环境中执行混沌工程实验时,你需要通知所在组织中的其他成员:你将要做什么,为什么要这么做,以及什么时间要做。

  5. 执行实验

    要盯住那些指标,因为你可能随时需要终止实验。随时终止实验的能力异常关键,因为现在是直接在生产环境中运行实验。

    要确保有足够的报警机制,能实时获悉这些关键指标是不是掉到了阈值以下。

  6. 分析实验结果

    试验结束后,用“选定假设”一步中指定的指标数据来验证之前的假设是否成立。

    要确保把实验结果反馈给所有相关的团队,一同从整体的角度来消除隐患。

  7. 扩大实验范围

    当你从小范围实验中获得了信心之后,就可以逐步扩大实验范围了。扩大实验范围的目的是进一步暴露小范围实验无法发现的一些问题。

  8. 自动化实验

    当你有信心手动执行混沌工程实验之后,就可以开始周期性地自动化运行实验,持续从中获得更大的价值。

第9章 混沌工程成熟度模型

我们将混沌工程的定义标准化的一个目的是,在执行混沌工程项目时,可以有标准判断这个项目做的是好是坏,以及如何可以做得更好。

混沌工程成熟度模型(CMM)给我们提供了一个评估当前混沌工程项目成熟度状态的途径。

CMM 的两个坐标轴分别是“熟练度”和“应用度”。缺乏熟练度的时候,实验会比较危险、不可靠,且有可能是无效的。缺乏应用度的时候,所做的实验就不会有什么意义和影响。要发挥混沌工程项目的最大作用都需要在这两个维度上保持一定的平衡。

熟练度

熟练度可以反映出,组织中混沌工程项目的有效性和安全性。

  • 入门

    • 未在生产环境中运行实验。
    • 全人工流程。
    • 实验结果反映系统指标,而不是业务指标。
    • 对实验对象注入一些简单事件,如“关闭节点”。
  • 简单

    • 用复制的生产环境中的流量来运行实验。
    • 自助式创建实验,自动运行实验,手动监控和停止实验。
    • 实验结果反映聚合的业务指标。
    • 对实验对象注入较高级的事件,如网络延迟。
    • 实验结果是手动整理的。
    • 实验的定义是静态的。
    • 具有可以支持对历史实验组和控制组进行比较的工具。
  • 高级

    • 在生产环境中运行实验。
    • 自动分析结果,自动终止实验。
    • 实验框架和持续发布工具集成。
    • 在实验组和控制组之间比较业务指标差异。
    • 对实验组引入一些事件,如服务级别的影响和组合式的故障。
    • 持续收集实验结果。
    • 具有可以交互式地比对实验组和控制组的工具。
  • 熟练

    • 在开发流程中的每个环节及所有环境中运行实验。
    • 设计、执行和终止实验完全自动化。
    • 将实验框架和A/B测试以及其他测试工具集成,以减少噪声干扰。
    • 可以注入如对系统的不同使用模式、返回结果和状态的更改等类型的事件。
    • 实验具有动态可调整的范围以找寻系统拐点。
    • 实验结果可以用来预测收入损失。
    • 对实验结果的分析可以用来做容量规划。
    • 实验结果可以用来区分服务实际的关键程度。

应用度

应用度用来衡量混沌工程实验覆盖的广度和深度。应用度越高,暴露的脆弱点就越多,我们对系统的信心也就越充足。

  • “暗中进行”

    • 对重要项目不采用。
    • 只覆盖少量系统。
    • 组织内部基本没有感知。
    • 早期使用者偶尔进行混沌工程实验。
  • 适当投入度

    • 实验获得正式批准。
    • 工程师兼职进行混沌工程实验。
    • 多个团队有兴趣并参与进来。
    • 一些重要服务会不定期地进行混沌工程实验。
  • 正式采用

    • 有专门的混沌工程团队。
    • 所有故障的复盘都会进入混沌工程框架来创建回归实验。
    • 定期对大多数关键服务进行混沌工程实验。
    • 偶尔进行实验性的故障复盘验证,例如通过“比赛日”的形式来做。
  • 成为文化

    • 对所有关键服务进行高频率的混沌工程实验。
    • 对多数非关键服务进行高频率的混沌工程实验。
    • 混沌工程实验是工程师入职流程的一部分。
    • 所有系统组件都默认要参与混沌工程实验,不参与的话需要进行特殊说明。

绘制成熟度模型图

绘制成熟度模型时,以熟练度作为纵轴,应用度作为横轴。

CMM 可以帮助我们理解混沌工程项目处在什么状态,建议我们应该聚焦在哪方面进行持续提升,同时给我们指出改进的方向。

第10章 结论

我们相信,在任何一个开发和运行复杂分布式系统的组织机构里,如果既想拥有高开发效率,又想保障系统具有足够的弹性,那么混沌工程一定是必备的方法。

混沌工程目前还是一个非常年轻的领域,相关的技术和工具也都在逐步发展中。我们热切地期望各位读者可以加入社区,和我们一起不断实践、拓展混沌工程。

一些资源

参考文章:

  • Fault Injection in Production: Making the Case for Resiliency Testing
  • Inside Azure Search: Chaos Engineering
  • Organized Chaos With F#
  • Chaos Engineering 101
  • Meet Kripa Krishnan, Google's Queen of Chaos
  • Facebook Turned Off Entire Data Center to Test Resiliency
  • On Designing And Deploying Internet-Scale Services

另外,还有很多为不同场景开发的开源工具:

  • Simoorg
  • Pumba
  • Chaos Lemur
  • Chaos Lambda
  • Blockade
  • Chaos-http-proxy
  • Monkey-Ops
  • Chaos Dingo
  • Tugbot

也有一些书籍讲到关于混沌工程的一些主题:

  • Drift Into Failure, Sidney Dekker (2011)
  • To Engineer Is Human: The Role of Failure in Successful Design, Henry Petroski (1992)
  • Searching for Safety, Aaron Wildavsky (1988)