随着容器时代的到来,开源社区中诞生了以 Docker、Rocket 为代表的优秀的容器引擎方案。本文旨在介绍通过容器技术对不同测试类型(应用层测试、中间层测试、内核测试、硬件驱动测试、编译测试)进行容器化改造的方案和收益,并通过具体实例的方式来展示容器化时代为软件测试带来的机遇。
1. 测试工程能力容器化技术沙盘
容器时代为云产业带来了前所未有的机遇,容器云平台如雨后春笋般的出现在大家的视野中。大部分容器云都提供了代码托管、镜像存储、编译、测试、打包、运维等功能。容器云中的容器通常配置有一致的内核,相近的硬件资源。
很多的软件测试任务可以通过容器云进行改进或加速。然而,还有一些类型的测试任务不适用容器云。需要说明的是容器云和容器是两个概念。不适用容器云的业务并不代表不能使用容器对软件测试业务进行改造。
以下沙盘覆盖服务器操作系统、虚拟化、实时操作系统、Web 终端、Linux 内核、互联网服务、安全测试业务,同时对各业务以测试工具、源码编译、应用层、中间层、内核、硬件为基准进行分层。绿色表示该业务适用容器云;黄色表示需要根据具体场景来确定是否适用于容器云;红色表示该业务不适用容器云。我们将在接下来的章节中讨论各业务的容器化改造方案。
2. 各测试类型容器化方案
以下列举了分层中的典型业务的容器化改造方案,某些方案可以在不同的业务中拉通。读者可以根据自己的实际场景有针对性的规划适合自身业务的容器化改造方案。
2.1. 测试工具容器化
容器云作为一个轻量级的容器虚拟化平台,可以集成各类工具,如持续集成、大数据等方面的工具,方便用户进行各类服务的部署。使用容器化来改造测试工具,主要有两点好处:
-
Docker 能将测试工具放在一个干净的容器环境中,并且不会影响到 Guest 文件系统;
-
Docker 镜像可以赋予系统管理员跨 Windows、Mac 和 Linux 服务器的简化管理能力,当服务端为 Linux 服务器时,客户端可以是 Windows、Mac 或 Linux 架构,这样会实现更快速的部署和管理。不过需要注意的是,除非使用虚拟机,Windows 的 Docker 镜像是无法在 Linux 系统中启动成功的。
现在许多主流的测试工具已经推出官方的 Docker 镜像,比如渗透测试领域的 Kali,持续集成的工具 Jenkins 等,同样还有很多未被容器化的测试工具,读者可根据需要自行进行容器化改造尝试,相信随着 Saas 和 Caas 的普及,会涌现出更多的容器云化测试工具。
2.2. 源码编译测试容器化
源码编译测试是运行在用户态中的程序,属于 CPU 密集型的测试任务。其执行测试所需时间与系统资源成反比。容器云是以时间和资源使用量为基准收费的。可以通过 Docker 快速部署、快速伸缩的特点,在短时间内获取容器云中大量系统资源支撑测试执行以达到测试加速的目的。在测试执行完之后,可快速释放所占资源,减少测试成本。同时可以在 Dockerfile 中梳理编译依赖包(如 gcc)和编译软件的安装,使得环境准备的步骤更加清晰。
2.3. 应用层软件测试容器化
应用程序属于最上层的软件,非常适合容器化改造。可参考以下几个方面进行改造。
(1) 将 Docker 和 Devops 结合起来使用,统一开发、测试、运维环境,打通全流程自动化任务。
(2) 利用容器云中 Docker 可扩展可伸缩的特性加速测试执行或者模拟多个终端用户来进行测试。
Selenium 是针对 Web 应用的自动化测试工具,其支持多种浏览器。通过 Selenium 可以模拟用户在浏览器中的操作。
Selenium Grid 是一个分布式 Web 测试工具,可以将测试任务分发到多个主机上并行执行测试。Selenium Grid 架构中包含两个主要角色:Hub 是中心点控制节点,而 Node 是 Selenium 的工作节点,它们注册到 Hub 上,并会操作浏览器执行由 Hub 下发的自动化测试用例。在传统的部署方式中,一个浏览器在操作系统上只能安装一个版本且只能有一个运行实例。为了针对不同版本的 Chrome 进行测试,需要将指定版本的 Chrome 浏览器安装到不同物理机或虚拟机上。这种部署方法耗费资源多,部署时间长。
通过容器云快速伸缩、环境隔离等特性能够快速启动多个容器并行进行不同浏览器版本的测试,可以有效的减少物料消耗和节约测试时间。
(3) 利用容器隔离性和快速环境清理的特性,并行执行软件兼容性测试 (如安装不同版本的数据库)。
(4) 通过容器模拟测试执行需要的外部环境。可将一些典型的测试场景进行容器化处理,将镜像存储到镜像仓库中。可以通过 docker-compose 以微服务的方式部署这些测试场景,便于其他团队复用已有的测试场景。而微服务化的测试策略已经远超本文的范畴,感兴趣的读者可以阅读 [《Testing Strategies in a Microservice Architecture》)]。
(5) 如想在容器云中运行与内核相关的应用,可将应用程序进行内核特性解耦,将内核模块对应的部分代码移植到应用程序中。
2.4. 中间层软件测试容器化
Linux 外围包(通常是 rpm 或 deb 包)是操作系统的中间层,绝大部分外围包与内核无强依赖,是适合使用容器云进行加速的。因为在容器云中无法自定义内核,所以与内核相关性强的外围包的测试是不适合使用容器云加速的。对于这类测试,读者可以搭建可定制内核的私有云来实现测试加速。
除此之外,还有两个特例需要说明一下。
(1)grub 包:grub 是多系统启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。因为与主机启动强相关,所以不适用容器加速。
(2)ntp 包:ntp 是网络时间协议 (Network Time Protocol),它是用来同步网络中各个计算机的时间的协议。因为当前内核不支持 time namespace,即容器与主机间、容器与容器间不能使用不同的时间(注意是时间不是时区)。而更改容器时间就不得不更改主机时间,这样就无法构建具有不同时间的测试环境。如果使用不同主机中的容器来规避该问题,虽然可以构建对应的测试环境,但又无法有效利用容器快速部署、执行带来的收益,相当于为了使用容器而使用容器,没有实际意义。
2.5. 内核测试容器化
Ltp 测试套中包含了最全面的内核功能测试用例集。该测试套的调度程序是串行执行测试用例的。可以通过如下几个方面对内核测试进行容器化改造。
(1) 启动多个容器并发执行内核功能测试来提高 cpu 负载,以便减少测试执行时间。然而,内核测试的容器化会有一定的局限性。这主要体现在:
A. 在容器内执行内核测试时会有部分用例执行失败。例如, pidns32 测试用例是用来测试 namespace 的最大嵌套深度,因为启动容器时已经默认嵌套了一层 namespace,当在容器中执行用例时其还会尝试最大嵌套,这就导致了测试用例执行不通过。对于此类用例,我们需要对其进行容器化适配,设定在容器内可嵌套的层数要比主机上少一层。
B. 某些内核测试项是排他的,需要单独运行,这部分测试需要进行额外的操作处理,比如一些中断操作、寄存器操作。
C. 内核性能测试的执行不适用容器化,否则会有失真。
(2) 可以在容器内搭建内核测试环境。Ltp 测试套中的测试用例源码默认是被动态编译的,其运行需要一些动态链接库。而很多系统特别是嵌入式系统往往缺少这些动态链接库,导致部分内核测试用例无法执行。可将必要的动态库集成到 Docker 镜像中,这样既保证了测试用例可以顺利执行,又可以避免在主机中直接安装库文件造成的环境污染。
(3) 使用 Docker 构建内核集成测试场景。因为内核是底层技术,很难找到一个能够包含多个内核特性的集成测试场景。通过以下 Docker 命令可以轻松搭建 namespace、cgroup、capability、seccomp 等内核特性的集成测试场景。
$ docker run --memory 20m --cap-add sys_admin --security-opt seccomp=unconfined --userns-remap default ubuntu:14.04 bash
2.6. 硬件驱动测试容器化
Docker 的设计初衷是来屏蔽各硬件平台差异的。因此利用 Docker 来改进硬件驱动测试的难度较大。庆幸的是 Docker 利用内核的 device cgroup 特性可以实现设备直通的功能,可将主机中的设备映射到容器中进行测试。
收益:测试环境不会污染主机环境;便于设备驱动并行测试;即使主机文件系统缺少测试所需的库文件,仍然可以使用容器的文件系统中的工具来辅助测试。
例子:串口测试
$ docker run -device /dev/console:/dev/console ${your_image} ${your_test_script}
2.7. 压力稳定性测试容器化
有一句行话叫做“无压力不测试”,意思是说很多软件在无压力环境中是运行正常的,但当系统持续加压时,软件可能会暴露出很多稳定性和容错性方面的问题。因此在多个领域的测试中,都会在集成测试或者系统测试阶段开展长时间压力稳定性测试。服务器操作系统、虚拟化与 linux 内核等业务中传统的长稳测试框架均存在以下痛点:
(1) 部署工作复杂。
加压测试套编译与运行有很强的平台依赖性,当测试涉及多平台复杂组网时,会出现很多兼容性问题。系统巡检工具的安装依赖包多,需要人工进行各个版本依赖包的适配工作。一套完整的长稳测试框架的部署(包括背景加压、系统巡检与用例执行)会耗费大量人力,影响了测试效率。
(2) 加压模型单一。
(3) 容错能力差。
当系统中因为误操作或者资源竞争导致加压或巡检进程异常退出时,系统不具备任何应急处理能力。
基于以上痛点,我们对该项测试进行了容器化改造的实践。首先是背景加压容器化,将 cpu、内存、io 的测试套打包成 Docker 镜像,以 cpu 加压为例,可以运行下面的命令来对系统进行 cpu 加压:
$ docker run -tid -m 3g --restart=always back_stress bash -c "bash cpu_stress/test_20.sh
这样完全可以解决编译安装时间长与平台化差异所带来的影响,-m 3g --restart=always 增强了加压框架的容错性,限制了加压容器最大使用的内存量,可以避免因加压程序导致系统垮掉的问题(如加压程序有内存泄露时,当容器试图使用超过限定内存时会触发 oom,在 restart 规则下容器所占资源会被清理同时容器会快速重启并持续进行加压)。test_20.sh 明确了加压程度为 20%,可以通过增加容器的数量来实现加压强度的叠加,常见的加压模型如上图所示。
(点击放大图像)
其次,我们实现了系统巡检的容器化。将Zabbix 巡检工具的server 端与agent 端均打包为镜像,在多平台组网复杂的环境中,可以完全屏蔽平台化差异,通过对应镜像可以一键拉起巡检容器,只需要在web 端进行配置即可实现长稳环境的系统巡检。
3. 总结
本文针对不同的测试场景,列举了容器化改造方案。读者在针对实际的测试场景进行容器化改造时,不必拘泥于本文的理论和方法,可因地制宜的制定有效的容器化改造策略。
作者简介
孙远,华为中央软件院资深工程师,硕士毕业,9 年软件行业经验。目前在华为从事容器 Docker 项目的测试工作。工作涉及到功能测试、性能测试、压力测试、稳定性测试、安全测试、测试管理、工程能力构建等内容。参与编写了《Docker 进阶与实战》的 Docker 测试章节。先前曾经就职于美国风河系统公司,作为 team leader 从事风河 Linux 产品测试工作。活跃于 Docker 社区和内核测试 ltp 社区,目前有大量测试用例被开源社区接收。
研究方向:容器技术、Docker、Linux 内核、软件测试、自动化测试、测试过程改进。
魏妍,华为中央软件院测试工程师,厦门大学毕业,目前在华为从事容器 Docker 项目的测试工作。工作涉及到容器功能测试、性能测试、压力测试、 稳定性测试、安全测试、测试工程能力构建等内容。主导了华为容器项目长稳测试的构建与开展,提出了多个容器化持续改进,并获得优秀持续改进奖与华为知识管理精品成功故事奖。活跃于 Docker 社区,目前有多个 PR 被开源社区接收。
感谢木环对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论