引言

开发和发布软件可能是一个复杂的过程,特别是随着应用程序、团队和部署基础设施本身的复杂性不断增长。 通常,随着项目的增长,挑战会变得更加明显。 为了以快速和一致的方式开发、测试和发布软件,开发人员和组织创建了三个相关但不同的策略来管理和自动化这些流程。

持续集成的重点是每天多次将单个开发人员的工作集成到主存储库中,以尽早发现集成错误并加速协作开发。 持续交付关注于减少部署或发布过程中的摩擦,自动化部署构建所需的步骤,以便代码可以在任何时候安全地发布。 持续部署通过在每次代码更改时自动部署来进一步实现这一点。

在本指南中,我们将讨论这些策略中的每一个,它们如何相互关联,以及如何将它们合并到您的应用程序生命周期中,从而转变您的软件开发和发布实践。 要更好地了解各种开源 ci / cd 项目之间的差异,请查看我们的 ci / cd 工具比较。

什么是持续集成,为什么它有帮助?

持续集成是一种鼓励开发人员尽早并经常地将他们的代码集成到共享存储库的主要分支中的实践。 代码不是在开发周期结束时孤立地构建和集成特性,而是由每个开发人员在一天中多次将代码与共享存储库集成。

这个想法是通过尽早考虑使集成成本最小化。 开发人员可以尽早发现新代码和现有代码之间的冲突,而冲突仍然相对容易调和。 一旦冲突得到解决,工作可以自信地继续下去,新代码将满足现有代码库的需求。

集成代码本身往往不能保证新代码或功能的质量。 在许多组织中,集成是昂贵的,因为手动过程用于确保代码符合标准,不会引入错误,并且不会破坏现有的功能。 当自动化水平与现有的质量保证措施不匹配时,频繁的集成可能会产生摩擦。

为了解决集成过程中的这种摩擦,实际上,持续集成依赖于健壮的测试套件和运行这些测试的自动化系统。 当开发人员将代码合并到主存储库中时,自动化过程开始构建新代码。 然后,对新构建运行测试套件,以检查是否引入了任何集成问题。 如果构建或测试阶段失败,团队将收到警告,以便他们能够修复构建。

持续集成的最终目标是使集成成为一个简单的、可重复的过程,成为日常开发工作流程的一部分,以减少集成成本并尽早对缺陷做出响应。 努力确保系统是健壮的、自动化的和快速的,同时培养一种鼓励频繁迭代和响应性来构建问题的团队文化是战略成功的基础。

什么是持续交付和为什么它有帮助?

持续交付集成是持续集成的延伸。 它关注于软件交付过程的自动化,这样团队可以在任何时候轻松而自信地将他们的代码部署到生产环境中。 通过确保代码库始终处于可部署状态,发布软件成为一件不起眼的事情,没有复杂的仪式。 团队可以确信他们可以在任何需要的时候发布,而不需要复杂的协调或后期测试。 就像持续集成一样,持续交付管理是一种需要技术和组织改进才能有效的实践。

在技术方面,持续交付公司严重依赖于部署管道来自动化测试和部署过程。 部署管道是一个自动化系统,它将构建作为一系列连续阶段运行越来越严格的测试套件。 因此,可靠的持续集成设置是实现持续交付集成的先决条件。

在每个阶段,构建要么未通过测试(这会提醒团队) ,要么通过测试(这会导致自动升级到下一个阶段)。 随着构建在管道中移动,后面的阶段将构建部署到尽可能贴切地反映生产环境的环境中。 通过这种方式,构建、部署过程和环境可以同时进行测试。 管道以一个构建结束,该构建可以在单个步骤中的任何时候部署到生产环境中。

持续交付的组织方面鼓励优先考虑“可部署性”作为一个主要问题。 这对特性的构建和与代码库的其余部分挂钩的方式产生了影响。 在代码设计中必须考虑到这一点,以便在任何时候都可以安全地将特性部署到生产环境中,即使是在不完整的情况下。 一些技术已经出现,以协助在这一领域。

持续交付是有吸引力的,因为它自动化了将代码检查到存储库和决定是否将经过良好测试的功能性构建发布到生产基础设施之间的步骤。 有助于确认代码质量和正确性的步骤是自动化的,但是关于发布什么的最终决定权掌握在组织手中,以获得最大的灵活性。

