0x0034's Blog.

Chaos Enginnering

字数统计: 2.9k阅读时长: 11 min
2021/12/09

简述

起源

Netflix工程师创建了Chaos Monkey,使用该工具可以在整个系统中在随机位置引发故障。正如GitHub上的工具维护者所说,“Chaos Monkey会随机终止在生产环境中运行的虚拟机实例和容器。”通过Chaos Monkey,工程师可以快速了解他们正在构建的服务是否健壮,是否可以弹性扩容,是否可以处理计划外的故障。

2012年,Netflix开源了Chaos Monkey。今天,许多公司(包括谷歌,亚马逊,IBM,耐克等),都采用某种形式的混沌工程来提高现代架构的可靠性。 Netflix甚至将其混沌工程工具集扩展到包括整个“Simian Army(中文可以译为猿军)”,用它攻击自己的系统。

目的

混沌工程,是一种提高技术架构弹性能力的复杂技术手段。Chaos工程经过实验可以确保系统的可用性。混沌工程旨在将故障扼杀在襁褓之中,也就是在故障造成中断之前将它们识别出来。通过主动制造故障,测试系统在各种压力下的行为,识别并修复故障问题,避免造成严重后果.

混沌工程与故障注入,故障测试的关系

混沌工程和其他方法之间的主要区别在于,混沌工程是一种生成新信息的实践,而故障注入是测试一种情况的一种特定方法。当想要探索复杂系统可能出现的不良行为时,注入通信延迟和错误等失败是一种很好的方法。但是我们也想探索诸如流量激增,激烈竞争,拜占庭式失败,以及消息的计划外或不常见的组合。如果一个面向消费者的网站突然因为流量激增而导致更多收入,我们很难称之为错误或失败,但我们仍然对探索系统的影响非常感兴趣。同样,故障测试以某种预想的方式破坏系统,但没有探索更多可能发生的奇怪场景,那么不可预测的事情就可能发生。

社区现状

工具名称 当前版本 维护状态 构建语言 设计场景 特定依赖
ChaosMonkey v2.0.2 2016停止维护 Go EC2 实例 基于spinnaker
chaostoolkit 1.10.1 维护中 Python 集成多Iaas Paas平台,可使用更多个故障注入工具定制场景 插件的形式支持多个Iaas,Paas 包括AWS,AZure,Kubernetes
PowerfulSeal v3.3.0 维护中 Python Kill vm,container, Pods 基于openstack / AWS /实体机
toxiproxy v2.2.0 维护中 Go 模拟网络故障
pumba v0.9.0 维护中 Go kill容器, 暂停进程, 网络延迟,丢包,限流 基于Docker
chaos-monkey-spring-boot v2.5.4 维护中 Java 高延迟,异常处理,内存过高 依赖Sprint-Boot
chaosblade v1.4.0 维护中 Go 基础资源,java应用,云原生平台

原则

来源: http://principlesofchaos.org/

混沌工程是在分布式系统上进行实验的学科, 目的是建立对系统抵御生产环境中失控条件的能力以及信心。

遵循步骤

  1. 首先,用系统在正常行为下的一些可测量的输出来定义“稳定状态”。
  2. 其次,假设这个在控制组和实验组都会继续保持稳定状态。
  3. 然后,在实验组中引入反映真实世界事件的变量,如服务器崩溃、硬盘故障、网络连接断开等。
  4. 最后,通过控制组和实验组之间的状态差异来反驳稳定状态的假说。

破坏稳态的难度越大,我们对系统行为的信心就越强。如果发现了一个弱点,那么我们就有了一个改进目标。避免在系统规模化之后被放大。

高级原则

以下原则描述了应用混沌工程的理想方式,这些原则基于上述实验过程。对这些原则的匹配程度能够增强我们在大规模分布式系统的信心。

建立一个围绕稳定状态行为的假说

要关注系统的可测量输出, 而不是系统的属性。对这些输出在短时间内的度量构成了系统稳定状态的一个代理。 整个系统的吞吐量、错误率、延迟百分点等都可能是表示稳态行为的指标。 通过在实验中的系统性行为模式上的关注, 混沌工程验证了系统是否正常工作, 而不是试图验证它是如何工作的。

