持续集成简述

1 概述

互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration, CI)。持续集成也被认为是敏捷开发的最重要实践之一。

本文主要介绍持续集成的概念以及一些工具与实践。

2 概念

图2.1 持续集成概念示意图之一
图2.1 持续集成概念示意图之一

上图来源于谈谈持续集成,持续交付,持续部署之间的区别,这图很直观地描述了持续集成相关的概念之间的关系,与持续集成密切相关的概念还有持续交付、持续部署,因此在下文将会就这三个概念进行阐述。

2.1 持续集成

维基百科的定义(Continuous integration - Wikipedia):

在软件工程里,持续集成(Continuous Integration, CI)是指这样的一种实践:在一天里多次将所有开发人员的代码合并到一个共享的主干里,每次合并都会触发持续集成服务器进行自动构建,这个过程包括了编译、单元测试、集成测试、质量分析等步骤,结果只有两个:成功或者失败。如果得到失败的结果,说明有人提交了不合格的代码,这就能及时发现问题。

在瀑布模型中,软件的开发过程被分为以下几个阶段:

  • 需求分析
  • 系统设计
  • 编码实现
  • 测试
  • 集成
  • 部署
  • 维护

而一般在实际的开发中,基本也就遵循这些步骤进行开发,大部分情况下,不会完全按照瀑布流的方式执行,但是这些步骤都是有的,无非就是在文档的落实、步骤的次序与侧重有所变化。其中,所谓的集成,在平时的开发过程中很少直接提到这个名词,其含义是(见:System integration - Wikipedia):

将子系统的组件整合到一个系统中并且确保子系统的功能可以组合为一个系统的过程。

所以,所谓的『集成』就是在存在多个系统协作的情况下,将子系统集成为一个大系统的过程,这个概念与『集成测试』中的『集成』是同一个概念。但是有些文章在解释『持续集成』这个概念的时候,将其解释为『将代码集成到主干分支』,目前也没见到有人说这是错的。

如果只有一个系统,那么就不存在『系统集成』这一个步骤,但是『持续集成』这个做法还是可以应用其中的,因为『持续集成』的精髓不在于『集成』,而在于『持续』,而我所理解的『持续』,就是将一些重复的操作自动化,然后频繁地触发这个自动化过程。

所以,结合上面提到的瀑布模型的几个阶段,以及『持续集成』这个名字,图2.1应该改为这样:

图2.2 持续集成概念示意图之二
图2.2 持续集成概念示意图之二

从上图中看来,持续集成应该至少包括以下几部分:

  • 自动化构建
  • 自动化测试
  • 自动发布

图2.3 持续集成阶段示意图
图2.3 持续集成阶段示意图

每一次的构建与测试,都应该得到一个结果:通过或者不通过,开发人员应该都能看到每一次构建与测试的结果,得到不通过的结果时应该能马上修复相关的缺陷,这就需要有一种合适的反馈渠道。

2.1.1 自动化构建

自动化构建包括以下过程:

  • 将源码编译成为二进制码
  • 打包二进制码
  • 运行自动化测试
  • 生成文档
  • 生成分发媒体(例如:Debian DEB、Red Hat RPM或者Windows MSI文件)

自动化构建可以通过两类工具实现:

  1. 构建自动化软件(Build automation utility)
    • 例如Make、Ant、Maven、Gradle,目的是通过编译等活动来生成构建产物(build artifact)。
  2. 构建自动化服务器(Build automation servers)
    • 一般是基于web的工具,通过计划任务或者是事件触发的方式调用构建自动化软件。一个CI服务器就是一类构建自动化服务器。

2.1.2 自动化测试

自动化测试是持续集成必不可少的一部分,基本上,没有自动化测试的持续集成,都很难称之为真正的持续集成。我们希望持续集成能够尽早的暴露问题,但这远非配置一个 Hudson/Jenkins服务器那么简单,只有真正用心编写了较为完整的测试用例,并一直维护它们,持续集成才能孜孜不倦地运行测试并第一时间报告问题。

测试自动化是使用特定的软件(独立于被测试的软件)来控制测试的执行以及比较实际输出与预期输出。测试自动化可以将某些重复但必要的任务自动化,或者执行某些难以手动执行的额外测试。

自动化测试还包括单元测试、集成测试、系统测试、验收测试、性能测试等,在不同的场景下,它们都能为软件开发带来极大的价值。

2.1.2.1 单元测试

