持续交付:发布可靠软件的系统方法

版本:V1
注:第二版增加很多新内容

内容提要

本书讲述如何实现更快、更可靠、低成本的自动化软件交付,描述了如何通过增加反馈,并改进开发人员、测试人员、运维人员和项目经理之间的协议来达到这个目标。本书由三部分组成。

  • 第一部分阐述了持续交付背后的一些原则,以及支持这些原则的实践。
  • 第二部分是本书的核心,全面讲述了部署流水线。
  • 第三部分围绕部署流水线的投入产出讨论了更多细节,包括增量开发技术、高版本控制模式,以及基础设施、环境和数据的管理和组织治理

成本:是商品经济的价值范畴,是商品价值的组成部分。人们要进行生产经营活动或达到一定的目的,就必须耗费一定的资源,其所费资源的货币表现及其对象化称之为成本

引言

软件发布应该是一个快速且可重复的过程。

提问:"在你的公司里,仅设计一行代码的改动需要多长时间才能部署上线?你的处理方式是否可重复且可靠呢?"

从"决定做某种修改"到"该修改结果正式上线"的这段时间称为周期时间(cycle time)。对任何项目而言,它都是一个极为重要的度量标准。

我们的目标是改变软件交付方式,将其由开发人员的手工操作变成一种可靠、可预期、可视化的过程并在很大程度上实现了自动化的流程,而且它要具备于理解和风险可量化的特点。

交付成功软件的成本绝大部分都是在首次发布后产生的。这些成本包括技术支持、维护、增加新功能和修复缺陷的成本。通过迭代方式交付的软件更是如此,因为首次不发只会包含能给用户提供价值的最小功能集合。

因此,本书的书名<持续交付>就来源于 敏捷宣言 的第一原则:"我们的首要是尽早持续交付有价值的软件并让客户满意。"

这也反映了这样一个现实:对于成功的软件,首次发布只是交付过程的开始。

书中描述的所有技术都与交付软件新版本给客户相关,旨在减少时间和降低风险。这些技术的核心是增加反馈,改进负责交付的开发、测试和运维人员之间的协作

第一部分

第一章 软件交付的问题

:描述了很多软件开发团队中常见的反模式,然后阐述了我们的目标以及实现方式。最后总结了软件交付的一些原则。

1.1 引言

作为软件从业人员,我们面临的最重要问题就是,如果有人想到一个好点子,我们如何以最快的速度将它交付给用户?本书将给出这个问题的答案。

部署流水线大致的方式如下:对于应用程序的配置、源代码、环境或数据的每个变更都会触发创建一个新流水实例的过程。流水线的首要步骤之一就是创建二进制文件和安装包,而其余部分都是基于第一部的产物所做的一系列测试,用于证明其达到了发布质量。每通过一步测试,我都会更加相信这些二进制文件、配置信息、环境和数据所构成的特殊组合可以正常工作。如何这个产品通过了所有的测试环节,那么它就可以发布了。

image

部署流水线的目标有三个:

  • 首先,它让软件构建、部署、测试和发布过程对所有人可见,促进了合作。
  • 其次,它改善了反馈,以便在整个过程中,我们能够更早地发现并解决问题。
  • 最后,它使团队能够通过一个完全自动化的过程在任意环境上部署和发布软件的任意版本

1.2 一些常见的发布反模式

1. 手工部署软件

每个步骤都需要人为判断的事情,因此很容易发生人为错误。这些步骤的执行顺序和时机的不同也会导致结果的差异性。

特征如下:

  • 有一份非常详尽的文档,该文档描述了执行步骤及每个步骤中易出错的地方
  • 以手工测试来确认该应用程序是否支持允许正确
  • 在发布当天开发团队频繁地街道电话,客户要求解释部署为何会出错
  • 在发布时,常常会修正一些在发布过程中发现的问题
  • 如果要集群环境部署,常常发现在集群中各环境的配置都不相同,比如应用服务器的连接池设置不同或文件系统有不同的目录结构等
  • 发布过程需要较长的时间(超过几分钟)
  • 发布结果不可预测,常常不得不回滚或遇到不可预见的问题
  • 发布之后凌晨两点还睡眼惺忪地坐在显示器前,绞尽脑汁想着怎么让刚刚部署的应用程序能够正常工作

2. 开发完成之后才向类生产环节部署

我们的对策就是将测试、部署和发布活动也纳入到开发过程中,让它们成为开发流程正常的一部分。这样的话,当准备好进行系统发布时就几乎很少或不会有风险了,因为你已经在很多种环境,甚至类生产环境中重复的很多次,也就相当于测试过很多次了。而且要确保每个人都成为这个软件交付过程的一份子,无论是构建发布团队、还是开发测试人员,都应该从项目开始就一起共事。

3. 生产环节的手工配置管理

修改数据库的连接配置或增加应用服务器线程池中的线程数,手动修改

本书描述的关键实践之一就是配置管理,其责任之一就是让你能够重复地创建那些你开发的应用程序所依赖的每个基础设施。这意味着操作系统、补丁级别、操作系统配置、应用程序所依赖的其他软件及其配置、基础设置的配置等都应该处于受控状态。你应该具有重建生产系统的能力,最好是能提供自动化的方式重建生产环节。虚拟化技术在这一点上可能对你有所帮助。

你应该完全掌握生产环节中的任何信息。这意味着生产环节中的每次变更都应该被记录下来,而且做到今后可以查阅。

应用软件之间通常会有一些依赖关系。我们应该很容易知道当前发布的是软件的哪个版本。

自动化是关键,变更首先应该被提交到版本控制系统中,然后通过某个自动化过程对生产环境进行更新。

我们也应该有能力在部署出错时,通过同一个自动化过程将系统回滚到之前的版本。

自动化(Automation)是指机器设备、系统或过程(生产、管理过程)在没有人或较少人的直接参与下,按照人的要求,经过自动检测、信息处理、分析判断、操纵控制,实现预期的目标的过程。

4. 我们能做的更好吗?

软件发布能够(也应该)成为一个低风险、频繁、廉价、迅速且可预见的过程。

1.3 如何实现目标

减少周期时间:周期时间是从决定进行变更的时刻开始,包括修正缺陷或增加特性,直至用户可以使用本次变更后的结果

快速交付:验证那些新开发的特性或者修复的缺陷是否真的有用。然而,直到使用者真正使用之前,这些全是未经过验证的假设。这也是为什么减少周期时间并建立有效反馈环如此重要的原因。

有用性的一个重要部分是质量。我们的软件应该满足它的业务目的。质量并不等于完美,正如伏尔泰所说"追求完美是把事情做好的大敌",但我们的目标应该一直是交付质量足够高的软件,给客户带来价值。因此,尽快地交付软件很重要,保证一定的质量是基础。

因此,我们来调整以下目标,即找到可以以一种高效、快速、可靠的方式交付高质量且有价值的软件的方法