什么是持续部署,为什么它有帮助?

持续部署是持续交付的扩展,可以自动部署通过整个测试周期的每个构建。 持续部署系统不需要等待人工看门人来决定什么时候部署到生产环境,而是部署所有已经成功通过部署管道的内容。 请记住,虽然新代码是自动部署的,但存在的技术是为了在以后激活新特性或用户子集。 部署自动将特性和修复快速推送给客户,鼓励在有限的范围内进行较小的更改,并有助于避免对当前部署到生产环境的内容产生混淆。

这种完全自动化的部署周期可能会成为组织焦虑的根源,因为他们担心放弃对自动化系统发布内容的控制权。 自动部署所提供的权衡有时被认为对于它们所提供的回报来说太危险了。

其他团队利用自动发布的承诺作为确保始终遵循最佳实践的方法,并将测试过程扩展到有限的生产环境中。 在部署一段代码之前,如果没有最终的手动验证,开发人员必须负责确保他们的代码设计良好,并且测试套件是最新的。 这将决定什么时候和什么时候向主存储库提交,以及什么时候和什么时候向生产发布到一个单一的点上,这个点牢牢地存在于开发团队的手中。

持续部署还允许组织从一致的早期反馈中获益。 特性可以立即提供给用户,缺陷或无用的实现可以在团队将大量精力投入到非生产性方向之前就被及早发现。 快速反馈一个功能是没有帮助的,这样可以让团队转移注意力,而不是把更多的精力投入到一个影响最小的领域。

连续过程的关键概念和实践

虽然持续集成、交付和部署在其涉及的范围上各不相同,但是有一些概念和实践对于每个概念和实践的成功都是至关重要的。

小的,迭代式的改变

在采用持续集成时,最重要的实践之一是鼓励小的变更。 开发人员应该练习将较大的工作分解成较小的部分,并尽早提交这些部分。 一些特殊的技术,比如逐个抽象的分支和特性标志(参见下面) ,可以帮助保护主分支的功能不受正在进行的代码更改的影响。

小的更改可以最小化集成问题的可能性和影响。 通过在尽可能早的阶段承诺共享分支,然后在整个开发过程中不断地承诺,集成的成本就会降低,而且不相关的工作也会定期同步。

以主干为本的发展

使用基于主干的开发,工作在存储库的主要分支中完成,或者经常性地合并回共享存储库中。 只要短期的特性分支表示小的更改并尽快合并回来,它们就是允许的。

基于主干的开发背后的思想是为了避免违反上面讨论的小型迭代更改的概念的大型提交。 代码可以在早期提供给对等点,这样当冲突的范围很小时就可以解决它们。

发行版是从主分支或者从主干创建的发行版分支执行的,特别是为了这个目的。 在发布分支上不会发生任何开发,以便保持将主分支作为真理的唯一来源的焦点。

保持构建和测试阶段的快速

每个过程都依赖于自动构建和测试来验证正确性。 因为必须频繁地执行构建和测试步骤,所以必须对这些流程进行简化,以最小化花在这些步骤上的时间。

增加构建时间应该被视为一个主要问题,因为每次提交都会启动一个构建,从而加剧了影响。 因为持续的流程迫使开发人员每天参与这些活动,所以在这些领域减少摩擦是一个值得追求的目标。

如果可能的话,并行运行测试套件的不同部分可以帮助更快地在管道中移动构建。 还应该注意确保每种测试的比例是有意义的。 单元测试通常非常快,并且维护开销最小。 相比之下,自动化系统或验收测试往往是复杂的,并且容易破坏。 为了解决这个问题,最好严重依赖单元测试,执行相当数量的集成测试,然后退回到稍后更复杂的测试数量上。

整个部署管道的一致性

因为一个持续交付或部署实现应该是测试发布性能的,所以在过程的每个步骤---- 构建本身、部署环境和部署过程本身---- 保持一致性是至关重要的:

  • 代码应该在管道开始时生成: 生成的软件应该存储起来,以便以后的进程可以访问,而不需要重新生成。 通过在每个阶段使用完全相同的构件,您可以确定不会由于不同的构建工具而引入不一致性。
  • 部署环境应该是一致的: 一个组态管理 / 服务系统可以控制各种环境,环境变化可以通过部署管道本身进行,以确保正确性和一致性。 应该在每个测试周期配置干净的部署环境,以防止遗留条件损害测试的完整性。 阶段化环境应该尽可能地与生产环境匹配,以减少在提升构建时出现的未知因素。
  • 应该使用一致的流程在每个环境中部署构建: 每个部署应该自动化,每个部署应该使用相同的集中化工具和过程。 应该消除临时部署,只使用管道工具进行部署。

