什么是持续集成(CI)持续部署(CD)?
在软件开发中经常会提到 持续集成(Continuous Integration)(CI)和 持续交付(Continuous Delivery)(CD)这⼏个术语。但它们真正的意思是什么呢?在谈论软件开发时,经常会提到 持续集成(Continuous Integration)(CI)和 持续交付(Continuous Delivery)(CD)这⼏个术语。但它们真正的意思是什么呢?在本⽂中,我将解释这些和相关术语背后的含义和意义,例如 持续测试(Continuous Testing)和 持续部署(Continuous Deployment)。
概览
⼯⼚⾥的装配线以快速、⾃动化、可重复的⽅式从原材料⽣产出消费品。同样,软件交付管道以快速、⾃动化和可重复的⽅式从源代码⽣成发布版本。如何完成这项⼯作的总体设计称为“持续交付”(CD)。启动装配线的过程称为“持续集成”(CI)。确保质量的过程称
为“持续测试”,将最终产品提供给⽤户的过程称为“持续部署”。⼀些专家让这⼀切简单、顺畅、⾼效地运⾏,这些⼈被称为 运维开发(DevOps)践⾏者。
“持续”是什么意思?
“持续”⽤于描述遵循我在此提到的许多不同流程实践。这并不意味着“⼀直在运⾏”,⽽是“随时可运⾏”。
在软件开发领域,它还包括⼏个核⼼概念/最佳实践。这些是:
1.频繁发布: 持续实践背后的⽬标是能够频繁地交付⾼质量的软件。此处的交付频率是可变的,可由开发团队或公司定义。对于某些产品,⼀季度、⼀个⽉、⼀周或⼀天交付⼀次可能已经⾜够频繁了。对于另⼀些来说,⼀天可能需要多次交付也是可⾏的。所谓持续也有“偶尔、按需”的⽅⾯。最终⽬标是相同的:在可重复、可靠的过程中为最终⽤户提供⾼质量的软件更新。通常,这可以通过很少甚⾄⽆需⽤户的交互或掌握的知识来完成(想想设备更新)。
2.⾃动化流程: 实现此频率的关键是⽤⾃动化流程来处理软件⽣产中的⽅⽅⾯⾯。这包括构建、测试、分析、版本控制,以及在某些情况下的部署。
3.可重复: 如果我们使⽤的⾃动化流程在给定相同输⼊的情况下始终具有相同的⾏为,则这个过程应该是可重复的。也就是说,如果我们把某个历史版本的代码作为输⼊,我们应该得到对应相同的可交付产出。这也假设我们有相同版本的外部依赖项(即我们不创建该版本代码使⽤的其它交付物)。理想情况下,这也意味着可以对管道中的流程进⾏版本控制和重建(请参阅稍后的 DevOps 讨论)。
4.快速迭代: “快速”在这⾥是个相对术语,但⽆论软件更新/发布的频率如何,预期的持续过程都会以⾼效的⽅式将源代码转换为交付物。⾃动化负责⼤部分⼯作,但⾃动化处理的过程可能仍然很慢。例如,对于每天需要多次发布候选版更新的产品来说,⼀轮集成测试(integrated testing)下来耗时就要⼤
半天可能就太慢了。
什么是“持续交付管道”?
将源代码转换为可发布产品的多个不同的 任务(task)和 作业(job)通常串联成⼀个软件“管道”,⼀个⾃动流程成功完成后会启动管道中的下⼀个流程。这些管道有许多不同的叫法,例如持续交付管道、部署管道和软件开发管道。⼤体上讲,程序管理者在管道执⾏时管理管道各部分的定义、运⾏、监控和报告。
持续交付管道是如何⼯作的?
软件交付管道的实际实现可以有很⼤不同。有许多程序可⽤在管道中,⽤于源代码跟踪、构建、测试、指标采集,版本管理等各个⽅⾯。但整体⼯作流程通常是相同的。单个业务流程/⼯作流应⽤程序管理整个管道,每个流程作为独⽴的作业运⾏或由该应⽤程序进⾏阶段管理。通常,在业务流程中,这些独⽴作业是以应⽤程序可理解并可作为⼯作流程管理的语法和结构定义的。这些作业被⽤于⼀个或多个功能(构建、测试、部署等)。每个作业可能使⽤不同的技术或多种技术。关键是作业是⾃动化的、⾼效的,并且可重复的。如果作业成功,则⼯作流管理器将触发管道中的下⼀个作业。如果作业失败,⼯作流管理器会向开发⼈员、测试⼈员和其他⼈发出警报,以便他们尽快纠正问题。这个过程是⾃动化的,所以⽐⼿动运⾏⼀组过程可更快地到错误。这种快速排错称为 快速失败(fail fast),
并且在抵达管道端点⽅⾯同样有价值。
“快速失败”是什么意思?
管道的⼯作之⼀就是快速处理变更。另⼀个是监视创建发布的不同任务/作业。由于编译失败或测试未通过的代码可以阻⽌管道继续运⾏,因此快速通知⽤户此类情况⾮常重要。快速失败指的是在管道流程中尽快发现问题并快速通知⽤户的⽅式,这样可以及时修正问题并重新提交代码以便使管道再次运⾏。通常在管道流程中可通过查看历史记录来确定是谁做了那次修改并通知此⼈及其团队。
所有持续交付管道都应该被⾃动化吗?
管道的⼏乎所有部分都是应该⾃动化的。对于某些部分,有⼀些⼈为⼲预/互动的地⽅可能是有意义的。⼀个例⼦可能是 ⽤户验收测试(user-acceptance testing)(让最终⽤户试⽤软件并确保它能达到他们想要/期望的⽔平)。另⼀种情况可能是部署到⽣产环境时⽤户希望拥有更多的⼈为控制。当然,如果代码不正确或不能运⾏,则需要⼈⼯⼲预。有了对“持续”含义理解的背景,让我们看看不同类型的持续流程以及它们在软件管道上下⽂中的含义。
什么是“持续集成”?
持续集成(CI)是在源代码变更后⾃动检测、拉取、构建和(在⼤多数情况下)进⾏单元测试的过程。
持续集成是启动管道的环节(尽管某些预验证 —— 通常称为 上线前检查(pre-flight checks) —— 有时会被归在持续集成之前)。持续集成的⽬标是快速确保开发⼈员新提交的变更是好的,并且适合在代码库中进⼀步使⽤。
持续集成是如何⼯作的?
持续集成的基本思想是让⼀个⾃动化过程监测⼀个或多个源代码仓库是否有变更。当变更被推送到仓库时,它会监测到更改、下载副本、构建并运⾏任何相关的单元测试。持续集成如何监测变更?⽬前,监测程序通常是像 Jenkins 这样的应⽤程序,它还协调管道中运⾏的所有(或⼤多数)进程,监视变更是其功能之⼀。监测程序可以以⼏种不同⽅式监测变更。这些包括:
1.轮询: 监测程序反复询问代码管理系统,“代码仓库⾥有什么我感兴趣的新东西吗?”当代码管理系统有新的变更时,监测程序会“唤醒”并完成其⼯作以获取新代码并构建/测试它。
2.定期: 监测程序配置为定期启动构建,⽆论源码是否有变更。理想情况下,如果没有变更,则不会构建任何新内容,因此这不会增加额外的成本。
3.推送: 这与⽤于代码管理系统检查的监测程序相反。在这种情况下,代码管理系统被配置为提交变更到仓库时将“推送”⼀个通知到监测程序。最常见的是,这可以以 webhook 的形式完成 —— 在新代
码被推送时⼀个 挂勾(hook)的程序通过互联⽹向监测程序发送通知。为此,监测程序必须具有可以通过⽹络接收 webhook 信息的开放端⼝。
什么是“预检查”(⼜称“上线前检查”)?
在将代码引⼊仓库并触发持续集成之前,可以进⾏其它验证。这遵循了最佳实践,例如 测试构建(test build)和 代码审查(code review)。它们通常在代码引⼊管道之前构建到开发过程中。但是⼀些管道也可能将它们作为其监控流程或⼯作流的⼀部分。例如,⼀个名为 Gerrit 的⼯具允许在开发⼈员推送代码之后但在允许进⼊(Git 远程)仓库之前进⾏正式的代码审查、验证和测试构建。Gerrit 位于开发⼈员的⼯作区和 Git 远程仓库之间。它会“接收”来⾃开发⼈员的推送,并且可以执⾏通过/失败验证以确保它们在被允许进⼊仓库之前的检查是通过的。这可以包括检测新变更并启动构建测试(CI 的⼀种形式)。它还允许开发者在那时进⾏正式的代码审查。这种⽅式有⼀种额外的可信度评估机制,即当变更的代码被合并到代码库中时不会破坏任何内容。
什么是“单元测试”?
单元测试(也称为“提交测试”),是由开发⼈员编写的⼩型的专项测试,以确保新代码独⽴⼯作。“独⽴”这⾥意味着不依赖或调⽤其它不可直接访问的代码,也不依赖外部数据源或其它模块。如果运⾏代码需要这样的依赖关系,那么这些资源可以⽤ 模拟(mock)来表⽰。模拟是指使⽤看起来像资源的 代码
存根(code stub),可以返回值,但不实现任何功能。在⼤多数组织中,开发⼈员负责创建单元测试以证明其代码正确。事实上,⼀种称为 测试驱动开发(test-driven develop)(TDD)的模型要求将⾸先设计单元测试作为清楚地验证代码功能的基础。因为这样的代码可以更改速度快且改动量⼤,所以它们也必须执⾏很快。由于这与持续集成⼯作流有关,因此开发⼈员在本地⼯作环境中编写或更新代码,并通单元测试来确保新开发的功能或⽅法正确。通常,这些测试采⽤断⾔形式,即函数或⽅法的给定输⼊集产⽣给定的输出集。它们通常进⾏测试以确保正确标记和处理出错条件。有很多单元测试框架都很有⽤,例如⽤于 Java 开发的 JUnit。
什么是“持续测试”?
持续测试是指在代码通过持续交付管道时运⾏扩展范围的⾃动化测试的实践。单元测试通常与构建过程集成,作为持续集成阶段的⼀部分,并专注于和其它与之交互的代码隔离的测试。除此之外,可以有或者应该有各种形式的测试。这些可包括:
1.集成测试: 验证组件和服务组合在⼀起是否正常。
2.功能测试: 验证产品中执⾏功能的结果是否符合预期。
3.验收测试: 根据可接受的标准验证产品的某些特征。如性能、可伸缩性、抗压能⼒和容量。
所有这些可能不存在于⾃动化的管道中,并且⼀些不同类型的测试分类界限也不是很清晰。但是,在交付管道中持续测试的⽬标始终是相同的:通过持续的测试级别证明代码的质量可以在正在进⾏的发布中使⽤。在持续集成快速的原则基础上,第⼆个⽬标是快速发现问题并提醒开发团队。这通常被称为快速失败。
除了测试之外,还可以对管道中的代码进⾏哪些其它类型的验证?
除了测试是否通过之外,还有⼀些应⽤程序可以告诉我们测试⽤例执⾏(覆盖)的源代码⾏数。这是⼀个可以衡量代码量指标的例⼦。这个指标称为 代码覆盖率(code-coverage),可以通过⼯具(例如⽤于 Java 的 JaCoCo)进⾏统计。还有很多其它类型的指标统计,例如代码⾏数、复杂度以及代码结构对⽐分析等。诸如 SonarQube 之类的⼯具可以检查源代码并计算这些指标。此外,⽤户还可以为他们可接受的“合格”范围的指标设置阈值。然后可以在管道中针对这些阈值设置⼀个检查,如果结果不在可接受范围内,则流程终端上。SonarQube 等应⽤程序具有很⾼的可配置性,可以设置仅检查团队感兴趣的内容。
什么是“持续交付”?
持续交付(CD)通常是指整个流程链(管道),它⾃动监测源代码变更并通过构建、测试、打包和相关操作运⾏它们以⽣成可部署的版本,基本上没有任何⼈为⼲预。持续交付在软件开发过程中的⽬标
是⾃动化、效率、可靠性、可重复性和质量保障(通过持续测试)。持续交付包含持续集成(⾃动检测源代码变更、执⾏构建过程、运⾏单元测试以验证变更),持续测试(对代码运⾏各种测试以保障代码质量),和(可选)持续部署(通过管道发布版本⾃动提供给⽤户)。
如何在管道中识别/跟踪多个版本?
提交的东西不能更改
版本控制是持续交付和管道的关键概念。持续意味着能够经常集成新代码并提供更新版本。但这并不意味着每个⼈都想要“最新、最好的”。对于想要开发或测试已知的稳定版本的内部团队来说尤其如此。因此,管道创建并轻松存储和访问的这些版本化对象⾮常重要。在管道中从源代码创建的对象通常可以称为 ⼯件(artifact)。⼯件在构建时应该有应⽤于它们的版本。将版本号分配给⼯件的推荐策略称为 语义化版本控制(semantic versioning)。(这也适⽤于从外部源引⼊的依赖⼯件的版本。)语义版本号有三个部分: 主要版本(major)、 次要版本(minor) 和 补丁版本(patch)。(例如,1.4.3 反映了主要版本 1,次要版本 4 和补丁版本 3。)这个想法是,其中⼀个部分的更改表⽰⼯件中的更新级别。主要版本仅针对不兼容的 API 更改⽽递增。当以 向后兼容(backward-compatible)的⽅式添加功能时,次要版本会增加。当进⾏向后兼容的版本 bug 修复时,补丁版本会增加。这些是建议的指导⽅针,但只要团队在整个组织内以⼀致且易于理解的⽅式这样做,团队就可以⾃由地改变这种⽅法。例如,每次为发布完成构建时增加的数字可以放在补丁字段中。
如何“分销”⼯件?
团队可以为⼯件分配 分销(promotion)级别以指⽰适⽤于测试、⽣产等环境或⽤途。有很多⽅法。可以⽤ Jenkins 或 Artifactory 等应⽤程序进⾏分销。或者⼀个简单的⽅案可以在版本号字符串的末尾添加标签。例如,-snapshot 可以指⽰⽤于构建⼯件的代码的最新版本(快照)。可以使⽤各种分销策略或⼯具将⼯件“提升”到其它级别,例如 -milestone 或 -production,作为⼯件稳定性和完备性版本的标记。
如何存储和访问多个⼯件版本?
从源代码构建的版本化⼯件可以通过管理 ⼯件仓库(artifact repository)的应⽤程序进⾏存储。⼯件仓库就像构建⼯件的版本控制⼯具⼀样。像 Artifactory 或 Nexus 这类应⽤可以接受版本化⼯件,存储和跟踪它们,并提供检索的⽅法。管道⽤户可以指定他们想要使⽤的版本,并在这些版本中使⽤管道。
什么是“持续部署”?
持续部署(CD)是指能够⾃动提供持续交付管道中发布版本给最终⽤户使⽤的想法。根据⽤户的安装⽅式,可能是在云环境中⾃动部署、app 升级(如⼿机上的应⽤程序)、更新⽹站或只更新可⽤版本列表。这⾥的⼀个重点是,仅仅因为可以进⾏持续部署并不意味着始终部署来⾃管道的每组可交付成果。它实际上指,通过管道每套可交付成果都被证明是“可部署的”。这在很⼤程度上是由持续测试的连续级别完成的(参见本⽂中的持续测试部分)。管道构建的发布成果是否被部署可以通过⼈⼯决策,
或利⽤在完全部署之前“试⽤”发布的各种⽅法来进⾏控制。
在完全部署到所有⽤户之前,有哪些⽅法可以测试部署?
由于必须回滚/撤消对所有⽤户的部署可能是⼀种代价⾼昂的情况(⽆论是技术上还是⽤户的感知),已经有许多技术允许“尝试”部署新功能并在发现问题时轻松“撤消”它们。这些包括:
蓝/绿测试/部署
在这种部署软件的⽅法中,维护了两个相同的主机环境 —— ⼀个“蓝⾊” 和⼀个“绿⾊”。(颜⾊并不重要,仅作为标识。)对应来说,其中⼀个是“⽣产环境”,另⼀个是“预发布环境”。在这些实例的前⾯是调度系统,它们充当产品或应⽤程序的客户“⽹关”。通过将调度系统指向蓝⾊或绿⾊实例,可以将客户流量引流到期望的部署环境。通过这种⽅式,切换指向哪个部署实例(蓝⾊或绿⾊)对⽤户来说是快速,简单和透明的。当新版本准备好进⾏测试时,可以将其部署到⾮⽣产环境中。在经过测试和批准后,可以更改调度系统设置以将传⼊的线上流量指向它(因此它将成为新的⽣产站点)。现在,曾作为⽣产环境实例可供下⼀次候选发布使⽤。同理,如果在最新部署中发现问题并且之前的⽣产实例仍然可⽤,则简单的更改可以将客户流量引流回到之前的⽣产实例 —— 有效地将问题实例“下线”并且回滚到以前的版本。然后有问题的新实例可以在其它区域中修复。
⾦丝雀测试/部署
在某些情况下,通过蓝/绿发布切换整个部署可能不可⾏或不是期望的那样。另⼀种⽅法是为 ⾦丝雀(canary)测试/部署。在这种模型中,⼀部分客户流量被重新引流到新的版本部署中。例如,新版本的搜索服务可以与当前服务的⽣产版本⼀起部署。然后,可以将 10% 的搜索查询引流到新版本,以在⽣产环境中对其进⾏测试。如果服务那些流量的新版本没问题,那么可能会有更多的流量会被逐渐引流过去。如果仍然没有问题出现,那么随着时间的推移,可以对新版本增量部署,直到 100% 的流量都调度到新版本。这有效地“更替”了以前版本的服务,并让新版本对所有客户⽣效。
功能开关
对于可能需要轻松关掉的新功能(如果发现问题),开发⼈员可以添加功能开关(feature toggles)。这是代码中的 if-then 软件功能开关,仅在设置数据值时才激活新代码。此数据值可以是全局可访问的位置,部署的应⽤程序将检查该位置是否应执⾏新代码。如果设置了数据值,则执⾏代码;如果没有,则不执⾏。这为开发⼈员提供了⼀个远程“终⽌开关”,以便在部署到⽣产环境后发现问题时关闭新功能。
暗箱发布
在暗箱发布(dark launch)中,代码被逐步测试/部署到⽣产环境中,但是⽤户不会看到更改(因此名称中有 暗箱(dark)⼀词)。例如,在⽣产版本中,⽹页查询的某些部分可能会重定向到查询新数据源的
服务。开发⼈员可收集此信息进⾏分析,⽽不会将有关接⼝,事务或结果的任何信息暴露给⽤户。这个想法是想获取候选版本在⽣产环境负载下如何执⾏的真实信息,⽽不会影响⽤户或改变他们的经验。随着时间的推移,可以调度更多负载,直到遇到问题或认为新功能已准备好供所有⼈使⽤。实际上功能开关标志可⽤于这种暗箱发布机制。
什么是“运维开发”?
运维开发(DevOps) 是关于如何使开发和运维团队更容易合作开发和发布软件的⼀系列想法和推荐的实践。从历史上看,开发团队研发了产品,但没有像客户那样以常规、可重复的⽅式安装/部署它们。在整个周期中,这组安装/部署任务(以及其它⽀持任务)留给运维团队负责。这经常导致很多混乱和问题,因为运维团队在后期才开始介⼊,并且必须在短时间内完成他们的⼯作。同样,开发团队经常处于不利地位 —— 因为他们没有充分测试产品的安装/部署功能,他们可能会对该过程中出现的问题感到惊讶。这往往导致开发和运维团队之间严重脱节和缺乏合作。DevOps 理念主张是贯穿整个开发周期的开发和运维综合协作的⼯作⽅式,就像持续交付那样。
持续交付如何与运维开发相交?
持续交付管道是⼏个 DevOps 理念的实现。产品开发的后期阶段(如打包和部署)始终可以在管道的每次运⾏中完成,⽽不是等待产品开发周期中的特定时间。同样,从开发到部署过程中,开发和运维
都可以清楚地看到事情何时起作⽤,何时不起作⽤。要使持续交付管道循环成功,不仅要通过与开发相关的流程,还要通过与运维相关的流程。说得更远⼀些,DevOps 建议实现管道的基础架构也会被视为代码。也就是说,它应该⾃动配置、可跟踪、易于修改,并在管道发⽣变化时触发新⼀轮运⾏。这可以通过将管道实现为代码来完成。
什么是“管道即代码”?
管道即代码(pipeline-as-code)是通过编写代码创建管道作业/任务的通⽤术语,就像开发⼈员编写代码⼀样。它的⽬标是将管道实现表⽰为代码,以便它可以与代码⼀起存储、评审、跟踪,如果出现问题并且必须终⽌管道,则可以轻松地重建。有⼏个⼯具允许这样做,如Jenkins 2。
DevOps 如何影响⽣产软件的基础设施?
传统意义上,管道中使⽤的各个硬件系统都有配套的软件(操作系统、应⽤程序、开发⼯具等)。在极端情况下,每个系统都是⼿⼯设置来定制的。这意味着当系统出现问题或需要更新时,这通常也是⼀项⾃定义任务。这种⽅法违背了持续交付的基本理念,即具有易于重现和可跟踪的环境。多年来,很多应⽤被开发⽤于标准化交付(安装和配置)系统。同样, 虚拟机(virtual machine)被开发为模拟在其它计算机之上运⾏的计算机程序。这些 VM 要有管理程序才能在底层主机系统上运⾏,并且它们需要⾃⼰的操作系统副本才能运⾏。后来有了 容器(container)。容器虽然在概念上与 VM 类似,但⼯作
⽅式不同。它们只需使⽤⼀些现有的操作系统结构来划分隔离空间,⽽不需要运⾏单独的程序和操作系统的副本。因此,它们的⾏为类似于 VM 以提供隔离但不需要过多的开销。VM 和容器是根据配置定义创建的,因此可以轻易地销毁和重建,⽽不会影响运⾏它们的主机系统。这允许运⾏管道的系统也可重建。此外,对于容器,我们可以跟踪其构建定义⽂件的更改 —— 就像对源代码⼀样。因此,如果遇到 VM 或容器中的问题,我们可以更容易、更快速地销毁和重建它们,⽽不是在当前环境尝试调试和修复。这也意味着对管道代码的任何更改都可以触发管道新⼀轮运⾏(通过 CI),就像对代码的更改⼀样。这是 DevOps 关于基础架构的核⼼理念之⼀。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。