你好,我是葛俊。

在上一篇文章中,我和你分享了代码入库前的流程优化,即持续开发。今天,我会继续与你介绍流程优化中,代码入库和入库后的3种持续工程方法,即持续集成(Continuous Integration, CI)、持续交付(Continuous Delivery, CD)和持续部署(Continuous Deployment, CD)。

在接下来的分享中,首先我会与你介绍这3种方法的定义和作用,帮助你深入理解这3个“持续”背后的原理和原则;然后我会以Facebook为参考,给你介绍基于这些原则的具体工程实践。我希望,这些内容能够帮助你用好这三个“持续”方法,提高团队的研发效能。

首先,我先来介绍一下,持续集成、持续交付和持续部署是用来解决什么问题的,也就是它们的定义和作用。

3个“持续”的定义和作用

不知道你是否还记得,在开篇词中,我提到过一个低效能的情况,即产品发布上线时出现大量提交、合并,导致最后时刻出现很多问题。这个情况很常见,引起了很多用户的共鸣。产生这个问题最主要原因是,代码合并太晚。这里,我再与你详细解释一下原因。

当多个人同时开发一款产品的时候,很可能会出现代码冲突。而解决冲突,需要花费较多的时间;同时很可能出现冲突解决失败,导致产品问题。

如果没有一个机制督促我们尽早把代码推到主仓进行集成的话,我们通常会尽量先在自己的分支上进行开发。结果往往是,在冲刺快要结束,或者功能即将发布时,才出现大量的代码合并。

而这时因为很长时间没有进行过代码集成了,进行集成的代码量通常比较大,不同开发者的代码区别很大,冲突也很严重,难以解决。具体的负面影响是:发布推迟,产品质量不高,每一次发布时的熬夜和紧张影响团队士气。

而持续集成的根本出发点,就是为了解决这个问题。也就是说,它能够帮助开发人员尽量早、尽量频繁地把自己的改动推送到共享的代码仓库分支上,进行代码集成,从而减少大量代码冲突造成的低效能问题。

所以,持续集成的定义就是:在团队协作中,一天内多次将所有开发人员的代码合并入同一条主干

代码入库后,剩下工作是把代码编译打包成可以发布的形式,先发布到测试环境进行测试,再发布到类生产环境进行测试,最终部署到生产环境。

在这个过程中,有两个问题需要特别注意。

而解决这两个问题,正是持续交付和持续部署的出发点。

持续交付的目标是,对每一个进入主干分支的代码提交,构建打包成为可以发布的产品。它的定义是:一种软件工程方法,在短周期内完成软件产品,以保证软件保持在随时可以发布的状态。也就是说,对每一个提交,把集成后的代码部署到“类生产环境”中进行验证。如果代码没有问题,后续可以手动部署到生产环境中。

而持续部署,则更进一步。它把持续交付产生的产品立即自动部署给用户,定义就是:将每一个代码提交,都构建出产品直接部署给用户使用。

以上就是持续集成、持续交付与持续部署的作用和定义。在实现上,它们共同的本质是,让每一个变更都经过一条自动化的检验流水线,来检查每一个变更的质量,通过就进入下一个阶段。

这里的“下一个阶段”具体包括:代码并入主仓、产品进入测试环境、产品进入类生产环境、产品最终进入生产环境,如下图所示。

图1 CI/CD流水线示意图

你应该已经注意到了,整条流水线中,持续部署只是持续交付的最后一步,也就是自动化上线的那一步,前面的各种检查,都属于持续交付流水线。所以,我在后面的内容中再提到流水线时,CI/CD指的就是“持续集成/持续交付”。

CI/CD流水线,能够大大提高代码入库的速度和质量,是这几年硅谷互联网公司做到高效研发的必要条件。接下来,我就与你介绍CI/CD流水线的具体原则以及最佳实践,然后以Facebook的具体实践为例帮助你加深理解。

需要注意的是,在这篇文章中,我会重点与你分享CI/CD流水线的搭建原则。而关于具体的搭建方式,通常是持续集成工具+代码仓管理系统+检查工具+测试工具,比如Jenkins+GitLab+SonarQube+Linter+UnitTest的组合。你可以参考这个链接提供的方式去搭建。

CI/CD流水线的具体原则以及最佳实践

根据上面提到的3个“持续”的本质,要做到高效,有3条基本原则:

基本原则1:流水线的测试要尽量完整

CI/CD流水线的测试只有尽量完整,代码和产品的质量才能有保证。所以,最主要的工程实践,就是在流水线中运行大量高质量的测试和检查。

Facebook就有大量的单元测试和集成测试用例、安全扫描,以及性能专项测试用例。如果某个验证在流水线中失败,开发人员会考虑是否要添加测试用例来防止再出现类似的问题。

