持续集成(译)

2022-12-05 12:22:10 浏览数 (2)

什么是持续集成

持续集成是一种软件开发实践,团队成员经常集成他们的工作,通常每个人至少每天集成 - 导致每天进行多次集成。每个集成都通过自动构建(包括测试)进行验证,以尽快检测集成错误。许多团队发现这种方法可以显着减少集成问题,并允许团队更快地开发有凝聚力的软件。本文是对持续集成的快速概述,总结了该技术及其当前使用情况。

“持续集成”一词起源于 Kent Beck 的极限编程开发过程,是其最初的十二种实践之一。当我开始在 Thoughtworks 担任顾问时,我鼓励我正在合作的项目使用该技术。Matthew Foemmel 将我含糊的劝告变成了切实的行动,我们看到该项目从罕见而复杂的集成变成了我所描述的非事件。Matthew 和我在这篇论文的原始版本中写下了我们的经验,它是我网站上最受欢迎的论文之一。

尽管持续集成是一种不需要特定工具来部署的实践,但我们发现使用持续集成服务器很有用。最著名的此类服务器是 CruiseControl,它是一个开源工具,最初由 Thoughtworks 的几个人构建,现在由一个广泛的社区维护。从那时起,出现了其他几个 CI 服务器,包括开源和商业的——包括来自 Thoughtworks Studios 的Cruise 。

使用持续集成构建功能

我首先将当前集成源的副本复制到我的本地开发机器上。我通过从主线签出工作副本来使用源代码管理系统来做到这一点。

现在我拿着我的工作副本,做我需要做的任何事情来完成我的任务。这将包括更改生产代码以及添加或更改自动化测试。持续集成假设高度自动化的软件测试:我称之为 自我测试代码的工具。这些通常使用流行的 XUnit 测试框架的一个版本。

完成后(通常在我工作的不同时间点),我会在我的开发机器上进行自动构建。这将获取我工作副本中的源代码,将其编译并链接到可执行文件中,然后运行自动化测试。只有当所有构建和测试都没有错误时,整个构建才被认为是好的。

通过良好的构建,我可以考虑将我的更改提交到存储库中。当然,扭曲之处在于,在我有机会提交之前,其他人可能并且通常已经对主线进行了更改。所以首先我用他们的更改更新我的工作副本并重建。如果他们的更改与我的更改发生冲突,它将在编译或测试中表现为失败。在这种情况下,我有责任解决这个问题并重复,直到我可以构建一个与主线正确同步的工作副本。

一旦我自己构建了一个正确同步的工作副本,我就可以最终将我的更改提交到主线,然后更新存储库。

但是我的提交并没有完成我的工作。此时我们再次构建,但这次是在基于主线代码的集成机器上。只有当这个构建成功时,我们才能说我的更改已经完成。我总是有可能错过了我的机器上的某些东西并且存储库没有正确更新。只有当我提交的更改在集成上成功构建时,我的工作才能完成。这个集成构建可以由我手动执行,也可以由 Cruise 自动完成。

持续集成实践

上面的故事是 CI 的概述以及它在日常生活中的工作原理。让这一切顺利进行显然远不止于此。我现在将重点介绍构成有效 CI 的关键实践。

维护单一源存储库

软件项目涉及大量文件,需要将这些文件编排在一起以构建产品。跟踪所有这些是一项重大工作,尤其是在涉及多人时。因此,多年来软件开发团队构建了管理所有这些的工具也就不足为奇了。这些工具 - 称为源代码管理工具、配置管理、版本控制系统、存储库或各种其他名称 - 是大多数开发项目的组成部分。可悲和令人惊讶的是,它们并不是所有项目的一部分。这种情况很少见,但我确实遇到了不使用这种系统并且使用本地和共享驱动器的混乱组合的项目。

因此,作为一个简单的基础,请确保您获得一个体面的源代码管理系统。成本不是问题,因为可以使用优质的开源工具。当前选择的开源存储库是 Subversion。(较旧的开源工具CVS仍在广泛使用,总比没有好得多,但 Subversion 是现代选择。)有趣的是,当我与开发人员交谈时,我知道大多数商业源代码管理工具不如 Subversion 受欢迎。我一直听到人们说唯一值得付费的工具是Perforce。

