关于CI的服务器与最佳实践,这里有一些思考
CI是敏捷和DevOps关键性的一步,数人云最近给大家分享了《致:正在选择CI平台的你(10大要素需把控)》今天的文章将讨论如何使用CI服务器创建高质量产品以及相关扩展插件和指标度量。最后针对业内标准规则进行了自我思考。
CI服务器
拥有自动化的构建系统,是实现CI唯一强制性要求。但在实践中,除了自动化构建系统外,还可以安装和配置“CI服务器”。
概述
CI服务器是大脑,在幕后工作,无缝地将各种行业标准实践整合到CI实现过程中。
下图是个典型的CI工作流,包含6个阶段,从代码检入开始,直到向企业内不同的人员提供反馈信息。
启动过程
通常,开发人员将代码签入源存储库时,CI流程就会启动。
CI工作流也可以通过其他方式触发,下面列举几个方法——
- 手动:用户通过CI服务器管理控制台或脚本手动执行时。
- 计划:预先配置的时间表,如某个夜间构建。
- 触发:每次提交到源代码版本控制系统。
夜间构建通常会对代码执行更大检查和更久时间。
获取最新代码
流程的第二步,CI服务器从源控件获取最新代码,可以通过轮询或推送机制实现。
在轮询机制中,CI服务器配置为源控制服务器,不断地在固定时间间隔内对给定的位置进行轮询,检测任何新的代码更改,一旦检测到,会从源控制服务器下载最新的代码副本到磁盘上。
推送机制中,源控制系统由一个:“钩子”提供给CI服务器,当开发者向提交更改时,调用“钩子”,并让CI服务器了解更改是可用的。
构建
源代码一般独立于构建,即必要的构建脚本与源代码共存,最新的代码在前面步骤中被详细地获取,绑定的脚本会触发构建。
对于Java项目,通常构建自动化脚本使用Maven或分级。使用不支持内置依赖管理的构建系统并不常见,在不需要依赖管理的情况下,Make 使用了在Unix系统中广泛使用的构建机制。
有很多构建工具可以使用,Wiki列出了一些:(https://en.wikipedia.org/wiki/List_of_build_automation_software)
执行测试
在此过程中执行单元测试和集成测试,这些测试和代码一般捆绑在一起。
基于Java的系统,测试使用类似于Junit的测试框架执行,允许对测试依赖项进行简单的模拟。
某些编程技术自带测试运行器框架,如Spring的Spring Junit运行器、Java EE中的Arquillian等。
在CI服务器上运行测试有以下几点:
曾经遇到过测试在一台机器上传递,但在另一台上失效的情况吗?通过在集中的服务器上运行测试,消除了测试环境的问题。
测试会立即执行每个更改,若有代码破坏预期行为能被快速发现。
很多团队并行处理特性,甚至团队成员在全球范围内分布,每次开发者向存储库提交代码,任何故障都能即刻识别和修复。
结果
CI过程的最后有两种结果:构建和测试失败或通过。
每个嵌入都是经过验证的,并且确保不会破坏现有的代码,代码被合并到主干中,会被构建和测试,允许减少主线代码被破坏的时间。
一般情况下,CI服务器失败会发送电子邮件(给整体团队或只负责以前签入的人),可以快速看到失败的状况,并且尽快采取纠正措施。
CI服务器还凸显了最近构建的状态,以及最近几个构建在红-绿-琥珀光指示器上的状态,例如,Jenkins使用了下面的构建指示器:
CI服务器插件
上一节介绍了CI的基本工作流程,但大多数企业会使用它完成更多任务,通过安装各种插件扩展CI服务器,开拓行为,下面介绍一些常用的扩展:
技术债务与代码质量
在应用中引入的任何变化都会增加系统复杂和无序性被称之为“技术债务”,当新代码引入系统时会增加技术债务,如果被忽视太久,越来越多的新功能被紧凑的最后期限所填满,此时想要在可控范围内几乎不可能,这将对应用的生产效率和可维护性造成负面影响。
迭代开发、自动化检测、和CI的使用来监控每个签入技术债务是控制技术债务的唯一方法。
如SonarQube工具,采用识别代码质量和跟踪技术债务的开源平台,可以轻松地集成到任何CI服务器中,为技术债务提供实时数据。
其不仅仅衡量技术债务,也度量并涵盖了代码质量的7个核心:
SonarQube在简单的WEB中展现分析结果,是非常有用的工具,不仅是开发者,对于管理其他非技术的人也是如此。
了解更多请参考:
语义
对于开发者来说,引入CI在另一个重要的方面非常具有价值——可以衡量代码质量——语义及常见的反模式。
静态代码工具可以作为CI过程的一部分,以深入了解代码的健康度,历史数据也可以存储,从而在一段时间内提供质量度量。可以为两个提交作比较,确定每个提交的债务度量标准。
很多工具可以静态地分析代码并计算质量度量的多样性,同时可以被集成到CI服务器中自动运行,例如:
- Checkstyle
- Findbugs
- Sonar
代码评审
开发者在特性分支上工作,并尽可能频繁地提交代码。
代码分析工具(如:Sonar)可以在CI服务器中执行自动代码检测,然后每个开发者负责处理提交时生成的评论注释。
自动代码审查基于一组预定义的规则,是发现潜在技术问题很好的方法,这些规则可以从主流的开发语言中下载。
解决了自动化评审意见,会进行同行代码评审,捕捉那些不能用自动化审查的问题,如验证业务需求、体系结构问题、代码易读性、扩展性等。
如果有一定数量的人没对代码进行审查,CI服务器可以配置成阻止对主线分支任何的提交,常见办法是在主线分支中强制要求每个提交至少2个审核员,Java的项目中,常见工具是Gerrit。
Headless Testing
若想在CI服务器上运行UI测试,必须依赖于Headless(Headless:在缺少显示屏、键盘或者鼠标时的系统配置)测试,因为浏览器没有显示,这意味着要在没有图形用户界面的情况下运行UI测试,需要类似于流行WEB浏览器的Headless浏览器,通过命令行界面执行,并具有UI元素的内存模式。内存中的模型用于模拟UI交互,如,在UI中单击一个按钮即可模拟内存中的模型对象。
Selenium测试工具可以用于无头测试,如Phantom JS的Headless浏览器也被广泛应用,在构建中引入这些测试,CI服务器能够验证失败的UI。
安装CI服务器
CI服务器主要有三种:
- 独立
- 托管
- 私有云
独立的CI服务器安装在一台机器上进行,通常用于小型项目或少于10人的开发团队。
托管的CI服务器可以在公有云上使用,大多数情况下,都与CI服务器可以访问的基于云的源控制系统相连接。这些公司都不希望维护CI或无法维护的公司使用,对于中等规模的团队,是非常可行的选择。
私有云是想要完全控制和基于安全考虑的大公司使用的,多个CI服务器代理配置在高性能服务器的集合中,以支持数百个开发者繁重工作负载。
一些被广泛使用的CI服务器:
- Jenkins
- Travis CI
- TeamCity
- CruiseControl
CI最佳实践
Martin Fowler(国际著名面向对象分析设计、UML、模式等方面的专家,敏捷开发方法的创始人之一)在他关于CI的白皮书中提到了为任何CI设置的一些关键实践。这些建议已经成为多年来CI的最佳实践。
这里将根据文章作者的见解来审视每一种最佳实践。
维护单个源存储库
“提倡使用版本控制系统实现项目的源代码,所有与构建项目相关的都应存放在存储库中,在此实践中,约定系统应该从一个新的签出中构建,不需要额外依赖项,极限开发倡导者Martin Fowler提到,在使用工具支持分支的情况下,应尽量减少分支的使用,更倾向于进行集成而不是同时维护多个版本的应用,主线(或主干),应该是应用的工作版本。”
提示
原则并非一定按照字面上只需一个单一的存储库,关键是拥有在存储库中构建项目所需的所有相关工件,该项目应该从一个新的签出中进行构建,而不需要额外的依赖项或手动步骤。
“项目”的定义取决于自身,如果代码库很小,或者代码所构建的逻辑模块组件,那么就意味着是完整可交付的产品。
观点
实践中建议不要在版本控制系统中使用分支,相反,建议只开发项目的某个分支,且让其一直处于开发阶段。
本文作者不同意这种说法,在多数企业中,有许多分支需要同时并行开发,企业需要支持产品的前一个版本,修复BUG,而其他团队成员则开始着手下一个版本的工作,所以需要在代码库中使用多个分支。
自动化构建
“单个命令应该具备构建系统的能力,许多构建工具如Make,已经存在多年,其他近期的工具经常用于CI环境中,构建的自动化应该包括自动化集成,包括部署到类似产品的环境中,在许多情况下构建本本不仅编译二进制文件,还生成文档、网站页面、统计信息和分发介质(如Debian DEB、Red Hat RPM或Windows MSI文件)”
提示
构建的自动化应该包括:编译代码、执行单元测试和集成测试,以及许多工具——代码质量检测、语义检测、测量技术债务等,多数现代构建工具都支持这些额外集成,并且用于开发CI环境。
在实际项目中,不同的团队负责开发系统不同部分,都有自己的存储库,因此这种情况几乎不可能发生,将整个产品进行自动化构建没有必要,为系统单独部分开发构建自动化就足够。
观点
定义CI的目的除了自动化构建过程外,是否还有投资CI的意思?作为CI的一部分,打算度量哪些标准?很多人将CI设置看做开发者的工具。
CI不是敏捷或DevOps,只是整体过程的工具之一,敏捷或DevOps已经超越了开发技术层面,升华成一种文化。
构建自测
“构建代码后,所有测试都应运行以确认它如开发者的预期一样。”
提示
代码至少要包含单元测试,如Junit框架可以模拟依赖关系。
特定组件与其他模块是不应该交互的,要确保独立运行。
观点
单元测试应该测试行为,而不是实现细节,例如,不要去在乎如何计算汽车的速度,只要使用的方法和工具是正确的即可。
许多测试框架允许断言模拟对象,如测试模拟对象是否被调用,是否在特定的参数中传递等,除非测试实现本身是主要关注点,否则应将其弱化。
保持快速构建
“构建需要快速完成,因此如果集成有问题,会很快被识别。”
提示
- 应该以更快地执行测试为目标,所以需要做比其他类型更多的单元测试。
- 避免在单元测试中使用数据库,如果可以,也要必满在集成测试中使用。通常需要集成测试使用另一种数据源,指向内存中的数据库。如果不可避免,每次测试前都需刷新数据库,确保数据处于已知状态,并且测试会以一致的数据开始。
注意
不要依赖大量的UI测试,其非常脆弱,需要大量维护,建议使用如Selenium类似的UI测试框架来缓解UI测试的一些问题,如屏幕上更改UI元素的位置、处理UI事件等。
自动化部署
“很多CI系统允许在构建完成后运行脚本,可以编写一个脚本将应用部署到每个人都可以查看的实时测试服务器上,此种思维方式进一步发展是CD(持续部署),要求将应用直接部署到生产中,需要额外的自动化防止BUG和回滚。”
观点
不是所有的项目都需要自动化部署,如果生产站点是由同一家公司托管,那么在CD中投资更加有利,CD是CI的下一个逻辑步骤。
不是所有提交的结果都是可交付的产品,在敏捷社区中每个构建都是可交付的产品是常见的误解,可交付的产品与工作应用是不同的概念。
原文作者:Deepak Karanth
原文链接:https://dzone.com/articles/continuous-integration-part-3-best-practices