为了达到这些目标(短周期、高质量),我们需要频繁且自动化地发布软件。

  • 自动化
  • 频繁做。如果能够做到频繁发布,每个发布版本之间的差异会很小。这回大大减少与发布相关的风险,且更容易回滚。频繁发布也会加快反馈速度,而客户也需要它。

对于频繁地自动化发布来说,反馈是至关重要的。下面关于反馈的三个标准是很有用的:

  • 无论什么样的修改都应该触发反馈流程
  • 反馈应该尽快发出
  • 交付团队必须接收反馈,并根据它作出相应的行动

1.4 收效

1. 授权团队

部署流水线的一个关键点是,它是一个"拉动"(pull)系统,它使测试人员、运维人员或支持服务人员能够做到自服务,即他们可以自行决定将哪个版本的应用程序部署到哪个环境中。

  • 测试人员可以选择性地部署较旧的版本,以验证新版本上的功能变化
  • 技术支持人员可以自己部署某个已发布的版本,用于重现缺陷
  • 作为灾难恢复手段,运维人员可以自己选一个已知的正确版本,将其部署到生产环境中
  • 发布方式也变成一键式的了

2. 减少错误

我么这里要说的错误是:由不良好的配置管理引入到生产环节的错误

现在,让我们想一下到底需要哪些东西才可以让一个应用程序正确地工作,当然肯定需要正确版本的代码,除此之外呢?我们还需要数据库模式的正确版本、负载均衡器的正确配置信息、应用程序所依赖的web服务的正确URI等。

通过积极地管理在版本控制库的所有可能变动的内容,比如配置文件、创建数据库及其模式的脚本、构建脚本、测试工具,甚至开发环境和操作系统的配置,我们让计算机来做它们擅长的所有事情,即确保所用的比特和字节都在它们应该在的位置上,至少确保在代码将要运行时确实如此。

3. 缓解压力

绝大多数经历过项目发布的人都会认为,当项目越临近发布日期,就越能感受到压力。根据我们的经验,压力本身就是问题的根源所在。

4. 部署的灵活性(略)

5. 多加练习,使其完美(略)

1.5 候选发布版本

release candidate

对于代码的任何一次修改都有被发布出去的可能性

开发人员对代码库的每次修改都应该是以某种方式为系统增加价值。每次代码到版本控制系统的提交都应该是对当前所开发软件的提高或增强。我们如何知道它的正确性呢?唯一的方法就是允许这个软件,看它的行为是否符合我们的期望。大多数项目都将这部分工作推迟到开发的后期。这就意味着,即使它不能工作,也只有当有人测试或使用这个软件时才能被发现,而此时的修复成本通常会比较高。这个阶段通常称作集成阶段,常常是整个开发过程中最不可预测、最不易管理的阶段。由于集成这件事太痛苦了,所以团队总是推迟集成工作。然而,集成频率越低,集成时我们就会越痛苦。

如果在软件开发中的某个任务令你非常痛苦,那么解决痛苦的方法只有更频繁地去做,而不是回避。因此,我们应该频繁做集成,事实上应该在每次提交修改后都做集成。持续集成这个实践将频繁集成发挥到了极致。持续集成会及时检测到任何一次破坏已有系统或者不满足客户验收测试的提交。一旦发生这种情况,团队就立刻去修复问题(这是持续集成的首要规则)。如果能够坚持这个实践,那么软件会一直处于可用状态。假如测试足够全面,且运行测试的环境与生产环境足够相近(甚至相同)的话,那么可以说,你的软件一直处于可发布状态。

1.6 软件交付的原则

1. 为软件的发布创建一个可重复且可靠的过程

这种可重复性和可靠性来自于以下两个原则

  1. 几乎将所有事情自动化
  2. 将构建、部署、测试和发布软件所需的东西全部纳入到版本控制管理之中

归根结底,软件部署包括三件事:

  • 提供并管理你的软件所需要的运行环境,这包括硬件配置、所依赖的软件、基础设施以及所需的外部服务
  • 将你的应用程序的正确版本安装在其之上
  • 配置你的应用程序,包括它所需要的任何数据以及状态

2. 将几乎所有事情自动化

当然,你不需要把所有的东西一次性地全部自动化。你应该看一下在构建、部署、测试和发布过程中,哪个环节是瓶颈。

3. 把所有的东西都纳入版本控制

将构建、部署、测试和发布的整个过程中所需的东西全部保存在某种形式的版本存储库中,包括需求文档、测试脚本、自动化测试用例、网络配置脚本、部署脚本、数据库创建、升级、回滚和初始化脚本、应用程序所依赖的软件集合的配置脚本、库文件、工具链以及技术文档等。这些变更集都应该有唯一标识,比如构建号、版本控制库中的版本号。

一个刚刚加入团队的新成员应该可以坐在一台新分配给他的开发电脑前,直接从项目的版本库中签出代码,并只需要运行一条命令就能构建应用程序,并将其部署到任意一个允许的环境中,包括本地开发机器。

另外,应该也能够方便地直到当前每个环境中到底部署了应用程序的哪个版本,及其在版本库中所对应的版本号。

4. 提前并频繁地做让你感到痛苦的事

这是最通用的原则,也是最有启发性的。

极限编程:加强交流;从简单做起;寻求反馈;勇于实事求是

5. 内建质量

越早发现缺陷,修复它们的成本越低。

6. "DONE"意味着"已发布"

一个特性只有交到用户手中才能算"DONE"

7. 交付过程是每个成员的责任(略)

8. 持续改进

戴明环:计划-执行-检查-处理(PDCA)

第二章 配置管理

2.1 引言

:阐述了管理构建、部署、测试和发布应用程序所需的一些要素,包括源代码、构建脚本,以及环境和应用程序配置。

:指一个过程,通过该过程,所有与项目相关的产物,以及他们之间的关系都被唯一定义、修改、存储和检索

加入项目中有良好的配置管理策略,那么你对下列所有问题的回答都应该是"YES"

  • 你能否完全再现你所需要的任何环节(这里的环境包括操作系统的版本及其补丁级别、网络配置、软件组合,以及部署在其上的软件应用及其配置)?
  • 你能很轻松地对上述内容进行增量式修改,并将修改部署到任意一种或所有环境中吗?
  • 你能否很容易地看到已被部署到某个具体环境中的某次修改,并能追溯到修改源,直到是谁做的修改,什么时候做的修改吗?
  • 你能满足所有必须遵守的规章制度吗?
  • 是否每个团队成员都能很容易地得到他们所需要的信息,并进行必要的修改呢?这个配置管理策略是否会妨碍高效交付,导致周期时间增加,反馈减少呢?

最后这一点非常重要。

2.2 使用版本控制

目的:

  • 首先:它要保留每个文件的所有版本的历史信息,并使之易于查找。这个系统还提供一种基于元数据(描述数据的存储信息)的访问方式,使元数据与某个单个文件或文件集合相链接。
  • 其次,它让分布式团队愉快地协作

2.3 依赖管理