多样化真实世界的事件

混沌变量反映了现实世界中的事件。 我们可以通过潜在影响或估计频率排定这些事件的优先级。考虑与硬件故障类似的事件, 如服务器宕机、软件故障 (如错误响应) 和非故障事件 (如流量激增或伸缩事件)。 任何能够破坏稳态的事件都是混沌实验中的一个潜在变量。

在生产环境中运行实验

系统的行为会依据环境和流量模式都会有所不同。 由于资源使用率变化的随时可能发生, 因此通过采集实际流量是捕获请求路径的唯一可靠方法。 为了保证系统执行方式的真实性与当前部署系统的相关性, 混沌工程强烈推荐直接采用生产环境流量进行实验。

持续自动化运行实验

手动运行实验是劳动密集型的, 最终是不可持续的。所以我们要把实验自动化并持续运行,混沌工程要在系统中构建自动化的编排和分析。

最小化爆炸半径

在生产中进行试验可能会造成不必要的客户投诉。虽然对一些短期负面影响必须有一个补偿, 但混沌工程师的责任和义务是确保这些后续影响最小化且被考虑到。

混沌工程是一个强大的实践, 它已经在世界上一些规模最大的业务系统上改变了软件是如何设计和工程化的。 相较于其他方法解决了速度和灵活性, 混沌工程专门处理这些分布式系统中的系统不确定性。 混沌工程的原则为我们大规模的创新和给予客户他们应得的高质量的体验提供了信心。

爆炸半径

混沌工程变量

阿里按照优先分析P1和P2故障,按照IaaS、PaaS、SaaS层的角度绘制了故障画像,如下图:
混沌工程变量

ChaosBlade

https://github.com/chaosblade-io/chaosblade/blob/master/README_CN.md

一个简单易用且功能强大的混沌实验实施工具.
ChaosBlade 不仅使用简单,而且支持丰富的实验场景,场景包括:

  • 基础资源:比如 CPU、内存、网络、磁盘、进程等实验场景;
  • Java 应用:比如数据库、缓存、消息、JVM 本身、微服务等,还可以指定任意类方法注入各种复杂的实验场景;
  • C++ 应用:比如指定任意方法或某行代码注入延迟、变量和返回值篡改等实验场景;
  • Docker 容器:比如杀容器、容器内 CPU、内存、网络、磁盘、进程等实验场景;
  • 云原生平台:比如 Kubernetes 平台节点上 CPU、内存、网络、磁盘、进程实验场景,Pod 网络和 Pod 本身实验场景如杀 Pod,容器的实验场景如上述的 Docker 容器实验场景;

场景大图

场景大图

示例使用

OS层

CPU
1
2
# 创建 CPU 满载实验
blade create cpu load

cpu满载

CPU监控

内存
磁盘

以上请参考官方文档

JVM

prepare jvm
1
2
3
4
5
-j, --javaHome string   指定 JAVA_HOME 路径,用于指定 java bin 和 tools.jar,如果不添加此参数,默认会优先获取 JAVA_HOME 环境变量,如果获取失败,会解析指定进程参数获取 JAVA_HOME,获取失败,会使用 chaosblade 自带的 tools.jar
--pid string java 进程ID
-P, --port int java agent 暴露服务的本地端口,用于下发实验命令
-p, --process string java 进程关键词,用于定位 java 进程
-d, --debug 开启 debug 模式

指定 pid 执行 java agent 挂载

1
2
3
4
blade prepare jvm --pid 26652

# 命令也可简写为
blade p jvm --pid 26652

执行成功,会返回实验准备的 UID,例如:
{“code”:200,”success”:true,”result”:”2552c05c6066dde5”}
2552c05c6066dde5 就是实验准备对象的 UID,执行卸载操作需要用到此 UID.

create jvm
  • delay: 指定类方法调用延迟

