云IaaS测试系列一分布式存储测试实践2-可靠性高可用测试

高可用和可靠性是从软件架构设计层面就需要考虑的因素,本文主要从测试角度去思考如何通过测试手段来保障软件系统的高可用和可靠性。

概述

系统可用性和可靠性是软件架构设计中经常要考虑的一个重要因素,它是用来衡量软件系统质量情况的一个关键指标。可用性通常是指系统能够提供服务的时间比例(可服务时间/总时间),可用性越高,服务时间越长;可靠性是指系统无故障运行的时间,可靠性越高,无故障运行时间也越久,系统也越稳定。可靠性和可用性既有联系又有区别,可用性需要关注故障后的恢复时间。要保证系统的高可用,一方面要保证系统的可靠性,另一方面要保证系统的故障恢复时长;所以一般高可用和可靠性测试是紧密不分的。

从概率学的角度来讲,所有事物都不是100%可靠的,都是有失败或者错误概率的,只要运行或者变化次数足够多,失败或者错误的频次就会增大。那么高可用和可靠性本质上就是为了针对这种不可靠的事物,或者面对风险进行设计的。比如:软件系统依赖的第三方软件可能会失效,硬件和网络可能会损坏,一旦这些外部依赖出现不可预期的错误,会不会导致软件系统自身出现故障或者不可用,以及出现故障后能不能快速恢复,这是考量软件系统对错误的容忍度,也是高可用和可靠性设计需要解决和考虑的问题。

高可用和可靠性是从软件架构设计层面就需要考虑的因素,本文主要从测试角度去思考如何通过测试手段来保障软件系统的高可用和可靠性。

高可用和可靠性测试

如前文所讲,高可用和可靠性主要是面对各种不可靠风险进行设计和测试,而不可靠的原因有很多:

  • 系统本身的软硬件出现异常
  • 依赖的第三方软硬件出现异常
  • 来自客户端非预期的IO模型和流量
  • 来自客户端超并发的请求
  • 长时间运行软硬件系统可能出现的老化现象
  • 运维人员进行变更可能会犯错导致,也可能出现异常

对于不可靠因素出现的故障,是否能够快速恢复(如秒级恢复)是高可用设计和测试需要考虑的因素。高可用和可靠性测试主要包含了故障注入类测试和业务场景构造类测试。

故障注入测试

故障注入测试主要是指在系统运行时,采用特定的方法向系统本身或者系统依赖的第三方服务注入一个或多个异常,通过观察系统的表现来评估软件系统的可靠性和可用性,一个典型的故障注入测试流程如下图:

开展故障注入测试首先要面临的几个灵魂问题:

  1. 注入什么样的故障?
  2. 如何注入故障?
  3. 故障需要在什么时间注入,什么时间恢复?
  4. 如何衡量故障注入或恢复后的系统表现是否符合预期?

故障模式库

首先解答第一个问题:要注入什么样的故障?

故障模式库定义了软件系统测试过程中涉及的所有故障类型,也是开展故障注入测试所依赖的基本内容,故障类型与软件架构及网络拓扑强相关,根据其在现网出现的频率以及故障的复杂度分为L0、L1、L2等级别,下面以CBS产品为例,列举下CBS产品的故障模式库。

CBS由多个分布式集群子系统组成,包含若干组件,组件之间通过交换机网络连接,部署在物理和虚拟机等服务器上,组件之间通过特定协议协同工作,并与网络/数据库/对象存储等第三方业务交互,因此根据业务架构和组网设计设计以下几种故障类型:

  • 网络级故障:CBS系统内部通过网络连接,网络分为TCP和RDMA网络,涉及的网络级故障主要是指对网络拓扑内的网络链路注入故障、亚健康等故障,常见的有网络时延、丢包、错包、限流、网卡重启、端口故障等。
  • 组件级故障:主要是指组件出现异常crash、重启或者假死,包含软件系统内部组件以及系统依赖的第三方组件。
  • 硬件级故障:CBS是基于物理硬件构建的存储系统,对硬件如网卡、硬盘、HBA卡有较强的依赖,此处的硬件级故障主要是指承载软件系统或者依赖的第三方硬件故障,如常见的硬盘异常、HBA 卡异常、网卡异常、交换机端口异常等。
  • 系统类故障:CBS软件系统是部署在Linux系统的物理和虚拟机服务器之上,这里的系统类故障主要指服务器级别的故障,如常见的重启、掉电、内核crash、系统时间错乱等。
  • 资源类故障:CBS软件系统运行需要依赖多种资源,基本的系统资源:如基本的CPU、内存、句柄、进程端口等,以及软件系统的一些内部资源:如Cache。这里的故障主要指承载软件系统的服务器资源以及软件系统内部的业务资源出现异常,如常见的资源耗尽、高负载、目录空间耗尽等
  • 集群类故障:CBS是一个分布式集群,所以必须考虑集群粒度的故障影响,这里的故障主要指集群掉电、断网、主备切换等与业务架构强相关的故障。
  • 运维类操作:主要指跟运维操作相关的场景,如常见的组件变更、系统的可服务性操作等