第三方库文件,以及该软件需要用到的正由其他团队开发的模块或组件间的关系。库一般以二进制文件的形势部署,不会被你自己的团队修改,而且也不经常更新。然而,组件和模块会被其他团队频繁修改。

2.4 软件配置管理

数据库、文件目录和注册表是比较方便存储配置信息的场所,它们可以被远程访问。但是,为了审计性和可回滚性,一定要将配置项的修改历史保留下来。

小提示:不要把密码签入到版本控制系统中,也不要把它硬编码到应用程序中

2.5 环境管理

每个应用程序都依赖于硬件、软件、基础设施以及外部系统才能正常工作。

需要考虑的环境配置信息如下:

  • 环境中各种各样的操作系统,包括其版本、补丁级别以及配置设置
  • 应用程序所依赖的需要安装到每个环境中的软件包,以及这些软件包的具体版本和配置
  • 应用程序正常工作所必需的网络拓扑结构
  • 应用程序所依赖的所有外部服务,以及这些服务的版本和配置信息
  • 现有的数据以及其他相关信息(比如生产数据库)

其实高效配置管理策略的两大基本原则是:

  1. 将二进制文件与配置信息分离
  2. 将所有的配置信息保存在一处

第三章 持续集成

:讲述了在程序每个变革变更后执行构建和运行自动化测试相关的实践,以确保你的软件一直处于可工作状态

在开始做持续集成之前,你需要做三件事情:

  1. 版本控制
  2. 自动化构建
  3. 团队共识

本质上,持续集成软件包括两个部分。第一部分是一个一直运行的进程,它每隔一定的事件就进行一个简单的工作流程。第二部分就是提供展现这个流程运行结果的视图,通知你构建和测试成功与否,让你可以找到测试报告,拿到生成的安装文件等。

通常,持续集成工作流以规定的时间间隔对版本控制系统进行轮询。一旦发现版本库有任何变化,它就会将项目的一个副本签出到服务器或构建代理机器的某个目录中,然后运行你指定的命令。典型情况下,这些命令会构建你的应用程序,并运行相关的自动化测试。

必不可少的实践:

  1. 构建失败之后不要提交新代码
  2. 提交前在本地运行所有的提交测试,或者让持续集成服务器完成此事
  3. 等提交测试通过后再继续工作
  4. 回家之前,构建必须处于成功状态
  5. 时刻准备着回滚到前一个版本
  6. 在回滚之前要规定一个修复时间
  7. 不要将失败的测试注释掉
  8. 为自己导致的问题负责
  9. 测试驱动的开发

第四章 测试策略的实现

:介绍了每个项目中的各种手工和自动化测试,并讨论了如何确定适合自己项目的策略

4.1 引言

测试策略的设计主要是识别和评估项目风险的优先级,以及决定采用哪些行动来缓解风险的一个过程。好的测试策略会带来很多积极作用。测试还为开发流程提供了一种约束机制,鼓励团队采用一些好的开发实践。

4.2 测试的分类

image
根据两个维度对测试进行了分类:

  • 一个维度是依据它是业务导向,还是技术导向。

  • 另一个维度是依据它是为了支持开发过程,还是为了对项目进行评价

1. 业务导向且支持开发过程的测试

通常称作功能测试或验收测试

回归测试是自动化测试的全集。它们用来确保任何修改都不会破坏现有的功能,还会让代码重构变得容易些。在写自动化验收测试时,应该时刻牢记,这些测试时回滚测试套件的组成部分。

2. 技术导向且支持开发过程的测试

由开发人员创建并维护

  • 单元测试
    • 用于单独测试一个特定的代码段
    • 不应该访问数据库、使用文件系统、与外部系统交互
    • 代价:错过应用系统不同部分之间交互时产生的一些缺陷。比如,通常某些对象或者应用程序数据的生命周期是非常不同的。此时,只有当对更大范围的代码进行测试时才能发现一些缺陷,这些缺陷出现的原因是你没有正确处理某些数据或对象的生命周期
  • 组件测试:测试更大的功能集合。有时被称作"集成测试"
  • 部署测试:用于检查部署过程是否正常。换句话说,就是应用程序是否被正确地安装、配置,是否能与所需的服务正确通信,并得到相应的回应

3. 业务导向且评价项目的测试

每当在现实生活中有用户试用一个应用,他们就会发现这个应用还有改进的空间。用户会破坏一些东西,因为他们会尝试执行从前没有人执行过的一系列操作。

用户也会抱怨,认为应用程序应该能更好地帮助他们完成他们要经常做的工作。

他们可能会从应用软件里得到一些启发,发现某种新功能能帮助他们更好地完成工作。软件开发是一个很自然的迭代过程,它建立在一个有效的反馈环之上,而我们却骗自己是否有其他方式来预见它。

一种非常重要的面向业务且评价项目的测试是演示。在每个迭代结束时敏捷开发团队都向用户演示器开发完成的新功能。

很多组织使用金丝雀发布,即让一个应用程序同时有几个版本允许在生产环境中,而这几个版本之间只是稍有不同,用于比较效果差异。这些组织会收集一些关于新功能使用情况的统计数据。如果分析证明新功能无法带来足够的价值,就会删除它。这种方法可以让新功能不断演进,从而得到更为有效的功能。

4. 技术导向且评价项目的测试

这类测试和允许这类测试的工具可能与特定与功能验收条件的测试和工具有很大不同。这类测试常常需要很多的资源,比如需要比较特殊的环境来运行测试,并且可能需要专业知识来建立和实现测试,另外它们还通常需要花更长时间来运行。因此,这类测试的实现一般会比较靠后。即使所有非功能测试都被自动化了,与功能验收测试相比,其允许频率也会更低一些,而且很可能是在部署流水线的最后阶段进行。

然而,这种情况正在发生改变。执行这种测试的工具越来越成熟。对于复杂或关键的项目,应该在项目一开始就考虑分配一些时间去研究并实现非功能测试

5. 测试替身

自动化测试的一个关键是在运行时用一个模拟对象来代替系统中的一部分。这样,应用程序中被测试的那部分与系统其他部分之间的交互可以被严格地掌控,从而更容易确定应用程序中这一特定部分的行为。这样的模拟对象常常就是mock、stub、dummy。

测试提升分类:

  • 哑对象(dummy object)是指那些被传递单不被真正使用的对象。通常这些哑对象只是用于填充参数列表
  • 假对象(fake object)是可以真正使用的实现,但是通常会利用一些捷径,所以不适合在生产环境中使用。一个很好的例子是内存数据库
  • 桩(stub)是在测试中为每个调用提供一个封装好的响应,它通常不会对测试之外的请求进行响应,只用于测试
  • spy是一种可以记录一些关于它们如何被调用的信息的桩。这种形式的桩可能是记录它发出去了多少个消息的一个电子邮件服务
  • 模拟对象(mock)是一种在编程时就设定了它预期要接收的调用。如果收到了未预期的调用,它们会抛出异常,并且还会在验证时被检查是否收到了它们所预期的所有调用

