http://www.programmer.com.cn/15093/
文 / 许呙兢,陈舟锋,郑刚
飞天测试的挑战
飞天开放平台基于一个核心系统,即飞天大规模分布式计算系统(简称飞天)。飞天期望把几千台PC构成一台“超级计算机”,为上层多种不同的开放服务和云应用提供通用的分布式存储、计算和任务调度等多重功能。由此可以看出,飞天具有平台化、通用性和大规模的特性,于是,飞天测试的挑战也由此而来。
挑战一:平台软件的复杂性和互联网发布节奏之间的矛盾。
飞天包含多个复杂的分布式模块。模块本身的复杂性乘以各模块之间的协议依赖,按照传统软件开发流程计算,发布一个质量可靠的稳定版本通常需要1~2年。这样的发布节奏远远满足不了上层开放服务和云应用快速发展的需要。
挑战二:通用平台支持多种不同应用带来测试用例数的爆炸。
对于飞天,不同的应用场景、不同的数据量、不同的请求压力、不同的机器规模,有可能在代码里面走的路径完全不一样,对系统的压力点也各不相同。无论是试图覆盖所有应用对飞天的所有用法,还是从设计出发遍历模块接口的各种组合,对测试用例设计而言都是不收敛的。那么,当测试用例剪枝无门,是否还有其他捷径?
挑战三:对于大规模生产集群上的问题如何用小规模测试集群暴露。
在阿里各地的数据中心,飞天的生产集群是上千台物理机组成的。考虑到成本,测试集群规模通常不超过生产集群的十分之一。统计数据显示,100台和1000台的分布式环境的软硬件故障率、压力瓶颈点、数据量级、网络性能都会有很大差异。常规测试方法很难在小集群上发现大规模的问题。
下面,我们来谈一下飞天测试实践当前是如何应对这三种挑战的。
分层测试和持续集成
目前,飞天底层模块的发布节奏是半年一次,上层模块的发布则更为频繁,最短可以达到三周发布一次。这样的发布节奏主要依靠分层测试和持续集成的机制。按测试层次来分,飞天测试可以分为单元测试、功能测试、系统测试、集成测试、E2E测试(端到端测试)。为了加速飞天新版本的质量收敛,飞天团队几乎每个成员都会参与到上述测试类型中——无论是开发同学,还是测试同学。
一般来说,产品只会对外部接口进行功能测试和系统测试,但由于飞天模块本身就是分布式的,每个模块都具有一个传统软件产品的复杂度,所以模块团队除了负责单元测试,也会进行功能测试和系统测试。模块团队内,开发同学除负责单元测试之外,还会承担功能测试和局部特性的系统测试,测试同学通常更专注在测试设计和模块级别的系统测试。
飞天有独立于模块的集成测试团队,集成测试主要负责两块。
一方面,通过持续集成的回归测试集来保证系统中的各个模块改动集成在一起能够很好地工作,一旦发现无法短期修复的质量回退的话,模块改动会被立刻关闭或回滚。为保证持续集成效果,不同层次的回归测试集都被尽可能自动化,并且定义合适的回归频率,模块改动在设计时也会考虑方便关闭或回滚。
另一方面,集成测试也进行平台级别的系统测试,极尽所能地对飞天进行各种严刑拷打,考察底层模块的功能、性能和系统容量,以及在极端或典型应用场景下系统的稳定性和服务可用性。
飞天新版本上线前,开放服务团队一般都会用集成测试通过的版本跑E2E测试。E2E测试的责任人需负责向应用方了解具体需求,这个需求不仅是一个对接口功能的需求,还包含了数据量的需求、机器规模的需求、吞吐率的需求、延迟时间的需求以及业务量在一天或者一周内的曲线等信息。E2E测试通常要构造近乎完整的应用场景,尽可能模拟/重现真实的数据情况和压力特征,并且要通过长时间的稳定性测试。有些上层应用还会常备试运行环境(Staging Environment)来随时做E2E测试的验证。通常,我们只有在通过了最后的E2E测试之后才能上线给应用生产集群。
为保证测试本身的质量,各层测试覆盖有不同的衡量方法。单元测试用Coverage工具来检验行覆盖和分支覆盖;功能测试一般考察功能点覆盖外,这些都是大家熟知的。此外,我们对系统测试、集成测试和E2E测试设计了一种特别的覆盖——Log Coverage。Log Coverage工具能够通过测试运行过程中飞天输出的Log信息的多少来判断测试是否有足够的覆盖率。通过拿到生产集群的Log与测试中的Log进行比较,会找到我们之前没有测试到的地方。另外,通过对代码中从未打印出的Error Log的检查,我们也可以知道有多少异常逻辑我们没有测试过。
基于监控的探索性测试和灰盒测试
如挑战二所述,测试用例的爆炸一度让我们非常纠结。我们发现无法通过黑盒测试的设计思路来穷举所有的情况,即使能设计出足够完整的测试用例,也没有足够机器、人手和时间来执行这些测试。
所幸,我们还有探索性测试,监控系统则成为我们探索方向的指引。
飞天有详细的监控系统,可以监控整个集群的各种参数,这些参数不仅是OS层面的参数,更多的是飞天模块本身通过调用我们监控系统的API来完成对自身某些指标的统计。这些统计不仅在线上系统能够起到监控报警的作用,也能给探索性测试提供依据。执行测试的人员可以通过不断改变测试的各项参数,结合这些指标的变化进行探索式的测试,一个压力测试用例在执行时可以变化出贴近应用的各种极端场景。通常,通过指标在某些压力变化下或者随着时间推移时的异常行为,测试人员会更容易找到一些深藏的Bug。另外,在平台级别的系统测试时,通过对模块内部Error Log的监控也能达到很好的效果。
探索性测试固然重要,但仅有探索,如果测试人员本身不了解系统的一些内部逻辑的话,就会出现两种情况:第一种是只验证设计好的场景,其他一些异常的情况,自己无法解释,但本身又不是验证标准,导致很多隐藏的问题最终在线上爆发;第二种像一个无头苍蝇,漫无目的地进行探索,浪费了时间,却达不到好的效果。在飞天测试中,我们要求测试人员必须从方案设计之初甚至是讨论需求时就和开发的同学在一起讨论,测试的同学需要比开发更加理解系统的设计原则。
了解分布式系统的工作原理后,测试同学会明白如何去做一个有效的灰盒测试。比如,在某个关键点加请求压力会事半功倍;在哪个时机去做测试结果断言会更方便、彻底或完整;甚至知道对一个模块进程如何注入代码,模拟重现机率很小的协议通信丢包的问题。
有一个很典型的例子。早期,我们内部开发一个基于表结构的存储引擎时,曾出现过这样一件事情:测试程序在最终验证一致性时,一直都是通过的,但业务方和我们一起做E2E测试时,会有很低的概率发现数据读出来是错误的。测试人员百思不得其解,最后发现,这份数据在写入的时候,会先在三个地方进行修改,但由于一些时序和锁的问题,在改过了两个地方之后就返回成功了,第三个地方是在内存中,过一阵子就会被重新刷成正确的值。如果当时测试的同学知道系统里面的这些设计,当时就会设计写入过程中,对数据一致性进行实时检测,就不会在代价更高的E2E测试中发现问题,解决的效率也会因此而提高。
带压力和随机故障模拟的长时间稳定性测试
文首提到大规模生产集群上的问题,很难在小规模的测试集群上发现。究其原因,主要是两方面导致的。
• 大规模集群中原本小概率的单机故障会随机器数增加,导致集群整体的硬件故障率线性提升,甚至多种故障同时发生的概率也大大增加。
• 机器数增多会导致对飞天模块的压力点发生转移。以弹性计算(ECS)为例,在300台变600台时发现,原本担心的文件系统Master还未成为QPS瓶颈,负责锁文件协同的命名服务首先成为瓶颈。
此外,各种故障的组合爆炸也让完整的容错测试在设计和执行上的代价变得太大。
为解决上述困难,在飞天测试实践中,我们逐渐积累出一套带背景压力和随机故障模拟的长时间稳定性测试方案。背景压力主要是针对各个底层模块的读写压力,通常会针对某类应用场景来模拟。
基于分而治之和系统仿真的方法,我们实现了一些轻量级的压力工具,让各模块的Master机器和Slave机器分别接受到大规模生产集群上的类似访问压力和连接规模。另外,还增加一些诸如CPU、Memory、Network的资源消耗器,以模拟生产环境业务繁忙导致机器资源紧张的场景。
故障模拟上,一方面,尽可能丰富软件手段模拟软硬件故障,比如磁盘错误(包括坏盘、只读等)、机器宕机、重启、断网、交换机重启、主要模块进程重启、假死等;另一方面,这些故障模拟操作都会按照预先设定比例进行随机组合。在这样的背景压力和随机故障操作下,长时间(至少7×24小时)持续运行上层应用模拟程序/作业,同时通过在线监控系统来检查飞天是否正常。
简而言之,我们用背景压力解决压力点问题,用长时间跑解决小集群的故障小概率问题,用随机故障模拟和组合来解决容错测试设计和执行的代价问题。
实践证明,飞天很多重要的Bug都是通过这个测试被发现的。当然,这类测试也有短处,就是问题调查需要较长的时间,要求测试人员对系统有较深的了解和诊断能力。
结束语
大规模分布式系统的测试是一项非常有挑战的工作。尽管我们持续落实各层测试,积累实践经验,创新测试方法,但由于测试条件的限制、生产环境的复杂,软件问题仍然无法完全依赖测试消除。本文仅仅提到了飞天测试在Test in Lab方向的一些思考和实践,完善的飞天质量保证体系其实需要Test in Lab和Test in Production双管齐下,这才是飞天测试乐此不疲的努力方向。
作者许呙兢,阿里云飞天技术专家,负责飞天开放服务质量保障,长期工作在测试工作第一线,目前专注于分布式大规模在线场景的测试。
作者陈舟锋,阿里云飞天技术专家,负责飞天基础模块集成测试。毕业于北京航空航天大学,毕业后一直从事软件测试开发工作,曾就职于微软亚洲工程院和搜索技术中心,在UI自动化测试和分布式系统测试上有浓厚兴趣和较多积累。
作者郑刚,阿里云飞天高级专家,负责飞天集成测试和版本发布。毕业于南京大学,主要兴趣在大规模分布式系统的系统测试、持续集成和系统仿真。