故障注入方法

解答第2个问题:故障如何注入?

故障注入方式取决于故障类型,常见的方式主要有故障模拟和物理故障,两种方式各有优劣,故障模拟效率更高更灵活,但是故障本身与实际的故障会有部分差异;物理故障是真实的故障,场景更加真实,但是效率低,成本高。

举两个例子说明:

  1. 对于网络丢包类和硬盘类故障,构造物理故障成本比较高,因此我们常用一些开源工具或者业务自行开发工具来模拟
  2. 对于交换机网络异常类故障,没有比较好的方法来模拟,就需要借助实际的硬件操作来注入,如重启交换机、关闭交换机端口等

无论是故障模拟还是真实的物理故障,都是在成本和效率两方面进行权衡和评估,对于物理故障需要依赖专用的测试设备和环境拓扑,对于故障模拟,一方面借助业界比较常用的一些工具,但是对于一些故障注入方法缺失的场景,需要业务团队专门开发故障工具来模拟,常见的如磁盘坏道、慢盘、RDMA网络异常等,故障工具开发是故障注入测试过程中必须面临的问题,因为不是所有故障工具都可以用拿来主义。比如:磁盘类故障注入方法,利用SystemTap模拟磁盘故障。当然还有更加其他比较牛逼的工具用于辅助故障注入测试。

故障场景设计

解答第3、4个问题:故障注入及恢复时间,以及系统的表现是否符合预期?

故障场景设计主要是指将一个或多个故障与业务流程的不同阶段进行叠加,解决故障在什么时间注入以及注入和恢复后系统预期的表现。主要分为基于灰白盒的面向预期结果的故障测试和基于黑盒的面向未知预期的随机故障测试。

灰白盒故障场景测试设计是建立在对业务场景和系统流程比较熟悉的基础上,需要测试人员和开发人员等相关角色共同配合完成,需要对故障场景和系统流程进行深入分析,并以一定的策略来叠加。

故障场景分析

故障场景分析是指将故障模式库中的相关故障与组件和组网拓扑进行组合,生成每一条故障场景,故障场景分析框架如下:

将组件列表和故障模式库进行正交,删除无效故障场景,并根据故障类型进行L0/L1等级别划分,最终实现故障场景集合。

下面以CBS的某条存储IO流程和网络类故障为示例:

上述架构中描述的几条核心网络链路:

  • 客户端通过网络访问存储集群副本进行io的读写
  • 存储集群副本之间通过网络进行数据同步
  • 客户端通过网络与管控节点交互进行管理数据以及元数据的访问
  • 管控节点通过网络与zookeeper集群进行元数据的存取以及HA切换
  • 管控节点与存储集群之间通过网络进行心跳及管理数据的同步

网络异常故障需要与存储副本/管控节点/客户端/zookeeper组件进行正交,得到以下故障场景:

  • 客户端与存储集群之间网络异常
  • 客户端与管控节点间网络异常
  • 存储副本之间网络异常
  • 存储集群与客户端之间网络丢包
  • 存储副本与管控节点之间网络丢包
  • 管控节点与客户端之间网络丢包
  • 管控节点与存储副本之间网络丢包
  • 管控节点与zk集群的网络丢包

其中2/6,1/4,5/7为等价类故障,因此去除等价类后剩余5种故障场景,丢包率设置多少以及时延多高等故障程度主要依据历史测试经验,并参考现网运营过程中的故障数据以及系统在故障后的资源消耗等情况进行综合设计,比如丢包率5%~8%等。如:

  • L0:客户端与cell网络出现8%丢包率,持续xx min后恢复
  • L0:存储副本单节点/多节点与管控节点网络出现100ms时延,持续xx min后恢复