4.3 现实中的情况与应对策略

1.新项目

最重要的事情是一开始就要写自动化验收测试。为了能做到这一点,你需要:

  • 选择技术平台和测试工具
  • 建立一个简单的自动化构建
  • 制定遵守Invest原则(即独立的、可协商的、有价值的、可估计的、小的且可测试的)用户故事及考虑其验收条件

然后就可以严格遵守下面的流程:

  • 客户、分析师和测试人员定义验收条件
  • 测试人员和开发人员一起基于验收条件实现验收测试的自动化
  • 开发人员编码来满足验收条件
  • 只要有自动化测试失败,无论是单元测试、组件测试还是验收测试,开发人员都应该把它定为高优先级并恢复它

2. 项目进行中

我们经常发现自己工作在一个大规模且资源有限的团队中,代码库再不断地快速变化,并且面临着很大的交付压力

引入自动化测试最好的方式是选择应用程序中那些最常见、最重要且高价值的用例为起点。这就需要与客户沟通,以便清楚地识别真正的业务价值是什么,然后使用测试来做回归,以防止功能被破坏。

3. 遗留系统

没有自动化测试的系统就是遗留系统

一定要聚焦于系统中高价值的功能。向客户解释一下,创建回归测试套件的价值在于保护系统当前的功能,这样就很容易了。

坐下来与用户一起识别系统中高价值的功能是非常重要的。利用之前所说的技术,创建一套广泛的自动化测试,覆盖这些高价值的核心功能。

对于覆盖核心功能的测试就是非常重要的冒烟测试了。

这种遗留系统的特点在于:代码通常没有标准组件化,结构比较差。所以修改系统某部分的代码却影响了另一部分代码的事情经常发送。此时,通常比较有效的策略是在测试结束后仔细地验证系统的状态。

切记,只写那些有价值的自动化测试就行。基本上,可以将应用程序分成两部分。一部分是实现系统功能的具体代码,另一部分则是在这些代码之下,为实现系统功能提供支持的框架代码。绝大多数回归缺陷都是因为修改这些框架代码引起的。因此,如果只是增加新功能,而不需要修改这个提供支撑的框架代码时,为这部分代码写全面的测试是没有什么价值的。

4. 集成测试

加入你的应用程序需要通过一系列不同的协议与各种外部系统进行交互,或者它由很多松散耦合的模块组成,而模块之间还有很复杂的交互操作的话,集成测试就非常重要了。

我们要确保在正式部署到生产环境之前,应用程序不要与真实的外部系统进行交互,否则就要想办法告诉外部系统,这个应用程序所发送的数据只是用于测试的。一般来说,有两种常见的方法来保证你可以安全地测试自己开发的应用程序,而不必与真正的外部系统进行交互,而且通常要同时使用这两种方法:

  • 在测试环境中使用一个“防火墙”将该应用程序与外部系统隔离开来,而在开发过程中,越早这么做越好。当外部系统不可用时,这也是测试应用程序行为的一个好方法
  • 在应用程序中使用一组配置信息,让其于外部系统的模拟版本进行交互

在理想情况下,服务提供商会提供一个复制版的测试服务,除了性能以外,它可以提供与真正的服务完全相同的行为。你可以在此之上进行测试。然而,在现实世界中,你常常需要开发一个测试用具。比如当:

  • 外部系统还没有开发完成,但接口已经提前定义好了(此时你需要有心理准备,因为这些接口很可能会发生变化)
  • 外部系统已经开发完了,但是还不能为了测试而部署它,或者用于测试目的的外部系统运行太慢,或缺陷太多,无法支持正常自动化测试的运行
  • 虽然有测试系统,但它的响应具有不确定性,从而导致无法对自动化测试结果进行验证(比如,某个股票市场的实时数据)
  • 外部系统很难安装或者需要通过用户界面进行手工干预
  • 需要为涉及外部系统服务的功能写一份标准的自动化验收测试,而这些测试应该一直在测试替身上运行
  • 自动化持续集成系统需要承担的工作量太大且其所需要的服务水平太高,远不是一个仅用于做手工探索性测试的轻量级测试环境所能承受或提供的

测试用具不但应该能返回服务调用所期望的响应,而且还要能返回不可预期的响应。能模拟因远程服务不正确或者是基础设施问题而导致的极端行为。比如网络传输问题、网络协议问题、应用协议问题以及应用逻辑问题。这些行为的例子包括这样一些病态现象,如拒绝网络连接、接受连接后即断开、接受连接后却不回复、响应速度奇慢、发送回大量非期望的数据、回复一些无效数据、拒绝认证证书、发回异常,或者在当前应用程序状态下回复格式符合规则但无效的响应等。

这些自动化集成测试可以当成向生产环境部署系统时的冒烟测试,也可以作为一种诊断方法来监控生产环境中的系统行为。

4.4 流程

召集项目干系人开个会。在一个文本编辑器中用自然语言写验收条件,然后再写代码让这些验收条件变成可执行的测试,并且如果对这些测试代码进行重构,它们也会更新相应的测试规范。另一种方法是为测试创建一种DSL(领域专属语言),并用这种DSL来书写验收条件。

将缺陷分为严重、阻塞、中、低四个级别

4.5 小结

在很多项目中,测试被认为是一个由一些专职人员负责的独立阶段。可是,只有当测试称为与软件交付相关的每个人的责任,并从项目一开始就被引入并持续进行时,才能产生高质量的软件。吃啥主要是建立反馈环,而这个反馈环会驱动开发、设计和发布等活动。

第二部分 部署流水线

第五章 部署流水线解析

:本书的核心-每次代码修改后从提交到发布的一个自动化过程。我们也讨论了如何在团队级别和组织级别实现流水线。

5.1 引言

持续集成的主要关注对象是开发团队。持续集成系统的输出通常作为手工测试流程和后续发布流程的输入。在软件的发布过程中,很多浪费来自于测试和运维环节。例如,我们常常看到:

  • 构建和运维团队的人员一直在等待说明文档或缺陷修复
  • 测试人员等待"好的"版本构建出来
  • 在新功能开发完成几周之后,开发团队才能收到缺陷报告
  • 开发快完成时,才发现当前的软件架构无法满足该系统的一些非功能需求

解决方案就是采取一种更完整的端到端的方法来交付软件

5.2 什么是部署流水线

image
image
不同阶段的使用者拥有各自的环境权限

我们所做的这一切都是为了尽快得到反馈。为了加速这个反馈循环,就必须能够看到每个环境中都部署了哪个版本,每个构建版本在流水线中处于哪个阶段。

5.3 部署流水线的相关实践

1.只生成一次二进制包

:指所有资源文件的集合

在每个不同的环境上部署时都要重新编译一次。会引入"编译结果不一致"的风险。

这种反模式违反了两个重要原则。第一个原则就是"保证部署流水线的高效性,使团队今早得到反馈"。重复编译违反了这一原则,因为编译需要花时间,在大型软件系统中进行的编译尤其如此。第二原则就是"始终在已知可靠的基础上进行构建"。