单元测试(Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

通常来说,程序员每修改一次程序就会进行最少一次单元测试,在编写程序的过程中前后很可能要进行多次单元测试,以证实程序达到预期的工作目标,没有程序错误。

2.1.2.2 集成测试

集成测试(Integration Testing,有时也叫Integration and Testing, I&T)即对独立的软件模块组装起来看成是一个整体进行测试。集成测试一般在单元测试之后、系统测试之前进行。实践表明,有时模块虽然可以单独工作,但是并不能保证组装起来也可以同时工作。

集成与单元测试最大的区别是它需要尽可能的测试整个功能及相关环境,对于测试Web应用而言,通常有这么几步:

  1. 启动Web容器
  2. 部署待测试Web应用
  3. 以Web客户端的角色运行测试用例
  4. 停止Web容器

通常有三种手段实现集成测试:

  1. 大爆炸(Big Bang)
    将所有单元组合到一起一次性测试一遍。

  2. 自上而下(Top Down)
    先测试高层次的单元,然后逐渐测试低层次的单元。

  3. 自下而上(Bottom Up)
    先测试低层次的单元,然后逐渐测试高层次的单元。

2.2 持续交付


图2.4 持续交付示意图

持续交付(Continuous Delivery, CD)是一种软件工程的手段,让软件在短周期内产出,确保软件随时可以被可靠地发布。其目的在于更快、更频繁地构建、测试以及发布软件。通过加强对生产环境的应用进行渐进式更新,这种手段可以降低交付变更的成本与风险。一个简单直观的与可重复的部署过程对于持续交付来说是很重要的。

2.2.1 与DevOps的关系

持续交付与DevOps的含义很相似,所以经常被混淆。但是它们是不同的两个概念。DevOps的范围更广,它以文化变迁为中心,特别是软件交付过程所涉及的多个团队之间的合作(开发、运维、QA、管理部门等),并且将软件交付的过程自动化。另一方面,持续交付是一种自动化交付的手段,关注点在于将不同的过程集中起来,并且更快、更频繁地执行这些过程。因此,DevOps可以是持续交付的一个产物,持续交付直接汇入DevOps。

2.2.2 与持续部署的关系

有时候,持续交付也与持续部署混淆。持续部署意味着所有的变更都会被自动部署到生产环境中。持续交付意味着所有的变更都可以被部署到生产环境中,但是出于业务考虑,可以选择不部署。如果要实施持续部署,必须先实施持续交付。

2.2.3 原则


图2.5 持续交付流水线示意图

持续交付将部署流水线(deployment pipeline)这个老生常谈的概念看作一种简单的防错方法:一系列的确认。通过这些确认,一款软件必须遵循特定的路径来发布。每当有变更被提交到源码控制仓库中时,代码要首先进行编译(有必要的话),然后由构建服务器进行打包,接着由一些不同的技术来进行测试,最后才可以将代码标记为可发布的(releasable)。

在一个持续交付环境中工作时,习惯了漫长开发周期的开发人员或许需要改变他们心态。任何代码提交在任何时间点都可能会向客户发布,明白这一点是很重要的。对于在早期提交尚未准备好让用户使用的代码,类似功能开关这样的模式是很有用的。使用NoSQL可以消除持续交付工作流中的数据迁移和模式变更的步骤、手工操作步骤以及异常情况。对于代码隔离的其他有用的技术——例如代码分支——在持续交付工作流中并未过时,但是必须加以修改以适应持续交付的原则。例如,运行多个长生命周期的代码分支是不实际的,因为一个可发布的产物如果要经历流水线的所有阶段,它必须从一个单独的分支中构建而来。

2.3 持续部署


图2.6 持续部署示意图

如图所示,持续部署与持续交付之间的差异就是前者将部署自动化了。

在持续交付的实践中,交付的目标是QA,但是实际上,软件最终是要交付到客户手上的。在SaaS领域里,持续部署采用得比较广泛,因为服务比较容易做到静默升级。

采用持续部署的前提是自动化测试的覆盖率足够高。

采用持续部署的好处是能减少运维的工作量,缩短新特性从开发到实际交付的周期。

3 代价与好处

3.1 代价

  1. 构造自动测试用例会耗费大量的工作,而且要去覆盖不断增加的新功能点,也要随着代码的改动去修改测试用例。
    • 测试被视为软件开发的一种最佳实践。无论是否采用持续集成,在一些方法论里——例如测试驱动开发——自动化测试都是必不可少的一部分。
    • 采用持续集成的时候也可以不使用任何测试用例,但是在发布一个产品之前的质量保证(Quality Assurance)成本就会变得很高,因为一切都要频繁地采用人手操作。
  2. 要花费精力去部署一个构建系统,这有时候会比较复杂,难以灵活地修改
    • 然而,有大量的开源的持续集成软件项目,选择很多
  3. 如果项目比较小,或者包含了未经测试的遗留代码,持续集成的价值就并不是那么大
  4. 持续集成所增加的价值依赖于测试的质量以及代码实际上的可测试程度。
  5. 团队规模大就意味着代码会非常频繁地提交到集成队列里,跟踪提交就会比较困难,并且排队进行构建会减慢每个人的工作,如果构建失败,整个开发团队必须停下手里的工作,立刻修复他们的错误。
    • 但是持续集成是可以灵活变通的,不一定要在一天里多次构建,可以配置每天下班前半小时自动构建,有错误的话开发人员可以在下班前进行处理;或者每天凌晨自动构建,第二天早上开发人员一上班就能看到构建的结果报告,然后进行处理。
  6. 一天里有多个提交和合并,一个新功能的部分代码能轻易地推送上去,在新功能完成之前,集成测试都是不通过的。

3.2 好处

  1. 能快速发现错误和定位错误
  2. 避免在发布日期大家都在检查自己有冲突的版本,造成混乱
  3. 当单元测试不通过或者出现bug的时候,如果开发人员需要回滚代码,只会造成少量的改动丢失,因为集成是频繁的。
  4. 防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
  5. 无论对于测试、demo还是发布的目的或需求,总是有一份当前的构建可用。
  6. 频繁的代码检查驱使开发人员编写模块化的、低复杂度的代码。

4 具体实践

4.1 工具

名称 授权 价格 Git支持 Docker支持 自动测试 备注
Jenkins MIT 免费 不支持 不支持 需插件支持
GitLab CI MIT 免费 支持 支持 需自配测试服务
Phabricator Apache 2.0 免费 支持 Facebook出品
Travis CI 免费 支持 支持 不支持私有部署
Bamboo 收费 支持 支持 支持
Codeship 免费/收费 关联Github, GitLab 支持 不支持私有部署
CircleCI 免费/收费 关联Github 支持 支持大部分测试框架 不支持私有部署
Hudson Eclipse Public License 1.0 免费 需插件 需插件

其实这里的支持,意思应该是直接的支持,例如Jenkins,其实和git结合也很简单,通过脚本就可以实现。

5 参考资料

  1. Maven实战(五)——自动化Web应用集成测试
    http://www.infoq.com/cn/news/2011/03/xxb-maven-5-integration-test
  2. 单元测试 - 维基百科
    https://zh.wikipedia.org/wiki/%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95
  3. Integration testing – Wikipedia
    https://en.wikipedia.org/wiki/Integration_testing
  4. Continuous integration – Wikipedia
    https://en.wikipedia.org/wiki/Continuous_integration
  5. Continuous integration | ThoughtWorks
    https://www.thoughtworks.com/continuous-integration
  6. 另一种声音:持续集成已死
    http://www.infoq.com/cn/news/2014/10/continuous-integration
  7. 持续集成是什么?
    http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html
  8. 25 best continuous integration tools as of 2017 - Slant
    https://www.slant.co/topics/799/~best-continuous-integration-tools
  9. Bamboo vs Jenkins Comparison | Atlassian
    https://www.atlassian.com/software/bamboo/comparison/bamboo-vs-jenkins
  10. 通过Docker容器运行持续集成/持续部署
    http://dockone.io/article/468
  11. Continuous Integration, Deployment & Delivery with Codeship
    https://codeship.com
  12. 谈谈持续集成,持续交付,持续部署之间的区别
    http://blog.flow.ci/cicd_difference/
  13. Continuous delivery – Wikipedia
    https://en.wikipedia.org/wiki/Continuous_delivery
  14. Build automation – Wikipedia
    https://en.wikipedia.org/wiki/Build_automation
  15. List of build automation software – Wikipedia
    https://en.wikipedia.org/wiki/List_of_build_automation_software
  16. Software development process - Wikipedia
    https://en.wikipedia.org/wiki/Software_development_process
  17. System integration - Wikipedia
    https://en.wikipedia.org/wiki/System_integration
  18. Continuous Integration - Martin Fowler
    https://martinfowler.com/articles/continuousIntegration.html#PracticesOfContinuousIntegration
  19. Integration Testing - Software Testing Fundamentals
    http://softwaretestingfundamentals.com/integration-testing/
  20. Practical continuous deployment: a guide to automated software delivery
    https://www.atlassian.com/blog/continuous-delivery/practical-continuous-deployment