DevOps: A Software Architect`s Perspective
testing brings errors, errors bring understanding. —— Burt Rutan
5. 构建与测试
概述
用于支持开发和部署的基础设施应支持以下需求:
- 团队成员可以并行地在系统的不同版本上工作;
- 若一个团队成员突然离开团队,该团队工作不会丢失;
- 团队成员的代码易于测试;
- 团队成员的代码易于与别的团队的代码集成;
- 系统的集成版本易于部署到不同环境中;
- 系统的集成版本在不影响系统生产版本的前提下易于完成完整的测试;
- 可以对系统最新部署的版本进行监控;
- 一旦代码放入环境,如果出现问题,可以立即找到旧代码;
- 代码可回滚;
定义 持续集成 的一种方法是 在一个与下一个阶段之间有一个自动触发器,直到集成测试。
持续交付 使用自动触发器,直到预发布系统;
持续部署 到最后部署到生产也是自动化的。
5.2 在部署流水线中移动系统
5.2.1 可追溯性
可追溯性意味着,对于生产中的任何系统,都可以准确知道它如何进入生产环境。
这要跟踪源代码,也要跟踪作用于系统元素的所有工具、所有命令。
“通过脚本的控制工具比通过命令的控制工具好很多。脚本以及相关配置参数应该像应用程序代码一样进行版本控制。”
该原理的应用:
“基础设计即代码 (Infrastructure-as-code)”:
脚本、测试通过版本控制系统来维护。配置参数像文件一样保存在版本控制系统中。
5.2.2 环境
一个执行系统可以看做 一组执行代码、环境、配置、与主系统交互的环境之外的系统、数据等。
- 提交前:环境一般是本地;
- 构建与集成测试:通常是 一个持续集成服务器。编译代码-构建组件-烧制为镜像;集成测试中产生的测试数据库包含了足够多case;配置参数把构建的系统与集成测试环境相连接;
- 用户验收测试/ 预发布/ 性能测试:此环境尽可能接近生产环境;
- 生产环境:实际;
此外维基百科列出的环境有:
- 本地
- 开发
- 集成:持续集成(Continuous Integration, CI) 构建目标,用于开发人员测试;
- 测试 / 质量保证 (Quality Assurance, QA):用于 功能、性能测试、质量保证;
- UAT:用户验收测试;
- 预发布/ 生产前:生产环境的镜像;
- 生产环境/ 实时:服务于终端用户。
5.3 横切关注点
框架测试、负面测试、回归测试、错误的可追溯性、组件的大小以及环境的拆解。
框架测试
测试框架 是一组软件及配置的测试数据,通过在各种条件下运行它,并对它的行为及输出进行监控,以便测试一个程序单元。
测试框架的特征:可打印各种log。
负面测试
测试环境通常假设用户正确输入且程序正确执行,负面测试即,使这些假设不成立,检查系统所有行为是否符合预期。
回归测试:
回归:在对系统现有功能或非功能领域进行 改进、打补丁或者配置更改后力求发现新的缺陷;
自动创建回归测试:在部署流水线中的后期检测到的失败,自动记录,并作为新的测试添加到单元或集成测试中;
错误的可追溯性
如果生产环境出现缺陷,需尽快找出产生该错误源于哪个版本的源代码。方法有二:
- 关联应用程序与flag: 例如用于说明起源的各部分软件和脚本提交的id;
- 把生产环境中每个机器的起源放到一个外部配置管理系统中。
小组件
小团队意味着小组件;
小组件路径少、接口参数也相对少,需要的测试用例少;
环境拆解
当一个环境不再用于某一特定目的时,如与发布测试,即应该将它拆解。原因在于一 释放与此环境相关的资源,二避免资源交互。
5.4 开发及提交前测试
版本与分支、功能开关、配置参数等。
5.4.1 版本控制与分支
版本控制核心功能:
- 可识别不同版本的源代码;
- 不同开发人员之间共享修订;
- 记录从一个版本到下一个版本谁做了修改;
- 支持建立分支;(分支 本质上是存储库(或一部分)的一个副本,允许多个工作流的独立演化)
- 记录修改范围。
分类:
集中式:CVS, SVN
每个开发人员都到中央服务器上检查代码,将变更提交到这台服务器。
分布式:git
每个开发人员都有包含所有内容的Git存储库的一个本地克隆(或 副本)。在本地存储库 submit,一系列变更可以通过中央服务器同步,其中服务器的变更与本地存储库同步 (pull),本地变更可提交给服务器使用 (push)。在执行 pull 命令过程中,对同一个文件所做的变更会自动合并。
5.4.2 功能开关
分支任务的弊端:一分支太多,以至于无法确定特定任务应该在哪个分支上完成;二合并两个分支的困难。分支的替代方案是 所有开发在主干上直接工作,开发人员可能同时在同一模块中的不同任务上开发,由于任务之间有顺序或依赖关系,一个团队的任务结束并不意味着可以提交,有可能需要等待相联系的任务的团队完成。—— 为解决这个问题,提出“功能开关”。
功能开关(“功能标志” 或 “功能切换”) 围绕不成熟代码的 if 语句。
if (feature_toggle) then
new code
else
old code
end;
5.4.3 配置参数
配置参数是一个能更好改变系统行为的外部设置变量。
参数数量应保持在一个 可管理的水平。
大部分编程语言有很好的库,提供相对健壮的配置管理。功能包括: 检查值是否已经指定(或 是否可以使用默认值)、格式范围是否正确;检查url有效性;检查配置是否与多个配置选项兼容等等。
5.4.4 在开发和提交前测试 中的测试
- 测试驱动开发:在开发功能的实际代码前,先为它开发一个自动化测试。然后开发功能,目标是满足测试的要求。测试通过后重构代码以满足更高的质量标准。
- 单元测试:代码级别测试,每个单元测试都是检测单独的类或方法,在特定输入下是否产生预期输出。
5.5 构建与集成测试
构建 是 从源代码和配置等的输入创建一个可执行文件的过程。包括 编译和打包执行所需的所有文件。
构建完成后,执行一组自动化测试,测试与系统其他部件的集成是否发生错误。
5.5.1 构建脚本
CI 服务器的输入应是 可由单个命令调用的脚本。即:
运维人员和服务器执行的唯一输入命令是 “build”,CI服务器其他操作都由脚本控制。
5.5.2 打包
生产环境决定打包的内容:
- 运行时特定的包;
- 操作系统的包;
- 虚拟机镜像:可以从模板镜像创建,已包含最新版本的变更;
- 轻量级容器:可以包含运行应用程序所需的所有库和其他软件,同时还保留过程、权限和文件等的独立性。与虚拟机镜像不同,其不需要主机上的虚拟机监视器,也不包含整个操作系统。其可以运行在开发人员本机、组织拥有的测试服务器或公有云资源上;
5.5.3 持续集成与构建状态
在构建设置为通过单个命令可调用的脚本后,持续集成可按如下方式完成:
- 持续集成服务器得到新提交的通知 或定期检查是否有新提交;
- 检测到新提交时,持续集成服务器可以检索它;
- CI服务器 运行构建脚本;
- 若构建成功,CI服务器运行自动化测试;
- CI服务器将运行结果报告给开发团队;
使构建失败:若编译/ 构建过程失败,或提交触发的自动化测试违反了一些度量指标可接受的值,说明此次提交使构建失败。
测试可分为 关键测试(单个测试失败可导致使构建失败)和 次关键测试(只有当失败测试的百分比超过某一设定阈值时造成构建失败)。
5.5.4 集成测试
集成测试环境包括 与外部服务联系。这要求有区分生产请求和测试请求机制。实现这种区分的方式是:提供模拟服务,使用服务的用户提供测试版本。
5.6 用户验收测试 / 预发布 / 性能测试
- 用户验收测试 (User Acceptance Test, UAT):可根据测试脚本测试,也可探索性测试;
- 自动化验收测试:可重复的UAT。该测试通过用户界面控制应用程序,模拟人类用户的操作。这样可以减轻人员重复性工作量;
- 冒烟测试:用于快速分析提交的代码是否破坏了应用程序的某些核心功能;
- 非功能测试:对性能、安全、容量以及可靠性等方面的测试。
5.7 生产环境
5.7.1 早期发布测试
-beta发布:beta测试主要用于软件内部部署 (on-premise use)。选择几个用户订阅 beta程序并具有访问应用程序某个 beta 版本的权限;
- 金丝雀测试:把新版本先部署到部分服务器上,观察它们如何执行;
- A/B测试:类似金丝雀测试,但目标不同,是衡量哪个版本的效果对特定业务的关键绩效指标更好;
5.7.2 现场测试
监控 是一种被动测试形式,在系统正常运行时收集其各种指标数据。
现场测试 在系统放入生产环境后,主动干扰其正常运行。Netfix 测试:Simian Army 向生产系统注入特定类型的错误;Chaos Monkey 随机杀死活跃的虚拟机;Latency Monkey 向消息注入延迟。