2.对不同环境采用同一种部署方式

如果使用同一个脚本在所有的环境上进行部署,那么当在某个环节上部署失败时,就可以确定某原因移动来自以下的三个方面:

  • 与该环境现相关的配置文件中,某项配置有问题
  • 基础设施或应用程序所依赖的某个服务有问题
  • 环境本身的配置有问题

3.对部署进行冒烟测试

当做应用程序部署时,你应该用一个自动化脚本做一下冒烟测试,用来确保应用程序已经正常启动并运行了。这个测试应该非常简单,比如只要启动应用程序,检查一下,能看到主页面,并在主页面上能看到正确的内容就行了。这个冒烟测试还应该检查一下应用程序所依赖的服务是否都已经启动,并且正常运行了,比如数据库、消息总监或外部服务等。

如果应用程序不能运行,这个冒烟测试应该能够告诉你一些最基本的诊断提示,比如应用程序无法允许是否是因为其依赖的外部服务无法正常工作。

4.向生产环境的副本中部署

很多团队实际部署应用上线时可能遇到的另一个主要问题是,生产环境与他们的开发环境或测试环境有非常大的差异。为了对系统上线充满信心,你要尽可能在与生产环境相似的环境中进行测试和持续集成。

要想确保所有的环境都一样,需要有很多纪律保障良好的配置管理实践。你要确保:

  • 基础设施是相同的,比如网络拓扑和防火墙的配置等
  • 操作系统的配置(包括补丁版本)都是相同的
  • 应用程序所用的软件栈是相同的
  • 应用程序的数据处于一个已知且有效的状态。系统升级过程中需要进行的数据迁移是部署活动的一个痛点

5.每次变更都要立即在流水线中传递(略)

6.只要有环节失败,就停止整个流水线(略)

5.4 提交阶段(略)

5.5 自动化验收测试之门(略)

单元测试仅仅是从开发人员的角度测试某个问题的解决方案。对于验证应用程序是否以用户期望的方式运行,单元测试的能力有限。如果想确保交付的软件为用户提供了我们希望它具有的价值,就需要其他形式的测试。

5.6 后续的测试阶段

1.手工测试

测试人员会做一些机器不太擅长而人比较擅长的测试。他们做探索性测试、易用性测试,在不同平台上测试程序的界面是否正确,并着眼于一些不可控制的最坏情况进行测试。自动化验收测试使测试人员节省出更多的时间做那些高价值的活动,而不是测试脚本的人力执行器。

2.非功能测试

每个系统都有很多非功能需求。比如,几乎每个系统都有容量和安全性方面的要求,或者必须遵守服务水平协议等。通常应该用某些自动化测试衡量应用程序是否满足这些需求。

在定义部署流水线结构时,必须回答一个问题,即容量测试阶段的结果是可以作为一个门槛,还是需要由人来决定?对于高性能应用来说,可以在验收测试阶段通过之后,就运行容量测试,作为该版本整个自动化测试的输出结果。如果这个版本不能通过容量测试,就不能把它看成是可部署的版本。

然而,对于很多应用程序来说,判定"什么是可接受的"更加具有主观性。通常根据实际容量测试阶段的结果,由人来判定该版本是否可以作为候选版本来部署会更有意义。

5.7 发布准备

生产环境应该是完全受控的,即对生产环境的任何修改都应该通过自动化过程来完成。这不仅包括应用程序的部署,还包括Udine配置、软件栈、网络拓扑以及状态的所有修改。只有在这种方式下,我们才可能对它们进行可靠地审计和问题诊断,并在可预计的时间内修复它们。随着系统复杂性的增加,不同类型服务器的增多,以及不断提高的性能需求,我们就更需要这种程度的控制力。

传统上,人们对新版本的发布常常存在恐惧心理,原因有两个。一是害怕引入问题,因为手工的软件发布郭陈很可能引入难以发现的人为错误,或者部署手册本身就隐藏着某个错误。二是担心由于发布过程中的一个过程或新版本的某个缺陷,使你原来承诺的发布失败。无论是哪种情况,你的唯一希望就是足够聪明且非常快速地解决这个问题。

最复杂的情况就是在部署和撤销中涉及生产数据的迁移

5.8 实现一个部署流水线

  • 对价值流建模,并创建一个可工作的简单框架
  • 将构建和部署流程自动化
  • 将单元测试和代码分析自动化
  • 将验收测试自动化
  • 将发布自动化

一套好的自动化验收测试会帮助你追查随机问题和难以重现的问题,如竞争条件、死锁,以及资源争夺。这些问题在应用发布之后,就很难再被发现。

  • 首先,并不需要一次实现整个流水线,而应该是增量式实现。

  • 其次,部署流水线是构建、部署、测试和发布应用程序整个流程中有效的,也是最重要的统计数据来源。部署流水线应该记录流程的每次开始和结束时间,以及流程的每个阶段中到底修改了什么。之后,我们可以使用这些数据衡量从提交开始到将其部署到生产环境的周期时间,以及花在没给阶段上的时间。这样就可以看到整个流程的瓶颈在哪里,并根据优先级来解决它们。

  • 最后,部署流水线是一个有生命的系统。随着不断改进交付流程,部署流水线也应该不断变化,加以改善和重构,就像改善和重构要交付的应用一样。

5.9 度量

反馈是所有软件交付流程的核心。改善反馈的最佳方法是缩短反馈周期,并让结果可视化。你应该持续度量,并把度量结果一一种让人无法回避的方式传播出去,比如使用张贴的墙上的海报或者用一个专门的计算机显示器以大号粗体字显示结果,这些设备就是信息辐射器。

然而,重要的问题是:度量是什么?选择什么样的度量项对团队行为有很大的影响。(霍桑效应)如果度量代码行数,开发人员就会把每行代码都写得很短。如果度量被修复的缺陷数的话,那么即使是和开发人员讨论一下就能修复的缺陷,测试人员也会把它们记录下来。

根据精益思想,应该做整体优化,而不是局部优化。如果你花很多时间去解决某个瓶颈,而这个瓶颈在整个交付流程中并不是一个真正约束的话,整个交付流程并不会有什么根本性的变化。因此,应该对整个流程进行度量,从而判定这个交付流程作为一个整体是否存在问题。

对于软件交付过程来说,最重要的全局度量指标就是周期时间。它指的是从决定要做某个特性开始,直到这个特性交付给用户的这段时间。这个指标很难度量,因为它涉及软件交付过程中的很多环节,然而,这个指标比其他任何度量项都更能反应软件交付过程的真实情况。

一旦知道了应用程序的周期时间,就能找到最佳办法来缩短它。你可以利用约束理论,按照下面的流程来做优化:

  1. 识别系统中的约束,也就是构建、测试、部署和发布这个流程中的瓶颈
  2. 确保供应,即确保最大限度地提供流程中这部分的产出
  3. 根据这一约束调整其他环节的产出,即其他资源都不会100%满负荷工作
  4. 为约束环节扩容,向该瓶颈环节增加资源
  5. 理顺约束环节并重复上述步骤,即在系统中找到下一个约束,并重复第(1)步