获得源代码管理系统后,请确保它是众所周知的每个人都可以获取源代码的地方。没有人应该问“foo-whiffle 文件在哪里?” 一切都应该在存储库中。

自动化构建

将源代码转换为运行系统通常是一个复杂的过程,涉及编译、移动文件、将模式加载到数据库等。然而,就像这部分软件开发中的大多数任务一样,它可以自动化——因此应该是自动化的。要求人们输入奇怪的命令或点击对话框是浪费时间,也是滋生错误的温床。

构建的自动化环境是系统的一个共同特征。Unix 世界已经有几十年了,Java 社区开发了 Ant,.NET 社区有 Nant,现在有 MSBuild。确保您可以通过单个命令使用这些脚本构建和启动您的系统。

一个常见的错误是没有在自动构建中包含所有内容。构建应该包括从存储库中获取数据库模式并在执行环境中启动它。我将详细说明我之前的经验法则:任何人都应该能够引入一台新机器,从存储库中检查源代码,发出一个命令,并在他们的机器上运行一个系统。

构建脚本有多种形式,通常特定于某个平台或社区,但并非必须如此。尽管我们的大多数 Java 项目都使用 Ant,但也有一些使用了 Ruby(Ruby Rake 系统是一个非常好的构建脚本工具)。我们从使用 Ant 自动化早期的 Microsoft COM 项目中获得了很多价值。

大型构建通常需要时间,如果您只进行了很小的更改,您就不想执行所有这些步骤。因此,一个好的构建工具会分析作为流程的一部分需要更改的内容。执行此操作的常用方法是检查源文件和目标文件的日期,并且仅在源日期较晚时才编译。依赖关系变得棘手:如果一个目标文件更改了那些依赖它的目标文件,则可能也需要重建。编译器可能会处理这种事情,也可能不会。

让您的构建进行自我测试

传统上,构建意味着编译、链接以及执行程序所需的所有其他内容。一个程序可以运行,但这并不意味着它做了正确的事情。现代静态类型语言可以捕捉到许多错误,但漏掉的错误要多得多。

更快更有效地捕获错误的一个好方法是在构建过程中包括自动化测试。当然,测试并不完美,但它可以发现很多错误——足够有用了。特别是极限编程 (XP) 和测试驱动开发 (TDD) 的兴起极大地促进了自测试代码的普及,因此许多人看到了该技术的价值。

经常阅读我的作品的读者会知道我是 TDD 和 XP 的忠实粉丝,但是我想强调的是,要获得自测试代码的好处,这两种方法都不是必需的。这两种方法都强调在编写使它们通过的代码之前编写测试——在这种模式下,测试既是关于探索系统设计的,也是关于捕获错误的。这是一件好事,但对于持续集成的目的来说并不是必需的,因为我们对自测试代码的要求较弱。(尽管 TDD 是我生成自测试代码的首选方式。)

对于自测试代码,您需要一套自动化测试来检查大部分代码库是否存在错误。测试需要能够从一个简单的命令开始并进行自我检查。运行测试套件的结果应指示是否有任何测试失败。对于要进行自测试的构建,测试失败应该导致构建失败。

每个人每天都致力于主线

集成主要是关于沟通。集成允许开发人员告诉其他开发人员他们所做的更改。频繁的沟通可以让人们快速了解变化的发展。

开发人员致力于主线的一个先决条件是他们可以正确构建他们的代码。当然,这包括通过构建测试。与任何提交周期一样,开发人员首先更新他们的工作副本以匹配主线,解决与主线的任何冲突,然后在他们的本地机器上构建。如果构建通过,那么他们可以自由地提交到主线。

通过经常这样做,开发人员可以快速发现两个开发人员之间是否存在冲突。快速解决问题的关键是快速找到它们。由于开发人员每隔几个小时提交一次冲突,因此可以在冲突发生后的几个小时内检测到冲突,此时并没有发生太多事情,而且很容易解决。数周未被发现的冲突可能很难解决。

在更新工作副本时构建的事实意味着您可以检测到编译冲突和文本冲突。由于构建是自测试的,因此您还可以检测代码运行中的冲突。如果后一种冲突在代码中长时间未被发现,则它们是特别难以发现的错误。由于提交之间只有几个小时的更改,因此问题可能隐藏的地方只有这么多。此外,由于变化不大,您可以使用差异调试来帮助您找到错误。

