cover_image

混沌工程故障注入原来如此

郭少芬 匠心独运维妙维效
2024年08月19日 22:02


01
引言

我们在上一篇文章G行混沌工程建设与应用中讲到,混沌工程是通过在系统环境中主动注入故障,分析故障对系统产生的影响,提前发现系统存在的问题,并针对性进行加固防范,将隐患消灭在初始阶段。因此,对于混沌工程来说,故障注入是第一步,也是混沌工程技术中的核心部分。本文将结合G行混沌工程实践经验,从故障场景分类、故障注入实现、实践效果总结三方面为大家逐步揭开故障注入的神秘面纱。

针对故障注入,如果您存在以下疑问,皆可在本文中找到答案。

问题1:目前支持的场景都有哪些?

问题2:一个服务,如何成为一个被故障注入对象?

问题3:这些故障注入能力是如何实现的呢?

问题4:故障注入能够实现对业务系统源代码故障注入且无侵入吗?比如Java语言的系统,如果可以,是如何做到的呢?

问题5:中间件类型的系统可以做故障注入吗?这部分是怎么实现的呢?

问题6:除了后端系统,前端可以被纳入故障注入的范畴么?


02

故障注入场景分类


目前的混沌工程场景按照架构层级分类分为以下场景:

系统资源类:系统资源类故障是混沌工程故障注入技术中最基础、最常用的故障场景,该故障通常会涉及到CPU、内存、磁盘、网络、文件、文件系统等方面,其中磁盘故障主要包括磁盘负载、磁盘I/O hang,磁盘读写故障、磁盘读写慢等;网络故障主要包括丢包、乱序、延迟、网络损坏、网络占用等,该类故障在生产环境中出现概率高,因此使用率也较高。另外对于主机与容器资源的故障也划分在这个类别,主机机器的重启、断电、时钟偏移等,容器相关的container pause、移除,pod删除、pod失败等场景。

数据库、中间件类故障:数据库的稳定性对于银行业务安全运行至关重要同时中间件比如缓存、消息队列、网关等,在银行业务中的应用也越来越广泛和深入。对于数据库、中间件,基础的故障能力例如:慢SQL、行锁、表锁、Redis大key、热key、消息积压、消息丢失等。进一步,数据库、中间件本身的高可用机制在各种故障下是否生效切实决定稳定性,比如:磁盘I/O hang时,数据库能否完成主备切换;内存打满时,缓存平台能否正常处理;消息队列主进程挂掉时,消息的消费是否还能如常进行等。另外,极端故障场景下,数据库、中间件本身的自愈能力如何;是否会对业务造成很大的影响,这类场景都是使用该组件需要关注的。

应用级别故障:应用级别的故障,是指对业务系统应用程序的故障注入,目前G行应用系统主要涉及的编程语言为Java、C。对于应用系统的故障场景主要为业务处理延时、业务抛出异常、业务数据篡改等,同时对于Java中JVM虚拟机进行针对性的故障比如full-gc、JVM线程池满等故障。

前端/端故障:随着智能手机、平板电脑等设备的普及,移动端成为许多用户获取信息和使用应用程序的主要平台之一。端作为一个重要的业务场景,其稳定性除了受本身产品迭代的影响之外,还有另一个重要的因素--操作系统和手机厂商的影响。由于系统和手机厂商经常会有升级更新,APP相关人员需紧跟其脚步,每次端系统升级都可能带来一些不可预期的影响,端的稳定性越来越受到考验。端系统故障场景主要包括:闪退、白屏、页面crash等属于端稳定性的重要基线指标;前端故障场景主要包括展示乱码、跳转拦截、点击无效等。

综上所述,故障注入场景分类以及各分类下具体场景总结如下脑图:

图片

1 故障注入场景脑图


03

故障注入实现步骤


混沌工程的核心之一是故障注入,那么一个被测系统实施故障注入的步骤是?三步走:

第一步:需要明确被测系统部署在哪些机器上

