跳到主要内容

混沌工程在建信金科的应用实践

作者:黄璐,栾琪,肖晶—— 建信金融科技,基础技术中心

当前,金融行业快速发展、各种需求日益新增、产品迅速迭代,随着系统规模的膨胀,传统的单体架构已经不能满足系统的发展,分布式微服务架构正在越来越多地被应用到业界中。金融行业涉及大量的资金交易,涵盖了多数据中心、多活、灾备、容器、虚拟机等复杂基础设施,系统间的交互非常复杂。分布式技术的应用,使得基础设施相比传统架构更为复杂、系统的运行态存在着不确定性,而金融业务的特点对系统的稳定性、可用性、可靠性都有着极高的要求。

传统的高可用测试,是通过经验来对系统的已知可能情况、预先注入特定故障、对测试结果进行判断来发现系统的影响,这种测试方法属于给定条件下的变量输入进行验证,测试范围是有限的,对于复杂的分布式系统,很难覆盖真实生产中遇到的各种情况。用传统高可用测试来保障分布式系统的可用性,遇到了非常大的挑战。为解决这个问题,建信金科引入了混沌工程,通过进行混沌实验及系统常态化故障演练,发现并修复系统中的潜在问题,从而提升分布式系统的可用性。

混沌工程是什么?

混沌工程是在分布式系统上进行实验的学科,目的是建立对系统抵御生产环境中失控条件的能力及信心。它起源于 2010 年 Netflix 创建的 Chaos Monkey,通过在系统中随机注入不同类型的故障,尽可能多地识别出易出故障的环节,从而可以有针对性地对系统进行加固和防范。

对比传统的高可用测试,混沌工程具有以下的差异:

差异性传统高可用测试混沌工程
实验范围实验的范围是比较局限的实验的可能性是无限的
实验目的对系统已知的可能取值进行测试获得更多关于系统新认知进行实验
测试方法特定的断言,测试会产生二元的结果,非真即假系统架构不同,实验千变万化,结果不同
思维方式按照一定的预先计划注入注入故障探索性地主动去寻找故障

表 1 传统高可用测试 vs 混沌工程

传统的高可用测试覆盖的范围有限,混沌工程通过实验的方法,通过随机性来获取对系统情况的认知,根据不同的分布式系统架构和不同的核心业务价值,实验可以千变万化,得出不同的结果。这种通过实验来探索性地主动寻找故障的方法,它和已有的测试已知属性的方法有本质上的区别,可以帮助获得更多的关于系统的新认知,通常能开辟出一个更广袤的对复杂系统的认知空间。

历经多年,混沌工程被越来越多的公司实践用以提高系统架构的可靠性。许多公司包括 Google、Amazon、Microsoft、阿里巴巴、腾讯、美团、京东、奇虎 360、网易等都在研究自己的混沌工程,在金融领域,建信金科也启动了分布式架构转型,并通过引入混沌工程来保障分布式系统的高可用能力。

混沌工程的技术调研及工具选型

混沌工程的实验工具,目前开源的有 Chaosmesh、Chaosblade、Litmus、Choas Monkey 等,使用这些开源工具可以注入各种类型的故障,进行一定的实验编排。

建信金科在实施混沌工程及故障演练平台研发应用及实践前,对目前现有的混沌测试工具进行调研,分析总结了各个工具故障注入的能力,如下表所示:

工具总结分类工具名称核心注入故障能力
代码层次ByteMonkey字节码注入 JVM 类应用
运维层次Chaostoolkit可集成其他注入故障工具以及监控平台,定制故障场景和监控观测指标
侧重容器故障Pumba杀掉、停止容器,网络延迟、丢包
ChaosMesh支持 pod、压力类、网络类、IO 类、时钟、kernel、DNS 等多种故障,具备场景编排能力
Litmus杀掉 pod、容器,网络延迟、丢包
KubeMonkeyChaosMonkey 的 k8s 集群实现,随机删除集群 pod
ChaosMonkey终止容器实例
Blockade网络故障、网络分区
侧重虚拟机故障Chaosd支持磁盘类、进程类、压力类、网络类、JVM 类等多种故障
支持容器和虚拟机故障chaosblade支持 pod、磁盘、网络、压力、JVM、数据库、消息队列多种故障,不具备场景编排能力