系统流程分析

系统流程分析是故障注入测试中很重要的一环,其主要目的是决定在什么阶段注入什么样的故障,以及故障持续时间和频次。不同阶段注入故障以及故障的方式(如持续故障还是间歇性故障)才能够多方位检测出系统的BUG,实际产品测试经验表明故障注入的精细度越高,拦截产品质量问题越有效。系统流程分析与故障场景叠加示意图如下:

如上图所示:流程1共有5个子流程:A-B-C-D

每个子流程中分别考虑:可能发生的异常,异常后会产生什么问题,需要异常多久?

然后使用等价类划分法,合并场景并去除无效的场景,作为该流程下的异常场景集,过于复杂的子流程会进行二次拆分。

同样以CBS产品为例:

这是常见的CBS的一些基本流程及操作,需要对每个流程进行分析,在系统流程里如何处理,涉及哪些组件以及哪些资源,此处不一一赘述,仅以io简要流程为例来讲:

IO从客户端发送到存储集群,存储侧使用io消息队列来控制流量,当io处理完成返回客户端后,流程结束并释放对应队列中的节点,客户端接收请求超时后会重试,存储端发送超时后也会重试。所以在故障场景设计时,需要考虑资源大小、申请释放机制、资源不足的影响以及超时处理流程等;以一个网络故障导致资源不足的BUG例子为例:
当存储端处理完成后向客户端返回rsp时,因网络单向异常(存储–>客户端)导致客户端没有收到包,客户端反复重试导致存储侧消息队列资源不足又无法释放,导致存储上其他客户端的io hang。经过分析(分析过程不赘述),该故障场景用例如下:

不同的故障和系统流程组合直接影响了故障注入及恢复后的系统表现是什么样的,系统表现完全与业务处理流程强相关,需要深入了解系统的处理流程才能精细化指定故障观察点,比如:副本与客户端之间的网络异常和副本与管控节点之间的网络异常对系统的影响是不一样的,前者对io影响比较大,后者对io影响比较小。

故障注入测试需要与系统流程分析结合来做,整个系统分析流程投入会比较大,对测试和研发同学的要求比较高,成本也比较高。因此,在前期的故障测试时,更多的采取一些粗粒度的随机故障测试,主要通过时间和次数来发现概率问题,成本相对较低,但是收益也比预期较小。灰白盒故障测试适合有一定故障注入测试经验的团队,因前期使用粗粒度的随机故障注入测试已经发现了很多高概率或者必现的问题,对于低概率问题,需要使用灰白盒分析方法来精细化故障注入测试。

随机故障测试

如前文所讲,随机故障测试相比灰白盒故障测试,粒度较粗,开展成本较低。主要包含两个方面的随机:

  1. 故障类型的随机

  2. 故障注入频次/阶段/时长的随机

  3. 简单来讲,就是不知道什么时候注入故障,那就使用随机的方式长时间频繁跑故障测试,以此来发现系统问题。随机故障测试对于系统的表现预期相对于灰白盒故障测试来说,观察点也相对较粗,主要是类似io抖动、进程crash等观察点。随机观察注入测试对业务监控系统依赖度比较高,适合通过自动化的方式来开展。

业务场景构造测试

业务场景类测试主要是指系统接收到一些对于系统有风险的场景后能否保证其可用性和稳定性。比如CBS作为存储对外提供存储服务,会接收来自客户端各种各样的数据流量,这里的测试主要指业务场景出现异常的情况,如场景的突发流量、长期持续高负载流量(也就是压力测试)以及一些特殊的异常流量报文等。一般包括以下几种类型:

  1. 压力测试:通过构造业务模型和配置,对系统进行持续大压力、高并发测试,观察系统的表现。
  2. 长稳测试:在一定业务压力模型下(高于正常压力或者完全高于系统负载)持续长时间运行的测试方法,也可以结合一些基本的故障注入来开展。
  3. 异常业务场景:如异常操作、异常配置、异常流量报文等

CFT及混沌测试

