分布式系统广泛应用于公有云、私有云以及大型的后台系统,分布式系统测试本身就是业界的一个难题,也是比较大的话题;本文主要针对云存储系统的测试实践来阐述下我们是如何思考分布式存储测试的
概述
分布式系统广泛应用于公有云、私有云以及大型的互联网后台系统,分布式系统测试本身就是业界的一个难题,也是比较大的话题;因此我们推出了IaaS测试系列文章来讲述整个IaaS测试实践,本文作为第一篇,主要针对腾讯云存储系统的测试实践来阐述下我们是如何做分布式存储测试的。
云分布式块存储
CBS是腾讯云块存储服务,它作为一种分布式存储系统,为云服务器/容器/数据库等产品提供持久化、高可靠性的块级存储服务。CBS内部有多个模块相互协作来支撑整个块存储和备份等服务,每个模块都是一个产品级的规模,具有很高的复杂度。
CBS产品的简要架构如下图:
测试体系
软件测试分为很多阶段:单元测试、接口测试、功能测试、系统集成测试和端到端测试,整个测试活动分布在研发流程的各个阶段,传统的产品测试只需要做接口测试和功能测试,实际上我们在测试初期也是如此;但是随着对分布式系统更多的理解,我们发现分布式存储系统的复杂度很高,内部有若干个模块,并与周边的诸多业务协同工作,每个模块都是一个产品级的复杂度,因此传统的接口测试和功能测试已经不能满足于分布式存储系统的质量保障,典型的比如分布式系统一致性如何保障和测试、分布式系统的可靠性和可用性如何测试等都是分布式测试的难题。所以我们需要系统性的设计一个能支撑如此大规模复杂系统的测试质量保障体系。
首先看看软件测试领域有一个比较行之有效的软件测试模型–来自Martin Fowler的金字塔模型,该模型阐述了软件测试分布在整个研发流程中的各个阶段。
备注:金字塔模型是一种通用模型,本模型的生成结合了业务与传统的金字塔模型
金字塔模型阐述了一个比较理想的测试模型–软件测试早期投入越大,最终的产品质量越好:
- 越往上,越接近测试、业务/最终用户,越往下,越接近开发人员;
- 越往上,测试执行越慢,效率越低,越往下,测试执行越快;
- 越往上,测试成本越高(越耗时,失败时的信息越模糊,越难定位跟踪),越往下,测试成本越低
按照测试金字塔模型以及投入/产出比,可以得知越向下回报率越高,所以应该使用大量的单元测试和全面的接口测试来覆盖产品提供的基本逻辑和功能,理论上金字塔底部的测试活动越充分,金字塔顶部的测试活动发现的问题越少,产品质量也越好;
理想的测试应该符合金字塔模型的流程,但是在当前的产品研发现状下,由于一些现实条件不允许,比如开发人员为了赶项目进度无暇做开发者测试(如单元测试/接口测试和组件测试),从而将产品质量完全依托于上层的系统集成测试(更多的是黑盒测试),即我们当前产品开发中的冰激凌甜筒模型(也叫倒金字塔模型,如下图)
在当前的测试现状下,前期的测试投入很低,整体质量仅依托于以黑盒测试为主的集成测试,测试到一定阶段就达到了瓶颈,有越来越多的问题在测试阶段难以发现,大部分都是小概率问题,有些甚至是代码级别的问题,在集成测试阶段需要用长时间测试来撞,有一定的随概率性,成本很高。对比上文中的冰激凌甜筒模型(当前现状)和金字塔模型(理想模型),我们需要将测试模型逐渐往金字塔模型靠拢,因此我们一方面需要加强前期的测试投入,另一方面需要提升后期的测试效率。
因此,依托于DevOps和自动化测试,我们做了研发流程和测试体系的优化,这得益于研发和测试同学的紧密配合,整体思想是通过提升前期测试的自动化程度(如单元测试、代码静态分析、代码Review、接口、功能测试)从开发阶段就不断夯实产品质量,利用自动化工具提升手工测试和回归测试的效率,并在系统测试和端到端测试阶段引入一些专项测试(比如更多的投入可靠性和可用性测试),如下图:
从上图可以看到,测试活动贯穿于整个研发流程,各阶段的测试活动由不同的角色来完成,下面简要介绍下各个测试活动的内容:
单元测试:单元测试是最小的测试单元,其关注的是代码的实现和逻辑,一般不包含业务逻辑,该类测试由开发人员完成,适合做自动化测试。
接口测试:主要验证模块间的调用返回以及不同系统、服务间的数据交换,有一定的业务逻辑,其关注的是数据,需要构造请求数据,适合做自动化测试。
功能测试:主要是针对产品的功能需求进行验证,包括模块级功能测试以及系统级场景功能测试,自动化率依赖产品提供的能力,一般P0/P1用例自动化程度较高。
可靠性测试:主要是做基于灰盒的系统级故障注入测试,用来验证系统在各种异常条件下的表现(服务是否能正常恢复,业务是否受影响等),故障注入测试依托于产品的故障模式库进行,分为L0/L1/L2等级别的故障以及随机故障注入测试;因故障注入的复杂度,可靠性测试自动化率不高,目前只能做到L0级的故障注入,更多可靠性测试细节请参考系列文章分布式存储之可靠性及高可用测试。
压力测试:主要是对被测系统进行加压测试,用来验证系统在一定负载压力下服务能够持续稳定,一般压力测试会结合故障注入测试一起来进行,用来发现更多的问题。压力测试一般也分为组件级别和E2E压测,组件压测用来验证组件最大的性能指标和瓶颈,E2E压测用来验证整个系统的性能瓶颈。
性能测试:主要是指被测系统在各种workload场景下的性能表现,如常见的OLTP/OLAP/大数据场景等。性能的好坏主要通过一些性能指标来定义,不同的业务对性能指标的要求和定义不同,对存储系统来讲,性能好坏主要通过以下指标来衡量:
- 延迟(Latency):衡量存储系统对请求响应的快慢程度
- 吞吐量(throughput):衡量存储系统在单位时间内所能处理的最大数据量
- IOPS:衡量存储系统中单位时间内所能处理的io请求数
- 吞吐量和iops是衡量一个存储系统的处理能力的强弱,更多存储性能测试细节请参考系列文章存储性能及性能测试。
一致性测试:分布式系统的数据一致性测试是一项非常有挑战和难度的测试,主要是指在分布式系统的处理过程中,为了保证多节点之间的数据一致性(如缓存一致性、事务一致性等)进行的测试活动和测试方法,请关注后续系列文章。
长稳测试:主要是指系统在一定压力长时间运行下,可结合故障和多特性交叉功能,验证系统是否会出现服务不可用、内存泄漏、性能稳定性等问题,一般在测试中后期运行,用来发现短期难以发现的问题。
灾难测试:主要是指在极端情况下(如数据中心掉电、网络中断等),系统能够在灾难恢复后正常恢复数据和服务,自动化难度较大。
兼容性测试:主要包含与其他软件系统的兼容性测试以及硬件兼容性测试,主要通过自动化来验证。
UI测试:主要指站在用户角度,在Web UI进行功能验收测试,自动化率低。
测试流程
开发者测试
开发者测试主要包含:单元测试/代码静态检查/接口及功能的全量自动化/新功能P0手工测试,自动化主要通过CI流水线承载
门禁测试
门禁测试是指提测过程中使用自动化测试进行门禁拦截,主要包含基础功能测试和场景类测试,门禁测试失败则提测打回
第一轮测试
版本提测后进行的第一轮测试,主要以新特性为主(包含P0、P1级别用例),同时通常伴随存量功能的回归测试、长稳和基础可靠性测试
第N轮测试
版本测试的轮数跟版本的测试策略相关,视版本大小而定,可以根据版本大小分为第二轮、第三轮等,主要包含更加复杂全面的系统测试和之前未覆盖的深入测试,如一些可靠性、压力、性能等专项测试,通常包含基于前一轮测试的风险点进行加固测试,以及一些P2、P3的用例
持续测试
持续测试主要基于自动化进行持续反复测试,与迭代的轮次一起并行测试。
探索性测试
分布式系统测试非常的复杂,不同的数据量、不同的压力,甚至不均衡的压力在系统内部可能都是对应不同的分支,不同的集群节点也会对应不同的处理逻辑,我们无法群居罗列所有的组合,覆盖所有的分支,因此需要引入一些探索性测试(如CFT测试)来做一些高随机性的并发测试。
回归测试
版本测试的最后一轮测试,在版本封版后,对主干基线P0用例、新需求P0以及问题单进行回归测试,通常是自动化+手工测试相结合的方式进行
测试质量评估
软件系统的质量评估是一个比较难的话题,因为即使通过上述测试流程层层测试,也难免会有漏网之鱼,我们主要通过以下手段来评估产品质量:
代码覆盖率
代码覆盖率分为单元测试覆盖率和自动化用例覆盖率,它只是作为质量评估的一个参考,根据代码覆盖程度来评估测试用例覆盖到了哪些代码,但是不能说明覆盖好了这些代码。
基于BUG单的质量分析
如前文所讲,我们在测试过程中会根据测试策略分为多轮测试,每一轮测试都有该轮的测试重点,我们根据bug单在整个测试轮次中的分布以及bug对应测试场景的用例覆盖度来评估质量。一个好的产品对应的bug数分布应该符合或者接近正态分布,如下图:
第一轮发现的问题相对较少,在第二轮达到峰值,后面逐渐收敛,最后在回归测试过程中收敛到0。
比质量评估更重要的是对研发流程和测试过程的数据管理,需要对发现bug对应的场景进行分析,按bug数对应的场景聚类,筛选出问题数居多的场景类(即风险最高),分析该场景下的用例覆盖度是否足够,从而得到整体的质量风险。
结语
分布式系统的测试非常复杂,我们需要在测试过程中继续探索测试方法,利用自动化工具加大测试频次和频率,更多更真实的模拟复杂的业务场景