尽管周期时间是软件交付中最重要的度量项,但还有一些其他度量项可以对问题起到警报作用。

  • 自动化测试覆盖率
  • 代码库的某些特征,比如重复代码量、圈复杂度、输入耦合度、输出耦合度、代码风格问题等
  • 缺陷的数量
  • 交付速度,即团队交付可工作、已测试过并可以使用的代码的速率
  • 每天提交到版本控制库的次数
  • 每天构建的次数
  • 每天构建失败的次数
  • 每次构建所花的时间,包括自动化测试的时间

如何呈现这些度量项是值得斟酌的。上面这些报告会产生很多数据,而如何解析这些数据就是一门艺术。比如程序经理可能想在一个项目健康报告中以非常简单的红黄蓝交通信号灯方式看到已分析的聚合数据,而不是看到一页有一页的报告。相比之下,一个团队中资深的软件工程师会希望看到更详细的情况,但也不会乐意查看多页的报告。

我们所要强调的是,一定要创建一个聚合所有信息,并且人脑可以直接通过其无比的模式匹配能力识别流程或代码库中问题的可视化报告。

每个团队的持续集成服务器在每次提交后都应该能够产生这样的报告和可视化效果,并将报告保存起来,以便今后对照某一数据库中的这些数据,对每个团队进行追踪分析。

5.10 小结

对于实现部署流水线这个复杂问题来说,没有万能钥匙一样的解决方案。关键还是在于创建一个记录系统,用来管理从提交到发布的任何变更,为你提供在流程中尽早发现问题所需要的信息。部署流水线可以帮助消除流程中的低效环节,这样可让反馈周期更短更有效。

第六章 构建与部署的的脚本化

:用于创建自动化构建和部署流程的脚本化技术,以及使用这些脚本的最佳实践。

配置应用程序,初始化数据,配置基础设施,操作系统和中间件,以及安装所需的模拟外部系统等。

6.2 构建工具概览

构建工具能推算出它需要执行这个依赖网络中的每一个任务。它可能从初始化开始,也可能从设置测试数据开始,因为这两个任务是独立的。一旦初始化完成以后,就可以编译源代码和测试了,而且一定是两个任务都要做,并在运行测试之前准备测试数据。尽管很多个任务都依赖于初始化,但它只会运行异常。

一个值得注意的小地方是每个任务都包括两点内容,一是它做什么,二是它依赖于什么。在每个构建工具都会对这两点进行建模。

然而,各构建工具的不同的在于它是任务导向的,还是产品导向的。任务导向的构建工具会依据一系列的任务描述依赖网络,而产品导向的工具,是根据它们生成的产物(比如一个可执行文件)来描述。

6.3 构建部署脚本化的原则与实践

  1. 为部署流水线的每个阶段创建脚本

  2. 使用恰当的技术部署应用程序

  3. 使用同样的脚本向所有环境部署

  4. 使用操作系统自带的包管理工具

  5. 确保部署流程是幂等的

  6. 部署系统的增量式演进

6.4 面向JVM的应用程序的项目结构(略)

6.5 部署校本化

  • 有用的测试示例:

  • 确认能从数据库中拿到一条记录

  • 确认能连上网站

  • 断言消息代理中的已注册的消息集合是正确的

  • 透过防火墙发送几次"ping"命令,证明线路是通的,且各服务器之间提供了一个循环负荷分配

6.6 小贴士

  1. 总是使用相对路径
  2. 消除手工步骤
  3. 从二进制包到版本控制库的内建可追溯性
  4. 不要把二进制包作为构建的一部分放到版本控制库中
  5. "test"不应该让构建失败,假如你有一个"test"任务,如果在其运行时,任何测试失败了,整个构建就将立即失败。通常来说,这种做法是不好的。相反,应该将当前失败的任务记录下来,然后继续构建流程的后续部分。
  6. 用集成冒烟测试来限制应用程序

6.7 小结

通常是指辅助我们进行构建、测试、部署和发布应用程序的所有自动化脚本。

在开发、测试和生产环境中共享同一种部署机制,但不要过早地纠结于工具的创建。在制定和创建这些机制时,一定要运维人员和开发人员一起做。

脚本应该是系统中的"一等公民"。这些脚本应该贯穿应用程序的整个生命周期。我们应该对这些脚本进行版本控制、维护、测试和重构,并且将其用作部署应用程序的唯一机制。

第七章 提交阶段

:讨论了部署流水线中的第一个阶段,即任何一次提交都应该触发的自动化过程。我们还讨论了如何创建一个快速、高效的提交测试套件
image

7.4 提交测试套件的原则与实践

  1. 避免用户界面
  2. 使用依赖注入
  3. 避免使用数据库
  4. 在单元测试中避免异步
  5. 使用测试替身
  6. 最少化测试中的状态
  7. 时间的伪装
  8. 蛮力

第八章 自动化验收测试

:展现了从分析一直到实现的自动化验收测试。我们讨论了为什么对于持续交付来说验收测试非常关键,以及如何创建一个成本合理的高效验收测试套件,来保护应用程序中那些有价值的功能。
image

第九章 非功能需要求的测试

:讨论了非功能需求,并重点介绍了容量测试,内容包括如何创建容量测试,以及如何准备容量测试环境

9.1 引言

容量(capacity)、吞吐量(throughput)、性能(performance)

  • 性能:对处理单一事务所花时间的一种度量,既可以单独衡量,也可以在一定的负载下衡量
  • 吞吐量:系统在一定时间内处理事务的数量,通常它受限于系统中的某个瓶颈,在一定的工作负载下,当每个单独请求的响应时间维持在可接受的范围内时,该系统所能承担的最大吞吐量被称为它的容量

即使当你清楚地知道非功能需求是什么的时候,也很难投入恰到好处的精力来保证这些非功能需求得到满足。很多失败的系统就是由于无法处理负载,不安全,运行得太慢,或者因为更常见的原因-低质量代码而导致无法维护。有些项目失败则是因为走向了另一个极端,即太担心非功能需求,所以使开发速度非常慢,或者由于系统变得太复杂或过度开发而使得无人知道如何做才能让开发更有效或更合适。

在项目开始就识别出哪些是重要的非功能需求,这一点至关重要。然后,团队就要找到某种方法来衡量这些非功能需求,并在交付时间表中考虑什么时候做出这些测试,把它们放在部署流水线的哪个文职。

9.2 非功能需求的管理

非功能需求会跨越其他需求的边界;非功能需求之间可能彼此排斥:对安全性要求极高的系统常常在易用性上做一些妥协,而非常灵活的系统经常在性能方面有所妥协,等等。

9.3 如何为容量编程