表 2 混沌工具调研对比表

基于对建信金科金融业务场景和分布式系统架构、运行环境的分析,建信金科的混沌工程需要具备统一的场景编排模型,对于底层设施而言,既能编排物理机/虚拟机又能编排容器故障的复杂场景,同时在实验的过程中,具备可观测性、可快速跟踪定位问题。然而,业界目前还没有一套工具能够同时满足上述要求。在这种情况下,建信金科决定在已有开源工具的基础上、建设自有的混沌工具及故障演练平台以满足需求。

当前开源的混沌工具的运行环境及故障注入能力的侧重点不同,结合建信金科现有的技术栈、对编排能力以及支持故障注入类型的需求,在选型时选取分别偏重于虚拟机和容器环境、故障注入类型更加全面的 ChaosMesh 和 Chaosd 开源框架。这两个框架的功能点比较全面,已覆盖 K8s、网络、磁盘、CPU、内存、IO、JVM 等多个类型故障,但在选型初期,其还未覆盖数据库、缓存、消息队列等特定组件异常。对于分布式系统来说,数据库、缓存、消息队列都是非常重要的组件,这些故障注入是必不可少的,所以,对上述两个框架进行了二次开发,扩展其故障注入的能力。另外,虽然 ChaosMesh 和 Chaosd 是两个不同的框架,但隶属于同一个开源维护者 PingCAP,其编码语言一致,内部引用关系清晰,两个框架可以实现互相调用聚合完成更强大的功能。因此,建信金科在 ChaosMesh 场景编排能力的基础上,抽象并设计统一编排模型,对编排引擎进行优化,统一调度 ChaosMesh 和 Chaosd,达到利用一个故障演练平台同时编排注入虚拟机和容器两种不同底层基础设施复杂故障场景的需求。

基于混沌工程的故障演练平台

建信金科在混沌工程上的研发应用,主要包含了两大部分:对开源混沌工具的研发拓展、基于混沌工程搭建的故障演练平台。

(一)建信金科混沌工具支持的故障

建信金科混沌工程故障类型.png

图 1 建信金科混沌工具支持的故障类型

建信金科经过对 ChaosMesh 和 Chaosd 的二次研发,如上图所示,目前该混沌工具支持虚拟机/K8s 容器不同底层基础设施的故障模拟,同时涵盖了磁盘、网络、进程、文件、JVM、压力、IO、DNS、内核、HTTP、生命周期、时钟偏移等多种故障类型。具体到每种故障类型,又包含了常见的磁盘读写、磁盘填充、杀死进程、CPU 打满、网络延迟、网络丢包等典型故障。该混沌工具支持的故障类型丰富,同时还可以借助故障演练平台的实验编排功能,构建复杂的故障演练场景,满足模拟生产环境中真实复杂场景的需求。

(二)建信金科故障演练平台整体架构

建信金科故障演练平台整体架构图.png

图 2 建信金科故障演练平台整体架构图

混沌工程故障演练平台的整体架构如上图所示,各个模块之间通过一定的调用关系来完成每次故障的演练模拟。其中,权限管理模块是一个比较独立的模块,主要管理整个平台的用户权限;场景管理模块是整个故障演练的入口,串联起发压监控、混沌实验一系列故障演练的步骤;对于有价值的故障场景则可以通过推送沉淀到专家库,并开放给所有用户共享使用;实验报告模块为已结束的实验提供了可视化的聚合报告;环境管理模块则是发压监控的前置工作,用户可以在此上传压测脚本、被测环境以及发压插件,保证后续发压步骤的顺利执行。

通过打造故障演练平台,可以实现混沌实验与压测、监控的集成整合,通过专家库中的实际案例沉淀与调用,使混沌实验具备更好的操作性与可观测性,从而达到混沌实验能方便进行常态化全链路压测与监控的目的。

(三)故障演练平台核心功能模块

建信金科故障演练平台核心模块图.png

图 3 建信金科故障演练平台核心模块图