解耦部署和发布

将代码的部署从发布分离给用户是持续交付和部署的一个非常强大的部分。 代码可以部署到生产环境中,而不需要最初激活它或者让用户可以访问它。 然后,组织决定何时发布独立于部署的新功能或特性。

通过将业务决策与技术过程分离,这为组织提供了很大的灵活性。 如果代码已经在服务器上,那么部署就不再是发布过程中敏感的部分,这样可以最大限度地减少发布时所涉及的个人数量和工作量。

有许多技术可以帮助团队部署负责某个特性的代码,而不需要发布它。 特征标志设置条件逻辑,以检查是否根据环境变量的值运行代码。 通过抽象分支,开发人员可以通过在资源消费者和提供者之间放置一个抽象层来替换实现。 仔细规划合并这些技术可以使您将这两个过程解耦。

测试类别

持续集成、交付和部署都严重依赖于自动化测试,以确定每个代码更改的有效性和正确性。 在这些过程中需要不同类型的测试来获得对给定解决方案的信任。

虽然下面的类别绝不是一个详尽的清单,虽然对每种类型的确切定义存在分歧,但这些广泛的测试类别代表了在不同情况下评估代码的各种方法。

烟雾测试

烟雾测试是一种特殊的初始检查,旨在确保非常基本的功能以及一些基本的实施和环境假设。 冒烟测试通常在每个测试周期的开始运行,作为运行更完整的测试套件之前的健全性检查。

这种类型测试背后的想法是帮助捕捉实现中的大红旗,并引起对可能表明进一步测试不可能或不值得进行的问题的注意。 烟雾测试的范围不是很广,但应该非常快。 如果一个更改没有通过冒烟测试,那么这是一个早期信号,表明核心断言已经中断,并且在问题得到解决之前不应该再花费任何时间进行测试。

特定于上下文的烟雾测试可以用于任何新阶段测试的开始,以确定基本假设和要求都得到了满足。 例如,冒烟测试可以在集成测试之前使用,也可以部署到临时服务器,但是测试的条件在每种情况下都会有所不同。

单元测试

单元测试负责以一种孤立和高针对性的方式测试单个代码元素。 单个函数和类的功能是自己测试的。 任何外部依赖关系都被替换为存根或模拟实现,以便将测试完全集中在相关的代码上。

单元测试对于测试单个代码组件的内部一致性和正确性至关重要,然后再将它们放到更复杂的上下文中。 测试的有限范围和依赖关系的删除使得查找任何缺陷的原因变得更加容易。 这也是测试各种输入和代码分支的最佳时机,这些输入和代码分支以后可能难以命中。 通常,在任何冒烟测试之后,单元测试是在进行任何更改时运行的第一个测试。

单元测试通常由单个开发人员在提交更改之前在自己的工作站上运行。 但是,持续集成服务器几乎总是在开始集成测试之前作为安全防范再次运行这些测试。

集成测试

在单元测试之后,集成测试是通过将组件组合在一起并将它们作为一个组装进行测试来执行的。 当单元测试隔离地验证代码的功能时,集成测试确保组件在相互接口时进行协作。 这种类型的测试有机会捕获通过组件之间的交互暴露的完全不同类型的 bug。

通常,当代码签入共享存储库时,会自动执行集成测试。 持续集成服务器检查代码,执行任何必要的构建步骤(通常执行快速烟雾测试以确保构建成功) ,然后运行单元测试和集成测试。 模块以不同的组合连接在一起并进行测试。

集成测试对于共享工作非常重要,因为它们保护项目的健康。 更改必须证明它们没有破坏现有的功能,并且它们按预期与其他代码交互。 集成测试的第二个目标是验证更改是否可以部署到一个干净的环境中。 这通常是在开发人员自己的机器上执行的第一个测试周期,因此在这个过程中还可以发现未知的软件和环境依赖关系。 这通常也是第一次针对实际的外部库、服务和数据测试新代码。