第二步:这些机器可被混沌工程平台控制

第三步:开始真正的故障注入。

第一步可以很快确定。

第二步的本质在于混沌工程平台可以对被测机器进行任务下发。业内有两种方式,一种是建立SSH通道如AWX等,一种是安装agent。基于混沌工程的目标:可以稳定的对被测对象进行故障注入,同时观察被测对象的各项监控指标;另外考虑到SSH通道的方式实施下发脚本在安全性上有不稳定的风险,因此混沌工程选择安装agent的方式。

对G行应用物理部署情况分析,被测对象主要分为两大类别:主机和容器。其中主机包括物理机和虚拟机安装探针agent方式如图2所示

图片

图2 主机安装探针示意图

对于容器来说,可以分为两类,一类是主机内的容器,一类是k8s集群管理的容器。如果是主机内的容器,通过主机探针安装即可进行实验,如上图2。而k8s集群管理的容器要稍微复杂一些。

在Kubernetes中部署应用,通常我们会选择将应用定义为 Pod、Deployment、Statefulset 等资源类型,这些都是 Kubernetes 内置的资源类型;在实际应用中,内置的资源类型无法满足复杂的需求,Operator 是解决复杂应用容器化的一种方案,通过自定义资源和自定义控制器来实现复杂应用容器化,混沌工程在容器内的实施chaos-operator正是基于 Operator 实现的。

在 Kubernetes中安装完chaos-operator后,会生成一个deployment 资源类型的operator实例、一个daemonset资源类型的agent-tool实例。

用户用来自定义故障实验的资源称为chaosCR;每次新建演练就可以通过 kubectl或者chaos-cli创建chaosCR实例资源,chaosCR资源本身包含了混沌实验定义;chaos-operator会监听chaosCR资源的生命周期,当发现有chaosCR资源实例被创建时,同时就能拿到混沌实验定义,然后解析实验定义,去真正的注入故障--调度Agent-tool;Agent-tool是daemonset资源类型,一个Node节点必定会部署一个Agent-tool Pod,从 Linux Namespace的维度,该Pod网络、PID命名空间与Node节点处在同一命名空间,因此可以做到Node/pod/container各个级别的演练。图片

3 k8s集群安装故障注入探针原理示意图

第三步,具体的故障注入执行。由以上分析,无论是主机还是容器,故障注入的执行都是通过被测对象上安装的agent来完成的。Agent不仅负责与混沌平台的通信,同时需要包含具体的故障注入与对被测系统指标数据采集。本文主要聚焦故障注入的部分。

对于第一节中的不同场景,都有其独立的执行器Executor,如下图所示。

图片

4 混沌工程故障注入原理示意图


04

故障注入原理剖析


接下来介绍不同的故障注入执行器原理,进一步为大家解开混沌工程故障注入的神秘面纱。


3.1 资源类型故障

首先是资源类型的故障,该类故障主要指对于被测机器的CPU、MEM、磁盘、网络等资源的故障注入。

从基本原理来讲,这部分能力对于一个确定的机器是基本的通用能力,具体能力原理如下表所示:

图片

1 资源类型故障原理示意表


另外需申明,对于主机和容器的实施原理还有一个区别点。我们知道,对于 Linux 虚拟化技术而言,核心实现原理就是 Linux Namespace 的隔离能力、Linux Cgroups 的限制能力,以及基于 rootfs 的文件系统;所以容器的执行需要先找到主机上该容器的pid,然后再具体执行相关的命令来达到实验目的。而对于主机而言,无需中间步骤,直接操作即可。


3.2 数据库、中间件类故障

对于数据库、中间件来讲,相关的故障注入分为三个层次:

功能层故障:数据库、中间件本身系统机制类故障,比如Redis大key构造,数据库慢查询,等等,该类故障需要先拿到被测服务本身的信息:比如用户名、密码、地址、端口等信息,进而连接到服务中,进行服务内嵌的操作才能完成。更深一步,还可以通过开发数据库、中间件插件的方式完成能力更强大的故障注入,当然具体情况需要根据实际需求来决定。