另外,Facebook持续在测试用例的开发上投入。在内部工具团队,有一个专门的测试工具团队来建设测试框架和测试流程,方便开发人员自己开发测试用例。比如,我在Facebook那几年,他们就一直在改进JavaScript的Mock框架,对开发人员写测试用例来说非常方便。

基本原则2:流水线的运行速度一定要快

因为每一个变更都要通过CI/CD流水线的检验,所以流水线的速度关乎研发速度。而要提高这条流水线的速度,我们可以从以下两个方面考虑。

首先,从技术角度考虑。比如:

其次,权衡流水线的运行速度、流水线资源和测试完整性的关系。不难理解,运行速度快、占用资源少、测试完整难以兼顾,因此我们必须做出权衡。这里我推荐几个方法:

基本原则3:流水线使用的环境,尽量和生产环境一致

这里的环境,包括机器环境、数据、软件包、网络环境等。环境不一致可能导致问题暴露在用户面前,损失严重;另外,在非生产环境上难以复现生产环境的问题,调试困难。

保证流水线环境与生产环境一致,具体方法包括:

以上就是CI/CD流水线的3个基本原则和最佳实践。通过提高验证的完整性、速度,以及保证环境的一致性,我们可以降低成本,提高产品质量和验证产品价值假设的速度。

接下来,为了帮助你理解并正确运用这些原则和最佳实践,我们一起来看看Facebook是怎么做的。

具体案例:Facebook是如何实施CI/CD来提高效能的?

Facebook一直就非常注重CI/CD,早在2009年就建设了顺畅的CI/CD流水线,而且一直在持续改进。

在CI方面,加强建设持续开发,让开发人员能在开发环境上进行大量的验证。本地的所有验证,与CI流水线上的验证方式保持一致,这就大大提高了开发人员在本地发现问题的能力,从而大量避免了有问题的代码提交到CI流水线,浪费资源。

代码入库的步骤,采用Phabricator作为CI的驱动,并作为质量检查中枢,尽量提高入库前代码审查的流畅性。在这个过程中,Facebook做到了以下几点:

代码入库之后,进入持续交付步骤。Facebook使用大仓,同一个仓中每天有几千个代码提交,所以持续交付的挑战很大。他们有一个专门的发布工具团队,自研了一套发布工具来实现自动化流水线,通过以下两点比较好地实现了流水线资源和测试完整性的平衡。

这里需要说明的是,2017年以前,Facebook并没有把每一个在主干分支上成功通过流水线验证的软件包作为发布候选,而是在每周五的固定时间,从主干分支上拉出一个发布分支,稳定3天后上线。也就是说,这并不是严格意义上的持续交付。这是因为当时的自动化检验还不能确保产品达到上线要求。其实,这对很多公司来说都很常见,都需要一些额外的测试和检验来确保上线产品的质量。

最后,是持续部署的操作。在2017年以前,Facebook并没有持续部署,而是采用的每周全量代码部署的方式。但到2017年,因为代码提交实在太多,每次周部署代码,处理的提交量会超过10000,需要很长时间才能稳定发布分支,所以Facebook转向了持续部署。

具体的方法是,极致地进行自动化测试验证。关于实施细节,你可以参考Facebook的第一个发布工程师Chuck Rossi对持续部署流程的描述

值得一提的是,跟持续交付一样,Facebook的持续部署也不是纯粹的持续部署。因为代码提交太多,他们并没有每个提交都单独部署,而是采用类似持续交付的方法,把一段时间之内的提交一起部署。这种不教条的方式,是我从Facebook学到的一个重要的做事方法。

小结

Facebook在CI/CD上做到了极致,对每一个代码提交都高效地运行大量的测试、验证,并采用测试分层、定时运行等方式尽量降低资源消耗。正因为如此,他们能够让几千名开发人员共同使用一个大代码仓,并使用主干开发,产生高质量的产品,从而实现了超大研发团队协同下的高效能。

在前面几篇文章中,我们多次提到“持续”。这个词,近些年在软件研发中比较流行,比如我今天与你分享的持续集成、持续交付、持续部署,加上持续开发,一共有4个了。

实际上,在CI/CD流水线中,做为流水线的一部分,测试一直在运行并最快地给开发者提供反馈。这正是另一个“持续”,也就是“持续测试”的定义。

“持续”如此重要的原因是,软件开发是一个流程,只有让这个流程持续运转才能高效。这里我把这5个持续都列举出来,方便你复习、参考。

图2 5个“持续”方法定义与关键点对比

思考题

  1. 在几千名开发人员共同使用一个大代码仓的工作方式下,做好CI有很大的挑战性。你觉得挑战在哪里,容易出现什么样的问题,又应该怎么解决呢?
  2. 今天我提到了持续开发在CI中的作用,请你结合上一篇文章,思考一下持续开发和CI/CD是怎样互相促进的。

感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!