回到主页

万字长文 | 持续集成应有的标准规范

· 操作指南

作者

丁浩宸,优维科技资深DevOps专家,解决方案专家。曾就职于华为通讯事业部,具有多年核心网解决方案测试经验和运维研发经验,对 DevOps 项目实践落地有丰富经验。在此处添加文本段落

概述

持续集成是软件工程领域中的一种最佳实践,即鼓励研发人员频繁的向主干分支提交代码,频率为至少每天一次。每次提交都触发完整的编译构建和自动化测试流程,缩短反馈周期,出现问题第一时间进行修复,从而保证软件代码质量,减少大规模代码合并的冲突和问题,让软件随时处于可发布状态

broken image

成熟度评估

首先针对企业当前的持续集成(集成服务、集成频率、集成方式、反馈周期)做一次成熟度评估,了解所处的持续集成阶段,并有针对性的制定持续集成目标和改造计划。在此处添加文本段落

broken image

持续集成前提条件

在开始持续集成之前,需要先做好以下事情,以让持续集成能够更加有效。

1. 频繁提交

研发人员至少每天一次的将代码提交到主干上。

2. 全面的自动化测试套件

单元测试用于单独测试应用程序中某些小单元的行为(比如一个方法、一个函数, 或一小组方法或函数之间的交互)。

单元测试应该运行得非常快,即使对于一个大型应用来说,整个单元测试套件也应该在十分钟之内完成。

3. 保持较短的构建和测试过程

理想情况下,提交前的预编译和测试过程,以及持续集成服务器上的编译和测试过程应该都能在几分钟内结束。我们认为,十分钟是一个极限了,最好是在五分钟以内,九十秒内完成是最理想的。

测试需要划分阶段,第一个阶段用于编译软件,运行所有类级别的单元测试,并创建用于部署的二进制文件。这个阶段叫做“提交阶段”。

第二个阶段应该利用第一个阶段所生成的二进制文件进行验收测试、集成测试。假如你有性能测试的话,也要一并运行。

4. 管理开发工作区

开发环境的管理目标:

  1. 当开发人员刚开始新任务时,应该总是从一个已知正确的状态开始。

  2. 开发人员应该能够运行构建、执行自动化测试,可以在开发机上部署其开发的应用程序。只有在特殊的情况下,才应使用共享环境开发。

  3. 在本地开发环境上运行应用程序时,应确保所使用的自动化过程与持续集成环境、测试环境、生产环境中一致。

达到这一目标,需要做以下事项:

  1. 第一个先决条件就是细心的配置管理,将源代码、测试数据、数据库脚本、构建脚本和部署脚本放在版本控制库中。当编码开始时,应该以它们“最新的正确版本”作为起点。“最新的正确版本”是指那个在持续集成服务器上最近一次通过所有自动化测试的那个版本。

  2. 其次是确保第三方依赖的配置管理(开发中所用的库文件和组件)的版本与正在开发的源代码的版本是相互匹配的。对于大部分项目来说,其所依赖的第三方库文件的版本不会经常发生改变,所以最简单的方法就是将这些库文件随你的代码一起提交到版本控制库中。

  3. 最后就是确保自动化测试(包括冒烟测试)都能够在开发机上运行。让开发人员于每次提交前在自己的开发机上将应用程序运行起来,并在其上跑一遍冒烟测试,可以大大改善应用程序的质量。

持续集成时间理论

持续集成工作流一般是研发先进行提交前操作,然后提交变更代码到版本管理库。促发持续集成服务器拉取代码后进行构建和测试,将构建和测试结果反馈回开发,最后促发自动部署(持续发布的内容)。本章以工作流为顺序介绍持续集成中的最佳实践。

broken image

1. 提交前工作

首先查看当前构建服务器上是否有构建在运行。如果有的话,你要等它运行完。如果它失败了,你要与团队中的其他人一起将其修复。

一旦当前构建完成且测试全部通过,开发人员需要从源码管理系统里签出或者克隆最新的代码到本地开发机器。随后,开发人员继续基于主干分支创建出一个新的功能分支。该分支将专门用于引入仅在新功能范畴内的更改。

建议开发人员经常保持他们的代码和主干代码分支的内容同步。该步骤将会把主干分支的变更带到功能分支,所以除了自己的功能代码之外,开发人员总是使用主干分支代码的最新副本。一般来说,每个开发者至少每天将主干分支同步一次到他们的功能分支。

