混沌工程(Chaos Engineering)的核心思想是在真实环境中引入故障,从而评估和提升系统的稳定性。作为指导故障演练进行系统性实验的学科,同样也遵守实验作为科学研究的基本方法,即通过有控制的测试和观察来验证假设、探索现象或建立因果关系。
有控制的测试指的是引入的故障,有控制的观察则代表故障产生的影响。那么,故障演练能否用来评估未知故障对系统的影响?我想不能,从业务的角度来说,未知故障代表没有相应的风险预案,从实验的角度来说,未知故障脱离了可控制的范畴,也就失去了科学的合理性。于是在混沌工程的实验中,故障是已知的,影响是未知的,而控制和未知又是一组具有矛盾意义的词,无疑构成了故障演练的难点。
在《混沌工程原则》中声明了一项“最小化爆炸半径”的最佳实践,引入故障是为了暴露问题,不是创造问题,演练应该尽量避免对业务造成不可接受的实质伤害,要确保负面影响最小化且都被考虑到。爆炸半径有两方面的含义,一是故障的发生圈定在一定范围内,二是故障的影响圈定在一定范围内。
如何确保圈定故障发生的范围,首先需要能够圈选指定的资源,其次要能在指定资源上触发相应故障行为,最后应保证触发行为不存在冲突,三者分别对应权限、动作和隔离。
权限在一定程度上也是隔离的一部分,这里主要指资源的可见和可操作。在故障演练中,资源应该如何归属,指导原则是归属于了解资源使用情况的人,如资源的拥有者和业务的管理员,暗含的逻辑是如果不了解使用也就不了解影响,如果不了解影响也就无法准备预案,如果没有预案也就不能进行演练。反过来讲,圈选指定资源不是考究权限的扩张而是权限的收缩,赋予演练的设计者和实施者刚好的资源控制权限,不仅是控制引入故障影响的一部分,也是组织和项目管理的一部分。
动作的触发要求精确,精确体现在故障的实现和实现的精度,例如CPU负载100%中,负载是故障的实现,100%是实现的精度。另外,一种故障不连带产生其他影响也是精确性的一部分,如CPU负载实验不应引发显著的内存利用率波动。这里没有其他法门,只有深入每类故障的基本原理,并通过可编程的方式进行实现,可编程也是可管理的基础,大体上有两种模式,一是脚本化,二是代码化,不与故障注入原理相关,主要是集成和触发机制的区别,具体有是否利用现成工具、是否依赖操作系统调度、是否添加逻辑约束等。
隔离是相同资源上不同动作的执行粒度,可以再进一步分为实验间隔离和实验内隔离。原则上同一个资源不要同时关联两个运行中的演练实验,在同一个实验中也最好不要同时运行两个不同动作,目的是为了防止故障的交叉影响,本质上是避免破坏故障预案的假设前提。当然这不能简单地作为强制要求,其中实验间的隔离是必要的,否则极大增加管理成本,实验内隔离还需要具体情况具体分析,例如风险预案就是为了应对机器负载和网络负载同时飙升的影响,但还是需要保证并行动作不能有逻辑冲突,例如机器掉电和机器过载并不能同时发生。
严格意义上讲,故障的影响是无法被准确圈定的,甚至可以说故障演练的目的也只是为了检查故障的影响范围,再以此倒推预定处理方案的完整性。
系统的行为会依据环境和流量模式而有所不同,最佳的拟真环境就是真实的生产环境,但稳态破坏的风险暗含故障影响的不可逆性,如果预期外影响的发生不能被提前阻止,那就应该及早中止,尽可能缩短这类影响的持续时长,控制住了时长也就代表控制住了波及范围。
控制故障的不预期影响首先需要感知这些影响,这是可观测在故障演练实践中的必要性,指标代表数据化的系统表现,时序代表系统表现的过程变化,与过往规律偏离的过程变化则代表注入故障产生的影响。感知行为有主动感知和被动感知两种模式,在实现上主动感知常表现为轮询,被动感知常表现为通知,时效方面通知要强于轮询,依赖于响应式的监控告警机制。
引入故障一般都会对系统产生影响,评判故障影响面在不在预期内取决于故障演练的风险处置预案是否包含,也就是说预案无法消化的影响是不预期的影响,例如引入数据库主节点故障后,主备切换预案应能恢复业务,过程中短暂服务不可用是预期内影响,但切换后备节点无法连通导致的长不可用则是预期外影响。预期外影响没有第二预案去处理,中止还原引入的故障或许是最快的应对方式,很多时候也是唯一方式。
故障的注入与中止是所引入故障的生命周期管理,实现故障生命周期的可控制,首先要实现对引入故障的可追溯。管理台的演练流程记录是必需且能直接访问的,但它不一定是准确的,也不一定是完整的,故障发生处的本地检查机制是所引入故障不脱离管控的最终保障。故障是某种行为产生的特定状态,可以从状态进行判断,例如主机已关闭,也可以从特定标签识别行为,例如注入故障的进程,反过来的要求是,如果一项故障注入后无法本地判断其自身状态,那么就不具备成为故障演练动作的条件。
可追溯解决的是数据层面的问题,实现可控制还需要解决交互层面的问题。一个远程交互模型重点在通道的建立与维持,以控制台的视角来看,原则上是主动建立通道,但也依赖于对端暴露服务,所以实际上通道算是被动建立的,只有行为发起是主动的。通道的不稳定也就意味着本地故障不能只依赖远程控制,要兼顾两个方面,一是维护通道可建立条件,二是本地任务自管理。
通道的可建立条件指的是控制台连接目标的端点保活,根据目标类型会有所不同,如果是API驱动的故障,则代表API Server的可用性,如果是agent驱动的故障,则代表agent进程的运行态,除此之外还有二者之间网络的连通性、访问控制权限及其他外部约束。保活手段也要因地制宜,例如API拨测、权限补齐和进程自启等。
本地任务自管理在这个场景下最重要的是处理远程控制中断后所引入故障游离的情形,要求具备检测和中止的能力,检测的基本能力同前面可追溯的讨论,关键在于中止的判断条件。对于故障注入的本地节点来说,一般情况下是不具备判断故障的可持续条件的,这些强业务相关的内容,要么预先实现好处理器并定义好规则在注入时下发,由本地进行更复杂的流程接管,要么本地节点就只能实现无业务规则的简单逻辑,例如超时中断。
总得来说,控制所引入故障影响外溢的基本方法,首先要构建基于应用可观测数据的监听与响应机制,其次是将故障的持续时间控制在一定的范围内,同时为了避免控制失联,故障发生本地也应具备这项功能。