前面讲了基于灰白盒和黑盒的故障注入测试,但是分布式系统的复杂性通过上述测试还是远远不够的,不同的数据量、不同的压力,甚至不均衡的压力在系统内部可能都是对应不同的分支,不同的集群节点也会对应不同的处理逻辑,我们无法群居罗列所有的组合,覆盖所有的分支,因此需要引入一些探索性测试。

CFT(Cross Feature Test)测试是一种基于灰盒的多特性交叉随机的测试,是我们目前正在尝试的一种探索性测试,它的基本逻辑如下图所示:

测试单元最小粒度的测试行为,可以是单个测试用例,也可以是单个测试步骤,比如正常的盘操作用例/运维变更步骤或者故障步骤,通过自动化调度引擎将多个测试单元按照顺序/随机/循环以及按照权重等方式组合成测试套进行并发随机运行,你无法知道在某个时刻具体是哪个操作/或者哪个故障在运行,这种测试在一定程度上按照未知的时序进行并发随机运行,极大的模拟了一种探索性测试,该类测试必须通过自动化的手段进行测试,无法人工执行;CFT测试需要依赖很强的系统监控能力,这样在测试执行时,一旦出现问题,监控系统能第一时间知晓。

混沌测试是另外一种工程实践方法,它是一种实验手段,混沌测试的第一步是定义系统的稳态(如iops、时延等指标),然后通过多样化的测试手段(如压力、故障注入等)对系统进行实验,最后对比实验结果,如果系统一直处于稳态,那么认为系统对于该故障是具有容错能力的,否则,就找到了一个系统的bug,去修复它来提高系统的可靠性。

混沌测试开展的前提是需要软件系统有一定的故障容错能力,它对系统架构的韧性有很高的要求,如果软件系统架构存在单点设计的情况,那么不适合开展混沌测试,一旦出现单点故障,整个系统也就奔溃了,没法验证混沌测试的效果。如果系统当前还没有容错能力,那么应该首先使用故障注入测试方法暴露出更多的系统容错能力,最后再去考虑使用混沌测试。混沌测试也是我们后续需要尝试和探索的一种测试方法。

元数据高可用测试

元数据在存储系统中的地位是非常重要的,它记录了数据在存储系统中的真实位置,因此对元数据的管理是至关重要的。腾讯云存储的元数据管理包括分布式拓扑结构、节点的HA数据以及存储数据的索引数据等,它是基于开源的分布式zookeeper集群进行管理的,Zookeeper分布式集群主要通过ZAB协议(Paxos算法的变种)来保证数据的分布式一致性。

分布式系统主要面临的一致性问题有以下几种:

  1. 数据同步问题:分布式系统中,数据一般都是存在多个节点上,数据需要在节点间进行同步,但是由于现实的网络通道并不是绝对可靠的,经常出现丢包、延迟、错报等问题,导致节点间的数据不能同步,容易出现数据错乱或者不一致的情况。
  2. 节点故障问题:分布式系统中某个或者某些节点故障后无法恢复,如何保证部分节点故障后不会导致整个分布式系统的数据不可用。
  3. 节点宕机恢复:某个或某些节点在A时刻宕机,并在B时刻恢复,宕机期间或者宕机恢复后的数据一致性问题如何解决。
  4. 孤岛节点问题:网络链接异常,将分布式集群分成多个相互隔离的信息孤岛,如何保证孤岛场景下的数据一致性问题。

因此为了保证元数据的高可用,需要结合系统流程对Zookeeper集群进行故障注入来测试,ZAB协议典型的读写流程如下图:

ZAB协议中ZK Leader给Follower发送同步复制请求后,只需要一半以上的follower进行了正确反馈(即返回ACK)后,Leader才会再次向所有follower发送事务提交,完成数据同步。因此在测试过程中,需要根据系统流程结合ZAB协议中的选主、事务提交等流程进行故障注入测试来验证元数据的高可用。

结语

可靠性和高可用测试是发现系统问题、保障产品质量的非常有效的手段,尤其在复杂度偏高的IaaS产品测试中更为有效。对于CBS测试来讲,我们在高可用和可靠性测试方面投入了很多精力,也发现了非常多的问题,其中不乏数据一致性问题,在实际产品测试过程中,70%的bug都是通过它发现的,但是现网仍然有很多低概率偶发问题。所以可靠性和高可用测试只是起点,永远没有终点。