开发人员开发新功能不仅仅有功能的变动,还应该包括新代码的单元测试。

开发人员必须在在自己的开发机上执行构建脚本,运行测试,以确保在你机器上的所有代码都工作正常。如果这些测试或者代码本身编译失败的话,必须修复这些集成问题。

broken image

2. 提交变更

当开发人员准备提交时,应该从版本控制库中签出代码,更新一下本地的项目副本,然后做一下本地构建,并运行提交测试。只有当全部成功以后,开发人员才能将代码提交到版本控制库中。

提交过程是一件轻量级的事儿,保证可以每隔二十分钟左右提交一次。但同时为保证新增的代码的确是按期望的方式运行的,提交前需确保在本地运行一次提交测试,做一下健全性检查(sanity check)。

提交前运行本地测试有以下两个好处:

  1. 如果在你根据版本控制进行更新之前,其他人已经向版本控制库中提交了新代 码,那么你的变更与那些新代码合并后,可能会导致测试失败。如果你自己先在本地更新代码并运行提交测试的话,假如有问题,就会在本地提前发现,提前修复,从而不会令持续集成服务器上的构建失败,不至于影响其他人及时提交。

  2. 在提交时经常犯的错误是,忘记提交那些刚刚新增加的东西到存储库中。如果遵守这个流程的话,当本地构建成功,而持续集成系统中的提交阶段失败了的话,那 么你就知道要么是由于别人与你同时提交了代码,要么就是你遗漏了一部分类或配置文件没有提交到版本控制系统中。

遵循这样的实践可以确保状态一直是正常的。

代码提交入库前可以通过添加代码检查来提高代码质量。将静态分析工具和人工审核配合实施,自动化的审查工具扩展了以人为之的复查,由于代码变的越来越长,越来越复杂,所以自动化的审查就变得很有必要。自动化代码审查的优点在于,当进行人工复查时,这个过程会变得更有效,因为代码的底层细节已经通过了扫描检查。人工复查可以关注哪些自动化工具不能处理的方面,注入代码时候满足需求,是否从长远来看易于维护等。

broken image

3. 代码拉取

持续服务器会负责从源码管理拉取最新的代码。这可以借助poll或者push机制实现。

使用poll机制的话,持续集成服务器会配置一个源码管理服务器的位置,以及它的安全证书。它会根据一个时间间隔定期地轮询指定位置,以检测是否有发生任何新的签入以及代码变更。一旦检测到有变更,它会从源码管理服务器下载代码最新的副本到本地磁盘。

使用push机制的话,源码管理系统会配置一个“钩子”指到持续集成服务器。当开发人员提交了一个变更到仓库时,之前配置的钩子将会被调起,而它会让持续集成服务器知道,这里发生了一次变更。

4. 构建

源代码一般是自包含构建的,即持续集成流程所需的构建脚本是放在源码仓库里的。正如在之前的步骤里详细介绍的那样,一旦最新的代码被拉取下来,一个捆绑好的脚本将会被用来触发该次构建。

broken image

构建的类型分成三个层次:为个人的、为团队的、为用户(客户)的。开发者(或结对的开发者)执行私有构建,集成构建将整个团队的工作成果集成起来,发布构建为用户准备好软件。

  1. 私有构建

    如果在你根据版本控制进行更新之前,其他人已经向版本控制库中提交了新代 码,那么你的变更与那些新代码合并后,可能会导致测试失败。如果你自己先在本地更新代码并运行提交测试的话,假如有问题,就会在本地提前发现,提前修复,从而不会令持续集成服务器上的构建失败,不至于影响其他人及时提交。

  2. 集成构建

    在提交时经常犯的错误是,忘记提交那些刚刚新增加的东西到存储库中。如果遵守这个流程的话,当本地构建成功,而持续集成系统中的提交阶段失败了的话,那 么你就知道要么是由于别人与你同时提交了代码,要么就是你遗漏了一部分类或配置文件没有提交到版本控制系统中。

  3. 发布构建

    发布构建准备好发布给用户的软件。持续集成的一个目标就是创建可以部署的软件。发布构建可能在一次迭代结束时进行,或在某个里程碑处进行,它可能包括更全面的性能测试和负载测试,并且必须包括所有的验收测试。

