块存储质量的铸就之路 — 测试左移在大型分布式系统中的工程实践

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 修复一个Bug的成本在不同阶段有着天壤之别,发现问题越早,修复代价便越低。本文将讲述阿里云块存储在真实业务场景中的测试左移实践。

为什么要测试左移?

       众所周知,软件工程原则:问题发现得越早,修复问题的代价越低。在《代码大全》书中,从软件工程实践角度说明一个Bug的成本在产品需求分析阶段、开发阶段、测试阶段、生产阶段有着天壤之别,在修复难度、引入新问题的可能性、沟通成本等方面计算,集成测试阶段修复一个Bug的成本是编码阶段的40倍。



      什么是测试左移?即测试向左扩展,让测试介入代码提测之前。比如,扩展到开发阶段,在架构设计时考虑产品的可测试性,进行开发自测。 测试左移是一个理念,代码门禁是测试左移的典型实践,即提交代码自动触发编译和测试,构建失败则阻塞代码提交。







图1 代码门禁

       代码门禁通过缩短测试反馈弧尽早发现缺陷,阿里云块存储团队代码门禁单日有效拦截百余次Case,拦截多个业务逻辑缺陷、百余次进程Crash、数据安全性缺陷、CPU/Mem资源使用缺陷等。如若没有代码门禁,将导致问题被掩盖,不断积累。



何时进行测试左移?

       在系统最初阶段建立严格的代码准入标准和CICD系统是否是最好的时机呢?从质量保证的单一维度考虑,答案是肯定的。但从业务整体效益来看并非全局最优。理性看待技术债,技术债像贷款,有好也有坏,可以超前消费变现贷款买房,但相应的,有利息,利滚利,开发难度越来越大。



       RethinkDB与MongoDB在竞争中失利。技术上,RethinkDB比MongoDB更追求完美,但是比MongoDB发布稳定版本晚了三年,错过了NoSQL的黄金时机,《RethinkDB:why we failed》。如下图所示,A B C三家公司关于技术债和业务先行的时间之间的平衡:

○A公司:只关注业务,不关注技术债;

○B公司:持续关注技术债,但对业务时机不敏感;

○C公司:持续关注业务和技术债。对业务机会很敏感,最初放手借贷,控制技术债并在合适的时候偿还。





图2 技术债 (来源:《质量与速度的均衡:让“唯快不破”快得更持久》葛俊)



       阿里云块存储在2018年初发布ESSD邀测,业界首个百万IOPS的云盘服务,性能上有50倍的飞跃,在真实业务场景中,PostgreSQL数据库的写入速度快了26倍。透支了大量的技术债抢先了市场之后,团队内部集中偿还技术债,进行质量建设,一年后ESSD达到规模铺量的质量标准并进行了商业化。



测试左移的原则和实践

提前透支大量技术债后,团队养成了“糙快猛”的研发习惯,对于改变习惯和测试左移将面临落地的挑战,需要从上至下调整预期,测试左移的前期必然带来项目交付周期的放缓,长远来看,整体效率更高。



测试左移原则不等同于测试原则,实践总结了三条测试左移的原则如下:





表1 测试左移原则

原则一:左移标准共识

建立左移标准并在团队内达成共识是测试左移最基础的原则,在代码门禁系统中要求代码覆盖率卡点、静态代码质量扫描,业务覆盖率要求是所有功能测试均需在代码门禁阶段添加相应的测试Case覆盖。

例如,块存储的云盘是一个分布式存储系统,通过建立了Cluster in Docker的一键秒级构建集群环境实现Function Test测试脚手架,使得全链路E2E测试在代码门禁阶段有了落地的土壤。新Feature评审时功能测试不接受手工测试报告,只接受Function Test List的Code Review,以避免手工测试无自动化沉淀,相同问题重复出现。



原则二:坚持快速反馈

早发现早治疗,越早治疗修复成本越低。对于架构设计、编码实现和测试不足遗漏到生产环境的缺陷,不断反问,该问题是否可以在更早的测试阶段拦截?

例如,分布式系统下的级联雪崩故障,RPC Timeout不合理 + 无限重试 + 无并发Queue Depth限流造成错误的循环持续运转,负反馈机制压崩了分布式集群。全链路的极限压测是必须的,同时对于单一功能测试验证也必须在代码门禁环节添加自动化Case,以自动化验证限流、重试和超时机制符合设计实现预期。



原则三:持续分解问题

持续分解问题是测试左移最核心的原则,把一个系统拆解为多个子系统,用抽象和分层的方法,让每个同学开发同时只面对有限的信息,并且能够有条理的深入到每一个子系统中查看细节。

例如,将复杂问题拆分成具体到每个模块松耦合的功能语义,各模块补充各自的契约测试覆盖。对于分布式系统Server热升级(热升级,即不影响服务的升级),需在升级前,中心管控节点Master将Server进程服务调度走,Server进程之间负责服务迁移(老进程Unload,新进程Load),Client需从Master/Server感知到服务已被调度走,需更换Location进行访问,拆解成Client/Master/Server的多个模块内Case覆盖。



在测试左移的实践过程中,总结了如下三个阶段:



