You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 41 | 从小工到专家:聊聊测试执行环境的架构设计(下)
你好,我是茹炳晟,今天我和你分享的主题是“从小工到专家:聊聊测试执行环境的架构设计(下)”。
在上一篇文章中我介绍了测试基础架构的概念以及早期的和经典的两种测试基础架构。在文章的最后我提到经典的测试基础架构中采用的Selenium Grid方案在测试用例的数量持续增加的情况下会带来集群扩容、Jenkins Job臃肿不堪等诸多问题因此我们考虑将Selenium Grid迁移到Docker并且提供便于Jenkins Job管理的统一测试执行平台。
所以,今天的这篇文章,我就会围绕这些瓶颈以及对应的解决方案来展开。
## 基于Docker实现的Selenium Grid测试基础架构
随着测试基础架构的广泛使用以及大量的浏览器兼容性测试的需求Selenium Grid中Node的数量会变得越来越大也就是说我们需要维护的Selenium Node会越来越多。
在Node数量只有几十台的时候通过人工的方式去升级WebDriver、更新杀毒软件、升级浏览器版本可能还不是什么大问题。但是当需要维护的Node数量达到几百台甚至几千台的时候这些Node的维护工作量就会直线上升。虽然你可以通过传统的运维脚本管理这些Node但维护的成本依然居高不下。
同时随着测试用例数量的持续增长Selenium Node的数量也必然会不断增长这时安装部署新Node的工作量也会难以想象。因为每台Node无论是采用实体机还是虚拟机都会牵涉到安装操作系统、浏览器、Java环境以及Selenium。
而目前流行的Docker容器技术由于具有更快速的交付和部署能力、更高效的资源利用以及更简单的更新维护能力也就使得Docker相比于传统虚拟机而言更加得“轻量级”。
因此,**为了降低Selenium Node的维护成本我们自然而然地想到了目前主流的容器技术也就是使用Docker代替原本的虚拟机方案。**
基于Docker的Selenium Grid可以从三个方面降低我们维护成本
1. 由于Docker的更新维护更简单使得我们只要维护不同浏览器的不同镜像文件即可而无需为每台机器安装或者升级各种软件
2. Docker轻量级的特点使得Node的启动和挂载所需时间大幅减少直接由原来的分钟级降到了秒级
3. Docker高效的资源利用使得同样的硬件资源可以支持更多的Node。也就是说我们可以在不额外投入硬件资源的情况下扩大Selenium Grid的并发执行能力。
因此现在很多大型互联网企业的测试执行环境都在向Docker过渡。
而具体如何基于Docker搭建一套Selenium Grid你可以参考我在第39篇文章[《从小作坊到工厂什么是Selenium Grid如何搭建Selenium Grid》](https://time.geekbang.org/column/article/40468)中介绍的方法。由此可见将原本基于实体机或者虚拟机实现的Selenium Grid改进成为基于Docker实现的过程也很简单、灵活。
如图1所示就是一个基于Docker实现的Selenium Grid的测试基础架构。
![](https://static001.geekbang.org/resource/image/56/75/56142f87923aa3c2e27746b91e2c0f75.png)
图1 基于Docker实现的Selenium Grid测试基础架构
## 引入统一测试执行平台的测试基础架构
在实际的使用过程中基于Docker的Selenium Grid使得测试基础架构的并发测试能力不断增强也因此会有大量项目的大量测试用例会运行在这样的测试基础架构之上。
当项目数量不多我们可以直接通过手工配置Jenkins Job并直接使用这些Job控制测试的发起和执行。但是当项目数量非常多之后测试用例的数量也会非常多这时新的问题又来了
1. 管理和配置这些Jenkins Job的工作量会被不断放大
2. 这些Jenkins Job的命名规范、配置规范等也很难实现统一管理从而导致Jenkins中出现了大量重复和不规范的Job
3. 当需要发起测试或者新建某些测试用例时都要直接操作Jenkins Job。而这个过程对于不了解这些Jenkins Job细节的人比如新员工、项目经理、产品经理来说这种偏技术型的界面体验就相当不友好了。
为此我们为了管理和执行这些发起测试的Jenkins Job实现了一个GUI界面系统。在这个系统中我们可以基于通俗易懂的界面操作完成Jenkins Job的创建、修改和调用并且可以管理Jenkins Job的执行日志以及测试报告。
这,其实就是统一测试执行平台的雏形了。
有了这个测试执行平台的雏形后,我们逐渐发现可以在这个平台上做更多的功能扩展,于是这个平台就逐渐演变成了测试执行的统一入口。
在这里,我列举了这个平台两个最主要的功能和创新设计,希望可以给你以及你所在公司的测试基础架构建设带来一些启发性的思考。
**第一,测试用例的版本化管理**。我们都知道,应用的开发有版本控制机制,即:每次提测、发布都有对应的版本号。所以,为了使测试用例同样可追溯,也就是希望不同版本的开发代码都能有与之对应的测试用例,很多大型企业或者大型项目都会引入测试用例的版本化管理。最简单直接的做法就是,采用和开发一致的版本号。
比如被测应用的版本是1.0.1那么测试用例的版本也命名为1.0.1。在这种情况下当被测应用版本升级到1.0.2的时候我们会直接生成一个1.0.2版本的测试用例而不应该直接修改1.0.1版本的测试用例。
这样当被测环境部署的应用版本是1.0.1的时候我们就选择1.0.1版本的测试用例而当被测环境部署的应用版本是1.0.2的时候我们就相应地选择1.0.2版本的测试用例。
所以,我们就在这个统一的测试执行平台中,引入了这种形式的测试用例版本控制机制,直接根据被测应用的版本自动选择对应的测试用例版本。
**第二提供基于Restful API的测试执行接口供CI/CD使用**。这样做的原因是测试执行平台的用户不仅仅是测试工程师以及相关的产品经理、项目经理很多时候CI/CD流水线才是主力用户。因为在CI/CD流水线中每个阶段都会有不同的发起测试执行的需求。
我们将测试基础架构与CI/CD流水线集成的早期实现方案是直接在CI/CD流水线的脚本中硬编码发起测试的命令行。这种方式最大的缺点在于灵活性差
* 当硬编码的命令行发生变化或者引入了新的命令行参数的时候CI/CD流水线的脚本也要一起跟着修改
* 当引入了新的测试框架时发起测试的命令行也是全新的那么CI/CD流水线的脚本也必须被一起改动。
因此为了解决耦合性的问题我们在这个统一的测试执行平台上提供了基于Restful API的测试执行接口。任何时候你都可以通过一个标准的Restful API发起测试CI/CD流水线的脚本也无须再知道发起测试的命令行的具体细节了只要调用统一的Restful API即可。
如图2所示就是引入了统一测试执行平台的测试基础架构。
![](https://static001.geekbang.org/resource/image/40/43/40be3cb1a925bbcf24c2a710f0711443.png)
图2 引入统一测试执行平台的测试基础架构
## 基于Jenkins集群的测试基础架构
这个引入了统一测试执行平台的测试基础架构看似已经很完美了。但是随着测试需求的继续增长又涌现出了新的问题单个Jenkins成了整个测试基础架构的瓶颈节点。因为来自于统一测试执行平台的大量测试请求会在Jenkins上排队等待执行而后端真正执行测试用例的Selenium Grid中很多Node处于空闲状态。
为此将测试基础架构中的单个Jenkins扩展为Jenkins集群的方案就势在必行了。如图3所示就是基于Jenkins集群的测试基础架构。
![](https://static001.geekbang.org/resource/image/db/f5/db974bd10bf6146c2ad1afbdb310ccf5.png)
图3 基于Jenkins集群的的测试基础架构
因为Jenkins集群中包含了多个可以一起工作的Jenkins Slave所以大量测试请求排队的现象就再也不会出现了。
而这个升级到Jenkins集群的过程中对于Jenkins集群中Slave的数量到底多少才合适并没有定论。一般的做法是根据测试高峰时段Jenkins中的排队数量来预估一个值。通常最开始的时候我们会使用4个Slave节点然后观察高峰时段的排队情况如果还是有大量排队就继续增加Slave节点。
## 测试负载自适应的测试基础架构
引入了Jenkins集群后整个测试基础架构已经很成熟了基本上可以满足绝大多数的测试场景了。但是还有一个问题一直没有得到解决那就是Selenium Grid中Node的数量到底多少才合适
* 如果Node数量少了那么当集中发起测试的时候就会由于Node不够用而造成测试用例的排队等待这种场景在互联网企业中很常见
* 而如果Node数量多了虽然可以解决测试高峰时段的性能瓶颈问题但是又会造成空闲时段的计算资源浪费问题。当测试基础架构搭建在按使用付费的云端时计算资源的浪费就是资金浪费了。
为了解决这种测试负载不均衡的问题Selenium Grid的自动扩容和收缩技术就应运而生了。
Selenium Grid的自动扩容和收缩技术的核心思想是通过单位时间内的测试用例数量以及期望执行完所有测试的时间来动态计算得到所需的Node类型和数量然后再基于Docker容器快速添加新的Node到Selenium Grid中而空闲时段则去监控哪些Node在指定时间长短内没有被使用并动态地回收这些Node以释放系统资源。
通常情况下几百乃至上千台Node的扩容都可以在几分钟内完成Node的销毁与回收的速度同样非常快。
至此测试基础架构已经演变得很先进了基本可以满足大型电商的测试执行需求了。测试负载自适应的测试基础架构具体如图4所示。
![](https://static001.geekbang.org/resource/image/da/a4/daeb20ac9fee266ecbabbfadfa2305a4.png)
图4 测试负载自适应的测试基础架构
## 如何选择适合自己的测试基础架构?
现在,我已经介绍完了测试基础架构的演进,以及其中各阶段主要的架构设计思路,那么对于企业来说,应当如何选择最适合自己的测试基础架构呢?
其实,对于测试基础架构的建设,我们切忌不要为了追求新技术而使用新技术,而是应该根据企业目前在测试执行环境上的痛点,来有针对性地选择与定制测试基础架构。
比如你所在的企业如果规模不是很大测试用例执行的总数量相对较少而且短期内也不会有大变化的情况那么你的测试基础架构完全就可以采用经典的测试基础架构而没必要引入Docker和动态扩容等技术。
再比如如果是大型企业测试用例数量庞大同时还会存在发布时段大量测试请求集中到来的情况那么此时就不得不采用Selenium Gird动态扩容的架构了。而一旦要使用动态扩容那么势必你的Node就必须做到Docker容器化否则无法完全发挥自动扩容的优势。
所以说,采用什么样的测试基础架构不是由技术本身决定的,而是由测试需求推动的。
## 总结
在今天这篇文章中,我从测试基础架构演进的视角,和你分享了测试基础架构发展的前世今生。
首先为了降低测试用例过多时Selenium Grid的维护成本我们用Docker容器代替了经典测试基础架构中的实体机/虚拟机形成了基于Docker实现的Selenium Grid测试基础架构。
而后我们发现测试用例的数量达到一定规模后管理和执行发起测试的Jenkins Job成了问题。于是我们引入了一个基于GUI界面的测试执行平台并在其上扩展了诸如测试用例版本化、提供基于Restful API的测试执行接口等功能。从而形成了由这个统一测试执行平台发起测试的测试基础架构形态。
而为了进一步解决由单个Jenkins带来的系统瓶颈问题我们过渡到了基于Jenkins集群的测试基础架构通过多个同时工作的Jenkins Slave解决了大量测试请求排队的问题。
随后为了解决Selenium Grid中Node的数量到底多少才合适的问题我和你谈论了创新设计的Selenium Grid的自动扩容和收缩技术。至此测试负载自适应的测试基础架构也终于“千呼万唤始出来”了。
最后,我谈论了不同企业该如何选择最适合自己的测试基础架构的问题。这里,我强调了一定要根据测试需求选择测试基础架构,而不能一味地追求最新的技术。
我希望通过这种授人以渔的方式,可以帮你拓宽思路,并将测试基础架构的设计思路、思想,运用到你的实际工作中去。
## 思考题
其实,在我讲述的这个测试基础架构的设计和搭建过程中,还有很多可以优化和创新的点。你觉得哪些部分可以再优化呢?你还有哪些想法呢?
欢迎你给我留言,我们一起讨论。