zoukankan      html  css  js  c++  java
  • Netflix 混沌工程手册

    https://www.infoq.cn/theme/13

    Netflix混沌工程手册Part 1:混沌工程简介

    本文翻译自Netflix工程师合著的 Chaos Engineering一书。这本书介绍了混沌工程的主要概念,以及如何在组织中实践这些概念和经验。也许我们开发的相关工具只适用于Netflix自身的业务和系统环境,但我们相信工具背后的原则可以更广泛地应用于其他领域。

    InfoQ 将就这一专题持续出稿,感兴趣的同学可以持续关注。

     

    译者序

     

    近年来,随着服务化、微服务和持续集成的逐渐普及,从开发到线上的便捷性大幅提高。我们在使用这些便利性所带来的好处的同时,对其负面性的问题也要有所认知。尤其是在一个复杂的分布式服务体系中,故障发生的随机性和不可预测性都大大增加了。快速迭代的门槛越来越低,但是对复杂系统稳定性的考验却在成倍增长。在这条路上,各家都有自己的经验实践。

     

    Chaos Engineering 混沌工程从出现到标准化成为一门学科,是伴随着 Netflix 过去三年多时间里同稳定性持续战斗的历程一起成长起来的,这是每一次故障带来的深度思考,抽象而成的理论和实践的结合。混沌工程是一门相对高级的系统稳定性治理方法论,它提倡采用探索式的研究实验,发现生产环境中的各种风险。为什么说相对高级,因为成功实施混沌工程,要求对现有系统的弹性有一定信心。

     

    前言

     

    混沌工程是一门新兴的技术学科,他的初衷是通过实验性的方法,让人们建立对于复杂分布式系统在生产中抵御突发事件能力的信心。

    ——混沌工程原则

     

    只要你有过在生产环境中实际运行过分布式系统的经历,你就应该清楚,各种不可预期的突发事件一定会发生。分布式系统天生包含大量的交互、依赖点,可以出错的地方数不胜数。硬盘故障、网络不通、流量激增压垮某些组件,我们可以一直列举下去。这都是每天要面临的常事儿,处理不好就会导致业务停滞,性能低下,或者是其他各种无法预期的异常行为。

     

    在复杂的分布式系统中,人力并不能够阻止这些故障的发生,我们应该致力于在这些异常行为被触发之前,尽可能多地识别出会导致这些异常的,在系统中脆弱的,易出故障的环节。当我们识别出这些风险,我们就可以有针对性地进行加固,防范,从而避免故障发生时所带来的严重后果。我们能够在不断打造更具弹性(弹性:系统应对故障、从故障中恢复的能力)系统的同时,树立运行高可用分布式系统的信心。

     

    混沌工程正是这样一套通过在系统基础设施上进行实验,主动找出系统中的脆弱环节的方法学。这种通过实证的验证方法显然可以为我们打造更具弹性的系统,同时让我们更透彻的掌握系统运行时的各种行为规律。

     

    实践混沌工程可以简单如在 STG 环境的某个实例上运行 kill -9 来模拟一个服务节点的突然宕机,也可以复杂到在线上挑选一小部分(但足够代表性)的流量,按一定规则或频率自动运行一系列实验。

     

    混沌工程在Netflix的发展历程

    2008年Netflix开始从数据中心迁移到云上,之后就开始尝试在生产环境开展一些系统弹性的测试。过了一段时间这个实践过程才被称之为混沌工程。最早被大家熟知的是“混乱猴子”(Chaos Monkey),以其在生产环境中随机关闭服务节点而“恶名远扬”。进化成为“混乱金刚”(Chaos Kong)之后,这些之前获得的小收益被无限扩大。规模的扩大得益于一个叫做“故障注入测试”(Fault Injection Test,FIT)的工具。我们随后确立了混沌工程的若干原则,用以将这个实践规范的学科化 ,同时我们推出了混沌工程自动化平台,能够在微服务体系架构上,24*7不间断地自动运行混沌工程实验。

    在开发这些工具和实践的过程中,我们逐渐意识到,混沌工程并非是简单的制造服务中断等故障。当然,尝试破坏系统和服务很简单,但并不是全都可以有建设性、高效地发现问题。混沌工程的意义在于,能让复杂系统中根深蒂固的混乱和不稳定性浮出表面,让我们可以更全面地理解这些系统性固有现象,从而在分布式系统中实现更好的工程设计,不断提高系统弹性。

     

    1. 为什么需要混沌工程

     

    混沌工程是一种通过实证探究的方式来理解系统行为的方法。就像科学家通过实验来研究物理和社会现象一样,混沌工程通过实验来了解特定的系统。

     

    实践混沌工程是如何提高系统弹性的呢?它通过设计和执行一系列实验,帮助我们发现系统中潜在的、可以导致灾难的、或让用户受损的脆弱环节,推动我们主动解决这些环节。相比现在各大公司主流的被动式故障响应流程,混沌工程向前迈进了一大步。

     

    1.1 混沌工程和测试的区别

     

    混沌工程,故障注入 FIT 和故障测试在侧重点和工具集上有一些重叠。举个例子,在 Netflix 的很多混沌工程实验研究的对象都是基于故障注入来引入的。混沌工程和这些其他测试方法的主要区别在于,混沌工程是发现新信息的实践过程,而故障注入则是对一个特定的条件、变量的验证方法。

     

    例如当你希望探究复杂系统如何应对异常时,对系统中的服务注入通信故障,如超时,错误等,这是一个故障注入的典型场景。但有时我们希望探究更多其他的非故障类的场景,如流量激增、资源竞争条件、拜占庭故障(例如性能差或有异常的节点发出有错误的响应、异常的行为、对调用者随机性的返回不同的响应,等等)、非计划中的或非正常组合的消息处理,等等。因为如果一个面向公众用户的网站突然收到激增的流量,从而产生更多的收入,我们很难称之为故障,但我们仍然需要探究清楚系统在这种情况下会如何变现。和故障注入类似,故障测试方法通过对预先设想到的可以破坏系统的点进行测试,但是并没能去探究上述这类更广阔领域里的、不可预知的、但很可能发生的事情。

     

    我们可以描述一下测试和实验最重要的区别。在测试中,我们要进行断言:即给定一个特定的条件,系统会输出一个特定的结果。测试一般来说只会产生二元的结果,验证一个结果是真还是假,从而判定测试是否通过。严格意义上来说,这个实践过程并不能让我们发掘出系统未知的或尚不明确的认知,它仅仅是对我们已知的系统属性可能的取值进行测验。而实验可以产生新的认知,而且通常还能开辟出一个更广袤的对复杂系统的认知空间。这整本书我们都是在探讨这个主题——混沌工程是一种帮助我们获得更多的关于系统的新认知的实验方法。它和已有的功能测试、集成测试等以测试已知属性的方法有本质上的区别。

     

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

     

    • 模拟整个云服务区域或整个数据中心故障;

    • 跨多实例删除部分Kafka topic来重现生产环境中发生过的问题;

    • 挑选一个时间段,和针对一部分流量,对其涉及的服务间调用注入一些特定的延时;

    • 方法级别的混乱(运行时注入):让方法随机抛出各种异常;

    • 在代码中插入一些指令可以允许在这些指令之前运行故障注入;

    • 强制系统节点间的时间不同步;

    • 在驱动程序中执行模拟I/O错误的程序;

    • 让某个Elasticsearch集群CPU超负荷。

     

    混沌工程实验的可能性是无限的,根据不同的分布式系统架构和不同的核心业务价值,实验可以千变万化。

     

    1.2 混沌工程绝不仅是 Netflix 的专属

     

    我们在和其他公司或组织的专业人士讨论混沌工程时,经常收到的一个反馈是,“哇哦,听起来非常有意思,但是我们的系统功能和业务与 Netflix 完全不同,所以这东西应该不适合我们。”

     

    虽然我们提供的案例都来自于 Netflix 的经验,但是书中所描述的基本原则并不针对任何特定的组织,所介绍的设计实验的指导也没有基于任何特定的架构或者工具集。后面,我们会讨论混沌工程成熟度模型,希望评估一下自身为什么,在什么时间点,以什么方式进行混沌工程实践的读者可以采用。

     

    像最近的一次混沌工程社区日(一个来自不同组织的混沌工程实践者的聚会),参会者来自 Google,Amazon,Microsoft,Dropbox,Yahoo!,Uber,cars.com,Gremlin Inc.,加州大学圣克鲁兹分校,SendGrid,北卡罗莱纳州立大学,Sendence,Visa,New Relic,Jet.com,Pivotal,ScyllaDb,GitHub,DevJam,HERE,Cake Solutions,Sandia National Labs,Cognitect,Thoughtworks,and O’Reilly 出版社。在本书里,你会看到来自各行各业(金融,电商,航空航天,等等)的关于混沌工程实践的案例和工具。

     

    混沌工程也同样适用于传统行业,如大型金融机构,制造业和医疗机构。交易依赖复杂系统吗?有大型银行正在使用混沌工程来验证交易系统是否有足够的冗余。是否有人命悬一线?在美国,混沌工程在许多方面被当做模型应用在了临床试验系统中,从而形成了美国医疗验证的黄金标准。横跨金融、医疗、保险、火箭制造、农业机械、工具制造、再到数字巨头和创业公司,混沌工程正在成为复杂系统改进学科的立足点。

     

    飞机启动失败?

    在伊利诺伊大学的香槟分校,Naira Hovakimyan和她的研究团队把混沌工程用在了喷气式战斗机上。团队由两名B-52飞行员、一名F-16飞行员、两名飞行测试工程师和两名安全飞行员组成。在试飞过程中,飞机被注入了几种不同的故障配置。这些配置甚至包括飞机重心突然变化和空气动力学参数变化!这给团队能否在故障发生时重新构建机体爬升动力学参数,以及应对其他会导致故障的配置都带来了极大的挑战。在制定并亲身实践了一系列故障情景之后,团队最终能够自信地认定该系统对于低空飞行是安全的。

     

    1.3 混沌工程的前提条件

     

    在判断你的组织是否已经准备好实施混沌工程之前,需要回答这样一个问题:你的系统是否已经具备一些弹性来应对真实环境中的一些异常事件,像某个服务异常、或网络闪断、或瞬间延迟提高这样的事件。

     

    如果你的答案是明确的“No”,那么在实施本书中讨论的各项原则之前,你需要先做一些准备工作。混沌工程非常适合于揭示生产系统中未知的脆弱环节,但如果你很确定混沌工程实验会导致系统出现严重的故障,那运行这样的实验是没有任何意义的。你需要先解决这个问题,然后再回到混沌工程,然后你不仅能继续发现更多不知道的脆弱点,还能提高对系统真实弹性水平的信心。

     

    混沌工程的另一个前提条件是监控系统,你需要用它来判断系统当前的各项状态。如果没有对系统行为的可见能力,就无法从实验中得出有效的结论。由于每个系统都是独一无二的,对于如何针对混沌工程揭示出的脆弱环节进行根本原因分析,我们留给读者作为练习。

     

    混乱猴子(Chaos Monkey)

    2010年底,Netflix向全世界推出了“混乱猴子”。这家流媒体服务提供商在之前的几年开始迁移到云上。之前的数据中心垂直扩展给Netflix带来过很多单点故障,其中一些故障甚至大规模中断了当时的DVD业务。云服务不光带来了水平扩展的机会,同时可以把重度的基础设施运维工作转移到可靠的第三方。

    数据中心本就会时不时的发生一些小故障,然而到了云服务的水平扩展架构中,提供同一个服务的节点数大幅增加,发生这类故障的几率也大幅增加。数以千计的服务节点里,时不时就会有节点出现异常或者掉线。所以需要有一种全新的方法,既可以保留水平扩展带来的好处,同时又有足够的弹性来随时应对节点故障。

    在Netflix,并没有制度要求工程师一定要按照某种规定来构建任何东西。相反,高效的领导者在工程师之间建立强有力的一致规约或原则,然后让工程师在自己的领域里找到解决问题的最好办法。在节点随时会发生故障的案例里,我们要建立的强力规约和原则就是,开发的服务要具备在单一节点突然掉线的情况下还能持续提供端到端服务的能力。

    混乱猴子在业务正常进行的时间段内,伪随机地关闭生产环境中正在运行中的节点,而且关闭的频率比正常节点故障频率还要高很多。通过高频率的触发这些不常见的且具备灾难性的事件,我们给了工程师强大的动力在开发他们的服务时必须考虑到如何轻松应对这类事件。工程师必须尽可能提早并快速地处理这类故障。再加上自动化、冗余、回滚策略,以及其他弹性设计的最佳实践,工程师很快就可以让自己的服务在这些故障发生时也能保持正常运行。

    经过几年的发展,混乱猴子逐渐变得更强大,现在它可以指定终止一组节点,并且通过与Spinnaker(持续发布平台)集成进行自动的线上实验。但从根本上它还是提供2010年以来一样的功能。

    混乱猴子最大的成就在于让我们的工程师之间形成了构建具备足够弹性服务的规约和原则。现在它已经是Netflix工程师文化中不可或缺的一部分了。在过去五年左右的时间里,仅有一次节点掉线影响了我们的服务。当时正巧混乱猴子终止了一个由于部署失误而没有冗余的服务节点,因而造成了问题。幸运的是,这个故障发生在白天工作时间,这个故障的服务刚刚部署不久,对用户的影响也非常小。可想而知,如果这个服务一直在线上运行了几个月,混乱猴子在某个周末的晚上终止了它的节点,且负责该服务的工程师没有在on-call的情况下,将造成多大的灾难。

    混乱猴子的美妙之处就在于此,它能尽可能地将服务节点失效的痛苦提到最前,同时让所有工程师在构建具有足够弹性应对失败的系统上,达成一致的目标。

     

    2. 管理复杂性

     

    复杂性对工程师来说既是挑战也是机遇。你需要一支技术纯熟,同时有足够应变能力的团队,来成功管理和运行一套包含许多组件和交互的分布式系统。在这样的复杂系统中充满了创新和优化的机会。

     

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

     

    性能

     

    ​ 在这里特指对延迟或资源成本的最小化。

     

    可用性

     

    ​ 系统正常响应和避免停机的能力。

     

    容错能力

     

    ​ 系统从非正常状态中恢复的能力。

     

    通常一个有经验的团队会同时针对这三个方面进行优化。

     

    在 Netflix,工程师们还会考虑第四个方面:

     

    新功能开发的速度

     

    指工程师可以把新功能,创新功能提供给用户的速度。

     

    Netflix 在软件工程中的决策过程中,非常鼓励端到端的功能开发速度,而不仅仅是快速部署本地功能。在以上四者中找到平衡的过程,可以为架构选型的决策提供必要的信息。

     

    在充分考虑了这些方面之后,Netflix 选择采用微服务架构。但我们要记住康威定律:

     

    任何组织在设计一套系统(广义概念)时,所交付的设计方案在结构上都与该组织的通信结构保持一致。

    Melvin Conway, 1967

     

    在微服务架构中,各团队彼此独立开发和运营自己的服务。每个团队可以自行决定何时将代码送入生产环境。这个架构策略以沟通协调为代价来提高新功能开发速度,通常工程组织被划分为许多个这样的小型团队。我们希望这些小团队的特点是松散耦合(即没有太多的组织架构限制,而是强调小团队之间的协作)和高度协调(即每个人都能看到更全面更大的全景,从而明确他们的工作是如何有助于和其他团队一起实现更大的目标)。小团队之间的有效沟通是成功实施微服务架构的关键。混沌工程适时的提供了系统弹性的验证能力,来支持快速功能开发,实验,以及团队对系统的信心。

     

    2.1 理解复杂系统

     

    想象一个向消费者提供产品信息的分布式系统。如下图,这个服务由 7 个微服务组成,从 A 到 G。A 服务存储了用户的个人信息。B 服务存储用户的登录账户信息,如用户上一次登录的时间和访问了什么信息。C 服务是关于产品信息的。D 服务作为 API 网关处理所有来自外部的接口访问。

     

     

    图 1 微服务架构

     

    来看一个请求的例子,用户通过手机 App 访问了一些信息:

     

    • 请求首先进入D服务,即API服务;

    • D服务本身并没有所有需要的信息,所以它进一步请求C服务和F服务以获取必要的数据;

    • C服务和F服务也同时都需要更多的信息来满足请求,于是C服务请求A服务,F服务请求了B服务和G服务;

    • A服务也需要访问B服务,B服务需要访问E服务,同时G服务也需要访问E服务;

    • 对D服务的一个请求,扩散到了整个微服务架构里。而且当所有依赖的服务没有返回或者超时之前,API不会向手机App返回响应。

     

    这样的请求模式非常常见,而且在有一定规模的系统中这类交互的数量要大得多。相比紧密耦合的单体应用来说,有趣的是传统架构师的角色被显著削弱了。传统架构师的角色更多是负责理解系统中各个组成部分是如何组成整个系统的,以及他们之间是如何有效交互的,然而在一个大型分布式系统中人类难以胜任这个角色。太多的组件,频繁的改动和革新,无数非计划中的组件交互,人类是不可能把这些内容全都放在脑中。微服务架构给我们带来了开发速度和灵活性的提升,代价却是牺牲了我们的掌控性和可理解性。这个缺失恰好为混沌工程创造了机会。

     

    其实在任何一个复杂系统中都是这样,即使是一个单块系统,在它变得越来越大,依赖越来越多的时候,也不会有一个架构师可以理解添加一个新的功能对整个系统意味着什么。也许有个非常有趣的例外就是有一类系统的设计原则里本来就不会考虑可理解性,例如深度学习,神经网络,遗传进化算法和其他机器智能算法。如果人类揭开这些算法的盖子来看看他们的内部构造时,一系列的权重和非无效解产生的浮点数对个人理解来说就太困难了。只有系统整体发出的响应才能被人类所理解。整个系统应该具有意义,而系统的任何子部分都不需要有意义。

     

    在一个请求/响应的过程中,意大利面条式的调用图代表了典型的,需要混沌工程关注的系统固有混乱。传统测试,如单元测试,功能测试,集成测试,在这里是不够的。传统测试只能告诉我们正在测试的系统中的某个属性的断言是真还是假。但现在我们需要更进一步发现会影响系统行为的更多未知属性。也许一个基于真实事件的例子有助于说明这个不足。

     

    2.2 系统复杂性示例

     

    E 服务包含提供用户定制化体验的信息,例如预测用户下一个动作,用以在手机 App 上展示相应的选项。一个用于展示这些选项的请求也许会先去 A 服务获取用户的信息,然后去服务 E 获取用于个性化的信息。

     

    现在我们先对这些微服务是如何设计和运行的做一些合理的假设。由于请求数量很大,我们采用一个固定的散列函数把用户请求均衡分散开,这样一个固定的用户只会由一个特定的节点服务,而不是所有 A 服务的节点都面对整个用户群。例如在 A 服务背后的数百个节点中,所有来自用户“CLR”的请求只会被路由到节点 A42。如果 A42 出现任何问题,足够智能的路由逻辑会将 A42 的职责路由到集群中的其他节点。

     

    如果下游所依赖的服务出现异常,A 服务有合理的预案。如果 A 服务无法和持久层通信,它就从本地缓存中返回结果。

     

    在运行时,每个微服务会平衡监控、报警和资源的考量,以合理的兼顾性能和对内部的洞察,而不会对资源的利用率不管不顾。扩展规则会基于 CPU 负载和 I/O 性能来决定是否在资源稀缺时加入更多节点,以及在资源闲置时去掉多余的节点。

     

    现在我们的环境就绪了,让我们来看看请求的模式。用户“CLR”启动了手机 App 应用,然后发送了一个请求来获取内容丰富的 App 首页。不巧的是,他的手机目前并不在服务区。用户并不知道自己不在服务区,于是他发出了多次对首页的请求,这些请求都被手机操作系统缓存在了本地队列,以等待网络连接恢复后发出。App 本身也有重试机制,它在操作系统的本地队列之外,也将这些请求缓存在了 App 自身的队列中。

     

    突然网络连接恢复了。手机操作系统同时把数百个请求一次性的发送出去。因为用户“CLR”发起的是对首页的请求,所以 E 服务被同时请求了数百次以获取和用户个性化体验相关的信息。每一个对 E 服务的请求都会先请求 A 服务。于是 A 服务同时被打开首页和其他服务(如 E 服务)请求了数百次。由于 A 服务的架构设计,所有“CLR”的请求都被路由到了节点 A42。A42 在这么大流量下无法对所有请求都从持久层获取数据,所以它切换到从本地缓存中获取数据。

     

    从缓冲中响应请求大大减少了为每个请求提供服务所需的处理时间和 I/O 开销。事实上,A42 的 CPU 和 I/O 突然降到很低的水平,以至于其负载平均值低于集群扩展策略的回收阈值。于是策略考虑资源的有效利用,对集群进行了收缩,对 A42 进行了回收,同时将流量转发到了集群的其他节点。其他节点这时就需要额外的处理本属于 A42 需要做的工作。比如 A11 接管了来自“CLR”的请求。

     

    在 A42 转移到 A11 的过程中,E 服务中对 A 服务的请求超时了。于是 E 服务为了自身能应对上游的请求,启动了它的预案,返回了一些不含个性化的内容。

     

    用户“CLR”最终收到了响应,注意到内容不像平时那样个性化,于是多刷新了几次首页。A11 比平时需要处理的工作更多了,所以它也逐渐切换到从缓存中返回一些稍稍过时的信息。CPU 和 I/O 相应的降低了,再一次提示集群可以收缩了。

     

    其他一些用户也逐渐注意到他们的 App 展示给他们的内容不像往常那样个性化了。他们也开始不断刷新内容,这又触发了更多的对 A 服务的请求。额外的流量压力致使 A 服务中更多的节点选择从缓存中返回信息,CPU 和 I/O 进一步相应降低,集群进一步加速收缩。更多的用户注意到了问题,触发了用户引起的重试风暴。最终,整个集群都开始从缓存中返回信息,重试风暴压垮了其余节点,A 服务掉线了。B 服务对于 A 服务掉线没有预案,于是进一步拖垮了 D 服务,于是整个服务基本上都中断了。

     

    2.3 从例子中学到了什么

     

    上面的场景在系统理论中被称为“牛鞭效应”。输入中的一点扰动会触发一个自我强化的循环,最终导致输出结果的剧烈波动。在上面的例子中,输出的波动拖垮了整个应用。

     

    上述例子中最重要的一个特征是每一个单一的微服务的行为都是合理的,只有在特定场景下这些行为组合起来才导致了系统预期之外的行为。这一类交互的复杂性不是人力可以完全预期到的。每一个微服务都可以被全面的测试覆盖,但我们任然不能在任何测试场景或集成测试环境中看到这类行为。

     

    期待任何人类的架构师能理解这些组件和组件的交互模式,从而能充分预测这些预期之外的系统效应,都是不合理的。而混沌工程提供了可以让这些效应浮出水面的工具,从而让我们建立对复杂分布式系统的信心。有了这个信心,我们就可以为这些又庞大又充满迷雾,无法被某一个个人全部理解的系统设计有效的架构,同时兼顾功能开发的速度。

     

    混乱金刚(Chaos Kong)

    在混乱猴子(Chaos Monkey)成功的基础之上,我们决定继续深入。混乱猴子的功能是关闭节点,而混乱金刚是用来关闭整个AWS区域。

    Netflix视频的每一个字节都来自于CDN。在峰值的时候,我们贡献了大约北美互联网三分之一的流量。这是世界上最大的CDN,它同时也包含着许许多多“令人着迷”的工程问题,我们先把混沌工程问题放在一旁,现在先聚焦在Netflix其他的一些服务上,我们称之为Netflix服务的“控制平面”(Control Plane)。(这类服务类似于路由器中执行Routing功能的部分。)

    除了视频流媒体来自CDN之外,所有其他与服务的交互都是由AWS云服务的三个区域提供的。在数千种我们支持的设备上,从2007年的蓝光播放器一直到最新款的智能手机,云上应用处理全流程的服务,包括启动、注册、浏览、选择视频、播放,播放时的心跳检测,等等。

    2012年的圣诞节期间,AWS的一个单一区域发生了严重的中断故障,这个事故迫使我们必须尽快采取多区域的策略。如果你对AWS区域不太了解,可以把它们想象为多个数据中心。通过多区域的故障恢复策略,我们可以把所有用户从故障的区域转移到另一个,最大限度地控制单个区域中断造成影响的范围和时长。

    这项工作需要负责微服务架构的多个团队之间进行沟通协调。我们在2013年底完成了混乱金刚,我们打算用它来关闭整个AWS区域。这个硬性的强制促进了工程师们建造可以在AWS区域间平滑过渡服务的一致目标。这里我们只是模拟整个区域中断的故障,毕竟我们没有权限把整个AWS区域真正中断掉。

    当我们认为已经为跨区域故障恢复做好了准备时,就开始了每个月一次的混乱金刚演习。在第一年,我们经常会发现故障恢复中各种各样的问题,这些问题给了我们大量的空间进行改进。到第二年我们已经可以非常平滑地进行演习。我们现在已经能够非常规律地进行演习,确保服务时刻具备应对整个区域中断故障的弹性,无论这类故障是基础设施导致的还是自己的软件问题导致的。

     

    以上是本书的第一部分,接下来的第二部分将会介绍混沌工程原则,有兴趣的同学可以持续关注。

     

    Netflix混沌工程手册Part 2:混沌工程原则

    本文翻译自Netflix工程师合著的 *Chaos Engineering*一书。这本书介绍了混沌工程的主要概念,以及如何在组织中实践这些概念和经验。也许我们开发的相关工具只适用于Netflix自身的业务和系统环境,但我们相信工具背后的原则可以更广泛地应用于其他领域。

    InfoQ 将就这一专题持续出稿,感兴趣的同学可以持续关注。

    本文略长,共计1.3万字,预计阅读时间35分钟。

     

    混沌工程原则

     

    优化复杂系统的性能通常需要在混乱的边缘进行,即系统行为将要变得混乱且无迹可寻之前。

    Sydney Dekker,《陷入失败》

     

    “混乱”一词让我们想起随机性和无序。然而,这不意味着混沌工程的实施也是随机和随意的,也不意味着混沌工程师的工作就是引发混乱。相反,我们把混沌工程视为一种学科,一种实验学科。

     

    在上面的引用中,Dekker 观测了分布式系统的整体行为,他也主张从整体上了解复杂系统是如何失效的。我们不应该仅仅着眼于发生故障的组件,而是应该尝试去理解,例如组件交互中的一些偶发意外行为,最终如何导致系统整体滑向不安全,不稳定的状态。

     

    你可以将混沌工程视为一种解决“系统离混乱的边界有多远” 的经验方法。从另一个角度去思考,“如果我们把混乱注入到系统里,它会怎么样?”

     

    在这一部分,我们会介绍混沌工程实验的基本设计,之后我们会讨论一些更高级的原则。这些原则建立在真实实践混沌工程的大规模系统之上。在实践混沌工程的过程中,并不是必须遵照所有高级原则,但我们发现,运用的原则越多,你对系统弹性的信心就越充足。

     

    实验

     

    在大学里,电气工程专业的学生必须学习一门“信号和系统”的课程,在这个课程中他们学习如何使用数学模型来推理电气系统的行为。其中一门需要掌握的技术被称为拉普拉斯变换。你可以用拉普拉斯变换,将整个电路的行为用一个数学函数表达,我们称之为传递函数。传递函数描述的是系统在受到脉冲时如何响应,输入信号包含所有可能的输入频率的总和。一旦你有了一个电路的传递函数,就可以预测它在受到所有可能的输入信号时会如何响应。

     

    软件系统里并没有类似的传递函数。像很多复杂系统一样,我们无法为软件系统表现出的各种行为建立一个预测模型。如果我们有这样一个模型,可以推导出一次网络延迟骤升会给系统带来什么影响,那样就太完美了。但不幸的是,迄今为止我们并没有发现这样一个模型。

     

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

     

    但是,我们肯定不会给系统随机的不同的输入。我们在系统性分析之后,期望可以最大化每个实验可以获得的信息。正如科学家通过实验来研究自然现象一样,我们通过实验来揭示系统的行为。

     

    FIT 故障注入测试

    分布式系统的经验告诉我们,各种系统问题基本都是由预期外的事件或不良的延迟导致的。2014年初,Netflix开发了一个名为FIT的工具,意思是故障注入测试(Failure Injection Testing)。这个工具能让工程师在访问服务的一类请求的请求头中注入一些失败场景,当这些注入了失败场景的请求在系统中流转时,微服务中被注入的故障锚点会根据不同的失败场景触发相应的逻辑。

    例如,我们要测试系统在某个保存用户数据的微服务中断时的弹性能力。我们预计系统中某些服务不会如预期运行,但诸如重放等基本功能仍适用于已登录用户。使用FIT,我们指定进入服务的所有请求中,有5%会在请求头中包含失败场景。当这些请求在系统中传播时,只要发到客户数据微服务的请求都会自动收到故障响应。

     

    高级原则

     

    在开发混沌工程实验时,牢记以下原则将有助于实验设计。在接下来的章节里将会深入探讨以下每个原则:

     

    • 建立稳定状态的假设;

    • 多样化现实世界事件;

    • 在生产环境运行实验;

    • 持续自动化运行实验;

    • 最小化“爆炸半径”。

     

    预测和预防故障

    在2017年美洲SRECon大会上,Preetha Appan介绍了她和她的团队在 indeed.com 开发的一个引入网络故障的工具。在演讲中,她阐述了预防故障的切实需求,而不是仅仅在故障发生时做出响应。他们的工具Sloth,作为一个守护进程,运行在基础设施的每一个节点上,包括数据库和索引服务器。参见 https://www.usenix.org/conference/srecon17americas/program/presentation/appan

     

    3. 建立稳定状态的假设

     

    对于任何复杂系统,都会有许多可变动的部件,有许多形式的输入输出。我们需要有一个通用的方式来区分系统行为是预期中的,还是预期之外的。我们将系统正常运行时的状态定义为系统的“稳定状态”。

     

    如果你在开发或运行一个软件服务,你如何清楚地了解它是否在正常工作?你如何认定它的稳定状态?你应该从哪里着眼来回答上面的问题?

     

    稳定状态

    在系统思维社区中使用“稳定状态”这个术语,来指代系统维持在一定范围内或一定模式的属性,诸如人体维持体温在一定范围内一样。我们期望通过一个模型,基于所期望的业务指标,来描述系统的稳定状态。这是我们在识别稳定状态方面的一个目标。要牢记稳定状态一定要和客户接受程度一致。在定义稳定状态时,要把客户和服务之间的服务水平协议(SLA)纳入考量。

     

    如果你的服务是一个新服务,想知道它是否正常工作的唯一途径可能只有自己去运行一下。例如服务可以通过网站访问,那你可能需要打开网页并尝试触发一个任务或事务来检查这个服务。

     

    这种快速检查系统健康的方法显然并不理想:他是劳动密集型的,也就是说我们基本不会或不常会做这件事。我们可以自动化运行类似测试,但这还不够。如果这些测试不能找到系统中的问题,该怎么办?

     

    更好的方法是先搜集和系统健康有关的数据。如果你在阅读本书,我们相信你已经在使用某种指标收集系统来监控你的系统。有大量的开源和商业工具可以采集系统方方面面的数据:CPU 负载,内存使用情况,网络 I/O,以及各类时序信息,例如需要多长时间响应一个 Web 请求,或者查询各种数据库的耗时。

     

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

     

    • 我们正在流失用户吗?

    • 用户目前可以操作网站的关键功能吗?例如在电子商务网站里为订单付款,添加购物车等。

    • 目前存在较高的延迟致使用户不能正常使用我们的服务吗?

     

    某些组织有非常明确的,和收入直接相关的实时指标。例如像 Amazon 和 eBay 会跟踪销售量,Google 和 Facebook 会跟踪广告曝光次数。

     

    由于 Netflix 使用的是按月订阅模式,所以我们没有这类指标。我们也会测量注册率,这是一个重要的指标,但是只看注册率不能反映整体系统的健康状况。

     

    我们真正想要的是一个可以反映当前活跃用户的满意状况的指标,因为满意的用户才有可能连续订阅。可以这么说,如果当前和系统做交互的用户是满意的,那么我们基本可以确定系统目前是健康的。

     

    遗憾的是,我们目前还没找到一个直接、实时的可以反映用户满意度的指标。我们会监控客服电话的呼叫量,这或许是一个可以间接反映客户满意度的指标,但是从运营角度出发,我们需要更快、更细粒度的反馈。Netflix 有一个还不错的可以间接反映用户满意度的指标——播放按钮的点击率。我们管这个指标叫做视频每秒开始播放数,简称为 SPS(Starts per sencond)。

     

    SPS 很容易测量,而且因为用户付费订阅服务的直接目的就是看视频,所以 SPS 应该和用户满意度密切相关。这个指标在东海岸下午 6 点会明显高于早上 6 点。我们就可以据此来定义我们系统的稳定状态了。

     

    相比某个服务的 CPU 负载来说,Netflix 的可靠性工程师(SREs)更关注 SPS 的下降:SPS 下降会立刻向他们发送警报。CPU 负载的尖刺有时重要有时不重要,而像 SPS 这样的业务指标才是系统边界的表述。这才是我们要关注并验证的地方,而不是那些像 CPU 负载类的内部指标。

     

    很多现有的数据采集框架默认采集大量的系统级别指标,所以通常来说,让系统有能力抓取到业务级别的指标比系统级别更难。然而花精力采集业务级别指标是值得的,因为它们才是系统健康的真实反映。

     

    这些指标获取的延迟越低越好:那些在月底算出来的业务指标和系统今天的健康状况毫无关系。

     

    对于选择的任何指标,需要平衡以下几点:

     

    • 指标和底层架构的关系;

    • 收集相关数据需要的工作量;

    • 指标和系统后续行为间的时间延迟。

     

    如果你还不能直接获取和业务直接相关的指标,可以暂时先利用一些系统指标,比如系统吞吐率,错误率,99%以上的延迟等。你选择的指标和业务关系越强,得到的可以采取可执行策略就越强。你可以把这些指标想象成系统的生命特征指标,像脉搏、呼吸、血压、体温等。同样重要的是,在客户端验证服务产生的警报可以提高整体效率,而且可以作为对服务端指标的补充,以构成某一时刻用户体验的完整画面。

     

    3.1 如何描述稳定状态

     

    与人体的生命体征一样,你需要清楚“健康”的数值范围。例如我们知道 37 摄氏度以下是人体的健康温度。

     

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

     

    很多业务指标并不像我们的体温那样稳定,他们也许会经常剧烈波动。我们再举一个医学中的例子,心电图测量心脏附近人体表面的电压差,这个信号是用来观测心脏行为的。但心电图采集的信号会随着心脏跳动而变化,因此医生不能将这个信号与单个阈值进行比较来判断患者是否健康。医生需要比较的是信号波动的模式和患者健康时的模式是否一致。

     

    在 Netflix,SPS 也不是一个和人体体温一样的稳定指标,它也随着时间波动。下图描绘的就是 SPS 随时间变化的波动情况,可以看出,它有一个稳定的模式。这是因为人们习惯于在晚餐时间看电视节目。因为 SPS 随时间的变化可以预期,所以我们就可以用一周前的 SPS 波动图作为稳定状态的模型。Netflix 的可靠性工程师们总是将过去一周的波动图放在当前的波动图之上,以发现差异。就像下图中当前的图线是红色,上一周的图线是黑色。

     

     

    图 1 SPS 随时间变化

     

    你所处的行业决定了你的指标是否以一种可以预期的方式随时间波动。例如,如果你负责一个新闻网站,流量的尖刺可能来源于一个大众关注度高的新闻事件。某些事件的尖刺可以预期,比如选举、重大赛事,但是其他类型的事件不太可能被预测到。在这一类场景中,准确描述系统的稳定状态将会非常复杂。无论哪种情况,描述稳定状态都是建立有意义假设的必要前提。

     

    3.2 建立假设

     

    每当你进行混沌工程实验的时候,你应该首先在心里对实验结果有一个假设。将你的系统直接置于各种事件里看看系统会怎么样的想法可能比较诱人,然而,没有一个预先的假设,你就不清楚应该从数据里找什么,最终也难以得出有效结论。

     

    定义好指标并理解其稳定状态的行为之后,你就可以使用它们来建立实验的假设。思考一下当你向系统注入不同类型的事件时,稳定状态行为会发生什么变化。例如你向中间层的服务增加请求数量,稳定状态会被破坏还是保持不变?如果被破坏了,你期待系统如何表现?

     

    在 Netflix,我们使用混沌工程来提高系统弹性,因此我们实验的假设一般是这样的形式:向系统注入的事件不会导致系统稳定状态发生明显的变化。

     

    例如,我们在弹性实验里故意让一个非关键服务中断,以验证系统是否可以优雅降级。我们可能会中断基于用户浏览历史展示的个性化影片列表服务,这时系统应该返回默认的影片列表。

     

    每当我们执行非关键服务中断实验时,我们的假设都是注入的故障不会对 SPS 产生影响。换句话说,我们的假设是实验措施不会使系统行为偏离稳定状态。

     

    我们还会定期执行演习,例如将一个 AWS 区域的流量全部转移到另外两个区域。目的是验证我们在进行这类故障恢复时,SPS 不会偏离稳定状态。这可以让我们对出现一个区域中断性故障时执行故障恢复充满信心。

     

    最后,让我们思考一下,如何衡量稳定状态行为的变化。即便你已经建立了稳定状态行为模型,你也需要定义清楚,当偏离稳定状态行为发生时你要如何测量这个偏差。如果你曾调节过报警系统的阈值,就应该清楚,定义偏离稳定状态的偏差是否在合理的范围是具有挑战性的。只有定义清楚“正常”的偏差范围,才可以获得一套验证假设的完善的测试集。

     

    金丝雀分析

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

    我们使用名叫自动金丝雀分析ACA(Automated Canary Analysis)的内部工具,通过稳定状态的指标,来验证金丝雀集群是否健康。ACA拿金丝雀集群与一个大小相同,并运行旧代码的基准集群,比较一系列系统指标。金丝雀集群的得分必须足够高才能通过金丝雀发布阶段。服务拥有者还可以向ACA中添加应用的自定义指标。

    通过ACA,工程师可以清晰地观察描述稳定状态的重要参数,同时可以对基于稳定状态建立的假设,在不同集群间的表现进行比较验证。我们其他的一些混沌工程工具也会使用ACA提供的服务来测试稳定状态变化的各类假设。

     

    4. 多样化现实世界事件

     

    每个系统,从简单到复杂,只要运行时间足够长,都会受到不可预测的事件和条件的影响。例如负载的增加、硬件故障、软件缺陷、还有非法数据(有时称为脏数据)的引入。我们无法穷举所有可能的事件或条件,但常见的有以下几类:

     

    • 硬件故障;

    • 功能缺陷;

    • 状态转换异常(例如发送方和接收方的状态不一致);

    • 网络延迟或隔离;

    • 上行或下行输入的大幅波动以及重试风暴;

    • 资源耗尽;

    • 服务之间的不正常的或者预料之外的组合调用;

    • 拜占庭故障(例如性能差或有异常的节点发出有错误的响应、异常的行为、对调用者随机返回不同的响应,等等);

    • 资源竞争条件;

    • 下游依赖故障。

     

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

     

    要彻底阻止对可用性的各种威胁是不可能的,但是我们可以尽可能减轻这些威胁。在决定引入哪些事件时,我们应当估算这些事件发生的频率和影响范围,然后权衡引入他们的成本和复杂度。在 Netflix,我们选择关闭节点的一方面原因就是,节点中断在现实中发生频率很高,同时引入关闭事件的成本和难度很低。对于区域故障来说,即使引入的成本高昂且复杂,我们还是必须要做,因为区域性故障对用户的影响是巨大的,除非有足够的弹性应对它。

     

    文化因素也是一种成本。例如对于传统数据中心来说,健壮性、稳定性、变更的严格控制,在文化上要优先于敏捷性——随机关闭节点类型的实验对传统数据中心文化上是一种挑战。随着迁移到云上带来的硬件职责外部化,工程部门对硬件故障越来越习以为常。这种认知实际上在鼓励一种对待故障可预期性的态度,这种态度可以进一步推动混沌工程的采用和被支持。虽然硬件故障并不是线上事故的最常见原因,但是它相对容易理解,同时也是在组织里引入混沌工程并获益的一个较简单的途径。

     

    和硬件故障一样,一些现实世界的事件也可以直接注入:例如每台机器的负责增加、通信延迟、网络分区、证书失效、时间偏差、数据膨胀等等。除此之外,其他的一些事件的注入可能会具有技术或文化的障碍,所以我们需要找寻其他方法来看看它们会如何影响生产环境。例如发布有缺陷的代码。金丝雀发布可以阻止许多简单和明显的软件缺陷被大规模发布到生产环境,但并不能阻挡全部的缺陷被发布出去。故意发布有缺陷的代码风险太大,可能会造成对用户过度的影响(参见最小化“爆炸半径”一节)。要模拟这类发布带来的缺陷问题,一种办法是对相应的服务调用注入异常。

     

    Blockade

    戴尔云管理团队开发了一个开源的,基于Docker的,用来测试分布式应用的网络故障或隔离的混沌工程工具,名叫Blockade。它通过在Docker的宿主机网络管理中创建各种异常场景,来影响在Docker容器中运行的应用。这个工具的一些功能包括:在容器间创建任意分区、容器数据丢包,容器网络延迟注入,以及在故障注入时便捷的系统监控能力。

     

    我们了解了通过对相应服务调用注入异常,来模拟一次失败的部署,这是因为错误代码带来的影响已经被隔离在仅仅是运行他们的几台服务器中。通常来说,故障隔离既可以是物理隔离也可以是逻辑隔离。隔离是容错的必要但不充分条件。要获得一个可以接受的结果,还需要某种形式的冗余或者优雅降级。只要子部件的故障能引发整个系统不可用,那么这个故障就没有被隔离。故障的影响范围和隔离范围被称为故障的故障域。

     

    提供产品的组织设定产品可用性的预期,并负责制定 SLA ——哪些东西一定不能失败或者失败是否具备预案。发现和验证故障域以确保满足产品的可用性预期是工程师团队的责任。

     

    故障域还为混沌工程提供了一个乘数效应。回到刚才的例子,如果对服务调用失败的模拟是成功的,那么这不仅可以验证该服务对部署缺陷代码的弹性,同时也可以验证它在高负载、错误配置、其他异常终止等场景引发失败时的弹性。此外,在故障域中你可以向系统注入各类故障来观察故障的症状。例如你在真实环境中看到了一些症状,你可以逆向找出症状的根源故障及其发生的几率。在故障域的级别进行实验还有个好处是,可以提前对不可预见的导致故障的原因做好准备。

     

    我们不应该给系统注入引发故障的根因事件。每一个资源都会形成一个故障域,这个故障域包括所有对它有强依赖的部件(当该资源不可用时,所有依赖也都不再可用)。向系统注入故障根因的事件会暴露出这些因为资源共享形成的故障域。团队也经常会为这类资源共享的情况而感到惊讶。

     

    我们不需要穷举所有可能对系统造成改变的事件,只需要注入那些频繁发生且影响重大的事件,同时要足够理解会被影响的故障域。工程师在设计系统架构的时候也许已经考虑了故障域。例如在微服务架构中,服务组是最重要的故障域之一。有时团队认为他们的服务不是关键服务,但最后却在故障时因为没有做合理的隔离导致整个系统宕机。所以,在系统中用实验验证这些预先定好的边界非常关键。

     

    再强调一次,注入的事件一定是你认为系统能处理的。同时,注入的事件应该是所有可能的真实世界的事件,而不仅仅是故障或延迟。我们上面举的例子更多关注软件部分,但真实世界里人的因素对系统弹性和可用性也起到了至关重要的作用。例如对故障处理中人工控制的流程和工具进行实验或演习也会提高可用性。

     

    Netflix的混沌工程的规范化

    在Netflix,混乱猴子和混乱金刚是由一个集中的团队进行开发,发布,维护和制定规则和执行的。而FIT是一个自助服务的工具。这也是第一次我们的工具需要微服务工程师们的时间和接受才能运行。当然,他们不得不对工具发现的对弹性的威胁做出回应。即便许多团队在开发阶段确实尝试到了FIT的好处,但还是难以广泛和高频率的使用。我们在FIT有这么强大的工具来提高弹性,但是我们却遇到了普及问题。

    Netflix的混沌工程团队认为现在我们分别有了小尺度和大尺度(如关闭节点和关闭整个区域)上的实践,但我们还缺少中间环节的最佳实践:持续提高我们对微服务各类故障的弹性。FIT对这方面的探索提供了一个基础,但是执行这样一个实验的负担,使得我们并没能像混乱猴子和混乱金刚那样,在工程师团队中形成一致。

    我们重新回过头从头到尾仔细思考了如何将混沌工程作为一个落地的实践。我们走访了一些工程师,询问他们混沌工程对他们意味着什么。多数的回复都是混沌工程就是在生产环境中搞破坏。听起来很有意思,很多人都在生产环境中搞破坏,但这些破坏都毫无价值。我们要想办法让混沌工程规范化。

    2015年中,我们发表了混沌工程原则(Principles of Chaos Engineering),定义了混沌工程作为计算机科学中的一门新的学科。

    通过这个新的规范形式,我们在Netflix正式推动了混沌工程。我们为混沌工程的组成绘制了蓝图:我们清楚目标是什么,而且我们清楚如何评估做的够不够好。原则为我们奠定了基础,让我们可以把混沌工程提升到新的高度。

     

    5. 在生产环境运行实验

     

    在我们这个行业里,在生产环境中进行软件验证的想法通常都会被嘲笑。“我们要在生产环境中验证”这句话更像是黑色幽默,它被翻译成“我们在发布之前不打算完善地验证这些代码”。

     

    经典测试的一个普遍信条是,寻找软件缺陷要离生产环境越远越好。例如,在单元测试中发现缺陷比在集成测试中发现更好。这里的逻辑是,离生产环境越远,或者是离发布越远的时候,发现的缺陷就越容易被找到根本原因并彻底修复。如果你曾经分别在单元测试,集成测试,和生产环境中 debug 过问题,上述逻辑的智慧就不言而喻了。

     

    但是在混沌工程领域里,整个策略却要反过来了。在离生产环境的越近的地方进行实验越好。理想的实践就是直接在生产环境中执行实验。

     

    传统软件测试中,我们是在验证代码的逻辑正确性。是我们对函数和方法的行为有很好理解的情况下,写测试来验证它们对不对,换句话说,是验证代码写得对不对。

     

    而当我们执行混沌工程实验时,我们所感兴趣的是整个系统作为一个整体的行为。代码只是整个系统的重要组成部分,而除了代码之外,整个系统还有很多其他方面。特别是,状态、输入、以及第三方系统导致的难易预见的系统行为。

     

    下面我们深入了解一下为什么在生产环境中执行对混沌工程来说是至关重要的。我们要建立的是对系统在生产环境的信心,我们当然就需要在生产环境中进行实验。否则,我们就仅仅是在其他我们并不太关心的环境中建立信心,这会大大削弱这些实践的价值。

     

    5.1 状态和服务

     

    我们之前简要讨论过系统的“状态”。在这一节我们仔细讨论一下有状态服务。如果我们不需要在系统中维护任何状态,那么软件工程将会简单很多。然而状态却在我们建造的这类系统中无处不在。

     

    在微服务架构中,当我们提到状态的时候,通常我们说的是有状态服务,例如数据库服务。数据库仅仅保存一些测试设置开关的系统,与保存所有生产数据的系统,在行为上是不同的。其他一些有状态服务包括缓存服务,对象存储服务,以及可持久化的消息服务。

     

    配置数据是另一种影响系统行为的状态。无论使用静态配置文件,动态配置服务(像 etcd),还是两者的组合(像我们在 Netflix 一样),这些配置信息本身也是一种状态,而且它们可以严重影响系统行为。

     

    即使在无状态的服务中,状态仍然以内存中的数据结构的形式存在于请求之间,并因此影响到后续请求。

     

    还有无数的角落里隐藏着许许多多的状态。在云服务中,自动伸缩组中虚拟机或者容器的个数也是一个系统的状态,这个状态会随时间、随外部需求或集群变化而不断改变。网络硬件如交换机和路由器也有状态。

     

    总有一些意想不到的状态会伤害到你。如果你是我们的目标读者,你应该已经踩过一些坑了。要想解决混沌工程关注的对于系统弹性的威胁,你需要把生产环境中存在的各式各样的状态问题暴露给混沌工程实验。

     

    5.2 生产环境中的输入

     

    对于软件工程师来说,最难的一课莫过于,系统的用户永远不会如你预期的那样与你的系统进行交互。这一课在设计系统的 UI 的时候尤为典型,在设计混沌工程实验的时候也需要牢记。

     

    设想一下,你的系统提供了从用户接收不同类型请求的服务。你可以为用户输入设计一个组合数据模型,但因为用户永远不会如你预期般行动,生产系统总是会收到测试覆盖之外的输入数据组合。

     

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

     

    5.3 第三方系统

     

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

    Leslie Lamport

     

    即使可以预见所有在控制范围内系统的状态,我们也总是会依赖于外部系统,它们的行为我们不可能全都知道。2012 年的平安夜,在 Netflix 全体员工的记忆上烙上了深深的印记。AWS 一个区域的 ELB(Elastic Load Balancing)服务故障,导致了 Netflix 全部服务严重中断。

     

    如果系统部署在像 AWS 或 Azure 这样的云服务中,那么存在大量的你所依赖,而又不完全了解的外部服务是显而易见的。但即使你的系统全都运行在自己的数据中心,但在生产环境中,系统还是会依赖其他的外部服务,例如 DNS,SMTP,NTP,等等。就算这些服务你都自己部署,他们也经常会需要和外部的不受你控制的服务进行交互。

     

    如果你的服务提供了一个 Web UI,那么你的用户所使用的浏览器就变成了你系统的一部分,但它不受你控制。即使你对客户端拥有全面的控制,例如 IoT 设备,你仍然逃不开用户所连接的网络环境和设备的影响。

     

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

     

    面向云服务的混沌工程工具

    只要系统部署在云上,那么混乱就是不可避免的。所幸我们的社区已经注意到并且开发了一些优秀的基于云的混沌工程工具,并且可以和不同的云提供商整合使用。除了混乱猴子之外,值得一提的工具有Chaos Lambda,它可以让我们在生产时间随机关闭AWS的Auto Scaling Group(ASG)实例。还有Microsoft Azure的Fault Analysis Service,它是专为测试在Microsoft Azure Service Fabric所构建服务的工具。

     

    5.4 生产环境变更

     

    在 Netflix,我们的系统一直在不断更新。每天工程师和自动脚本都在通过不同的方式更新着系统,例如发布新代码,更改动态配置,添加持久化的数据,等等。

     

    如果我们扩展我们系统的概念来包括这些生产环境中的变更,那么很明显在测试环境中想要模拟这些系统行为有多困难。

     

    5.5 外部有效性

     

    当心理学家或教育研究人员等社会科学家进行实验时,他们的主要关注点之一就是“外部有效性”:这个实验的结果能否概括我们真正感兴趣的现象?或者测量结果的产品运行环境是否是专门为了测试而准备的?

     

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

     

    5.6 不愿意实践混沌工程的借口

     

    我们认识到,在有些环境中,直接在生产环境中进行实验可能非常困难甚至不可能。我们并不期待工程师将干扰注入到行驶中的自动驾驶汽车的传感器里。但是,多数用户应该都不是在操作这类安全悠关的系统。

     

    5.6.1 我很确定它会宕机!

     

    如果你不愿在生产环境执行混沌工程实验的原因是,你对系统在你注入事件时会如何反应缺乏信心,那么这可能是你的系统还不够成熟来应对混沌工程实验的信号。你应该在对系统的弹性具备一定信心的时候再进行混沌工程实验。混沌工程的一个主要目的是识别系统中的薄弱环节。如果已经看到明显的薄弱环节,那你应该首先专注于提高系统在这一点上的弹性。当你确信系统有足够的弹性时,就可以开始进行混沌工程实验了。

     

    5.6.2 如果真的宕机了,麻烦就大了!

     

    即使你对系统弹性有很大的信心,你也可能会犹豫不决要不要执行混沌工程实验,因为担心如果实验揭示出系统薄弱环节的同时造成过多的破坏。

     

    这是一个非常合理的顾虑,这也是我们正在致力解决的问题。我们采用的方法是通过下面两个途径来最小化潜在的影响范围:

     

    • 支持快速终止实验;

    • 最小化实验造成的“爆炸半径”。

     

    当你执行任何混沌工程实验之前,应该先有一个用来立即终止实验的“大红色按钮”(我们真的有一个大红色按钮,虽然是虚拟的)。更好的方法是自动化这个功能,当它监测到对稳定状态有潜在危害的时候立即自动终止实验。

     

    第二个策略涉及在设计实验时,要考虑到如何既能从实验中获得有意义的结论,同时兼顾最小化实验可能造成的潜在危害。这一点在后面的最小化“爆炸半径”一节中讨论。

     

    5.7 离生产环境越近越好

     

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

     

    不能在生产环境中做实验?

    在2015年纽约Velocity Conference上,来自Fidelity Investment的Kyle Parrish和David Halsey做了题为“太大而无法测试:如何破坏一个在线交易平台而不造成金融灾难”的演讲。他们在一个包括大型机的金融交易系统上运行了混沌工程实验。用他们的话来说,“我们意识到,用我们的灾备环境,配合生产环境的前端,就可以用现有的一些部件,再构造出一套生产环境。我们越深入,这个想法就越可行。我们发现大型机灾备系统非常理想,因为它与生产环境保持实时同步,包含所有生产环境的代码,数据,处理能力和存储能力,支持团队也完全了解它是如何运行的。我们也清楚的了解到,可以在这个系统上执行2倍或3倍于实际生产峰值时的流量。我们可以再造一套生产环境!”这是一个有创造力的绕过制度规范来模拟生产环境的实例。

     

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

     

    6. 持续自动化运行实验

     

    自动化是最长的杠杆。在混沌工程的实践中,我们自动执行实验,自动分析实验结果,并希望可以自动创建新的实验。

     

    6.1 自动执行实验

     

    手动执行一次性的实验是非常好的第一步。当我们想出寻找故障空间的新方法时,我们经常从手动的方法开始,小心谨慎地处理每一件事以期建立对实验和对系统的信心。所有当事人都聚集在一起,并向 CORE(Critical Operations Response Engineering,Netflix 的 SRE 团队的名称)发出一个警示信息,说明一个新的实验即将开始。

     

    用这种较恐惧和极端级别的态度有利于:1)正确运行实验 2)确保实验有最小的“爆炸半径”。当我们成功执行了实验之后,下一步就是自动化这个实验以让其持续运行。

     

    如果一个实验不是自动化的,那他就是作废了。

     

    当今系统的复杂性意味着我们无法先验的知道,生产环境的哪些变动会改变混沌工程实验的结果。基于这个原因,我们必须假设所有变动都会改变实验结果。在共享状态、缓存、动态配置管理、持续交付、自动伸缩、时间敏感的代码等等的作用之下,生产环境实际上处在一个无时不在变化的状态。导致的结果就是,对实验结果的信心是随着时间而衰减的。

     

    理想情况下,实验应该随着每次变化而执行,这有点儿像是混沌金丝雀。当发现新的风险时,操作人员如果相当确定根源是即将发布的新代码,那他就可以选择是否应该阻止发布新版本并优先修复缺陷。这种方法可以更深入了解生产中的可用性风险发生和持续的时间。在另一个极端,每年一次的演习中的问题调查难度更高,需要完全从零开始检查,而且很难确定这个潜在的问题存在于生产环境多久了。

     

    如果实验不是自动化的,那么它就不会被执行。

     

    在 Netflix,服务的可用性由该服务的开发和维护团队全权负责。我们的混沌工程团队通过培训,工具,鼓励和压力来帮助服务所有者提高服务的可用性。我们不能也不应该让工程师牺牲开发速度,专门花时间来手动定期执行混沌工程实验。相反的,我们自己投入精力来开发混沌工程的工具和平台,以期不断降低创建新实验的门槛,并能够完全自动运行这些实验。

     

    混沌工程自动化平台 Chaos Automation Platform(ChAP)

    我们的混沌工程团队在2015年的大部分时间里都在以咨询的形式,在关键微服务上运行混沌工程实验。这对于真正掌握FIT的能力和局限非常必要,但是我们也知道这样手把手的咨询方式没办法规模化。我们需要一个可以在整个组织规模化这个实践机制。

    到2016年初,我们有了一个计划将混沌工程的原则引入到微服务层。我们注意到FIT有一些问题妨碍了自动化和广泛应用。其中一部分可以在FIT本身解决,另外一部分则需要较大的工程量,比请求头修改和FIT提供的代码故障模拟点注入要复杂得多。

    2016年底我们发布了混沌工程自动化平台,简称ChAP,旨在解决这些不足之处。

    FIT中大多数问题都是由于缺乏自动化导致的。过多的对人工参与,例如设置故障场景,观测运行时关键指标的变化等,这些被证明是大规模应用的障碍。我们倾向于依靠现有的金丝雀分析(参见前面对金丝雀分析的介绍)来自动判断实验是否在可接受的范围内执行。

    随后我们使用一个自动化模板开始进行真正的实验。在上面讨论过的FIT例子里,我们影响了5%的流量,观察它对SPS是否有影响。如果我们没有观察到任何影响,我们会将受影响的流量提高到25%。任何影响都有可能被其他和SPS相关的噪音所掩盖。像这样用大量流量做实验是有风险的,它只能给我们较低的信心我们可以隔离一些小的影响,它也可以防止多个故障同时出现。

    为了最小化爆炸半径,ChAP为每个被实验的微服务创建一个控制节点和一个实验集群。例如我们像上述的实例中说的,测试一个用户信息的微服务,ChAP会询问我们的持续集成工具Spinnaker关于这个集群的信息。ChAP用这个信息创建这个服务的两个节点,一个作为控制节点,另一个作为实验节点。然后它会分流一小部分流量,并平均分配在控制节点和实验节点上。只有实验节点里注入了故障场景。当流量在系统中流转时,我们可以实时比对控制节点和实验节点上的成功率和操作问题。

    有了自动化的实验,我们就有了较高的信心,我们可以通过一对一比对控制节点和实验节点来监测到即使是很小的影响。我们只影响了流量中的很小部分,并且已经隔离了实验,因此我们就可以并行运行大量的实验了。

    在2016年底,我们将ChAP与持续交付工具Spinnaker集成在一起,这样微服务就可以在每次新发布时运行混沌工程实验了。这个新功能有些类似金丝雀,但是在这个场景下我们需要它不间断运行,不会立即自动优雅降级,因为这里我们的目的是要尽可能发现所有未来潜在的系统问题。服务所有者基于在他们的微服务中发现的薄弱环节,我们给予了他们防止降级发生的机会。

     

    6.2 自动创建实验

     

    如果你已经可以配置定期自动运行实验,你就处在一个非常好的状态了。然而,我们认为还可以追求一个更好的自动化水平:自动设计实验。

     

    设计混沌工程实验的挑战并非来自于定位导致生产环境崩溃的原因,这些信息在我们的故障跟踪中有。我们真正想要做的是找到那些本不应该让系统崩溃的事件的原因,包括那些还从未发生过的事件,然后持续不断的设计实验来验证,保证这些事件永远不会导致系统崩溃。

     

    然而这是非常困难的。导致系统波动的原因空间是非常巨大的,我们不可能有足够的时间和资源穷举所有可能导致问题的事件及其组合。

     

    Lineage-Driven Fault Injection (LDFI)

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

    2015年,Peter Alvaro与Netflix的工程师合作来研究是否可以把LDFI应用在我们的系统上。他们成功地在Netflix FIT框架的基础上开发了一个版本的LDFI,并且识别出了可能导致严重故障的一些错误事件组合。

    有关如何在Netflix应用这项工作的更多信息,请参阅“第七届ACM云计算研讨会论文集”(SoCC '16)上发表的论文“Internet规模的自动化故障测试研究”http://dx.doi.org/10.1145/2987550.2987555

     

    7. 最小化“爆炸半径”

     

    1986 年 4 月 26 日,人类历史上最严重的核事故之一发生在乌克兰的切尔诺贝利核电站。具有讽刺意味的是,灾难是由于一次弹性演习导致的:一次验证冷却剂泵冗余电源的演习。虽然我们大多数人并不从事像核电厂冷却系统这样高危的项目工作,但每一次混沌工程实验的确具备导致生产环境崩溃的风险。混沌工程师的一项专业职责就是要理解和降低生产风险,可以为实验而具备良好设计的系统可以阻止大规模的生产事故,仅仅影响到少量的用户。

     

    不幸的是,我们经常运行本来只会影响一小部分用户的测试,却由于级联故障无意中影响到了更多的用户。在这些情况下,我们不得不立即中断实验。虽然我们绝不想发生这种情况,但随时遏制和停止实验的能力是必备的,可以避免造成更大的危机。我们的实验通过很多方法来探寻故障会造成的未知的和不可预见的影响,所以关键在于如何让这些薄弱环节曝光出来而不会意外造成更大规模的故障。我们称之为最小化“爆炸半径”。

     

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

     

    最小风险的实验只作用于很少的用户之上。为此我们验证客户端功能时只向一小部分终端注入故障。这些实验仅限于影响一小部分用户或一小部分流程。他们不能代表全部生产流量,但他们是很好的早期指标。例如,如果一个网站无法通过早期实验,就没必要影响大量其余的真实用户。

     

    当自动化实验成功之后(或者小量设备验证没有涵盖要测试的功能时),下一步就是运行小规模的扩散实验。这种实验会影响一小部分百分比的用户,因为我们允许这些流量都遵循正常的路由规则,所以最终它们会在生产服务器上均匀分布。对于此类实验,你需要用你定义好的成功指标来过滤所有被影响的用户,以防实验的影响被生产环境的噪音掩盖。小规模扩散实验的优势在于它不会触动到生产环境的例如断路器阈值,所以你可以验证每一个单一请求的超时和预案。这可以验证系统对瞬时异常的弹性。

     

    下一步是进行小规模的集中实验,通过修改路由策略让所有实验覆盖的用户流量导向到特定的节点。这些节点会经历高度集中的故障、延迟等测试。这里我们会允许断路器打开,同时会暴露隐藏的资源限制。如果我们发现有无效的预案或者奇怪的锁竞争等情况导致服务中断,那么只有实验覆盖的用户会受到影响。这个实验模拟大规模生产环境故障,但同时可以把负面影响控制在最小,然而结果却能带来高度的信心。

     

    风险最大但最准确的实验是大规模无自定义路由的实验。在这个实验级别,实验结果应该在你的主控控制台显示,同时因为断路器和共享资源的限制,实验可能会影响到不在实验覆盖范围内的用户。当然,没有什么比让所有生产用户都参与实验,能给你更多关于系统可以抵御特定故障场景的确定性了。

     

    除了不断扩大实验范围,在实验造成过多危害时及时终止实验也是必不可少的。有些系统设计会使用降级模式来给用户带来稍小的影响,这还好,但是当系统完全中断服务的时候,就应该立即终止实验。这可以由之前讨论过的“大红色按钮”来处理。

     

    我们强烈建议实施自动终止实验,尤其是在定期自动执行实验的情况下。关于弄清楚如何构建一个可以实时监控到我们感兴趣的指标的系统,并可以随时实施混沌工程实验,这完全依赖于你手上的独特的系统构造,我们留给读者当做一个课后练习。

     

    为了让尽可能高效地应对实验发生不可预期的情况,我们要避免在高风险的时间段运行实验。例如我们只在所有人都在办公室工作的时间段运行实验。

     

    如果实验的工具和仪器本身就会对系统和指标产生影响,那么整个混沌工程的目的就被破坏了。我们要的是建立对系统弹性的信心,记住每次只检验一个可控的故障。

    Netflix 混沌工程手册 Part 3:实践方法

    本文翻译自 Netflix 工程师合著的 Chaos Engineering 一书。这本书介绍了混沌工程的主要概念,以及如何在组织中实践这些概念和经验。也许我们开发的相关工具只适用于 Netflix 自身的业务和系统环境,但我们相信工具背后的原则可以更广泛地应用于其他领域。

    InfoQ 将就这一专题持续出稿,感兴趣的同学可以持续关注。

     

    第一部分第二部分中,我们讨论了混沌工程的初衷和背后的理论。然而将理论付诸实践,是具有挑战性的。任何系统都没有现成的构建、调度,自动化实验的工具,而且即使是最好的混沌工程框架,也需要良好适配才能发挥作用。

     

    混沌工程起源于 Netflix,但它的影响力已经从技术领域扩展到了其他行业。在一些会议上,我们经常听到来自金融行业工程师们的一些保留意见。他们不愿实施混沌工程实验的原因是,害怕实验会给客户带来资金或行业合规性的影响。然而我们坚持认为,该发生的故障,不会因为你的意愿而不发生。即使实验暴露风险点的同时会导致一些小的负面影响,提前了解和控制影响范围,也比最终措手不及,应对大规模事故要好得多。现在我们已经看到很多银行和金融公司在实践混沌工程,为那些犹豫不决的企业提供了大量示例参考。

     

    医疗行业的工程师们也表达了类似的疑虑。诚然,娱乐服务的中断只会让人觉得不方便,金融交易的失败令人困惑并且可能导致大量损失,但医疗技术领域的任何失败都可能会危及生命。然而我们还是想指出,激励我们让混沌工程规范化的许多科学原则正是起源于医学。医学研究的最高标准就是临床试验。我们绝不会忽视将混沌工程引入医疗领域的潜在影响,只是想说明,实际生活中已经存在很多具有同样风险的先例,并且已经被广泛接受了。

     

    对混沌工程这样一个新生的学科来说,付诸实践是最有意义和最迫切的。了解关于如何实施、复杂性和实施时需要关注的问题,既可以帮助你找出目前在混沌工程之路上,你所处的位置,也可以指明组织中需要在何处发力来构建成功的混沌工程实践。

     

    8. 设计实验

     

    在讨论完理论原则之后,我们来如何看看设计混沌工程实验的细节。下面是大致的流程:

     

    1. 选定假设;

    2. 设定实验的范围;

    3. 识别出要监控的指标;

    4. 在组织内沟通到位;

    5. 执行实验;

    6. 分析实验结果;

    7. 扩大实验范围;

    8. 自动化实验。

     

    8.1 选定假设

     

    你需要做的第一件事就是选定你要验证的假设,我们在“多样化现实世界事件”一节中有过讨论。例如,你最近在访问 Redis 时有超时报错,于是想确保你的系统不会被这类缓存访问超时所影响。再比如,你希望验证主从数据库配置,在主数据库故障时可以无缝切换到从数据库。

     

    不要忘记你的系统中一个重要的组成部分是维护它们的人。人工的行为对于降低事故率至关重要。例如有的公司采用如 Slack 或 HipChat 之类的即时通讯工具在故障时的进行沟通,当即时通信工具不可用时,如何处理故障也有一套应急流程。那么如何得知 on-call 工程师是否掌握了这个应急流程?执行混沌工程实验就是很好的办法。

     

    8.2 设定实验的范围

     

    当选定了要验证的假设,下一步需要做的就是设定好实验的范围。这里有两个原则:“在生产环境运行实验”和“最小化爆炸半径”。实验离生产环境越近,从实验中获得的收益就越大。虽然这么说,但还是要仔细关注可能对系统和用户造成影响的风险。

     

    我们需要尽可能最小化实验对用户造成的影响,应该从一个小范围的测试开始,然后一步步扩大,直到我们认为系统可以应对预期的最大影响。

     

    因此,如“最小化爆炸半径”一节中描述过的,我们提倡第一个实验的范围控制得越小越好。而且应该在生产环境中执行实验之前,先在测试环境中试一试。一旦进入生产环境,一定要从最小量的用户流量开始尝试。例如,如果你要验证系统在缓存超时时会怎么做,可以先用一个测试客户端调用生产环境的服务,并且只针对这个客户端引入缓存超时。

     

    8.3 识别出要监控的指标

     

    明确了假设和范围之后,我们需要选定用来评估实验结果的指标,我们在“建立稳定状态的假设”一节中讨论过。要尽可能基于你的指标来验证假设。例如假设是“主数据库故障时,所有服务都正常”,那么在运行实验之前,你需要清晰定义什么是“正常”。再举个例子,如果你有一个明确的业务指标“每秒订单数”,或者更低级别的指标,像响应时间和响应错误率,在执行实验之前要明确定义清楚这些指标可以容忍的数值范围。

     

    如果实验产生的影响比预期中的大,你就应该准备好立即终止实验。可以预先设定好明确的阈值,例如,失败请求占比不超过 5%。这可以帮助你快速决定在实验进行中,要不要按下那个“大红色按钮”。

     

    8.4 在组织内沟通到位

     

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

     

    第一次执行的时候,你可能需要协调好每个需要参与的,对结果感兴趣的,以及对生产影响有顾虑的团队。随着越来越多次的执行实验,你和你的组织对系统的信心会越来越足,这时就没必要每次都为了实验发送通知了。

     

    关于混乱金刚的通知

    我们第一次执行混乱金刚实验,验证AWS区域故障恢复实践时,整个过程中与公司各团队做了大量沟通,目的是让每一个人都清楚我们将在什么时间点让某个区域失效。不可避免地会有很多例如功能发布等的要求,让我们推迟实验。

    随着我们一次又一次越来越频繁地进行实验,混乱金刚逐渐被大家接受为一个“常规”事件。结果就是事先沟通越来越少。现在我们每三周执行一次,但已不需要发出通知了。在我们内部有一个可以订阅的日历,标明混乱金刚实验在哪一天执行,但我们不会指明当天具体的执行时间。

     

    8.5 执行实验

     

    截止目前准备工作都已完成,是时候执行实验了!要盯住那些指标,因为你可能随时需要终止实验。随时终止实验的能力异常关键,因为我们是直接在生产环境运行实验,随时都可能对系统造成过度危害,进而影响外部用户。例如一个电商网站,你一定要密切关注用户是否能够正常结算。要确保有足够的报警机制,能实时获知这些关键指标是不是掉到了阈值以下。

     

    8.6 分析实验结果

     

    实验结束后,拿当时的指标数据来验证之前的假设是否成立。系统对于你注入的真实事件是否具备足够的弹性?有没有任何预期之外的事情发生?

     

    多数混沌工程实验暴露出来的问题,都会涉及多服务之间的交互,所以要确保把实验结果反馈给所有相关的团队,一同从整体的角度来消除隐患。

     

    8.7 扩大实验范围

     

    “最小化爆炸半径”一节中描述的,当你从小范围实验中获得了信心之后,就可以逐步扩大实验范围了。扩大实验范围的目的是进一步暴露小范围实验无法发现的一些问题。例如,微服务架构中一些小量的超时或许不会有什么问题,但超过一定比例就可能会导致整体瘫痪。

     

    8.8 自动化实验

     

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

     

    9. 混沌工程成熟度模型

     

    我们标准化混沌工程定义的一个目的是,在执行混沌工程项目时,我们有标准来判断这个项目做得是好是坏,以及如何可以做得更好。混沌工程成熟度模型(CMM)给我们提供了一个评估当前混沌工程项目成熟度状态的工具。把你当前项目的状态放在这个图上,就可以据此设定想要达到的目标,也可以对比其他项目的状态。如果你想要提升这个项目的状态,CMM 的坐标轴会给出明确的方向建议,你应该朝哪里努力。

     

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

     

    9.1 熟练度

     

    熟练度可以反映出,在你的组织中混沌工程项目的有效性和安全性。项目各自的特性会反映出不同程度的熟练度,有些完全不具备熟练度,而有些可能具备很高的熟练度。熟练度的级别也会因为混沌工程实验的投入程度而有差异。我们对熟练度用入门、简单、高级和熟练四个级别进行描述。

     

    入门

     

    • 未在生产环境中运行实验;

    • 全人工流程;

    • 实验结果只反映系统指标,而不是业务指标;

    • 只对实验对象注入一些简单事件,如“关闭节点”。

     

    简单

     

    • 用复制的生产流量来运行实验;

    • 自助式创建实验,自动运行实验,但需要手动监控和停止实验;

    • 实验结果反映聚合的业务指标;

    • 对实验对象注入较高级的事件,如网络延迟;

    • 实验结果是手动整理的;

    • 实验是预先定义好的;

    • 有工具支持历史实验组和控制组之间的比较。

     

    高级

     

    • 在生产环境中运行实验;

    • 自动结果分析,自动终止实验;

    • 实验框架和持续发布工具集成;

    • 在实验组和控制组之间比较业务指标差异;

    • 对实验组引入如服务级别的影响和组合式的故障事件;

    • 持续收集实验结果;

    • 有工具支持交互式的比对实验组和控制组。

     

    熟练

     

    • 开发流程中的每个环节和任意环境都可以运行实验;

    • 全自动的设计、执行和终止实验;

    • 实验框架和A/B测试以及其他测试工具集成以最小化影响;

    • 可以注入如对系统的不同使用模式、返回结果和状态的更改等类型的事件;

    • 实验具有动态可调整的范围以找寻系统拐点;

    • 实验结果可以用来预测收入损失;

    • 实验结果分析可以用来做容量规划;

    • 实验结果可以区分出不同服务实际的关键程度。

     

    9.2 应用度

     

    应用度用来衡量混沌工程实验覆盖的广度和深度。应用度越高,暴露的脆弱点就越多,你对系统的信心也就越足。类似熟练度,我们也对应用度定义了不同级别,分别是“暗中进行”,适当投入度,正式采用和成为文化。

     

    “暗中进行”

     

    • 重要项目不采用;

    • 只覆盖了少量系统;

    • 组织内部基本没有感知;

    • 早期使用者偶尔进行混沌工程实验。

     

    适当投入度

     

    • 实验获得正式批准;

    • 工程师兼职进行混沌工程实验;

    • 多个团队有兴趣并参与;

    • 少数重要服务也会不定期进行混沌工程实验。

     

    正式采用

     

    • 有专门的混沌工程团队;

    • 所有故障的复盘都会进入混沌工程框架来创建回归实验;

    • 大多数关键服务都会定期进行混沌工程实验;

    • 偶尔执行实验性的故障复盘验证,例如“比赛日”的形式。

     

    成为文化

     

    • 所有关键服务都高频率进行混沌实验;

    • 多数非关键服务高频率进行混沌实验;

    • 混沌工程实验是工程师日常工作的一部分;

    • 所有系统组件默认要参与混沌工程实验,不参与需要特殊说明。

     

    9.3 绘制成熟度模型图

     

    绘制成熟度模型图时,以熟练度作为 Y 轴,应用度作为 X 轴。会呈现出如下图所示的四个象限。

     

     

    图中我们拿混乱猴子,混乱金刚和混沌工程自动化平台 ChAP(帽子)作为示例。写作本文时,我们的 ChAP 处在熟练度较高的位置,在之前的发展历程中,我们努力的方向如图中箭头所示。现在根据这个图,我们就知道我们需要专注在它的应用度上,以发挥出 ChAP 的最大潜力。

     

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

     

    10. 结论

     

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

     

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

     

    一些资源

     

    我们创建了社区网站 http://chaos.community/ 和 Google Group https://groups.google.com/forum/#!forum/chaos-community 。 期待你加入我们。

     

    你也可以在 Netflix 的官方博客 https://medium.com/netflix-techblog 找到更多关于混沌工程的信息。当然,还有很多其他的组织在实践混沌工程,例如:

     

    • 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

     

    ​ Linkedin 开发的故障注入工具。它非常易于扩展,并且很多关键组件都是可插拔的。

     

    Pumba

     

    ​ 基于 Docker 的混沌工程测试工具以及网络模拟工具。

     

    Chaos Lemur

     

    ​ 可以本地部署的随机关闭 BOSH 虚拟机的工具。

     

    Chaos Lambda

     

    ​ 随机关闭 AWS ASG 节点的工具。

     

    Blockade

     

    ​ 基于 Docker,可以测试网络故障和网络分区的工具。

     

    Chaos-http-proxy

     

    ​ 可以向 HTTP 请求注入故障的代理服务器。

     

    Monkey-ops

     

    ​ Monkey-Ops 用 Go 实现,可以再 OpenShift V3.X 上部署并且可以在其中生成混沌实验。Monkey-Ops 可以随机停止 OpenShift 组件,如 Pods 或者 DeploymentConfigs。

     

    Chaos Dingo

     

    ​ Chaos Dingo 目前支持在 Azure 相关服务上进行实验。

     

    Tugbot

     

    ​ Docker 生产环境测试工具。

     

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

     

    Drift Into Failure by Sidney Dekker (2011)

     

    Dekker 的理论讲的是,在一个组织内部,事故的发生都是因为各系统随着时间慢慢滑向一个不安全的状态,而不是某个单点突发的问题造成的。你可以把混沌工程想象成专门用来对抗这种滑动过程的方法。

     

    To Engineer Is Human: The Role of Failure in Successful Design by Henry Petroski (1992)

     

    Petroski 描述了他们那里的工程师是如何从过去的失败中汲取教训来进步的,而不是从过去的成功中找经验。混沌工程正是一种既能发现系统中的问题点,又能避免大规模影响的方法论。

     

    Searching for Safety by Aaron Wildavsky (1988)

     

    Wildavksy 的主张是,为了提高整体安全性,所有风险必须要被管理好。尤其是采用不断试错的方法,长期来说会比尝试完全避免事故发生,要获得更好的结果。混沌工程也是同样的通过在生产环境进行实验来拥抱风险,以期获得系统更大的弹性的方法。

  • 相关阅读:
    java实现扑克牌排列
    java实现扑克牌排列
    java实现扑克牌排列
    java实现扑克牌排列
    java实现扑克牌排列
    java实现平面点最小距离
    java实现平面点最小距离
    java实现拍7游戏
    java实现拍7游戏
    using_where, Using temporary ,using_filesort 分享
  • 原文地址:https://www.cnblogs.com/dhcn/p/14168297.html
Copyright © 2011-2022 走看看