阶段一:建立测试脚手架

简单可依赖的CICD的测试框架是基建保障。业界有很多开源的CI测试框架,例如Jenkins、GitlabCI、Travis-CI、Tekton等,阿里的Aone和蚂蚁的LinkIn等

对于IaaS的底层块存储分布式系统,单个单元测试即达到4 Core Cpu和6GB Mem的资源需求,单机无法满足近万个门禁Case测试的及时性。业界和公司内的系统无法满足分布式编译构建和分布式测试的需求,块存储基于Kubernetes+Jenkins自研实现了门禁系统。





图3 EBS CI门禁系统



       通过Kubernetes实现Case资源隔离,每个Case独占容器,容器运行过程中CPU/Mem资源设置Limit,避免Case之间打架(例如:某个Case内存泄漏导致内存不足,发生争抢,CGroup可能误杀了其他Case),Case运行环境用完即抛确保环境一致性,提交代码即自动触发测试(Test as a Service),确保有了土壤可以落地后,门禁系统负责测试运行,开发者负责Case编写和不稳定Case的问题解决。



阶段二:高频测试,快速测试

"If it hurts, do it more often",高频测试是治理不稳定Case的法宝,越低频复现的问题越难调查,尽量多的暴露不稳定的失败,降低问题调查的门槛。通过分布式并发运行、提高构建速度(增量编译/分布式编译)、分层测试提升测试运行速度。



       块存储门禁系统基于Kubernetes实现,即具备测试并发度横向扩展能力,白天CI系统进行代码门禁卡点和自助构建任务,夜间高频百轮回归门禁Case和E2E回归。高频测试大大增加了低概率时序Bug的暴露频次,在门禁系统中通过CPU资源超卖,相当于模拟CPU主频降频,多次发现低概率的数据安全性、进程Crash等缺陷问题。





图4 EBS 各级测试运行轮数



阶段三:不稳定Case治理

不稳定Case治理可能是门禁系统中挑战最大的部分,不稳定Case若不治理将导致破窗效应,测试左移则前功尽弃。块存储门禁系统是全量卡点,即任一Case失败则阻塞代码提交,随着Case的增加,对Case的稳定性要求与日俱增。门禁系统上万个Case中如若有3个Case通过率为90%,则整体通过率为72%,关于不稳定Case的治理Google和微软在2016年和2017年相继发表了论文,Google链接微软链接



块存储治理不稳定Case主要通过高频测试和文化布道,一周内不稳定的Case的每日运行轮数是稳定Case的权重5倍,未修复Case更高频的暴露复现,对于已修复的Case以验证修复效果。团队内从上至下达成一致共识:生产问题优先级 > 不稳定Case优先级 > 新Feature项目研发优先级。下图是块存储近半年不稳定Case Top15的回归失败轮数,分子是失败轮数,分母是单日运行轮数,失败比例越高颜色越深。





图5 EBS 门禁Top15不稳定Case通过率



       在测试左移的实践落地的过程中会遇到很多挑战,知行合一很难,整个团队遵守相同的标准规范落地更难。块存储系统有百万行代码,在近一年中生产代码行数增加约20%,测试代码行增加约100%,近万个门禁Case,持续治理不稳定Case,门禁通过率从4.7%提升至70%,后续计划通过精准测试进一步提升门禁通过率。

       仰望天空,并脚踏实地。在测试左移的编码实践方面推荐学习TDD(Test-Driven Development),在《软件测试》《Google :Building Secure & Reliable Systems》《重构》 《重构与模式》《敏捷软件开发》《程序员的职业素养》……国外泰斗级程序员大叔的书里,全部都推荐了TDD(测试驱动开发)。TDD不是万能药,主要思维模式是,先想清楚系统的行为表现,再下手编码,测试想清楚了,开发的API/系统表现就清晰了,API/函数/方法语义就明确了。如何衡量测试的好坏?好的测试是 What,包含 Given When Then;差的测试是 How,若每次方法/函数修改之后如果必须完全重写测试,或许需要重新考虑测试实现和系统本身的设计结构了。