业务代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RequestMapping(value = "async")
@ResponseBody
public String asyncHello(final String name, long timeout) {
if (timeout == 0) {
timeout = 3000;
}
try {
FutureTask futureTask = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return sayHello(name);
}
});
new Thread(futureTask).start();
return (String)futureTask.get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
return "timeout, " + e.getMessage() + "\n";
} catch (Exception e) {
return e.getMessage() + "\n";
}
}

对 sayHello 方法调用注入 4 秒延迟故障,futureTask.get(2000, TimeUnit.MILLISECONDS) 会发生超时返回:

1
blade c jvm delay --time 4000 --classname=com.example.controller.DubboController --methodname=sayHello --process tomcat
  • return: 指定类方法的返回值,仅支持基本类型、null 和 String 类型的返回值。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value = "hello")
@ResponseBody
public String hello(String name, int code) {
if (name == null) {
name = "friend";
}
StringBuilder result = null;
try {
result = new StringBuilder(sayHello(name));
} catch (Exception e) {
return e.getMessage() + "\n";
}
return result.toString() + "\n";
}
1
blade c jvm return --value hello-chaosblade --classname com.example.controller.DubboController --methodname hello --process tomcat
  • OutOfMemoryError: 内存溢出场景,命令可以简写为:blade c jvm oom

参数:

1
2
3
4
--area string        JVM 内存区,目前支持 [HEAP, NOHEAP, OFFHEAP],必填项。用Heap来表示Eden+Old,,用NOHEAP来表示metaspace,用OFFHEAP来表示堆外内存
--block string 指定对象大小,仅支持 HEAP 和 OFFHEAP 区,单位是 MB
--interval string 单位ms,默认500两次oom异常间的时间间隔,只有在非暴力模式才生效,可以减缓gc的频率,不用担心进程会无响应
--wild-mode string 默认false,是否开启暴力模式,如果是暴力模式,在OOM发生之后也不会释放之前创建的内存,可能会引起应用进程无响应

用法:

1
2
3
blade c jvm oom --area HEAP --wild-mode true --process tomcat

{"code":200,"success":true,"result":"99b9228b9632e043"}
  • throwCustomException: 指定类方法抛自定义异常,命令可以简写为 blade c jvm tce

参数:

1
2
3
4
--effect-count string     影响的请求条数
--effect-percent string 影响的请求百分比
--exception string 异常类,带全包名,必须继承 java.lang.Exception 或 java.lang.Exception 本身
--exception-message string 指定异常类信息,默认值是 chaosblade-mock-exception

用例代码:

1
2
3
4
5
6
private String sayHello(String name) throws BeansException {
demoService = (DemoService)SpringContextUtil.getBean("demoService");
StringBuilder result = new StringBuilder();
result.append(demoService.sayHello(name));
return result.toString();
}

用法:

1
2
3
blade c jvm throwCustomException --exception java.lang.Exception --classname com.example.controller.DubboController --methodname sayHello --process tomcat --effect-count 2

{"code":200,"success":true,"result":"3abbe6fe97d6bc75"}
CATALOG
  1. 1. 简述
    1. 1.1. 起源
    2. 1.2. 目的
    3. 1.3. 混沌工程与故障注入,故障测试的关系
    4. 1.4. 社区现状
  2. 2. 原则
    1. 2.1. 遵循步骤
    2. 2.2. 高级原则
      1. 2.2.1. 建立一个围绕稳定状态行为的假说
      2. 2.2.2. 多样化真实世界的事件
      3. 2.2.3. 在生产环境中运行实验
      4. 2.2.4. 持续自动化运行实验
      5. 2.2.5. 最小化爆炸半径
  3. 3. 混沌工程变量
  4. 4. ChaosBlade
    1. 4.1. 场景大图
    2. 4.2. 示例使用
      1. 4.2.1. OS层
        1. 4.2.1.1. CPU
        2. 4.2.1.2. 内存
        3. 4.2.1.3. 磁盘
      2. 4.2.2. JVM
        1. 4.2.2.1. prepare jvm
        2. 4.2.2.2. create jvm