混沌工程帮助开发人员通过在分布式系统中模拟故障来提高系统的弹性。本文介绍了三个知名的开源混沌工程框架:Chaos Monkey、Chaos Blade 和 Chaos Mesh,它们分别来自 Netflix、阿里巴巴和 PingCAP。这些框架具有不同的特点和优势,例如 Chaos Monkey 是混沌工程的创始工具,Chaos Blade 支持丰富的实验场景和扩展性,而 Chaos Mesh 提供了强大的故障模拟和易用的可视化操作。混沌工程的实施需要满足四个先决条件:具备面向失败的设计和技术文化、系统弹性、合适的实验环境和有效的监控系统。加入我们,一起探讨混沌工程的实践和前景!
一,混沌工程定义和来源
混沌工程的定义
1,混沌工程是在分布式系统上进行实验的学科,目的是建立对系统抵御生产环境中失控条件的能力以及信心。
混沌工程,是一种提高技术架构弹性能力的复杂技术手段,旨在将故障扼杀在襁褓之中,也就是在故障造成中断之前将它们识别出来。通过主动制造故障。
简单的来讲:就是通过故障注入的方式,来验证系统的可靠性,同时为对应人员处理故障问题提供经验,来预防可能会出现的线上故障问题。(其实类似于消防演练)
2,对于我们日常生产过程中,以下这些都是可能会发生的故障,而这些故障可能任何一个都会引发巨大的线上事故,但如果我们在实际的生产能够提前把这些给演练好,那实际上就能把这些故障给扼杀在摇篮中
混沌工程的来源
2008年8月, Netflix 主要数据库的故障导致了三天的停机,从而导致了网飞公司对于整体线上故障有了一个新的看法,为此, Netflix 工程师创建了 Chaos Monkey ,会随机终止在生产环境中运行的 EC2 实例。工程师可以快速了解他们正在构建的服务是否健壮,有足够的弹性,可以容忍计划外的故障。至此,混沌工程开始兴起。
为什么需要混沌工程
分布式系统日益复杂,而且在系统逐渐云化的背景下,系统的稳定性受到很大的挑战。这里从四个角色来说明混沌工程的重要性。
-
对于架构师来说,可以验证系统架构的容错能力,比如验证现在提倡的面向失败设计的系统;
-
对于开发和运维,可以提高故障的应急效率,实现故障告警、定位、恢复的有效和高效性。
-
对于测试来说,可以弥补传统测试方法留下的空白,之前的测试方法基本上是从用户的角度去做,而混沌工程是从系统的角度进行测试,降低故障复发率。
-
对于产品和设计,通过混沌事件查看产品的表现,提升客户使用体验。所以说混沌工程面向的不仅仅是开发、测试,拥有最好的客户体验是每个人的目标 所以实施混沌工程,可以提早发现生产环
其实我认为,混沌工程最关键的,最本质的点在于提前把坑踩了,即使这个坑目前没人能触发到;因为根据墨菲定律,坑在那,总会有人踩的,与其不知道什么时候踩,不知道坑有多大,还不如自己先带好降落伞,然后往坑里冲下去之后,再把坑填平。
二,混沌工程的原则和理论
混沌工程的原则:
这个规则主要是网飞公司所提出来的,其实可以分为五大原则,详细可看这里principlesofchaos.org/zh/
1,建立一个围绕稳定状态行为的假说:
——“稳定状态”是指系统正常运行时的状态。具体来说,系统的稳定状态可以通过一些指标来定义,当系统指标在测试完成后,无法快速恢复稳态要求,可以认为这个系统是不稳定的。
这条原则想表达的意思是,我们的系统的稳定是否能量化,一般在这上面的实践,都是通过业务监控和基础监控相结合的方式来实现
然后通过一个量化的稳定状态,来做对应实施混沌工程后的假设。
比如:在代金券业务上,有两台机器同时操作,我们对应的混沌操作是——直接关掉某台机器,在实施这一操作之前,假设挂了这台机器后,自身业务(锁定量,消耗量)不会受影响,且对应另外一台机器的CPU和QPS占比会变高
若混沌注入后,按假设那样走,则验证代金券系统是无状态的。且单点故障不会对其有影响
若另外一台机器的CPU过高,导致机器自身挂掉或业务受影响,则证明代金券系统本身部署有相关的问题,需要修正
2,多样化真实世界的事件
——混沌变量反映了现实世界中的事件。 我们可以通过潜在影响或估计频率排定这些事件的优先级。考虑与硬件故障类似的事件, 如服务器宕机、软件故障 (如错误响应) 和非故障事件 (如流量激增或伸缩事件)。 任何能够破坏稳态的事件都是混沌实验中的一个潜在变量。
这条原则想表达的意思是,决定引入哪些事件时,我们应当估算这些事件发生的频率和影响范围,然后权衡引入他们的成本和复杂度。我们不需要穷举所有可能对系统造成改变的事件,只需要注入那些频繁发生且影响重大的事件,同时要足够理解会被影响的故障域(故障的影响范围和隔离范围被称为故障的故障域)。
换句话来说,我们要引入的混沌事件,必须是经常发生的,重要的,而且影响范围是能够被量化知晓的。
比如说,在我们自身的短信业务上,可能的故障有缓存插件挂掉,接口超时等等,而对于短信来说,缓存挂掉对于短信业务自身来说,不会是一个经常发生的事件,而且就算缓存插件发生故障,也不会对短信业务自身有任何的影响,因此引入缓存插件故障这一混沌事件,是不适合的;
但对于短信业务来说,短信发送的成功率由于运营商的缘故,在发送短信的时候会经常出现下降的趋势,而且之前也出现过某个运营商通道出现延迟导致发送短信受影响的情况存在,在这一情况上,我们的故障事件就可以为————短信运营商通道的配置错误
在此上面,我们定义的假设就可以是:短信成功率的监控会告警,且通过看板能看到具体是哪个运营商通道的发送错误,且对应的运营商通道能进行切换或运营商通道配置修改过后能恢复正常。
3,在生产环境中运行实验
——系统的行为会依据环境和流量模式都会有所不同。 由于资源使用率变化的随时可能发生, 因此通过采集实际流量是捕获请求路径的唯一可靠方法。 为了保证系统执行方式的真实性与当前部署系统的相关性, 混沌工程强烈推荐直接采用生产环境流量进行实验。
这条原则想表达的意思是环境越真实越能验证实际问题,但也不是说必须在生产环境中执行,只是实验环境越真实,混沌工程越有价值,如果知道系统在某个故障场景下不具备容灾能力,不可以执行此混沌实验,避免资损发生。
4,持续自动化运行实验
——手动运行实验是劳动密集型的, 最终是不可持续的。所以我们要把实验自动化并持续运行,混沌工程要在系统中构建自动化的编排和分析。最开始执行混沌实验,可能就是手动执行,但是实验的手动运行工作属于劳动力密集型任务,因此难以长久持续。相反的,我们应该自己投入精力来开发混沌工程的工具和平台,以期不断降低创建新实验的门槛,并能够完全自动运行这些实验。
这条原则的意思是,混沌事件的注入,一定要有平台或者服务去操作,不应该是人为的(类似于自动化测试脚本和gitlab CI)
5,最小化爆炸半径
——在生产中进行试验可能会造成不必要的客户投诉。虽然应该允许一些短期的负面影响, 但混沌工程师的责任和义务是确保这些后续影响最小化且被考虑到。
这条原则的意思是,要防止预期外的资损发生,可以通过环境隔离或者故障注入工具提供的配置粒度来控制。(其实意思就是别因为混沌事件的注入而真的把环境搞炸了或者搞了一个很大的线上事故)
混沌成熟度模型
这里在简单说说混沌成熟度模型,Netflix 总结了两个维度,一个是复杂度,一个就是接受度。前者表示的是混沌工程能有多复杂,而后者则表示的是混沌工程被团队的接受程度。
复杂度分为几个阶段:
-
初级
- 试验没有在生产中进行
- 进程被收工管理
- 结果只反映系统 metric,没有业务的
- 只有简单的事件进行试验
-
简单
- 试验可以在类生产环境中进行
- 能自动启动执行,但需要人工监控和终止
- 结果能反应一些聚合的业务 metric
- 一些扩展的事件譬如网络延迟可以进行试验
- 结果可以手工汇总和聚合
- 试验是预先定义好的
- 有一些工具能进行历史对照
-
复杂
- 试验直接在生产环境中进行
- 启动,执行,结果分析,终止都是自动完成
- 试验框架集成在持续发布
- 业务 metrics 会在实验组和控制组进行比较
- 一些组合错误或者服务级别影响的事件可以进行试验
- 结果一直可以追踪
- 有工具可以更好的交互式的对比试验和控制组
-
高级
- 试验在每个开发步骤和任意环境都进行
- 设计,执行和提前终止这些全部都是自动化的
- 框架跟 A/B 或者其他试验系统整合
- 一个事件譬如更改使用模式和返回值或者状态变更开始进行试验
- 试验包括动态作用域和影响,可以找到突变点
- 通过试验结果能保护资产流失
- 容量预测可以通过试验分析提前得出
- 试验结果可以区分不同服务的临界状态
而接受度也有几个阶段:
-
在暗处
- 相关项目不被批准
- 很少系统被覆盖
- 很少或者没有团队有意识
- 早期接受者不定期的进行试验
-
有投入
- 试验被被官方批准
- 部分资源被用于实践
- 多个团队有兴趣并投入
- 少部分关键服务不定期进行试验
-
接受
- 有专门的 team 进行混沌工程
- 应急响应被集成到框架,从而可以创建回归试验
- 多数关键系统定期进行混沌试验
- 一些试验验证会在应急响应或者游戏时间被临时执行
-
文化
-
所有关键服务都有频繁的混沌试验
-
大多数非关键服务定期进行
-
混沌试验已经是工程师的日常工作
-
默认所有系统组件都必须参与,如果不想进行,需要有正当的理由
-
如果要实际实现混沌工程,应该由浅至深,从复杂度和接受度均低的情况下,逐步演进到复杂度高且接受度高的情况。
三,混沌工程如何实现?
通常来说,混沌工程的如何实现落地可分为两部份,一部分是具化层面的,具体到了一次混沌实验相关模型步骤的定义和实现;另一部分则是抽象化的,如何对混沌平台进行演进和在企业内部进行推广并实际使用。
混沌实验执行的步骤:
1,根据混沌模型明确混沌实验的细节:
- Target:实验靶点,指实验发生的组件,例如 容器、应用框架(Dubbo、Redis、Zookeeper)等。
- Scope:实验实施的范围,指具体触发实验的机器或者集群等。
- Matcher:实验规则匹配器,根据所配置的 Target,定义相关的实验匹配规则,可以配置多个。由于每个 Target 可能有各自特殊的匹配条件,比如 RPC 领域的 HSF、Dubbo,可以根据服务提供者提供的服务和服务消费者调用的服务进行匹配,缓存领域的 Redis,可以根据 set、get 操作进行匹配。
- Action:指实验模拟的具体场景,Target 不同,实施的场景也不一样,比如磁盘,可以演练磁盘满,磁盘 IO 读写高,磁盘硬件故障等。如果是应用,可以抽象出延迟、异常、返回指定值(错误码、大对象等)、参数篡改、重复调用等实验场景
本质上是对这四点的明确
-
对什么做混沌实验
-
混沌实验实施的范围是是什么
-
具体实施什么实验
-
实验生效的匹配条件有哪些
举个例子:
对 Dubbo 组件(Target)进行故障演练,演练的是 10.0.0.1 主机(Scope)的应用,调用 com.example.HelloService@1.0.0 (Matcher)服务延迟 3s(Action)。这就是一次完整的混沌实验
2,根据罗列好的细节,通过平台编排或手工执行一次或多次混沌实验;若出现意料外的异常,则停止执行,若未出现异常,则继续执行
3,重复1至n次混沌实验,得出具体结果后,进行分析复盘。
混沌平台演进的过程:
从线下到生产
此条指的是环境的选择。
一般认为,混沌工程只有在生产环境实验才有意义;但我们认为一种比较温和的实验步骤是从线下逐渐走到生产。这也是综合考虑,从线下开始着手会让各方都比较放心。不过对于分布式系统而言,部署不同、流量不同都会带来不一样的结果,唯有在生产进行实验才能真正验证。一条比较好的路径是:
测试环境-> 预发布环境 -> 预览环境特定流量 -> 生产集群生产流量
从小到大
此条指的是故障范围的选择。
我们推荐故障应该从小范围,较温和的开始。当建立了足够的信心之后,再进一步扩大故障范围。一条比较好的路径是:
可控流量 -> 单个接口 -> 单机 -> 单集群 -> 单机房 -> 全链路
从面向过去到面向未来
此条指的是故障类型的选择。
我们认为曾发生过的故障是实验优先级最高的。而人类历史告诉我们,人们总是会在一个地方反复跌倒;生产发生过的故障,很有可能再次发生;且同样可能会在其他链路上发生类似故障。因此一条较好的路径是:
重现历史事故的故障 -> 来自历史事故的故障类型 & 相似链路 -> 各种随机故障 & 全链路。
从工作日到休息日
此条指的是混沌工程实验时间的选择。
休息日代指任意时间。我们推荐实验时间从工作日开始尝试,最优的是工作日下午 3 点左右(各业务根据自身高低峰期再行考虑)。这个时间段,相关人员一般都在工作岗位上,有任何情况都能及时处理。混沌工程的早期目标就是为了在可控的环境中提前暴露问题。当然,随着混沌工程不断走向成熟,我们将会慢慢开始尝试在任意时间进行实验。一条比较好的路径是:
工作日下午 -> 工作日晚上 -> 休息日 -> 随机时间
四,混沌工程的开源框架
1,Chaos-Monkey
chaos-monkey是netfix研发的第一代混沌工程(Chaos Monkey is a resiliency tool that helps applications tolerate random instance failures.),由于缺点太过明显:,除了支持关闭服务实例之外,还支持其他一些操作系统级别的破坏,例如提高CPU占用率、阻塞网络IO、写满硬盘空间等,对于可扩展性来说还是极差的,这里不做过多描述。(可能是netfix将精华部分自己封藏了不公开,但作为混沌工程的祖师爷,还是有他的历史背景和亮点的)
2,Chaos-Blade:
ChaosBlade 是阿里巴巴开源的一款遵循混沌工程原理和混沌实验模型的实验注入工具,帮助企业提升分布式系统的容错能力,并且在企业上云或往云原生系统迁移过程中业务连续性保障。
ChaosBlade 不仅使用简单,而且支持丰富的实验场景,场景包括:
-
基础资源:比如 CPU、内存、网络、磁盘、进程等实验场景;
-
Java 应用:比如数据库、缓存、消息、JVM 本身、微服务等,还可以指定任意类方法注入各种复杂的实验场景;
-
C++ 应用:比如指定任意方法或某行代码注入延迟、变量和返回值篡改等实验场景;
-
Docker 容器:比如杀容器、容器内 CPU、内存、网络、磁盘、进程等实验场景;
-
云原生平台:比如 Kubernetes 平台节点上 CPU、内存、网络、磁盘、进程实验场景,Pod 网络和 Pod 本身实验场景如杀 Pod,容器的实验场景如上述的 Docker 容器实验场景;
优点:
-
使用golang语言编写的,对云原生有很大的支持,能结合对应的k8原生命令,来执行,具体可看github.com/chaosblade-…
-
实验场景可扩展和自定义,只要按照他给的API接口去实现即可
缺点:
对golang应用的支持还未被开发,目前支持C++和java应用的实验场景
3,Chaos-mesh
Chaos Mesh是PIngCap开源的一个云原生混沌工程平台,提供丰富的故障模拟类型,具有强大的故障场景编排能力,方便用户在开发测试中以及生产环境中模拟现实世界中可能出现的各类异常,帮助用户发现系统潜在的问题。Chaos Mesh 提供完善的可视化操作,旨在降低用户进行混沌工程的门槛。用户可以方便地在 Web UI 界面上设计自己的混沌场景,以及监控混沌实验的运行状态。
优点;
-
- 核心能力稳固:Chaos Mesh 起源于 TiDB 的核心测试平台,发布初期即继承了大量 TiDB 已有的测试经验。
- 被充分验证:Chaos Mesh 被众多公司以及组织所使用,例如腾讯和美团等;同时被用于众多知名分布式系统的测试体系中,例如 Apache APISIX 和 RabbitMQ 等。
- 系统易用性强:图形化操作和基于 Kubernetes 的使用方式,充分利用了自动化能力。
- 云原生:Chaos Mesh 原生支持 Kubernetes 环境,提供了强悍的自动化能力。
- 丰富的故障模拟场景:Chaos Mesh 几乎涵盖了分布式测试体系中基础故障模拟的绝大多数场景。
- 灵活的实验编排能力:用户可以通过平台设计自己的混沌实验场景,场景可包含多个混沌实验编排,以及应用状态检查等。
- 安全性高:Chaos Mesh 具有多层次安全控制设计,提供高安全性。
- 活跃的社区:Chaos Mesh 为全球知名开源混沌测试平台,CNCF 开源基金会孵化项目。
- 强大的扩展能力:Chaos Mesh 为故障测试类型扩展和功能扩展提供了充分的扩展能
五,混沌工程的实现
对于混沌工程的实现,其实可以分为这四个先决条件。
混沌工程的先决条件
- 团队具备面向失败设计(可以使系统暴露出已有问题的设计)和拥抱失败的技术文化
- 系统具备一定的弹性来应对破坏性演练中的一些异常事件
- 具备一套破坏性演练的环境:理想实践是直接在生产环境中进行实验,其次是在离生产环境越近的地方进行实验(如预发环境)。如在预发环境进行演练,确保预发和生产环境保持一致很重要,只有这样才能体现演练意义
- 具备配套的监控系统,用于判断系统当前的各项状态。如果没有对系统行为的可见能力,那么也就无法从实验中得出有效的结论
而对于37手游而言,这几个先决条件已经是我们所具备的,对于这方面的实现,我们也在探索,欢迎大家可以一起讨论~