高德纳:过早优化是所有罪恶之根,97%的时间里我们都应该忘记那种小的概率提升。然而,我们也不能让另外非常关键的3%的机会与我们擦肩而过。一个优秀程序员不会因为这个原则而对其置之不理,他们非常聪明,只会在识别出那段关键代码后,才会非常细心地去查看

关键点在最后一句。在找到解决方案之前,必须先找出问题的根源。也就是说,我们要知道问题到底是什么。容量测试阶段的关键在于,它要告诉我们是否存在问题,以便我们可以修复它。不要妄自猜测,而要先进行度量。

过早且过分分地关注应用程序的容量优化是低效且昂贵的。

现代软件系统中,最昂贵的是网络通信或磁盘存储。在性能和应用程序的稳定性方面,跨进程或网络边界的通信是昂贵的,所以这类通信应该尽量最小化。

为了解决容量问题,可采取的策略如下:

  1. 为应用程序决定一种架构。通常要特别注意进程、网络边界和I/O
  2. 了解并使用正确的模式,避免使用那些影响系统容量和稳定性的反模式
  3. 除了采用适当模式以外,还要确保团队在已经明确的应用架构下进行开发,不要为容量做无谓的优化。鼓励写清晰且简单的代码,而不是深奥难以理解的代码。在没有明确测试结果表明有容量问题时,坚决不能再代码可读性上做出让步
  4. 注意在数据结构和算法方面的选择,确保它们的属性与应用程序相吻合。比如,只需要O(1)的性能,就不要用一个O(n)的算法
  5. 处理线程时要特别小心
  6. 创建一些自动化测试来断言所期望的容量级别。当这些测试失败时,用他们作为向导来修复这些问题
  7. 使用调测工具主要关注测试中发现的问题,并修复它
  8. 只要有可能,就使用真实的容量数据来做度量。生产环境是唯一真实度量的来源。使用这样的数据,并分析这些数据到底说明了什么。特别要注意系统的用户数,他们的行为模式以及生产环境中的数据量

9.4 容量度量

  • 扩展性测试。随着服务器数、服务或线程的增加,单个请求的响应时间和并发用户数的支持会如何变化
  • 持久性测试:这是要长时间运行应用程序,通过一段时间的操作,看是否有性能上的变化。这类测试能捕获内存泄漏或稳定性问题
  • 吞吐量测试:系统每秒能处理多少事务、消息或页面点击
  • 负载测试:当系统负载增加到类似生产环境大小或超过它时,系统的容量如何?

前两种测试和后两种有根本性的不同,前者是相对度量,及当改变系统某些属性时,系统性能的变化是怎样的。而后者只有作为绝对度量才有用

9.9 小结

非功能需求就好比是建造桥梁时对大梁的选择,它一定要足够强劲以支撑所期望得劲交通压力和各种天气。这种需求是现实的,必须要考虑这些需求,但这些需求并不是业务人员为大桥付钱的理由:业务人员只是想有某种东西可以让他们从河的一边到达另一边,并且这个东西看起来还不错。也就是说,作为技术人员,我们必须警惕自己更倾向于首先出现在脑海中的那种技术解决方案。我们必须和客户及用户紧密合作,共同确定应用程序中的敏感问题,并根据真实的业务价值定义详细的非功能需求。

这个工作一旦结束后,交付团队就可以决定使用哪种架构是正确的,然后像捕获功能需求那样,创建非功能需求及其验收条件。这些工作后就能评估,为了满足肺功能需求可能需要花费的精力,并和功能需求一起进行优先级的划分。

当这件事也做完后,交付团队需要创建和维护自动化测试,以确保这些需求得到满足。每次对应用程序、基础设施或者配置信息进行修改后,只要提交测试阶段和验收测试阶段成功通过,这些容量测试就要作为部署流水线的一部分运行。利用验收测试作为更广泛的基于场景的非功能需求测试的起点。这是一种策略,使你能够得到一个好的、全面的、可维护的非功能测试。

第十章 应用程序的部署与发布

:讲述了自动化测试之后应做什么:一键式将候选发布版本部署到手工测试环境、用户验收测试环境、试运行环境,直至最终发布。其中包括一些至关重要的主题,如持续部署、回滚以及零停机发布。

10.1 引言

本章将讲述如何为软件的发布(包括将其部署到测试环境上)创建并遵循一个策略。部署和发布之间的主要区别在于回滚的能力。本章还会介绍两种极有力的技术达到零停机发布和回滚,即使是大型生产环境也没有问题,它们就是蓝绿部署和金丝雀发布。

10.2 创建发布策略

当在项目一开始创建发布策略的第一个版本时,应该考虑下列内容:

  • 每个环节的部署和发布都是由谁负责的
  • 创建一个资产和配置管理策略
  • 部署时所用技术的描述。运维团队和开发团队应该对其达成共识
  • 实现部署流水线的计划
  • 枚举所有的环境,包括用于验收测试、容量测试、集成测试、用户验收测试的环境,以及每个构建在这些环境中的移动过程
  • 描述在测试和生产环境中部署时应该遵循的流程,比如提交一个变更申请,以及申请授权等
  • 对应用程序的监控需求,包括用于通知运维团队关于应用程序相关状态的API或服务
  • 讨论部署时和运行时的配置方法如何管理,以及它们与自动化部署流程时如何关联在一起的
  • 描述应用程序如何与所有外部系统集成。比如,在哪个阶段进行集成?
  • 如何记录日志详情
  • 制定灾难恢复计划
  • 对软件的服务级别达成一致
  • 生产环节的数量大小及容量计划:应用程序会创建多少数据?需要多少个日志文件或数据库?需要多少带宽或磁盘空间?客户对响应延迟的容忍度是多少?
  • 制订一个归档策略,以便不必为审计或技术支持而保留生产数据
  • 如何对生产环境进行首次部署
  • 如何修复生产环境中出现的缺陷,并为其打补丁
  • 如何升级生产环境中的应用程序以及迁移数据
  • 如何做应用程序的生产服务和技术支持

10.4 部署回滚和零停机发布

声明两个重要的约束。首先是数据。如果发布流程会修改数据,回滚操作就比较困难。另一个是需要与其他系统集成。如果发布中涉及两个以上的系统(也称为联合环境的发布,orchestrated release),回滚流畅也比较复杂。

当制定发布回滚计划时,需要遵循两个通用原则。首先,在发布之前,确保生产系统的状态已备份。其次,在每次发布之前都练习一下回滚计划,包括从备份中恢复或把数据库备份迁移回来,确保这个回滚计划可以正常工作。

1. 通过重新部署原有的正常版本来进行回滚

2.零停机发布

也称为热部署,关键在于将发布流程中的不同部分解耦,尽管使它们能独立发生。尤其是,在升级应用程序之前,就应该能将应用程序所依赖的共享资源的新版本放在适当的位置

3.蓝绿部署

两个相同的生产环节版本,一个叫蓝环境,一个叫绿环境。

