翻译者:pityonline
在软件开发中经常提到持续集成(CI)和持续交付(CD)这两个术语。但它们到底是什么意思呢?
在谈论软件开发时,经常会提到持续集成 (CI) 和持续交付 (CD) 这两个术语。但它们到底是什么意思呢?在本文中,我将解释这些术语以及相关术语(例如持续测试和持续部署)背后的含义和重要性。
概述
就像工厂中的装配线以快速、自动化和可重复的方式从原材料生产消费品一样,软件交付管道以快速、自动化和可重复的方式从源代码生产发布版本。实现这一过程的总体设计称为“持续交付”(CD)。启动装配线的过程称为“持续集成”(CI)。确保质量的过程称为“持续测试”,将最终产品交付给用户的过程称为“持续部署”。一些专家让这一切简单、顺畅、高效地运行,这些人被称为 DevOps 实践者。
“连续”是什么意思?
“持续”用于描述遵循我在此处提到的许多不同流程实践。它并不意味着“始终运行”,而是“随时可以运行”。在软件开发领域,它还包括几个核心概念/最佳实践。这些是:
什么是“持续交付管道”?
将源代码转化为可发布产品的许多不同任务和工作通常被串在一起形成一个软件“管道”,其中一个自动化流程的成功完成将启动管道中的下一个流程。这些管道有许多不同的名称,例如持续交付管道、部署管道和软件开发管道。一般来说,项目经理负责在管道执行过程中管理管道各个部分的定义、操作、监控和报告。
持续交付流水线如何工作?
软件交付管道的实际实施可能千差万别。管道中有许多应用程序可用于各种方面,例如源代码跟踪、构建、测试、指标收集、版本管理等。但总体工作流程通常是相同的。单个编排/工作流应用程序管理整个管道,每个流程都作为由该应用程序管理的独立作业或阶段运行。通常,在编排中,这些独立作业以应用程序理解的语法和结构定义,并可作为工作流进行管理。
这些作业用于一个或多个功能(构建、测试、部署等)。每个作业可能使用不同的技术或多种技术。关键在于作业是自动化、高效且可重复的。如果作业成功,工作流管理器将触发管道中的下一个作业。如果作业失败,工作流管理器会提醒开发人员、测试人员和其他人,以便他们尽快纠正问题。此过程是自动化的,因此可以比手动运行一组流程更快地发现错误。这种快速故障排除称为快速失败,在到达管道终点时同样有价值。
“快速失败”是什么意思?
管道的工作之一是快速处理变更。另一个是监控创建发布的不同任务/作业。由于编译失败或测试失败的代码会阻止管道继续运行,因此快速通知用户此类情况非常重要。快速失败是指在管道过程中尽快发现问题并快速通知用户,以便修复问题并重新提交代码以使管道再次运行。通常在管道过程中,您可以查看历史记录以确定谁进行了更改并通知该人员及其团队。
所有持续交付流程都应该自动化吗?
管道的几乎所有部分都应该自动化。对于某些部分,可能需要进行一些人工干预/交互。一个例子可能是用户验收测试(让最终用户试用软件并确保它按他们想要/期望的方式工作)。另一种情况可能是部署到生产中,用户希望有更多的人工控制。当然,如果代码不正确或不起作用,则需要人工干预。
在了解了“连续”的背景知识后,让我们看看不同类型的连续过程以及它们在软件管道中的含义。
什么是“持续集成”?
持续集成 (CI) 是自动检测、提取、构建和(在大多数情况下)单元测试源代码更改的过程。持续集成是启动管道的步骤(尽管在持续集成之前有时会包含一些预验证(通常称为飞行前检查)。
持续集成的目标是快速确保开发人员新提交的更改是良好的并且适合在代码库中进一步使用。
持续集成如何工作?
持续集成的基本思想是让一个自动化过程监视一个或多个源代码存储库的更改。当更改被推送到存储库时,它会检测到更改、下载副本、构建副本并运行任何相关的单元测试。
持续集成如何监控变化?
如今,看门狗通常是像 Jenkins 这样的应用程序,它还协调管道中运行的所有(或大多数)进程,并且监视更改是其功能之一。看门狗可以通过多种不同的方式监视更改。这些包括:
什么是“预检查”(也称为“启动前检查”)?
在将代码引入存储库并触发持续集成之前,可以进行额外的验证。这遵循了测试构建和代码审查等最佳实践。它们通常在将代码引入管道之前内置到开发过程中。但有些管道也可能将它们作为监控过程或工作流程的一部分。
例如,Gerrit 工具允许在开发人员推送代码之后、代码被允许进入(Git 远程)存储库之前进行正式的代码审查、验证和测试构建。Gerrit 位于开发人员的工作区和 Git 远程存储库之间。它“接收”来自开发人员的推送,并可以执行通过/失败验证,以确保它们在被允许进入存储库之前通过。这可以包括检测新的更改并启动测试构建(一种 CI 形式)。它还允许开发人员当时进行正式的代码审查。这还有一个额外的好处,那就是当更改后的代码合并到代码库中时,它不会破坏任何东西。
什么是“单元测试”?
单元测试(也称为“提交测试”)是由开发人员编写的小型、专门的测试,用于确保新代码独立运行。这里的“独立”是指不依赖或调用其他不可直接访问的代码,也不依赖外部数据源或其他模块。如果运行代码需要此类依赖项,则这些资源可以用模拟来表示。模拟是指使用看起来像资源的代码存根,可以返回值,但不实现任何功能。
在大多数组织中,开发人员负责创建单元测试来证明他们的代码是正确的。事实上,一种称为测试驱动开发 (TDD) 的模型要求首先设计单元测试作为明确验证代码功能的基础。由于此类代码可以快速大量地更改,因此它们也必须快速执行。
就持续集成工作流而言,开发人员在本地工作环境中编写或更新代码,并使用单元测试来确保新开发的功能或方法是正确的。通常,这些测试采用断言的形式,即给定的一组函数或方法的输入会产生一组给定的输出。他们经常进行测试以确保正确标记和处理错误条件。有许多有用的单元测试框架,例如用于 Java 开发的 JUnit。
什么是“持续测试”?
持续测试是指在代码通过持续交付管道时运行范围扩大的自动化测试的做法。单元测试通常与构建过程集成在一起,作为持续集成阶段的一部分,侧重于测试与交互的其他代码隔离的代码。
除此之外,还可以或应该进行各种形式的测试。这些可能包括:
所有这些可能都不存在于自动化管道中,并且某些不同类型的测试可能没有明确分类。但是,交付管道中持续测试的目标始终是相同的:通过持续的测试证明代码的质量可用于持续发布。基于持续集成快速的原则,第二个目标是快速发现问题并提醒开发团队。这通常被称为快速失败。
除了测试之外,还可以对管道中的代码执行哪些其他类型的验证?
除了测试是否通过之外,还有一些应用程序可以告诉我们测试用例执行(覆盖)的源代码行数。这是一个可以衡量代码量的指标示例。此指标称为代码覆盖率,可以通过 JaCoCo for Java 等工具进行统计。
还有许多其他类型的指标,例如代码行数、复杂度和代码结构分析。诸如 SonarQube 之类的工具可以检查源代码并计算这些指标。此外,用户可以为他们接受的“合格”指标范围设置阈值。然后可以在管道中设置对这些阈值的检查,如果结果不在可接受范围内,则流程结束。诸如 SonarQube 之类的应用程序具有高度可配置性,可以设置为仅检查团队感兴趣的内容。
什么是持续交付?
持续交付 (CD) 通常是指自动检测源代码变化并经过构建、测试、打包和相关操作以生成可部署版本的整个流程链(管道),基本上无需任何人工干预。
软件开发过程中持续交付的目标是自动化、效率、可靠性、可重复性和质量保证(通过持续测试)。
持续交付包括持续集成(自动检测源代码变化、执行构建过程并运行单元测试来验证变化)、持续测试(对代码运行各种测试以确保代码质量)和(可选)持续部署(通过管道自动向用户提供发布版本)。
如何识别/跟踪管道中的多个版本?
版本控制是持续交付和管道中的一个关键概念。持续意味着能够频繁集成新代码并提供更新版本。但这并不意味着每个人都想要“最新和最好的”。对于想要针对已知稳定版本进行开发或测试的内部团队来说尤其如此。因此,管道创建的这些版本化对象以及可以轻松存储和访问的内容非常重要。
管道中从源代码创建的对象通常可称为工件。工件在构建时应应用版本。为工件分配版本号的推荐策略称为语义版本控制。(这也适用于从外部来源引入的依赖工件的版本。)
语义版本号包含三个部分:主版本号、次版本号和补丁版本号。(例如,1.4.3 反映主版本号 1、次版本号 4 和补丁版本号 3。)其理念是,其中一个部分的更改代表工件中的更新级别。主版本号仅针对不兼容的 API 更改而递增。当以向后兼容的方式添加功能时,次版本号会递增。当进行向后兼容版本的错误修复时,补丁版本号会递增。这些是建议的指导方针,但团队可以自由地改变这种方法,只要他们在整个组织中以一致且易于理解的方式这样做即可。例如,可以在补丁字段中放置一个每次为发布完成构建时都会增加的数字。
文物该如何“分配”?
团队可以为工件分配提升级别,以指示其适用于测试、生产和其他环境或用途。有很多方法可以做到这一点。可以使用 Jenkins 或 Artifactory 等应用程序进行分发。或者一个简单的解决方案是在版本号字符串末尾添加一个标签。例如部署软件,-snapshot 可以指示用于构建工件的代码的最新版本(快照)。可以使用各种提升策略或工具将工件“提升”到其他级别,例如 -milestone 或 -production,作为工件版本稳定性和完整性的标记。
如何存储和访问多个工件版本?
管理工件存储库的应用程序可以存储从源代码构建的版本化工件。工件存储库就像是构建工件的版本控制工具。Artifactory 或 Nexus 等应用程序可以接受版本化工件、存储和跟踪它们,并提供检索方法。
管道用户可以指定他们想要使用的版本并使用这些版本的管道。
什么是“持续部署”?
持续部署 (CD) 是指能够自动将持续交付管道中的发布版本提供给最终用户。根据用户的安装方式,这可能是云环境中的自动部署、应用更新(如手机上的应用)、网站更新或只是可用版本列表的更新。
这里重要的一点是,仅仅因为可以进行持续部署并不意味着管道中的每一组交付物都始终可以部署。它实际上意味着通过管道的每组交付物都被证明是“可部署的”。这主要通过持续的持续测试来实现(请参阅本文中的持续测试部分)。
是否部署管道构建的版本可以通过手动决策来控制,或者在完全部署版本之前使用各种方法“试用”版本。
在向所有用户全面部署之前,有哪些方法可以测试部署?
由于必须对所有用户回滚/撤消部署可能会造成高昂的成本(无论是从技术角度还是从用户角度而言),因此有许多技术可用于“试用”新功能的部署,并在发现问题时轻松“撤消”它们。这些包括:
蓝/绿测试/部署
在这种部署软件的方法中,需要维护两个相同的主机环境,一个是“蓝色”,一个是“绿色”。 (颜色并不重要,只是为了识别。)相应地,其中一个是“生产环境”,另一个是“暂存环境”。
这些实例前面是调度系统,它们充当客户到产品或应用程序的“网关”。通过将调度系统指向蓝色或绿色实例,可以将客户流量引导到所需的部署环境。这样,切换指向哪个部署实例(蓝色或绿色)对用户来说既快速又简单,而且透明。
当新版本准备好进行测试时,可以将其部署到非生产环境中。经过测试和批准后,可以更改调度系统设置以将传入的生产流量指向它(因此它成为新的生产站点)。曾经的生产环境实例现在可用于下一个候选版本。
同样,如果在最新部署中发现问题,而之前的生产实例仍然可用,则只需进行简单的更改即可将客户流量重定向回之前的生产实例 - 有效地将有问题的实例“脱机”并回滚到以前的版本。然后可以在另一个区域修复有问题的新实例。
金丝雀测试/部署
在某些情况下,用蓝/绿版本切换整个部署可能不可行或不可取。另一种方法是针对金丝雀进行测试/部署。在此模型中,部分客户流量被重定向到新版本部署。例如,可以与当前服务的生产版本一起部署搜索服务的新版本。然后,10% 的搜索查询可以定向到新版本以在生产中对其进行测试。
如果新版本在处理该流量时没有出现问题,则可以逐渐将更多流量引导至该版本。如果仍然没有问题,则可以随着时间的推移逐步部署新版本,直到 100% 的流量都调度到新版本。这实际上“取代”了该服务的先前版本,并使新版本对所有客户都有效。
功能开关
对于可能需要轻松关闭的新功能(如果发现问题),开发人员可以添加功能切换。这是代码中的“如果-那么”软件功能开关,仅当设置数据值时才激活新代码。此数据值可以是全局可访问的位置,部署的应用程序会检查该位置以查看是否应执行新代码。如果设置了数据值,则执行代码;如果没有设置,则不执行。
如果在生产部署后发现问题,这为开发人员提供了一个远程“终止开关”来关闭新功能。
暗箱发布
在暗发布中,代码会逐步测试/部署到生产环境,但用户看不到这些变化(因此名称中带有暗发布一词)。例如,在生产版本中,网页查询的部分内容可能会重定向到查询新数据源的服务。开发人员可以收集这些信息进行分析,而无需向用户公开有关界面、交易或结果的任何信息。
其理念是获取候选版本在生产负载下的表现的真实信息,而不会影响用户或改变他们的体验。随着时间的推移,可以安排更多的负载,直到遇到问题或新功能被认为可供所有人使用。在实践中,功能切换标志可用于这种暗发布机制。
何为“运维开发”?
DevOps 是一套关于如何让开发和运营团队更轻松地合作开发和发布软件的理念和推荐做法。从历史上看,开发团队构建了产品,但并没有像客户那样以常规、可重复的方式安装/部署它们。这组安装/部署任务(以及其他支持任务)在整个周期中都留给了运营团队。这通常会导致很多混乱和问题,因为运营团队来晚了,必须在短时间内完成他们的工作。同样,开发团队经常处于不利地位——因为他们没有充分测试产品的安装/部署功能,他们可能会对过程中出现的问题感到惊讶。
这往往导致开发和运营团队之间严重脱节和缺乏合作。DevOps 概念提倡在整个开发周期中开发和运营之间采取一种全面、协作的工作方式,就像持续交付一样。
持续交付如何与开发和运营相交叉?
持续交付管道是多种 DevOps 概念的实现。产品开发的后期阶段(例如打包和部署)始终可以在管道的每次运行中完成,而不必等待产品开发周期中的特定时间。同样部署软件,在从开发到部署的过程中,开发和运营都可以清楚地看到事情何时有效以及何时无效。要使持续交付管道周期取得成功,它不仅必须通过与开发相关的流程,还必须通过与运营相关的流程。
更进一步,DevOps 建议将实现管道的基础设施也视为代码。也就是说,它应该自动配置、可跟踪、易于修改,并在管道发生变化时触发新的运行。这可以通过将管道实现为代码来实现。
什么是管道即代码?
管道即代码是一个通用术语,指的是通过编写代码来创建管道作业/任务,就像开发人员编写代码一样。其目标是将管道实现表示为代码,以便可以存储、审查、与代码一起跟踪,并在出现问题且必须终止管道时轻松重建。有几种工具可以实现这一点,例如 Jenkins 2。
DevOps 如何影响生产软件的基础设施?
传统上,管道中使用的每个硬件系统都有自己的配套软件(操作系统、应用程序、开发工具等)。在极端情况下,每个系统都是通过手动设置定制的。这意味着当系统出现问题或需要更新时,这通常也是一项定制任务。这种方法违背了持续交付的基本理念,即拥有一个易于重现和可追溯的环境。
多年来,人们开发了许多应用程序来标准化系统的交付(安装和配置)。同样,人们开发了虚拟机来模拟在其他计算机上运行的计算机程序。这些虚拟机需要在底层主机系统上运行虚拟机管理程序,并且需要自己的操作系统副本才能运行。
然后出现了容器。容器虽然在概念上与虚拟机相似,但工作方式不同。它们不是运行单独的程序和操作系统副本,而是仅使用一些现有的操作系统构造来划分隔离空间。因此,它们的行为类似于虚拟机,可以提供隔离,但没有过多的开销。
VM 和容器是根据配置定义创建的,因此可以轻松销毁和重建它们,而不会影响它们所运行的主机系统。这样也可以重建运行管道的系统。此外,对于容器,我们可以跟踪其构建定义文件的更改 - 就像我们对源代码所做的那样。
因此,如果我们在虚拟机或容器中遇到问题,我们可以更轻松、更快速地销毁并重建它们,而不是尝试在当前环境中调试和修复它们。
这也意味着对管道代码的任何更改都可以触发管道的新运行(通过 CI),就像对代码的更改一样。这是 DevOps 关于基础设施的核心概念之一。
通过:
作者:Brent Laster 主题:lujun9972 译者:pityonline 校对:wxy
本文由LCTT原创编译,Linux中国荣誉呈现