系统测试

一旦执行了集成测试,就可以开始另一个级别的测试,称为系统测试。 在许多方面,系统测试充当集成测试的扩展。 系统测试的重点是确保各组组件作为一个整体正确地发挥作用。

系统测试通常评估整个软件的外部功能,而不是关注组件之间的接口。 这组测试忽略了组成部分,以便将组成的软件作为一个统一的实体来衡量。 由于这种区别,系统测试通常侧重于用户或外部可访问的接口。

验收测试

验收测试是在交付之前在软件上执行的最后几种测试类型之一。 验收测试用于从业务或用户的角度确定一个软件是否满足所有需求。 这些测试有时是根据原始规范构建的,并且经常测试接口,以获得预期的功能和可用性。

验收测试通常是一个更复杂的阶段,可能会延伸到软件发布之后。 自动验收测试可以用来确保设计的技术要求得到满足,但手工验证通常也起到一定作用。

验收测试通常从将构建部署到镜像生产系统的登台环境开始。 从这里,可以运行自动化测试套件,内部用户可以访问系统,以检查系统是否按照他们需要的方式运行。 在向客户发布或提供 beta 访问之后,进一步的验收测试是通过评估软件在实际使用中的功能和收集用户的反馈来完成的。

附加术语

虽然我们已经讨论了上面的一些更广泛的概念,但是在您学习持续集成、交付和部署时可能会遇到许多相关的概念。 让我们来定义一些你可能会看到的术语:

  • 蓝绿色部署: 蓝绿色部署是在类似生产环境中测试代码和以最小停机时间部署代码的策略。 维护两组生产环境,并将代码部署到可以进行测试的非活动环境中。 当准备好发布时,生产流量将使用新代码路由到服务器,立即使更改可用。
  • 抽象的分支: 抽象的分支是一种在活动项目中执行重大重构操作的方法,这种方法在原始码储存库中没有长期存在的开发分支,而持续集成实践不鼓励这样做。 在消费者和现有的实现之间构建和部署一个抽象层,以便新的实现可以并行地构建在抽象背后。
  • Build (名词) : Build 是从源代码创建的特定版本的软件。 根据语言的不同,这可能是已编译的代码或一致的解释代码集。
  • 金丝雀版本: 金丝雀版本是向有限的用户子集发布更改的策略。 这个想法是为了确保所有的工作都能正常地与生产工作负载一起工作,同时最大限度地减少出现问题时的影响。
  • 黑暗启动: 黑暗启动是将代码部署到生产环境中的做法,它接收生产流量,但不影响用户体验。 新的更改与现有的实现一起部署,并且通常将相同的流量路由到这两个地方进行测试。 旧的实现仍然与用户界面挂钩,但是在幕后,可以使用生产环境中的实际用户请求评估新代码的正确性。
  • 部署管道: 部署管道是一组组件,通过越来越严格的测试和部署场景来移动软件,以评估其发布准备状态。 管道通常通过自动部署到生产环境或提供手动部署的选项来结束。
  • 功能标志或功能切换: 功能标志是一种在条件逻辑背后部署新功能的技术,它根据环境变量的值决定是否运行。 可以将新代码部署到生产环境中,而不需要通过适当地设置标志来激活。 为了发布软件,环境变量的值将被更改,从而激活新的代码路径。 功能标志通常包含允许用户子集访问新功能的逻辑,从而创建一种逐步推出新代码的机制。
  • 升级: 在持续过程的背景下,升级意味着将软件构建转移到测试的下一个阶段。
  • 浸泡测试: 浸泡测试涉及在重大生产或类似生产的负荷下测试软件一段较长的时间。

总结

在本指南中,我们介绍了持续集成、持续交付和持续部署,并讨论了如何使用它们安全和快速地构建和发布经过良好测试的软件。 这些过程利用广泛的自动化,并鼓励持续的代码共享,以尽早修复缺陷。 虽然实现这些解决方案所需的技术、流程和工具是一个重大的挑战,但一个设计良好且使用得当的系统的好处可能是巨大的。

要找出哪种 ci / cd 解决方案可能适合您的项目,请查看我们的 ci / cd 工具比较指南以获得更多信息。