并非所有的构建都是以同样的方式触发的。要根据构建的目标和频率来选择最合适的方式来触发某种构建。触发构建机制有以下4类思路:

  • 用户驱动:由某人手工发起集成构建

  • 定期执行:定期执行的过程由时间驱动,不论是否发生了变更。定期执行的活动适合下班后执行的过程比较合适。

  • 轮询变更:一个进程以固定的时间间隔唤醒,从版本控制库中签出变更。如果检测到变更,它就执行集成构建。所有持续集成服务器都支持某种类型的“轮询变更”机制。

  • 事件驱动:事件驱动与轮询变更类似,但不同的是,构建不是由持续集成工具来触发的,而是由版本控制库触发的,给予事先定义好的变更事件,如果版本控制库检测到变更,它将执行构建脚本。

broken image

5. 构建

在CI流程的这个阶段里,单元测试和集成测试将会被执行。一般来说,这些测试也会被打包到代码里。

针对基于Java ™实现的系统而言,这些测试会通过一个像JUnit这样的测试框架来执行,从而可以轻松模拟它们的一些测试依赖。某些编程技术可能有它们自己的测试运行框架,Spring的Spring Junit Runner,Java EE的Arquillian等。

在CI服务器上运行测试主要有下面这些好处:

  1. 你曾经有没有遇到过这样的场景,这些测试在一台机器上是通过的,但是在其他机器上却失败了?通过在一台中央服务器上执行这些测试,我们可以消除一些测试环境方面的问题

  2. 针对每一次变更都会立即触发执行测试,而且如果有任何新的代码变更打破了预期的行为的话,你能够马上知道

  3. 大多数团队都是并行地在开发着许多功能,甚至可能一些团队成员也是分布在全球各地。每当开发人员提交代码到仓库时,任何故障都可以被识别出来并且立即修复。

broken image

6. 反馈和通知

在CI流程的最后,只可能存在两种结果的其中一种,要么构建和测试失败了,要么通过了。

每一次签入都会被验证并且确保它不会破坏现有的代码。代码在它被合并到主干分支后不久会被构建和测试。这将可以降低主干代码崩溃的频次。

一般来说,CI服务器会配置成在遇到故障时发送邮件(发给团队里的每一个人或者仅仅单独抄送负责上一次签入的相关人员)。通过这种方式,可以快速知晓故障并且尽快采取更正措施。

大多数CI服务器还会突出展示最近一些构建的状态而且最近几次构建的状态还会用红-绿-琥珀色指示灯来标明。举个例子,Jenkins,使用的是如下指示灯来显示构建的情况。

broken image
broken image

Java项目的持续集成实践

1. 环境准备

easyops平台搭建

系统版本:centos 6.5~6.9

推荐配置:

broken image

2. 项目目录规范

broken image

以maven标准目录结构为依据

  1. 源代码管理

    源代码统一放在src/main/java下。

    遵循Java实践,将文件放在以包名为目录名的目录中,每个文件保存一个类。

    生成的任何配置或圆数据都不应该放在src目录下,而应该放在target中。

  2. 测试管理

    将所有要测试的源代码都放在src/test/java目录中,单元测试应该放在与包名相对应的目录中。

  3. 构建输出管理

    在用maven做构建的时候,会把生成的代码、元数据文件都放在项目根目录中一个叫target的文件夹中。这样清除前一次构建结果只需要删除整个目录就可以了。

    可以把测试报告存储在/target/reports目录下。

  4. 库文件管理

    库文件的管理有几种不同的方法。

    · 完全交给工具来管理。

    · 把库文件(包括构建、测试和运行时必须的所有库文件)都提交到版本控制库,最常· · 见的是放在lib文件夹下。

    · 建立组织级的第三方依赖库,将所有项目需要的所有依赖库文件都放在其中。

 

注意事项:

 

上述目录,包管理系统前台默认根据模板自动创建,无需人工创建。

若软件包有特殊类型文件则请建立文件夹存放,不得与已有文件类型混放。

任何文件不得直接存放在软件包顶层文件夹。

 

3. 版本控制服务器搭建

git搭建

 

4. 持续集成服务器搭建

使用easyops平台一键部署java环境、maven环境和Jenkins到持续集成服务器。

Jenkins v2.73.3 ,用于 Linux 平台,和 linux_jdk_1.8 搭配使用,默认启动端口8500,默认密码admin:admin。

broken image

使用easyops平台一键部署sonarqube(服务端)到持续集成服务器

broken image

使用easyops平台一键部署sonarqube scanner(客户端)到持续集成服务器和各开发机。

broken image

5. 流水线配置

配置持续集成流水线。

broken image