整个故障演练平台主要包含权限管理、发压监控、混沌实验、实验报告、专家库五个核心功能模块。

  1. 权限管理: 对平台的登录用户进行权限级别、环境使用、混沌实验各个维度的权限管理,达到不同程度的隔离。

  2. 发压监控:发压提供了整个故障演练过程的背景压力用以模拟真实生产环境的流量,监控则对整个发压过程中涉及到的服务器资源、压力机资源、业务指标、系统指标、错误信息等各种指标进行实时的监控展示,同时还对演练过程中的混沌实验事件进行标注。

  3. 混沌实验:支持各种常见类型的故障,涵盖物理机/虚拟机、容器不同底层基础设施,同时利用实验编排可以自动定时实现故障的并行/串行/挂起模拟注入以及故障的自我恢复。通过编排的能力可以构造复杂的故障场景而非只能完成单一故障任务的模拟。以串行场景编排为例,可以在该场景的 workflow 中添加多个故障子任务,同时每个子任务又可以嵌套多层的子任务,从而能够更真实地模拟生产环境中遇到的故障。

  4. 实验报告:为整个故障演练过程提供了可视化的聚合报告,包含了压测的数据(TPS、响应时间、失败数等),监控数据(CPU、内存、IO、网络等),混沌实验数据(实验事件、执行时间、执行状态等)。

  5. 专家库:用以沉淀各种高可用典型故障测试场景,用户在创建场景时可直接导入这些典型场景进行演练,降低了故障演练的难度同时提升了故障演练的效率。

在建信金科的应用实践

利用故障演练平台的能力,目前建信金科已在分布式系统测试中进行了混沌工程实践。以三个典型案例中对混沌工程的具体实践为例:

  1. 混沌工程在分布式系统多活及灾备能力验证上的应用

分布式多活及灾备架构以分布式框架和分布式数据为基础,支持大规模、突发、高并发场景,能够以低成本,快速应对海量用户数和海量数据的新型应用场景;同时分布式系统可以分散到多个数据中心或节点运行,降低了对单数据中心的处理能力和可靠性要求,可大大降低计算资源的投入成本。近年来分布式多活技术不断完善,形成稳定的技术栈以及生态圈;同时银行业也在不断探索、研究分布式技术,逐步积累经验,使得构建基于分布式架构的银行业务系统成为金融科技的选择。

如何对分布式系统多活及灾备的能力进行验证,提升系统的可用性,是系统能力建设的一部分,传统的高可用测试难以满足需求,相比传统的高可用测试,混沌工程可以模拟分布式系统多活及灾备中很多非故障类场景,如流量激增、资源竞争条件、拜占庭问题、非计划中的或非正常组合的消息处理等,这往往是传统高可用测试不具备的。

建信金科分布式系统多活及灾备架构图.png

图 4 建信金科分布式系统多活及灾备架构图

如上图所示,目前,建信金科已在分布式系统中搭建了“同城多活+异地灾备”的架构体系,在分布式系统整体或某组件发生同城单中心\双中心故障时,可自动切换至可用数据中心,提供无损服务;在发生城市级别故障时,可通过决策快速切换至异地灾备数据中心,继续提供服务。

传统的高可用测试,可以在测试环境中通过应用杀进程、断网等单点故障或多点故障,来模拟组件/部件级、机房/数据中心级、城市级故障,但对于分布式系统多活及灾备中遇到的多种非故障类场景,传统的高可用测试难以模拟,同时高可用测试覆盖的场景有限、对生产上可能遇到的问题难以提前预知,这种时候,需要通过使用混沌工程来进行多活及灾备能力的实验验证。

场景 1)流量激增:分布式系统在面临瞬间流量大量涌入时,会触发系统各组件及后端应用的极端情况,分布式系统的服务治理能力,可以通过负载均衡、流控、服务降级、熔断等进行调控,并在流量恢复正常后,逐步恢复正常的服务。对于这样的场景,传统的高可用测试需要通过大量压力机进行发压,来模拟流量激增,这会消耗大量的测试资源,同时这样的测试不会频繁或常态化进行,很难发现实际生产中的很多问题。而对于混沌工程实验,可以在混沌工程中设置对请求的指定访问频率来模拟具体流量及发生频率,同时通过故障演练平台进行常态化演练,在实战中多次实验演练,可以更好地发现潜在问题,从而提前解决潜在隐患。