系统用户被引导到当前正在作为生产环境的绿环境中。现在要发布一个新版本,所以先把这个新版本发布到蓝环境,然后在蓝环境上运行冒烟测试,检查它是否可以正常工作。当一切准备就绪以后,向新版本迁移就非常简单了,只需要修改一下路由配置。
image
如果只有一个生产环境,也可以使用蓝绿部署。只要让应用程序的两份副本一起运行在同一个环境中,每个副本都有自己的资源(自己的端口、在文件系统中有自己的根目录等等)

4.金丝雀发布

通常来说,“在任意时刻,生产环境中只有应用程序的一个版本正在运行”这个假设都是正确的
image

好处:

  • 非常容易回滚
  • 做A/B测试

最后,在生产环境中保留尽可能少的版本也是非常重要的,最好限制咋两个版本之内。支持多个版本是非常痛苦的,所以要将金丝雀的数目减少到最低限度。

10.5 紧急修复

需要牢记在心的最重要的事情是:任何情况下,都不能破坏流程。紧急修复版本也要走同样的构建、部署、测试和发布流程,与其他代码变更没什么区别。

紧急修复的另一种做法是回滚到以前使用的好版本上

下面是处理生产环境中的缺陷时应该考虑的一些因素:

  • 别自己加班到深夜来做这件事,应该与别人一起结对做这事
  • 确保有一个已经测试过的紧急修复流程
  • 对于应用程序的变更,避免绕过标准的流程,除非在极端情况下
  • 确保在试运行环境上对紧急修复版本做过测试
  • 有时候回滚比部署新的修复版本更划算。做一些分析工作,找到最好的解决方案。

10.7 小贴士和窍门

  1. 真正执行部署操作的人应该参与部署过程的创建
  2. 记录部署活动
  3. 不要删除旧文件,而是移动到别的位置
  4. 部署是整个团队的责任
  5. 服务器应用程序不应该有GUI
  6. 为新部署留预热期(在发布时,已经运行了一段时间,足以让应用服务器和数据库建立好它们的缓存)
  7. 快速失败
  8. 不要直接对生产环节进行修改

第三部分 交付生态圈

第十一章 基础设施和环境管理

:包括环境的自动化创建、管理和监控,包括虚拟化技术和云计算的使用

11.1 引言

环境:指应用程序运行所需的所有资源和它们的配置信息。用如下这些属性来描述环境:

  • 组成运行环境的服务器的硬件配置信息-比如CPU的类型与数量、内存大小,硬盘和网络接口卡等,以及这些服务器互联所需的网络基础设施
  • 应用程序运行所需要的操作系统和中间件(如消息系统、应用服务器和web服务器,以及数据库服务器等)的配置信息

通用术语基础设施代表了你所在组织中的所有环境,以及支持其运行的所有服务,如DNS服务器、防火墙、路由器、版本控制库、存储、监控应用、邮件服务器等等。

基于以下原则,用一个整体方法来管理所有基础设施:

  • 使用保存于版本控制库中的配置信息来指定基础设施所处的状态
  • 基础设施应该具有自治特性,即它应该自动地将自己设定为所需状态
  • 通过测试设备和监控手段,应该每时每刻都能掌握基础设施的实时状况

11.2 理解运维团队的需要

需要谨记的最重要的事情是:所有的项目干系人都能达成一个共识,即让发布有价值的软件成为一件低风险的事情。

运维团队依据一些关键的服务质量指标来衡量他们的效率,比如MTBF(mean time between failure,平均无故障时间)和MTTR(mean time to repair failure,平均修复时间)。运维团队常常还必须满足某些SLA(服务级别的条款)。对运维团队来说,任何变更都可能是风险。

第十二章 数据管理

:在应用程序的生命周期中,如何创建和迁移测试数据和生产数据

第十三章 组件和依赖管理

:讨论了在不使用分支的情况下让应用程序一直处于可发布状态。然后描述了如何将应用程序分解成多个组件,以及如何建立和测试这些组件

13.1 引言

组件是什么:指应用程序中一个规模相当大的代码结构,它具有一套定义良好的API,而且可以被另一种实现方式代替。对于一个基于组件的软件系统来说,通常其代码库被分成多个相互分离的部分,每个部分通过个数有限的定义良好的接口提供一些服务行为,与其他组件进行有限的交互。

在开发一个大型软件系统时,常常能看到这三个维度同时出现。在这样的系统中,组件会形成一种依赖关系,而且也会依赖于外部库。每个组件可能会有几个发布分支。

13.2 保持应用程序可发布

为了在变更的同时还能保持应用程序的可发布,有如下四种应对策略

  • 将新功能隐藏起来,直到它完成为止
    • 把新功能直接放进主干,但对用户不可见
  • 将所有的变更都变成一系列的增量式小修改,而且每次小的修改都是可发布
  • 使用通过抽象来模拟分支的方式对代码库进行大范围的变更
    • 在要修改的那部分代码上创建一个抽象层,然后在当前实现方法存在的同时,开发一种新的实现方式。当完成时,再把原始的实现和抽象层(它是可选的)删除
  • 使用组件,根据不同部分修改的频率对应用程序进行解耦

13.3 依赖

区分组件和库。库是指团队除了选择权以外,没有控制权的那些软件包,它们通常很少更新。相反,组件是指应用程序所依赖的部分软件块,但它通常是由你自己的团队或你公司中的其他团队开发的。组件通常更新频繁。

1.依赖地狱

DLL地狱。当一个应用程序依赖于某个库的特定版本,但实际部署的是另一个版本,或者根本没有部署时,依赖地狱就产生了。

2.库管理

一是提交到版本控制库中,另一种是显式地声明它们

康威法则:设计系统的组织不可避免地要产生与其组织的沟通结构一样的设计。

第十四章 版本控制进阶

:概述了最流行的一些工具,以及使用版本控制的不同模式

基于流的版本控制系统:把一系列修改一次性应用到多个分支上,从而减少合并时的麻烦。最大的区别在于,流之间可以相互继承。

第十五章 持续交付管理

:风险管理和负责度,并提出了配置和发布管理的一个成熟度模型,然后,我们讨论了持续交付带来的商业价值,和迭代增量交付项目的生命周期
image
团队组件与磨合常常经历五个阶段:创建期、风暴期、规范期、运转期和调整/重组期。

软件:识别、启动、初始、开发和部署、运维

敏捷开发失败的原因:

  • 缺乏承诺。敏捷过程依赖于透明性、协作性、纪律性和持续改进。在实施敏捷过程中,会突然释放出一些有用信息,将原来隐藏起来不便得到的真相推到聚光的下。
  • 忽视好的工作实践。
  • 将敏捷开发过程进行适应性调整,直到这个过程不再敏捷了。

风险管理是一个过程,它确保:

  • 项目的主要风险已经被识别
  • 已有适当的缓解策略对这些风险进行管控
  • 在整个项目过程中,持续识别和管理风险

一个文档越详细,就可能越快过时

posted @ 2023-04-06 16:34  LHX2018  阅读(78)  评论(0编辑  收藏  举报