相关实践学习
基于EBS部署高性能的MySQL服务
如果您通常是通过ECS实例部署MySQL来使用数据库服务,您可以参考本实验操作来搭建高性能的MySQL服务。本实验为您演示如何通过EBS ESSD云盘部署一个高性能的MySQL服务。
相关文章
|
5天前
|
测试技术
【测试】构建质量保证之路:编写测试用例的艺术
【测试】构建质量保证之路:编写测试用例的艺术
|
5天前
|
jenkins Devops 测试技术
单元测试与质量保证:确保Visual Basic代码的健壮性
【4月更文挑战第27天】在VB开发中,单元测试是保证代码质量和软件健壮性的关键。本文介绍了单元测试的基础,包括其定义和好处,如提高代码质量、促进重构。接着,讨论了MSTest、NUnit和xUnit等VB单元测试工具。遵循TDD原则和最佳实践,编写独立、有针对性的测试,并注重测试速度和覆盖率。通过示例展示了如何在Visual Studio中设置和运行测试。最后,提到了持续集成和自动化测试工具,如Jenkins和静态代码分析工具,以提升软件开发效率和质量。单元测试不仅是技术手段,更是提升团队协作和软件工程水平的文化体现。
|
1天前
|
机器学习/深度学习 人工智能 算法
深入理解与实践:基于AI的软件测试自动化
【5月更文挑战第1天】随着人工智能的不断发展,其在软件测试中的应用也日益广泛。本文将探讨如何利用AI进行软件测试自动化,包括其理论基础、实现方式以及在实际中的应用。我们将通过实例分析,展示AI在提高软件测试效率和质量方面的巨大潜力。
|
1天前
|
Java 测试技术 开发者
深入理解与应用单元测试:软件质量的守护者
【4月更文挑战第30天】 在现代软件开发过程中,单元测试作为保障代码健康的重要环节,其地位日益凸显。本文将探讨单元测试的核心概念、实施单元测试的重要性以及如何高效地设计并执行单元测试。通过实例分析,我们将揭示单元测试在确保软件产品质量和加速开发周期中的关键作用。
|
2天前
|
中间件 测试技术 API
探索自动化测试工具的新边界:Selenium与Appium的集成实践
【4月更文挑战第30天】 随着移动应用和Web应用的不断融合,传统的自动化测试工具需要适应新的测试环境。本文将详细分析Selenium和Appium这两款流行的自动化测试工具的集成实践,探讨如何构建一个能够同时支持Web和移动端应用的自动化测试框架。通过对比两者的技术架构、功能特性以及在实际项目中的集成过程,我们旨在为读者提供一个清晰的指导,帮助他们在复杂的应用环境中实现高效、稳定的自动化测试流程。
|
2天前
|
测试技术 块存储 开发者
阿里云块存储团队软件工程实践
本文介绍了阿里云团队软件工程实际开发流程,并简述了开发过程中遇到的一些问题。且附带案例,以及遇到案例中出现的情况应当如何应对。
|
2天前
|
机器学习/深度学习 人工智能 数据管理
深入探索自动化测试框架的设计与实践
【4月更文挑战第30天】 随着软件行业的快速发展,传统的手动测试方式在效率和可靠性方面逐渐暴露出不足。本文将重点探讨自动化测试框架的设计理念、关键组成部分以及实际应用中的挑战与解决方案。通过对自动化测试框架的深入研究,我们旨在为读者提供构建高效、可靠的自动化测试系统的指导和启示。文章首先分析了自动化测试的重要性,接着详细介绍了几种流行的自动化测试工具,并以此为基础提出了设计框架的核心原则和架构模式。最后,通过案例研究,展示了如何在实际项目中实施自动化测试框架,并讨论了面临的常见问题及解决策略。
|
2天前
|
机器学习/深度学习 人工智能 机器人
深入理解自动化测试:框架、工具与实践
【4月更文挑战第30天】 在现代软件开发周期中,自动化测试已成为确保产品质量和加速市场交付的关键环节。本文将探讨自动化测试的核心框架、常用工具以及实际应用的最佳实践,旨在为软件测试工程师提供深入的理解和有效的策略,以改进其自动化测试流程。我们将分析几种流行的测试自动化框架,包括Selenium、Appium和JUnit,并讨论如何根据项目需求选择适合的工具。此外,文中还将介绍持续集成(CI)环境下的自动化测试策略,以及如何通过测试结果分析和报告来优化测试过程。目标是帮助读者构建更健壮、更高效的自动化测试系统。
|
3天前
|
敏捷开发 机器学习/深度学习 Java
Java中的异常处理机制深入理解与实践:持续集成在软件测试中的应用探索自动化测试在敏捷开发中的关键作用
【4月更文挑战第29天】在Java编程中,异常处理是一个重要的概念。它允许开发者在程序执行过程中遇到错误或异常情况时,能够捕获并处理这些异常,从而保证程序的稳定运行。本文将详细介绍Java中的异常处理机制,包括异常的分类、异常的处理方式以及自定义异常等内容。 【4月更文挑战第29天】 随着敏捷开发和DevOps文化的兴起,持续集成(CI)已成为现代软件开发周期中不可或缺的一环。本文将探讨持续集成在软件测试领域内的关键作用、实施策略以及面临的挑战。通过对自动化构建、测试用例管理、及时反馈等核心要素的详细分析,揭示持续集成如何提高软件质量和加速交付过程。 【4月更文挑战第29天】 在当今快速发
|
3天前
|
Java 测试技术 Shell
单元测试3.0实践之Golang质量生态建设
研发是否必须写单测?文章提到,单元测试对于确保代码质量、提高软件可靠性至关重要。在顶级互联网公司中,单元测试被认为是必要的,因为它能快速执行、降低维护成本,并能发现代码中的问题。文章还讨论了Go语言中单元测试的优化,如支持不同版本的Go、提高测试覆盖率、处理并发问题等。此外,文章介绍了一个用于Go语言单元测试的插件,该插件可以集成到持续集成流程中,提供详细的测试报告和覆盖率信息。通过这个插件,团队可以方便地管理和执行单元测试,提升开发效率和代码质量。