场景 2)指定资源竞争条件:在分布式系统中,会出现多个线程对同一个内存数据进行操作,导致该数据的操作发生非原子化的情况。在这种情况下,前一个线程对数据的操作还没结束,后一个线程对同样的数据进行操作,从而导致不一致的问题。在传统的高可用测试中,模拟指定资源竞争,往往需要从应用端进行程序修改来进行模拟,或对资源进行侵入式故障注入,这样的方式操作较为复杂且不易恢复。而在混沌工程中,可以通过混沌编排工具 workflow 中的并行任务来同时执行两个相同的 JVM、注入修改数据值的操作来进行模拟,从而实现实验的简单化、易操作化。

场景 3)拜占庭问题:在分布式系统中,有可能会遇到拜占庭问题,即错误节点可能做任意事情,比如不响应、发送错误信息、对不同节点发送不同决定、不同错误节点联合起来出错等等。这样的问题,传统的高可用测试难以进行模拟。通过对混沌工具中的 JVM 故障类型进行二次研发,使其可接受自定义异常,可以满足更多的故障模拟场景。拜占庭问题便可以通过混沌工程中 JVM 类抛出自定义类型异常来注入,通过单点或多点注入服务不响应、伪造信息、恶意响应等故障,来对分布式系统面临拜占庭问题时的状况进行实验,从而更好地保障系统可用性。

  1. 混沌工程在分布式数据库验证上的应用

分布式数据库是常用的基础组件,数据库发生故障所引发的一系列的后果是无法想象的,而对于分布式数据库而言,其故障有可能是多种极端情况组合下才发生,从而引发严重的生产事故。传统的高可用测试,很难对极端情况进行同时或不同排列组合的模拟,难以在测试中,对这样的情况进行预判。以实际生产及测试中,遇到的分布式键值数据库的一个历史故障为例说明。

分布式键值数据库故障流程图.png

图 5 分布式键值数据库故障流程图

上图为某分布式键值数据库的架构,曾经在准生产环境的长期运行中,偶发遇到了故障,该故障的发生,需同时满足以下条件。

条件 1):分布式键值数据库在进行持续大流量的写入

条件 2):该分布式数据存放的多副本所在节点间存在着网络延迟

条件 3):某个数据副本节点的存储空间被写满,产生了数据副本的分裂,在数据副本分裂后需重新调整数据副本的 Region Leader

条件 4):上述数据副本分裂期间,恰好有新的写入 Commit 请求

上述 4 个条件同时发生时将导致该数据副本所在节点故障、而该节点的 KV-server 进程在故障后被挂起且无法自我恢复、从而使可用节点减少。此时若不能及时恢复,下一节点故障发生率将会逐渐提高,随着时间的推进,最终可能产生节点逐次故障、进而系统雪崩而导致严重的灾难。这样的复杂故障场景,采用传统的测试手段难以复现,利用混沌工程及故障演练平台的场景编排功能则可以较为容易地进行模拟。通过创建具体对应的故障场景,发起背景压力,配置好需要的监控指标,然后在适当时机进行故障实验的注入模拟,使用场景编排的故障注入场景包含以下 4 个步骤。

步骤 1):通过混沌工具在分布式集群的不同节点上制造网络延迟故障

步骤 2):通过混沌工具在一个数据副本上创建磁盘填充故障

步骤 3):通过混沌工具制造步骤 2 中数据副本所在节点故障

步骤 4):编排重复上述 3 个步骤的故障

通过这个案例可以看出,故障演练平台能够对基础设施组件的复杂故障场景进行模拟,弥补了传统测试手段的不足,有效保证了分布式系统各个基础设施组件的稳定运行。

  1. 混沌工程在银行时序异常场景下的应用

