文章目录
第5章 可部署性
从我们来到这个星球的那一天起
眨着眼睛,走进阳光里
有太多的风景,难以尽览
有太多的事情,难以做完——《狮子王》(The Lion King)
有那么一天,软件也像我们人类一样,必须离开 “安乐窝”,走向外面的世界,去体验真实的生活。与我们不同的是,由于需要进行各种更改和更新,软件往往要经历多次这样的 “旅程”。本章旨在探讨如何尽可能有序、高效,尤其是尽可能快速地完成这一转变。这就是持续部署的范畴,而可部署性这一质量属性在很大程度上推动了持续部署的实现。
为什么可部署性在众多质量属性中占据了重要地位呢?
在过去的 “糟糕岁月” 里,软件发布的频率很低 —— 大量的更改被打包成一个版本,按计划发布。一个版本中会包含新功能和漏洞修复。每月发布一次、每季度发布一次,甚至每年发布一次都是很常见的情况。在许多领域,尤其是在电子商务的引领下,竞争压力使得软件需要更短的发布周期。在这种情况下,软件可以随时发布 —— 有时一天可能会发布数百次 —— 而且每次发布可能都是由组织内不同的团队发起的。能够频繁发布意味着,特别是漏洞修复无需等到下一次计划发布时才进行,而是一旦发现并修复了漏洞,就可以立即发布。这也意味着新功能无需打包到某个版本中,而是可以在任何时候投入生产环境。
然而,这种情况并非在所有领域都可取,甚至在某些领域根本无法实现。如果你的软件处于一个复杂的生态系统中,存在许多依赖关系,那么在不与其他部分协调发布的情况下,可能无法单独发布其中的某一部分。此外,许多嵌入式系统、位于难以触及位置的系统以及未联网的系统,都不太适合采用持续部署的理念。
本章重点关注数量庞大且不断增长的一类系统,对于这类系统而言,即时发布新功能是一项重要的竞争优势,而即时修复漏洞对于安全性、保密性或持续运行至关重要。这类系统通常是基于微服务和云计算的,不过,这里介绍的技术并不局限于这些技术。
5.1 持续部署
部署是一个,从编码开始到生产环境中真实用户与系统进行交互结束的过程。如果这个过程完全自动化,也就是说没有人为干预,那么它就被称为 “持续部署”。如果在将系统的(部分)投入生产环境之前的过程是自动化的,而在最后这一步需要人为干预(可能是由于法规或政策要求),那么这个过程就被称为 “持续交付”。
为了加快发布速度,我们需要引入 “部署流水线” 的概念:这是一系列工具和活动的集合,从你将代码提交到版本控制系统开始,到你的应用程序部署完成并可供用户发送请求结束。在这期间,一系列工具会对新提交的代码进行集成和自动测试,对集成后的代码进行功能测试,以及对应用程序进行诸如负载性能、安全性和许可证合规性等方面的测试。
部署流水线中的每个阶段都在一个专门建立的环境中进行,这个环境旨在支持该阶段的隔离,并执行适合该阶段的操作。主要的环境如下:
- 代码在 “开发环境” 中针对单个模块进行开发,在此环境中代码要接受独立的单元测试。一旦代码通过测试,并经过适当的评审,就会提交到版本控制系统,从而触发集成环境中的构建活动。
- “集成环境” 会构建出你的服务的可执行版本。持续集成服务器会编译 1 你的新代码或已更改的代码,以及你的服务其他部分的最新兼容版本代码,并为你的服务构建一个可执行镜像 2。集成环境中的测试包括来自各个模块的单元测试(现在针对已构建的系统运行),以及专门为整个系统设计的集成测试。当各项测试都通过后,构建好的服务就会被推送到预发布环境。
- “预发布环境” 会对整个系统的各种质量属性进行测试。这些测试包括性能测试、安全性测试、许可证合规性检查,可能还包括用户测试。对于嵌入式系统,在这个环境中会使用物理环境模拟器(向系统输入模拟数据)来进行测试。通过了所有预发布环境测试(可能包括现场测试)的应用程序,会使用蓝绿部署模式或滚动升级模式(见 5.6 节)部署到生产环境。在某些情况下,会采用部分部署的方式来进行质量控制,或者测试市场对某项提议的更改或产品的反应。
- 一旦进入 “生产环境”,就会对服务进行密切监控,直到各方对其质量都有一定程度的信心。到那时,它就被视为系统的正常组成部分,会像系统的其他部分一样受到同等关注。
在每个环境中都要进行不同类型的测试,测试范围从开发环境中对单个模块的单元测试,扩展到集成环境中对构成服务的所有组件的功能测试,最后到预发布环境中的全面质量测试以及生产环境中的使用情况监控。
但并非一切都总是按计划进行。如果软件在生产环境中发现问题,通常需要在修复缺陷的同时回滚到之前的版本。
架构选择会影响可部署性。例如,通过采用微服务架构模式(见 5.6 节),负责每个微服务的团队都可以做出自己的技术选择;这就消除了之前在集成时可能会发现的不兼容问题(例如,在使用某个库的版本选择上存在不兼容情况)。由于微服务是独立的服务,所以这样的选择不会引发问题。
同样,持续部署的理念会促使你在开发过程的早期就考虑测试基础设施。这是必要的,因为为持续部署进行设计需要持续的自动化测试。此外,由于需要能够回滚或禁用功能,这就会导致在架构上做出关于功能开关和接口向后兼容性等机制的决策。这些决策最好尽早做出。
虚拟化对不同环境的影响
在虚拟化技术广泛应用之前,我们这里所描述的环境都是物理设施。在大多数组织中,开发环境、集成环境和预发布环境包含由不同团队采购和运维的硬件与软件。开发环境可能由几台被开发团队改作服务器使用的台式计算机组成。集成环境由测试团队或质量保证团队负责运维,可能由一些机架构成,机架上配备着从数据中心淘汰下来的上一代设备。预发布环境由运维团队负责运维,其硬件可能与生产环境中使用的硬件类似。
人们花费了大量时间去弄清楚为什么在一个环境中通过的测试,在另一个环境中却失败了。采用虚拟化技术的环境的一大优势在于能够实现 “环境一致性”,即各个环境可能在规模上有所不同,但在硬件类型或基础结构方面不会存在差异。各种配置工具支持环境一致性,它们允许每个团队轻松构建一个通用环境,并确保这个通用环境尽可能地模拟生产环境。
衡量部署流水线质量的三个重要方面如下:
- 周期时间指的是通过部署流水线的进展速度。许多组织每天会向生产环境部署数次,甚至数百次。如果需要人工干预,如此快速的部署是不可能实现的。如果一个团队在将其服务投入生产环境之前必须与其他团队进行协调,那么快速部署同样也无法实现。在本章的后面部分,我们将了解到一些架构技术,这些技术能够让团队在无需与其他团队协商的情况下进行持续部署。
- 可追溯性是指能够追溯导致某个组件出现问题的所有相关制品的能力。这包括该组件中包含的所有代码和依赖项,还包括在该组件上运行过的测试用例以及用于生成该组件的工具。部署流水线中使用的工具出现错误可能会在生产环境中引发问题。通常,可追溯性信息会保存在一个 “制品数据库” 中。这个数据库将包含代码版本号、系统所依赖的组件(如库)的版本号、测试版本号以及工具版本号。
- 可重复性是指使用相同的制品执行相同的操作时能够得到相同的结果。这并不像听起来那么容易。例如,假设你的构建过程获取的是某个库的最新版本。那么下次执行构建过程时,该库可能已经发布了新版本。再比如,假设某个测试修改了数据库中的某些值。如果没有恢复这些原始值,后续的测试可能就无法产生相同的结果。
开发运维
“开发运维(DevOps)” 是 “开发(development)” 和 “运维(operations)” 两个词的合成词,是一个与持续部署密切相关的概念。它是一种运动(与敏捷运动非常相似),是对一系列实践和工具的描述(同样,与敏捷运动类似),也是销售这些工具的供应商所鼓吹的一种营销模式。开发运维的目标是缩短产品上市时间(或发布时间)。与传统软件开发实践相比,其目标是大幅缩短从开发人员对现有系统进行更改(实现新功能或修复漏洞)到系统交付给终端用户之间的时间间隔。
开发运维的正式定义涵盖了发布的频率以及按需修复漏洞的能力:
开发运维是一系列实践,旨在减少将对系统的更改提交到将该更改投入正常生产之间的时间,同时确保高质量。[Bass 15]
实施开发运维是一项流程改进工作。开发运维不仅包括任何流程改进工作中的文化和组织要素,还高度依赖工具和架构设计。当然,所有环境都各不相同,但我们所描述的工具和自动化功能通常可以在为支持开发运维而构建的典型工具链中找到。
我们在此描述的持续部署策略是开发运维的核心概念。反过来,自动化测试是持续部署的一个至关重要的组成部分,而用于自动化测试的工具往往是开发运维在技术方面的最大障碍。某些形式的开发运维包括日志记录以及对这些日志的部署后监控,以便在 “总部” 自动检测错误,甚至通过监控来了解用户体验。当然,这需要系统具备 “回传信息” 或日志传输功能,而在某些系统中,这可能是无法实现的,或者是不被允许的。
“开发安全运维(DevSecOps)” 是开发运维的一个分支,它将安全措施(针对基础设施及其所生成的应用程序)融入到整个流程中。开发安全运维在航空航天和国防应用领域日益流行,但在任何开发运维有实用价值且安全漏洞可能导致高昂代价的应用领域中,它同样适用。许多信息技术应用都属于这一范畴。
5.2 可部署性
可部署性是指软件所具备的一种特性,表明该软件能够在可预测且可接受的时间和精力范围内完成部署,也就是将其分配到某个环境中执行。此外,如果新的部署不符合其规格要求,也能够在可预测且可接受的时间和精力范围内回滚。随着世界日益向虚拟化和云基础设施发展,并且随着已部署的软件密集型系统规模不可避免地不断扩大,确保以高效且可预测的方式进行部署,将整体系统风险降至最低,是架构师的职责之一。3
为了实现这些目标,架构师需要考虑可执行文件在主机平台上如何更新,以及随后如何对其进行调用、度量、监控和控制。特别是移动系统,由于带宽方面的问题,在更新方式上给可部署性带来了挑战。部署软件所涉及的一些问题如下:
- 软件如何到达主机(即采用推送方式,也就是未经请求就部署更新;还是采用拉取方式,即用户或管理员必须明确请求更新)?
- 软件如何集成到现有系统中?能否在现有系统运行时进行集成?
- 采用何种介质进行部署,比如 DVD、USB 驱动器还是通过互联网传输?
- 软件的封装形式是什么(例如,可执行文件、应用程序、插件)?
- 软件集成到现有系统后会产生什么结果?
- 执行部署过程的效率如何?
- 部署过程的可控性如何?
考虑到所有这些问题,架构师必须能够评估相关风险。架构师主要关注架构对以下类型部署的支持程度:
- 粒度性:部署可以是对整个系统进行,也可以是对系统内的元素进行。如果架构为更细粒度的部署提供了多种选择,那么某些风险就可以降低。
- 可控性:架构应具备在不同粒度级别进行部署的能力,能够监控已部署单元的运行情况,并对不成功的部署进行回滚。
- 高效性:架构应支持以合理的工作量实现快速部署(如有需要,还应支持快速回滚)。
这些特性将在可部署性通用场景的应对措施中得以体现。
5.3 可部署性通用场景
表 5.1 列举了描述可部署性的通用场景中的各项要素。
表 5.1 可部署性通用场景
场景部分 | 描述 | 可能的值 |
---|---|---|
来源 | 部署的触发因素 | 终端用户、开发人员、系统管理员、运维人员、组件市场、产品负责人。 |
触发事件 | 是什么引发了这些触发因素? | 有一个新元素可供部署。这通常是一个用新版本替换软件元素的请求(例如,修复缺陷、应用安全补丁、升级到某个组件或框架的最新版本、升级到内部生产的某个元素的最新版本)。新元素已获批纳入系统。现有一个元素或一组元素需要回滚。 |
构件 | 将要更改的内容是什么? | 特定的组件或模块、系统的平台、用户界面、运行环境,或者是与之进行互操作的另一个系统。因此,相关的构件可能是单个软件元素、多个软件元素,或者是整个系统。 |
环境 | 预发布环境、生产环境(或者两者中的特定子集) | 完全部署。对指定的部分用户、虚拟机、容器、服务器、平台进行子集部署。 |
响应 | 应该发生什么 | 整合新组件。部署新组件。监控新组件。回滚之前的部署。 |
响应度量 | 对一次部署,或一段时间内一系列部署的成本、时间或流程有效性的一种衡量方式。 | 成本方面的考量:
|
图5.1展示了一个具体的可部署性场景:“在组件市场上推出了一个身份验证/授权服务的新版本(我们的产品会用到该服务),产品负责人决定将这个版本纳入产品发布内容中。新服务经过测试,并在40个小时的时间内部署到了生产环境,且投入的人力不超过120人时。此次部署没有引入任何缺陷,也没有违反任何服务级别协议。”
图 5.1 具体可部署性场景示例
5.4 可部署性策略
新软件或硬件元素的发布推动了部署工作的开展。如果这些新元素能够在可接受的时间、成本和质量限制范围内完成部署,那么此次部署就是成功的。我们在 图5.2 中展示了这种关系,同时也展示了可部署性策略的目标。
图 5.2 可部署性策略的目标
可部署性的策略如 图 5.3 所示。在很多情况下,这些策略至少有一部分会由你购买而非自行搭建的持续集成 / 持续部署(CI/CD)基础设施来提供。在这种情况下,作为架构师,你的工作往往是选择并评估(而非实施)合适的可部署性策略以及这些策略的恰当组合。
图 5.3 可部署性策略
接下来,我们将更详细地介绍这六种可部署性策略。第一类可部署性策略侧重于管理部署流水线的策略,第二类则涉及在系统部署过程中以及部署完成后对系统进行管理的策略。
管理部署流水线
- 渐进式发布:渐进式发布并非将服务的新版本部署到全体用户,而是逐步将其部署到用户群体中经过筛选的子集,并且通常不会向这些用户发出明确通知。(其余用户继续使用该服务的旧版本。)通过逐步发布,可以对新部署的影响进行监控和评估,如有必要,还可以进行回滚操作。这种策略能将部署有缺陷服务可能带来的负面影响降至最低。它需要一种架构机制(并非所部署服务的一部分),根据用户身份,将用户的请求路由至新服务或旧服务。
- 回滚:如果发现某次部署存在缺陷或未达到用户预期,那么可以将其 “回滚” 到之前的状态。由于部署可能涉及多个服务及其数据的多次协同更新,回滚机制必须能够跟踪所有这些更新,或者能够撤销部署所进行的任何更新操作的影响,理想情况下应实现完全自动化。
- 编写部署命令脚本:部署工作通常较为复杂,需要精确执行和协调多个步骤。因此,部署过程常常会编写脚本。这些部署脚本应像代码一样进行处理,包括编写文档、进行审查、测试以及版本控制。脚本引擎会自动执行部署脚本,这样既能节省时间,又能最大程度减少人为错误的发生。
管理已部署的系统
- 管理服务交互:此策略支持系统服务的多个版本同时进行部署和执行。客户端的多个请求可以按照任意顺序被导向任一版本。然而,运行同一服务的多个版本可能会引入版本兼容性问题。在这种情况下,需要对服务之间的交互进行协调,以便主动避免版本兼容性问题。该策略是一种资源管理策略,无需完全复制资源来分别部署旧版本和新版本。
- 打包依赖项:此策略将一个元素及其依赖项打包在一起,以便它们能一同部署,并且在该元素从开发阶段进入生产阶段时,依赖项的版本能够保持一致。依赖项可能包括库、操作系统版本以及实用容器(例如边车容器、服务网格),我们将在 第 9 章 中对此进行讨论。打包依赖项的三种方式是使用容器、Pod 或虚拟机;我们将在 第 16 章 中更详细地讨论这些内容。
- 功能开关:即使你的代码经过了全面测试,在部署新功能后仍可能会遇到问题。因此,能够为新功能集成一个 “紧急关闭开关”(即功能开关)会非常方便。这个紧急关闭开关可以在运行时自动禁用系统中的某个功能,而无需你启动新的部署操作。这使得你能够在不承担实际重新部署服务的成本和风险的情况下,对已部署的功能进行控制。
5.5 基于策略的可部署性调查问卷
根据 5.4 节 中描述的策略,我们可以设计一系列受可部署性策略启发的问题,如下表 5.2 所示。为了全面了解为支持可部署性而做出的架构选择,分析师会提出每个问题,并将答案记录在表格中。这些问题的答案随后可成为后续工作的重点,比如对文档进行调查、对代码或其他制品进行分析、对代码进行逆向工程等等。
表 5.2 基于策略的可部署性调查问卷
策略组 | 策略问题 | 支持与否(是/否) | 风险 | 设计决策和定位 | 推理与假设 |
---|---|---|---|---|---|
管理部署流水线 | 你们是否采用渐进式发布的方式,逐步推出新版本(而不是采取要么全推要么全不推的方式)? | ||||
如果你们判定已部署的服务运行状况不理想,是否能够自动回滚这些服务? | |||||
你们是否会编写部署命令脚本,以便自动执行复杂的一系列部署指令? | |||||
管理已部署的系统 | 你们是否会对服务交互进行管理,以便能够安全地同时部署多个版本的服务? | ||||
你们是否会对依赖项进行打包,从而使服务与其所依赖的所有库、操作系统版本以及实用容器一同进行部署? | |||||
如果发现新发布的功能存在问题,你们是否会使用功能开关来自动禁用该功能(而不是回滚新部署的服务)? |
5.6 可部署性模式
可部署性模式可分为两类。第一类包含用于构建待部署服务结构的模式。第二类包含关于如何部署服务的模式,这又可细分为两个主要的子类别:全量部署或部分部署。可部署性的这两个主要类别并非完全相互独立,因为某些部署模式依赖于服务的特定结构属性。
服务结构模式
微服务架构
微服务架构模式将系统构建为一组可独立部署的服务集合,这些服务仅通过服务接口以消息的形式进行通信。不允许存在其他形式的进程间通信:没有直接链接,不能直接读取其他团队的数据存储,没有共享内存模型,也完全没有任何后门。服务通常是无状态的,并且(由于它们由一个相对较小的团队开发 4)规模相对较小,因此被称为 “微服务”。服务之间的依赖关系是非循环的。此模式的一个重要组成部分是服务发现机制,以便消息能够被正确路由。
优点:
- 缩短产品上市时间。由于每个服务都很小且可独立部署,对某个服务的修改可以在无需与负责其他服务的团队进行协调的情况下进行部署。因此,一旦某个团队完成了对服务新版本的开发并通过测试,就可以立即进行部署。
- 每个团队都可以为其负责的服务自主选择技术,只要所选技术支持消息传递即可。在库版本或编程语言方面无需进行协调。这减少了因集成过程中出现的不兼容问题而导致的错误,而这些不兼容问题正是集成错误的主要来源。
- 与粒度较粗的应用程序相比,微服务更易于扩展。由于每个服务都是独立的,动态添加服务实例非常简单。通过这种方式,服务的供应能够更轻松地与需求相匹配。
权衡:
- 与内存内通信相比,开销会增加,因为服务之间的所有通信都要通过网络消息进行。使用服务网格模式(见 第 9 章)在一定程度上可以缓解这一问题,该模式将某些服务的部署限制在同一主机上,以减少网络流量。此外,由于微服务部署的动态特性,服务发现机制会被大量使用,这进一步增加了开销。最终,这些服务发现机制可能会成为性能瓶颈。
- 由于在分布式系统中难以同步活动,微服务不太适合处理复杂事务。
- 每个团队自由选择技术是有代价的,组织必须维护这些技术以及所需的技术经验基础。
- 由于微服务数量众多,对整个系统进行全面的管理和把控可能会比较困难。这就需要有接口目录和数据库来辅助进行有效的管理。此外,合理组合服务以实现预期结果的过程可能会很复杂且微妙。
- 设计出具有适当职责和合适粒度的服务是一项艰巨的设计任务。
- 为了实现版本独立部署的能力,服务的架构设计必须支持这种部署策略。使用 5.4 节 中描述的管理服务交互策略有助于实现这一目标。
大量采用微服务架构模式的组织包括谷歌、网飞、贝宝、推特、脸书和亚马逊。许多其他组织也采用了微服务架构模式;市面上有相关书籍,也举办了一些会议,专注于探讨组织如何根据自身需求采用微服务架构模式。
服务完全替换模式
假设有 N 个服务 A 的实例,你希望用 N 个服务 A 的新版本实例来替换它们,且不保留原始版本的任何实例。你希望在不降低服务对客户端的服务质量的情况下完成此操作,因此必须始终保持有 N 个服务实例在运行。
有两种不同的完全替换策略模式,它们都是渐进式发布策略的具体实现。我们将一起介绍这两种模式:
- 蓝绿部署:在蓝绿部署中,会创建 N 个新的服务实例,并在每个实例中部署新的服务 A(我们将这些称为绿色实例)。在安装好 N 个新服务 A 的实例后,DNS 服务器或服务发现机制会被修改,使其指向服务 A 的新版本。一旦确定新实例运行正常,此时才会移除 N 个原始服务 A 的实例。在切换之前,如果发现新版本存在问题,只需简单地切换回原始版本(蓝色服务),几乎不会造成中断或中断时间很短。
- 滚动升级:滚动升级是一次用一个服务 A 的新版本实例替换一个旧版本实例。(实际上,你可以一次替换多个实例,但在每一步中替换的实例数量都很少。)滚动升级的步骤如下:
- 为服务 A 的一个新实例分配资源(例如,一台虚拟机)。
- 安装并注册服务 A 的新版本。
- 开始将请求导向服务 A 的新版本。
- 选择一个旧服务 A 的实例,让它完成所有正在进行的处理,然后销毁该实例。
- 重复上述步骤,直到所有旧版本的实例都被替换。
图 5.4 展示了网飞公司的 Asgard 工具在亚马逊 EC2 云平台上实现的滚动升级过程。
图 5.4 网飞公司的 Asgard 工具所实现的滚动升级模式流程图
优点:
- 这些模式的优点在于能够在无需让系统停止服务的情况下,完全替换已部署的服务版本,从而提高了系统的可用性。
权衡:
- 蓝绿部署方式的资源使用峰值为 2N个实例,而滚动升级的资源使用峰值为N + 1 个实例。无论哪种情况,都必须获取用于承载这些实例的资源。在云计算广泛普及之前,获取资源意味着购买:企业必须购买物理计算机来进行升级。大多数时候并没有升级操作在进行,所以这些额外的计算机大多处于闲置状态。这使得在成本方面的权衡一目了然,因此滚动升级曾是标准的做法。现在,计算资源可以按需租用,而无需购买,虽然成本方面的权衡不再那么突出,但仍然存在。
- 假设在部署新的服务 A 时发现了一个错误。尽管在开发、集成和预发布环境中进行了各种测试,但当服务部署到生产环境中时,仍然可能存在潜在的错误。如果使用蓝绿部署,当发现新服务 A 中的错误时,所有原始实例可能已经被删除,回滚到旧版本可能需要相当长的时间。相比之下,滚动升级可能使你在旧版本实例仍然可用时就发现新版本服务中的错误。
- 从客户端的角度来看,如果使用蓝绿部署模式,在任何时间点,要么是新版本处于活动状态,要么是旧版本处于活动状态,不会同时存在两个版本。如果使用滚动升级模式,两个版本会同时处于活动状态。这就带来了两种可能的问题:“时间不一致性” 和 “接口不匹配”。
服务部分替换模式
有时,对某个服务的所有实例进行更改并非理想之举。部分部署模式旨在为不同用户群体同时提供某个服务的多个版本,其用途包括质量控制(金丝雀测试)和市场测试(A/B 测试)等。
金丝雀测试
在全面推出新版本之前,在生产环境中对其进行测试是较为谨慎的做法,但参与测试的用户范围应有所限制。“金丝雀测试” 类似于持续部署环境下的公测。金丝雀测试会指定一小部分用户来测试新版本。有时,这些测试用户是所谓的高级用户,或者是来自组织外部的抢先体验用户,他们更有可能使用到普通用户较少触及的代码路径和边缘情况。这些用户可能知道,也可能不知道自己被当作了 “小白鼠”—— 或者说,“金丝雀”。另一种方法是使用软件开发组织内部的测试人员。例如,谷歌员工几乎从不使用外部用户所使用的版本,而是充当即将发布版本的测试人员。当测试的重点在于确定新功能的受欢迎程度时,会采用一种名为 “暗启动” 的金丝雀测试变体。
在上述两种情况下,被指定为 “金丝雀” 的用户会通过 DNS 设置或服务发现配置,被引导至相应版本的服务。测试完成后,所有用户将被统一引导至新版本或旧版本,被弃用版本的实例将被销毁。可以使用滚动升级或蓝绿部署的方式来部署新版本。
优点:
- 金丝雀测试能够让真实用户以模拟测试无法做到的方式对软件进行实际使用。这使得部署服务的组织能够收集 “实际使用中” 的数据,并以相对较低的风险开展可控实验。
- 金丝雀测试产生的额外开发成本极低,因为正在测试的系统最终无论如何都会投入生产。
- 金丝雀测试将可能接触到新系统严重缺陷的用户数量降至最低。
权衡:
- 金丝雀测试需要额外的前期规划和资源投入,并且需要制定一套评估测试结果的策略。
- 如果金丝雀测试针对的是高级用户,就必须先识别出这些用户,然后将新版本导向他们。
A/B 测试
市场营销人员会使用 A/B 测试,对真实用户开展实验,以确定几种不同方案中哪种能带来最佳的商业效果。一小部分但数量足够有意义的用户会接受与其他用户不同的体验。这种差异可能很细微,比如字体大小或表单布局的改变,也可能较为显著。例如,HomeAway(现 Vrbo)曾使用 A/B 测试来改变其全球网站的格式、内容和外观,以追踪哪些版本能带来最多的租赁业务。“获胜” 的版本会被保留,“失败” 的版本则会被弃用,然后再设计并部署新的测试版本。另一个例子是银行针对新账户推出不同的促销活动。一个广为人知的案例是,谷歌曾测试了 41 种不同深浅的蓝色,以决定使用哪种蓝色来显示搜索结果。
与金丝雀测试一样,A/B 测试会通过设置 DNS 服务器和服务发现配置,将客户端请求发送至不同版本。在 A/B 测试过程中,会对不同版本进行监测,以从商业角度判断哪个版本能获得最佳反馈。
优点:
- A/B 测试使市场营销和产品开发团队能够在真实用户身上开展实验,并收集相关数据。
- A/B 测试能够根据任意一组用户特征来精准定位目标用户。
权衡:
- A/B 测试需要实现多个不同方案,其中必有一个会被弃用。
- 需要提前识别出不同类型的用户及其特征。
5.7 扩展阅读
本章的大部分内容改编自伦恩・巴斯(Len Bass)和约翰・克莱因(John Klein)所著的《软件工程师的部署与运维》[Bass 19],以及 [Kazman 20b]。
与 DevOps 相关的,关于可部署性与架构的一般性讨论,可在 [Bass 15] 中找到。
可部署性策略在很大程度上借鉴了马丁・福勒(Martin Fowler)及其同事的研究成果,相关内容可在 [Fowler 10]、[Lewis 14] 和 [Sato 14] 中查阅。
[Humble 10] 中对部署流水线进行了更为详细的描述。
[Newman 15] 中介绍了微服务以及向微服务迁移的过程。
5.8 问题讨论
1. 利用通用场景中的每一种可能回复,编写一组具体的可部署性场景。
2. 为汽车(如特斯拉)的软件编写一个具体的可部署性场景。
3. 为一款智能手机应用编写一个具体的可部署性场景。现在,为与该应用通信的服务器端基础设施编写一个具体的可部署性场景。
4. 如果你需要显示搜索操作的结果,你会进行 A/B 测试,还是直接使用谷歌选择的颜色?为什么?
5. 参考 第 1 章 中描述的各种结构,在实现打包依赖项策略时会涉及哪些结构?你会使用 “使用” 结构吗?为什么会或为什么不会?是否还有其他需要考虑的结构?
6. 参考 第 1 章 中描述的各种结构,在实现管理服务交互策略时会涉及哪些结构?你会使用 “使用” 结构吗?为什么会或为什么不会?是否还有其他需要考虑的结构?
7. 在哪些情况下,你更倾向于向前推进到服务的新版本,而不是回滚到之前的版本?在什么情况下,向前推进是一个糟糕的选择?