高可用层故障:数据库、中间件等验证其自身高可用能力的故障,比如当数据库认证表主节点挂掉时,数据库会进行重新选主;Redis集群中主节点发生网络故障时,会重新选主;数据库数据主节点磁盘I/O hang时,其他从数据节点会迅速接管,等等,该类故障基本可以通过资源类型故障就可以完成。

连接层故障:应用通过调用数据库、中间件而完成业务逻辑,比如jdbc、jedis等连接,这部分故障通过下文中应用类型故障的能力,即可完成相应连接的超时、异常、数据篡改等。下面的内容中会详细介绍。

图片

图5 数据库、中间件与系统之间关系示意图


3.3 应用类型故障

对于应用系统的故障注入,是指对业务系统可以做到接口、代码级的故障注入。所以有一个基本点需要保障:不能因为故障注入而引入新的系统问题,即不能通过预编译代码前插入“脏代码”进行注入故障。G行应用系统主要为Java与C两种语言,目前G行已完成对这两种语言下针对接口、方法级别的延迟、异常、数据篡改等能力。因此主要介绍这两种语言的故障注入能力。


3.3.1 Java语言类系统故障注入

Java语言类系统的故障注入能力,主要使用了字节码增强技术和Java Agent能力。

一个Java语言程序从代码到执行的过程如下图,字节码增强技术(ASM)可以直接生成字节码文件,也可以动态修改字节码文件,


图片 

6 Java程序运行过程图


这样通过字节码增强的技术,就可以对想要注入故障的任意方法通过字节码文件的修改完成。


图片 

图7 字节码注入效果示意图


其次,字节码增强技术实现的程序是如何加载到被测程序中的,答案是Java Agent原理。Java Agent(Java 代理)是 JDK 1.5 之后引入的技术。Agent 就是JVMTI的一种实现,Agent 有两种启动方式:一是随Java进程启动而启动;二是运行时载入,通过attach API,将模块(jar包)动态地Attach到指定进程id的Java进程内。具体的实现方式有两种:

(1)静态加载:实现一个premain方法,但是这种方式实现的Java Agent只能在JVM启动的时候被加载;
(2)动态加载:实现一个 agentmain 方法,这种方式实现的Java Agent可以在JVM启动之后被加载。

3.3.2 C语言类系统故障注入

根据调研,目前业内对于C语言系统的故障注入能力没有开源方案。对于编译型语言,通常的故障注入方式可以分为debug方式、代码插装技术、网络及内核分析技术(Wiresharkebpf)。其中与C语言类似的C++语言开发的系统业内有开源的故障注入能力,该方式就是通过debug GDB(程序调试工具)来实现的,该方式有诸多限制:首先要求被注入程序为debug模式,对于很多老的业务系统,打包debug版本成本太高;其次调试方式进行故障注入性能也会受限。而通过网络及内核分析方式的系统入侵,主要是覆盖网络层与系统层的,对于方法调用粒度的故障注入比较有限。因此选择代码插装技术来实现C语言的故障注入。

代码插装技术的原理是一种通过修改机器码的方式来实现hook的技术,对于正常执行的程序,它的函数调用流程大概是这样的:

图片

图8 正常代码函数调用流程示意图


0x1000地址的call指令执行后跳转到0x3000地址处执行,执行完毕后再返回执行call指令的下一条指令。在hook的时候,可能会读取或者修改call指令执行之前所压入栈的内容。那么,可以将call指令替换成jmp指令,jmp到自己编写的函数,在函数里call原来的函数,函数结束后再jmp回到原先call指令的下一条指令。如图:

图片

图9 代码插装后函数调用流程示意图


G行基于调研结果使用Frida进行了实现,主要考虑到以下几点:

(1) Frida采用JavaScript的方式进行故障注入,操作性、灵活性很强,便于与已有平台进行集成。
(2)Frida能够为跨平台的动态代码插装提供支持,包括Windows、macOS、Linux、Unix、Android和iOS。
(3) Frida基于静态库的编译方案,不需要预装各种软件或包,对于不同系统的集成更方便。
(4) Frida作为主流的动态代码插装技术,获取学习资源更方便,技术更成熟。

3.4 端故障

对于端故障,从技术栈的角度讲,可以粗略分为后端和前端。后端技术主要涉及的是Java。该类型语言的故障如Java故障的注入前文已做介绍,只是对于端来讲需要以SDK的形式与APP程序一起打包,这里不做赘述。本文主要描述纯前端故障的基本实现原理。

目前,纯前端技术领域满足各种需求的框架可谓百花齐放,但是其基本原理还是基于js与css,因此本文主要介绍基本的故障注入方式,对于不同的框架,要实现具体的故障需要根据框架本身的特点进行具体实现。

前端故障主要通过单独的流量管理方式来避免对真实生产用户的影响,这样经过单独流量转发之后会进入策略管理部分,进而根据一定的策略让用户命中不同的故障,这样来观察不同故障注入之后系统的表现。

图片

图10 纯前端故障注入流程示意图


05

混沌工程实践效果


技术要为业务服务才具有价值,混沌工程的技术累积只是一个开始,真正发挥价值需要将其应用到实际的混沌实施中。

在根据实际需求建设以上相关能力的同时,G行也在混沌工程的实施方向不断探索。目前主要应用于应用系统、基础软件、红蓝对抗三个方向:

1.系统异常测试通过丰富的故障注入能力,验证系统整体可靠性和容错性,实现系统安全运行能力的前移。
2.基础软件可靠性验证通过全面故障注入,验证基础软件领域新技术、新产品的稳定性、可靠性和容错性。
3.红蓝对抗演练通过自动编排、模拟故障场景,支持高效、仿真的红蓝对抗演练,提升实际生产系统发生故障时的处置效率。


目前已取得阶段性成果:

(1)推广范围:通过异常测试与红蓝对抗等形式,目前已在全行23个重要业务系统进行混沌实施,接入400+虚拟机,容器数量4000+。形成200+实验场景,支撑每月300+次演练任务。同时演练过程具备演练与可观测能力一体化,提升各个应用场景混沌实施、演练效率的同时,助力业务系统快速、提前发现问题。图片

图11 目前纳管服务拓扑示意图


(2)场景沉淀:分布式数据库、分布式缓存、分布式消息、全栈云等基础领域进行专项混沌实施,并形成各自领域的专家库100+,目前正在深耕金融业务领域。

(3)风险挖掘:实施过程中发现了6类风险,并对此进行整改优化,提升系统的稳定性。

图片

表2 混沌实施发现问题列表


06

总结


对被测系统的深入掌握是混沌工程的建设和实践的基础,随着混沌工程实施,又会进一步推进我们对被测系统的全面掌握,二者相得益彰,让混沌工程这一提升系统稳定性的利剑发挥出最大价值。


END



作者:

郭少芬

负责混沌工程的建设与应用推广,爱好旅游、美食。关注个人成长,简单可依赖。

图片

编辑:

肖琦

22年加入光大,目前负责EverDB运维管控平台建设,热爱足球,羽毛球。

图片

图片

探析负载均衡器的实现原理
网络原来如此之浅谈金融行业网络传输技术应用与发展
科技运营操作风险防控---专家说
Windows运维之经验谈
G行信息系统灾备演练实践
G行网络安全纵深防御体系之蜜罐系统探索与实践
安全运营红蓝对抗探索与实践
初探eBPF技术的强大
数据中心那点事之提高冷冻水温度的方案探讨

G行探索全栈云容器环境降本增效之路——容器启停优雅之境

图片





继续滑动看下一个
匠心独运维妙维效
向上滑动看下一个