×

混沌工程简介

96
siddontang 595a1b60 08f6 4beb 998f 2bf55e230555
2017.12.09 16:39 字数 3516

最近看到 Netflix 的混沌工程的介绍,感触颇深。在 TiDB 里面,我们为了保证系统的健壮性,也做了很多工作。在内部我们开始叫做 stability test,后来进化成 Schodinger 平台。之前我一直苦于没有没法对我们做得工作进行归类,毕竟它可能是一个 test,但又比 test 做得多一点,现在知道,原来我们一直做的其实算是一门工程实践。

既然是工程,那么就会有方法论,也就能详细的归纳总结出来实施的步骤,这样后面的同学就能非常快速的学习掌握,而不会像我们之前那么漫无目的的探索了,但我们现在还做不到这一步,而且相比 Netflix,我们还有很多路要走。所以我打算深入研究下 Netflix 是如何做的,在想想如何提升我们自己的工作。

介绍

首先,我们需要知道,混沌工程到底是什么。根据 Netflix 的解释,混沌工程师通过应用一些经验探索的原则,来学习观察系统是如何反应的。这就跟科学家做实验去学习物理定律一样,混沌工程师通过做实验去了解系统。

image.png

上图就是混沌工程的典型代表 - 猴子。拜 Netflix 所赐,现在大部分的混沌工程项目都叫做 Monkey,也就是一只讨厌的猴子,在你的系统里面上蹦下窜,不停捣乱,直到搞挂你的系统。

然后,我们需要知道,为什么需要混沌工程。应用混沌工程能提升整个系统的弹性。通过设计并且进行混沌实验,我们可以了解到系统脆弱的一面,在还没出现对用户造成伤害之前,我们就能主动发现这些问题。

混沌工程其实是很重要的,但我之前一直以为混沌工程就是测试,但它们还是有区别的。虽然混沌工程跟传统测试通常都会共用很多测试工具的,譬如都会使用错误注入工具,但混沌工程是通过实践对系统有更新的认知,而传统测试则是使用特定方式对某一块进行特定测试。譬如在传统测试里面,我们可以写一个断言,我们给定特定的条件,产生一个特定的输出,如果不满足断言条件,测试就出错了,这个其实是具有很明确的特性。但混沌工程是试验,而试验会有怎样的新信息生成,我们是不确定的。譬如我们可以进行下面的这些试验:

  • 模拟整个 IDC 当掉
  • 选择一部分网络连连接注入特定时间的延迟
  • 随机让一些函数抛出异常
  • 强制 NTP 时间不同步
  • 生成 IO 错误
  • 榨干 CPU

这些试验到底会有什么样的结果,有些我们可以预料,但有些可能我们就不会预先知道,只有发生了,才会恍然大悟,有一种『喔,这也会出现!』的感叹。

原则

在开始应用混沌工程之前,我们必须确保系统是弹性的,也就是当出现了系统错误我们的整个系统还能正常工作。如果不能确保,我们就需要先考虑提升整个系统的健壮性了,因为混沌工程主要是用来发现系统未知的脆弱一面的,如果我们知道应用混沌工程能导致显而易见的问题,那其实就没必要应用了。

虽然 chaos 有混乱的意思,但混沌工程并不是制造混乱。相反,我们可以认为混沌工程是用经验的方法来定位问题的一门实验学科。譬如,我们可以思考:『如果我们在系统里面注入混乱了,这个系统会怎样?』,或者『我们系统离混乱的边界还有多远?』。所以,为了更好的进行混沌试验,我们需要有一些原则来进行指导。

假定稳定状态

在一个复杂系统里面,我们有特别多的组件,有很多不同的输入输出,我们需要有一个通用的方式来区别系统哪些行为是可以接受的,而哪一些则是不合适的。我们可以认为当系统处于正常操作时候的状态就是稳定状态。

通常我们可以通过自己测试,来确定一个系统的稳定状态,但这个方法当然是比较低效的,另一种更常用的做法就是收集 metric 信息,不光需要系统的 metric,也需要服务自身的 metric,但收集 metric 需要注意实时性的问题,你如果收集一个每月汇总的 metric 信息,其实没啥用,毕竟系统是实时变化的。现在市面上面有很多不错的开源 metric 系统,譬如我们就在用的 Prometheus。

当我们能收集到信息之后,就需要用这些信息去描述一个稳定状态。这个难度比较大,因为不同的业务是不一样的,即使是同一个业务,不同时间也可能变化很大。但也有一些方法,譬如我们可以根据前面一段时间譬如上周的 metric 的曲线得到一个大概合理的稳定状态,也可以自己做很多压力测试,得到相关的数据。

当有了 metric 以及知道稳定状态对应的 metric 是怎样之后,我们就可以通过这些来考虑混沌实验了。思考当我们注入不同的事件到系统中的时候,稳定状态会如何变化,然后我们就会开始做实验来验证这个假设。

变更真实世界事件

在真实的世界中,我们可能遇到各种各样的问题,譬如:

  • 硬件故障
  • 网络延迟和隔离
  • 资源耗尽
  • 拜占庭错误
  • 下游依赖故障

做混沌试验的时候需要模拟这些故障,来看系统的状态。但从成本上面考虑,我们并不需要模拟所有的故障,仅仅需要考虑那些会比较频繁发生,而且模拟之后会很有效果的。在 TiDB 里面,我们主要就是模拟的网络,文件系统的故障,但现在看起来还是不够,后面会逐渐的添加更多。