每个提交都应该在集成机器上构建主线

使用每日提交,团队可以获得频繁的测试构建。这应该意味着主线保持健康状态。然而,在实践中,事情仍然会出错。一个原因是纪律,人们在提交之前没有进行更新和构建。另一个是开发人员机器之间的环境差异。

因此,您应该确保在集成机器上进行常规构建,并且只有当此集成构建成功时,才应认为提交已完成。由于提交的开发人员对此负责,因此该开发人员需要监控主线构建,以便在它中断时修复它。这样做的必然结果是,在主线构建通过您当天晚些时候添加的任何提交之前,您不应该回家。

立即修复损坏的构建

进行持续构建的一个关键部分是,如果主线构建失败,则需要立即修复。使用 CI 的全部意义在于您始终在一个已知的稳定基础上进行开发。主线构建中断并不是一件坏事,尽管如果它一直在发生,这表明人们在提交之前对本地更新和构建不够小心。但是,当主线构建确实中断时,快速修复它很重要。

通常修复构建的最快方法是从主线恢复最新的提交,将系统恢复到最后一个已知的良好构建。当然,团队不应该尝试在损坏的主线上进行任何调试。除非损坏的原因立即显而易见,否则只需恢复主线并在开发工作站上调试问题。

为了避免完全破坏主线,您可以考虑使用挂起的 head。

当团队引入 CI 时,通常这是最难解决的事情之一。在早期,团队可能很难养成使用主线构建的常规习惯,尤其是当他们在现有代码库上工作时。耐心和稳定的应用似乎经常能奏效,所以不要气馁。

保持快速构建

持续集成的重点是提供快速反馈。没有什么比需要很长时间的构建更能吸收 CI 活动的血液了。在这里,我必须承认某个古怪的老家伙对被认为是一个长体型的东西很感兴趣。我的大多数同事都认为需要一个小时的构建是完全不合理的。我记得团队梦想着他们可以如此快速地实现它 - 有时我们仍然会遇到很难以这种速度构建的情况。

然而,对于大多数项目来说,十分钟构建的 XP 指南是完全合理的。我们的大多数现代项目都实现了这一点。值得付出集中精力来实现它,因为您减少构建时间的每一分钟都为每个开发人员每次提交时节省了一分钟。由于 CI 需要频繁提交,这会增加很多时间。

可能最关键的一步是开始着手建立部署管道。部署管道 (也称为构建管道或分阶段构建)背后的想法是实际上有多个构建按顺序完成。对主线的提交触发了第一个构建——我称之为提交构建。提交构建是有人提交到主线时所需的构建。提交构建是必须快速完成的构建,因此它会采用许多捷径,这会降低检测错误的能力。诀窍是平衡错误发现和速度的需求,以便良好的提交构建足够稳定,以供其他人使用。

一旦提交构建良好,其他人就可以放心地处理代码。但是,您可以开始进行更多、更慢的测试。其他机器可以在构建上运行需要更长时间的进一步测试例程。

在生产环境的克隆中进行测试

测试的重点是在受控条件下排除系统在生产中可能出现的任何问题。其中很重要的一部分是生产系统将在其中运行的环境。如果您在不同的环境中进行测试,每一个差异都会导致在测试中发生的事情不会在生产中发生的风险。

因此,您希望将测试环境设置为尽可能准确地模拟您的生产环境。使用相同的数据库软件,使用相同的版本,使用相同版本的操作系统。将生产环境中的所有适当库放入测试环境,即使系统实际上并未使用它们。使用相同的 IP 地址和端口,在相同的硬件上运行。

让任何人都能轻松获取最新的可执行文件

为了帮助完成这项工作,任何参与软件项目的人都应该能够获得最新的可执行文件并能够运行它:用于演示、探索性测试,或者只是看看本周发生了什么变化。

每个人都可以看到正在发生的事情

要沟通的最重要的事情之一是主线构建的状态。如果您使用 Cruise,则有一个内置网站会告诉您是否有正在进行的构建以及最后一次主线构建的状态。许多团队喜欢通过将持续显示连接到构建系统来使这一点更加明显 - 当构建工作时发出绿色的灯,如果失败则发出红色的灯很受欢迎。一种特别常见的触感是红色和绿色熔岩灯- 这些不仅指示构建的状态,而且还指示它处于该状态的时间。红灯上的气泡表明该构建已损坏太久。每个团队都在这些构建传感器上做出自己的选择——玩自己的选择是件好事(最近我看到有人用跳舞的兔子做实验)。