银行交易中,时序异常场景是交易中较为经典的业务场景。这里边包含了正交易与冲正交易,正交易为正常的交易,而冲正交易对一笔正交易的反交易,指的是在正交易发生错误时对该错误的行为进行纠正。比如一笔取款或者转账操作,当由于网络超时或者其他原因导致交易未全部成功时,需要对原始的正交易操作发起一个冲正交易,以防止用户取款未成功、但是账户却实际扣款,或者转账人的账户金额已扣除、但收款人的账户却未到账,这些类似的情况发生。

在分布式系统中,对冲正交易的处理流程,如下图所示:

分布式系统对冲正交易的处理流程.png

图 6 分布式系统对冲正交易的处理流程

对于正交易:通过分布式系统的应用路由及配置中心,读取业务索引,完成寻址,交易转发至下游应用处理;同时记录该笔交易,写入至流水索引中。

对于冲正交易:通过分布式系统的应用路由及配置中心,读取流水索引,完成寻址,交易转发至下游应用处理,进行冲正。而对于冲正交易先至的情况下,通过分布式系统的应用路由及配置中心,写入流水索引,通过默认寻址策略,交易转发至下游应用处理,完成冲正。

正交易与冲正交易的时序图.png

图 7 正交易与冲正交易的时序图

由上图可以看出,时序异常在正交易与冲正交易里,可能出现多种排列组合的异常。正常时序为 1->2->3->4,而异常时序可能出现 1->3->2->4, 1->3->4->2 等多种情况。对应到实际中时而会出现索引库异常导致无法读索引或者写索引、或者正交易处理过程中发生网络延迟导致了冲正交易先至从而导致时序异常,这两种情况都可能引发冲正交易查询不到正交易记录的流水索引造成与正交易寻址不一致以致冲正未真正成功。

在此,可以通过混沌工程及故障演练平台在程序执行方法中注入自定义异常故障、延迟执行及使用编排能力来模拟这些异常,编排该复杂场景所需要的故障步骤和对应的编排 workflow 流程图如下:

步骤 1):使用故障演练平台在正交易读取索引方法中注入 JVM 类延迟

步骤 2):使用故障演练平台在冲正交易读索引时注入 JVM 类自定义异常

步骤 3):恢复步骤 2 故障

步骤 4):使用故障演练平台在正交易写索引时注入 JVM 类自定义异常

步骤 5):恢复步骤 4 故障故障演练平台编排时序异常的workflow.png

图 8 故障演练平台编排时序异常的 workflow

通过上述混沌工具的故障注入方法及多种编排,能够更加细粒度地进行代码层次的故障注入,利用白盒的测试手段挖掘系统中更多潜在的风险,从而能够提前预防系统的更多问题。

类似以上相关案例和问题,都可以通过混沌工程及故障演练平台来进行,在常态化的演练中提升系统的稳定性和健壮性。下表为混沌工程在建信金科分布式系统中的一些实践成果,总计发现并解决问题数 20 余个,其中包括分布式系统中网关、接口、缓存、配置中心、数据库各个层次组件的错误。

混沌工程及故障演练平台实践成果.png

图 9 混沌工程及故障演练平台实践成果

通过混沌工程及故障演练平台,可以模拟及编排各种复杂类型的故障场景,同时覆盖系统各类组件故障及分布式系统特有问题,可以将其作为常态化故障演练的有效手段,来保障银行业务的稳态运行。

总结与展望

除分布式系统外,建信金科将加快推进对公司内其他业务各种故障类型的演练,稳步提升全公司各系统处理异常事故以及极端场景的能力,为各系统稳健发展提供有效保障。

同时,还将把混沌工程在分布式系统的应用实践拓展到整个金融业场景,通过在实践中沉淀的高可用场景案例集、故障编排方案,提供一体化服务的能力。通过使用混沌工程故障演练平台,用户只需要简单创建实验场景,即可自动化地对集群环境进行混沌实验及故障演练,有效避免生产环境事故。

未来还将进一步探索将机器学习的能力应用到混沌工程领域,对已知故障进行关联性分析,形成故障画像,并基于历史故障对可能发生的级联故障进行预测,从而不断加强公司内部对系统风险治理的能力,最终提升系统的可用性。

以上是混沌工程在建信金科的应用实践分享,希望和社区技术大佬们一起学习探讨、多多交流、碰撞想法!