在生产中进行试验

要看混沌试验有没有效果,在真实生产环境中进行验证是最好的方法。但我相信大部分的厂商还没这么大的魄力,这方面 Netflix 就做的很猛,他们竟然能够直接停掉 Amazon 上面的一个 Zone。

如果不能再生产环境中试验,一个可选的方法就是做 shadow,也就是通常的录制生产环境的流量,然后在测试中重放。或者模拟生产环境,自己造数据测试。

自动化持续执行

最开始执行混沌试验,我们可能就是人工进行,试验进行的过程中,看 metric,试验结束之后,通过收集的 metric 在对比,看系统的状态。这个过程后面完全可以做成自动化的,也就是定期执行,或者系统发布的时候执行等。

如果能做到自动化执行试验,已经很不错了,但我们可以做的更多,甚至有可能根据系统的状态自动化的生成相关的试验,这个 Netflix 已经做了很多研究,但我们这边还处于初级阶段,没考虑过自动化生成的问题。

最小化影响范围

在进行混沌试验的时候,一定要注意影响的范围,如果没预估好,把整个服务搞挂了,导致所有的用户都没法使用,这个问题还是很严重的。

通常都会使用一种 Canary 的方法,也就是类似 A/B 测试,或者灰度发布这种的,在 Canary 集群这边做很多试验。也就是说,如果真的搞坏了,那也只是一小部分用户被搞坏了,损失会小很多。

在 Canary 里面还有一个好处,因为我们知道整个系统的稳定状态,即使不做混沌测试,也可以观察 Canary 里面的状态是不是跟之前的稳定状态一致的,如果不一致,那也可能有问题。

实践

上面我们说了相关的原则,那么如何开始进行一次混沌试验呢?其实很简单,只要做到如下步骤就可以:

  1. 选择一个假设
  2. 选择试验的范围
  3. 明确需要观察的 metric 指标
  4. 通知相关的团队
  5. 执行试验
  6. 分析结果
  7. 增大试验的范围
  8. 自动化

譬如对于 TiDB 来说,譬如我们可以选择验证网络隔离对系统的影响,我们会:

  1. 假设一台机器的网络隔离对整个系统不会造成影响
  2. 将一个用户一台 TiKV 进行网络隔离
  3. 观察 QPS,latency,等指标
  4. 通知负责这个用户的 OPS 同学
  5. 断网
  6. 一段时间之后分析 metric
  7. 在多个集群测试
  8. 将这个流程自动化

上面只是一个简单的例子,实际还会复杂很多,但通过这种方式做了操作了很多次之后,大家都会更加熟悉自己的系统。

混沌成熟度模型

这里在简单说说混沌成熟度模型,Netflix 总结了两个维度,一个是复杂度,一个就是接受度。前者表示的是混沌工程能有多复杂,而后者则表示的是混沌工程被团队的接受程度。

复杂度分为几个阶段:

  • 初级

    • 试验没有在生产中进行
    • 进程被收工管理
    • 结果只反映系统 metric,没有业务的
    • 只有简单的事件进行试验
  • 简单

    • 试验可以在类生产环境中进行
    • 能自动启动执行,但需要人工监控和终止
    • 结果能反应一些聚合的业务 metric
    • 一些扩展的事件譬如网络延迟可以进行试验
    • 结果可以手工汇总和聚合
    • 试验是预先定义好的
    • 有一些工具能进行历史对照
  • 复杂

    • 试验直接在生产环境中进行
    • 启动,执行,结果分析,终止都是自动完成
    • 试验框架集成在持续发布
    • 业务 metrics 会在实验组和控制组进行比较
    • 一些组合错误或者服务级别影响的事件可以进行试验
    • 结果一直可以追踪
    • 有工具可以更好的交互式的对比试验和控制组
  • 高级

    • 试验在每个开发步骤和任意环境都进行
    • 设计,执行和提前终止这些全部都是自动化的
    • 框架跟 A/B 或者其他试验系统整合
    • 一个事件譬如更改使用模式和返回值或者状态变更开始进行试验
    • 试验包括动态作用域和影响,可以找到突变点
    • 通过试验结果能保护资产流失
    • 容量预测可以通过试验分析提前得出
    • 试验结果可以区分不同服务的临界状态

而接受度也有几个阶段:

  • 在暗处

    • 相关项目不被批准
    • 很少系统被覆盖
    • 很少或者没有团队有意识
    • 早期接受者不定期的进行试验
  • 有投入

    • 试验被被官方批准
    • 部分资源被用于实践
    • 多个团队有兴趣并投入
    • 少部分关键服务不定期进行试验
  • 接受

    • 有专门的 team 进行混沌工程
    • 应急响应被集成到框架,从而可以创建回归试验
    • 多数关键系统定期进行混沌试验
    • 一些试验验证会在应急响应或者游戏时间被临时执行
  • 文化

    • 所有关键服务都有频繁的混沌试验
    • 大多数非关键服务定期进行
    • 混沌试验已经是工程师的日常工作
    • 默认所有系统组件都必须参与,如果不想进行,需要有正当的理由

如果按照这上面两个维度来看,我们其实做的并不好,所以还有很大的提升空间。

总结

上面就是对混沌工程的简单介绍,后面我会考虑依照混沌工程的原则,开始工程化的实践。虽然之前我们做过很多工作,如果能用理论开始指导,就能更进一步了。

编程之路
Web note ad 1