自动化部署

要进行持续集成,您需要多个环境,一个用于运行提交测试,一个或多个用于运行辅助测试。由于您每天要在这些环境之间多次移动可执行文件,因此您需要自动执行此操作。因此,拥有可让您轻松将应用程序部署到任何环境的脚本非常重要。

这样做的一个自然结果是,您还应该拥有允许您轻松部署到生产环境中的脚本。您可能不会每天都部署到生产环境中(尽管我遇到过这样做的项目),但自动部署有助于加快流程并减少错误。这也是一个便宜的选择,因为它只使用您用于部署到测试环境中的相同功能。

持续集成的好处

总的来说,我认为持续集成的最大和最广泛的好处是降低了风险。我的思绪仍然浮现在我在第一段中提到的那个早期的软件项目上。他们在那里(他们希望)完成了一个漫长的项目,但并不真正知道要多长时间才能完成。

延迟集成的问题在于,很难预测需要多长时间才能完成,更糟糕的是,很难看出你在这个过程中走了多远。结果是,您将自己置于项目最紧张的部分之一的完全盲点 - 即使您是少数情况下您还没有迟到的情况之一。

持续集成完全解决了这个问题。没有长集成,你彻底消除了盲点。任何时候你都知道你在哪里,什么有效,什么无效,你的系统中存在的突出错误。

持续集成并没有消除错误,但它确实使它们更容易找到和删除。在这方面,它更像是自测代码。如果您引入错误并快速检测到它,那么摆脱它会容易得多。由于您只更改了系统的一小部分,因此无需多看。由于系统的那部分是您刚刚使用的部分,因此它在您的记忆中是新鲜的 - 再次使查找错误变得更容易。您还可以使用差异调试- 将系统的当前版本与没有错误的早期版本进行比较。

错误也是累积的。您拥有的错误越多,删除每个错误就越困难。这部分是因为您会遇到错误交互,其中故障显示为多个故障的结果 - 使每个故障更难找到。这也是心理上的——当有很多错误时,人们没有精力去发现和消除错误——这种现象被实用程序员称为破窗综合症。

因此,具有持续集成的项目在生产和过程中的错误往往会大大减少。但是我应该强调,这种好处的程度与您的测试套件的好坏直接相关。您应该会发现构建一个能够产生显着差异的测试套件并不难。然而,通常情况下,团队需要一段时间才能真正达到他们有可能达到的低级别错误。到达那里意味着不断努力和改进你的测试。

引入持续集成

第一步是使构建自动化。获取源代码控制所需的一切获取它,以便您可以使用单个命令构建整个系统。对于许多项目来说,这不是一项小事——但它对于其他任何事情的工作都是必不可少的。最初,您可能只是偶尔按需构建,或者只是进行自动化的夜间构建。虽然这些不是持续集成,但自动化的夜间构建是前进的一步。

在您的构建中引入一些自动化测试。尝试确定出现问题的主要区域并进行自动化测试以暴露这些故障。特别是在现有项目中,很难让一套真正好的测试快速运行——构建测试需要时间。不过,你必须从某个地方开始——所有关于罗马建造时间表的陈词滥调都适用。

尝试加快提交构建。在几个小时的构建上进行持续集成总比没有好,但是把这个神奇的十分钟数字降低到更好。当您打破对系统慢速部分的依赖时,这通常需要对您的代码库进行一些非常严肃的手术。

如果您要开始一个新项目,请从头开始持续集成。密切关注构建时间,并在您开始慢于十分钟规则时立即采取行动。通过快速行动,您将在代码库变得如此之大以至于成为主要痛苦之前进行必要的重组。

最重要的是得到一些帮助。找一个做过持续集成的人来帮助你。像任何新技术一样,当您不知道最终结果是什么样子时,很难引入它。找一个导师可能要花钱,但如果你不这样做,你也会付出时间和生产力的损失。

最后

如果您不使用持续集成,我强烈建议您尝试一下。如果你是,也许这篇文章中有一些想法可以帮助你更有效地做到这一点。在过去的几年里,我们学到了很多关于持续集成的知识,我希望还有更多的东西需要学习和改进。

0 人点赞