持续集成作为XP的实践之一,很早就被广泛地实践。在软件开发企业在进行DevOps实践时,持续集成扮演着一个重要的角色。这篇文章将会从持续集成的目标/价值/原则等方面进行阐述和总结,希望对企业的DevOps落地实践提供一些切实可行的帮助和建议。

    什么是持续集成
    提到持续集成,很多人会立即想到Jenkins。但实际上远在Jenkins之前,持续集成作为敏捷的一种实践已经得到了广泛地推广,比如在极限编程XP的最佳实践中,持续集成也被列入其中。

    XP的最佳实践

    XP是敏捷的重要践行之一,而持续集成则是XP的最佳实践之一

    项番 最佳实践(中文解释) Best Practices(English)
    No.1 计划游戏 Planning Game
    No.2 小型发布 Small Release
    No.3 系统隐喻 System Metaphor
    No.4 简单设计 Simple Design
    No.5 测试驱动 Test-driven
    No.6 重构 Refactoring
    No.7 结对编程 Pair Programming
    No.8 集体所有权 Collective Ownership
    No.9 持续集成 Continuous Integration
    No.10 每周工作40小时 40-hour Week
    No.11 现场客户 On-site Customer
    No.12 编码标准 Code Standards
    Martin Fowler的解读
    Martin Fowler先生在很久之前就对持续集成进行了解释,他认为持续集成是:

    Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible.

    从中我们可以简单有如下理解:

    第一:持续集成是一种实践而非工具。
    第二:”集成”说明了应用场景一般在于多人的团队作业。
    第三:”持续 “说明了频度对此种实践非常重要,至少要实现每人每天一次的集成目标(请注意Martin Fowler撰写此文是10年前)
    第四:持续集成包含自动编译和测试以使得尽快地发现集成中出现的错误。
    持续集成的价值
    持续集成的实践能够带来很多好处,比如

    价值 说明
    降低风险 持续集成使得问题能够在尽可能早的阶段被发现,从而提前对应,降低了集成的风险
    减少rework 自动化以及流程上的改善使得过去不规范或者手工的作业规范化或者自动化,减少了重复性工作,降低了成本
    提高速度 构建自动化以及测试自动化等节省了过去靠手工作业所需要的等待或者实施时间,提高了速度
    提高可视性 通过统一方式对项目状况进行把控,同时可以确认到质量趋势以及项目间的比较,用于辅助决策
    增强信心 持续集成使得产品的质量得到很好地提升,使得团队的自信心得以增强。
    在软件开发中,越早发现问题,修复的成本越低。持续集成能够带来很多收益,最重要的收益之一还是尽早发现可能存在的集成问题并进行修复,这也是软件开发领域比如XP等实践一直在倡导的。而这些也能确实能带来一些广告般的疗效:早发现,早治疗,成本小,效果好。

    实践步骤
    在软件开发中实施持续集成,一般遵循如下步骤

    步骤 详细说明
    Step 1 提交代码之前,保证本地构建和测试成功
    Step 2 单件流实践,保证同一时间只有一个在提交,并使用可视的方式使得其他人知晓,以避免并行提交
    Step 3 提交代码
    Step 4 在持续集成服务器上进行构建和测试并全部通过,否则限时修复或者回滚
    Step 5 完成之后,状态更新,其他人可以提交代码了
    具体实现的方式有很多,有很多商业的工具,比如早期的CC(CruiseControl )等,也有开源的Jenkins,都可以在Step 4上进行辅助,而持续集成的实践则是包含Step 1到Step 5的全部操作,并且不断循环和优化的这样一个过程。

    持续集成可能带来的问题
    持续集成是一种实践,由于牵扯到很多方面,在实际实施的时候并不一定会一帆风顺,相反,如果推行地不好,在初期甚至贯彻整个实践的过程中都有很多问题,比如:

    项番 问题点
    No.1 由于各种情况,很多人不会遵守本地完全构建和测试成功的准则,这使得主线上的代码经常是不可用的
    No.2 单件流实践动辄需要lock整个仓库,在实践中一般会遇到阻力,因为这样意味着别人无法工作.
    No.3 大型项目构建往往很慢,很多时候(比如紧急对应时)没有办法等全编译完成,而部分编译往往会带来问题,而NightBuild又不能及时完成持续集成的需要
    No.4 测试很慢,5分钟完成只是理想情况情况下才会存在,或者只是在新项目中才容易保证。
    … …
    实践原则
    持续集成会遇到很多问题,而一些已经被使用的实践能够引领企业在DevOps落地的时候少走弯路。结合一些实际案例的落地实施,从一些角度列出在实践中应该注意的角度:

    组织文化
    环境一致性
    版本管理
    速度控制
    质量保证
    精益&敏捷&服务连续性
    组织文化
    DevOps的落地并不只是简单的几种工具的组合来实现自动化,必须同时考虑到人和流程的因素,考虑到与之相伴的组织文化,同时定下整体的实践细则。而这些规则的有效实施则需要每一个成员的支持和合作才能达到。

    原则 1: 持续集成实践之稳定策略:确保始终在一个稳定的状态上进行持续集成

    持续集成,前提是能够持续稳定地进行改善。出错没有任何问题,能够尽快修复或者回滚才是关键。

    原则 2:持续集成实践之提交策略:保证构建的成功,制定组织的提交策略。

    保证持续集成流程是实时可用的,比如开发人员需要在本地构建成功之后再提交,确保持续集成尽可能少的失败,而且项目成员都需要意识到他们的行为会对整个项目造成影响。在整体的提交策略上必须明确多长时间之内必须完成等详细要求,比如使用分布式版本管理的跨域团队成员当日下班之前必须提交自己承诺的代码并能保证自己提交的代码不会影响他人。

    原则 3:持续集成之提交原则

    保存在仓库中的最新代码,建议在一定是构建成功并且通过了了所有测试的。结合第2条原则使得提交策略更加完整。但凡提交,必定能成功编译并且测试通过。这样的规则能保证项目成员之间的影响得到很大程度的降低。

    原则 4:持续集成实践之提交频度

    开发人员应该经常性地进行代码的提交以触发自动集成,建议每人每天至少一次提交,或者根据项目情况2-4小时进行提交。

    原则 5:持续集成实践之构建目标

    确定持续集成中构建的目标为生成可发布的产品。比如二进制制品或者镜像文件为目标

    原则 6:持续集成实践之触发方式

    持续集成的触发方式可以根据项目需要使用如下两种方式进行触发:

    定时触发
    按需触发
    环境一致性
    “It Works on my machine”, 这是实际开发时候经常会出现的问题,问题的核心在于环境的一致性。关于环境一致性有很多需要考虑的问题,比如:

    本地开发环境的一致性*
    测试环境的一致性
    设定的一致性
    二进制制品的一致性

    原则 1:本地环境之更新策略

    随着持续集成的不断进行,本地环境需要不断地更新以保证本地环境尽可能地保持一致性,以降低不必要的合并等操作。所以开发人员需要经常性地更新持续集成后的最新代码到本地环境,建议每天至少一次,以降低不必要的合并等操作所带来的rework。

    原则 2:持续集成环境一致性策略

    使用专门的持续集成服务器以保证持续集成环境的一致性,一旦代码提交之后,通过使用统一的持续集成服务器上的环境以保持构建和测试的一致性。

    原则 3:测试环境一致性策略

    测试环境尽可能跟生产环境保持一致,从软硬件到IP或者Port的设定,但是在现实中往往有很多限制,比如生产环境设备很昂贵,很难保证生产环境和测试环境完全一致。另外还有一些是无法做到完全一致,但是整体的策略是要尽可能保持一致,同时清晰地知道测试环境和生产环境的不同。
    另外,虚拟化或者容器化是目前解决这种环境不一致性的一个重要方式,虽然还有很多限制和依赖,但是至少给出了一个可能有解的趋势。

    原则 4:环境构建之Infrastructure as code策略

    使用Infrastructure As Code的方式,可以从零开始创建构建环境,随着虚拟化技术和云计算的普及,硬件标准化带来的好处越加明显。环境即代码,环境的变更即代码版本的管理,使用这种策略使得环境的构建和扩展以及故障的对应变得非常便捷和安全。结合原则1-3,保证了一致和稳定的环境进行构建和测试。

    原则 5:结合IDE使得自动集成更有效率。

    使用IDE,结合各种plugin使得实际工作在一致的前提下更有效率。

    版本管理
    不仅仅是源代码进行版本管理,测试用例/设定文件/环境构建脚本等所有的一切能够进行管理的进行管理,有效合理利用版本管理工具和二进制制品管理工具能能够带来很好的效率。

    原则 1:版本管理对象

    将能够纳入管理的全部文件进行版本管理,比如

    代码
    设定文件
    测试用例
    环境构建脚本

    原则 2: 版本管理整体策略

    基于Trunk的开发在持续集成中能够起到很好的作用,调查也表明高绩效的团队更多的进行小批量的开发而不是使用长期存在的特性开发的分支。版本管理整体遵循如下策略:

    分支策略:同时实际进行的分支尽量少于三个。
    合并频度:至少保证每天合并代码到Trunk
    分支存在时间:分支或者fork存在时间尽可能短,通常推荐为一天之内
    原则 3:二进制制品管理策略

    由代码生成的二进制制品,秉承如下原则和策略:一次构建,处处使用。这样保证测试和发布都是使用同一个二进制制品,二进制制品的一致性能够得到保证。

    原则 4:脚本和设定文件的管理策略

    除了代码和二进制制品之外,脚本和设定问文件等等经常容易被忽视的文件也需要有效管理起来,使用如下原则和策略:

    部署脚本和设定文件分开,设定文件体现不同环境的区别
    不同环境的部署使用相同脚本
    脚本和设定文件均进行版本管理
    秉承Infrastructure As code的观点,将环境创建代码化同样进行版本管理
    原则 5:版本管理和二进制制品之间的关联策略

    使用版本管理工具对代码等文件进行管理,使用二进制制品管理工具管理构建生成的二进制制品,为了更好的进行管理和追踪,需要建立版本管理和二进制制品之间的追踪机制。

    速度控制
    就像XP的10分钟构建实践那样,速度对于持续集成非常重要,因为长时间的构建往往意味着等待的发生,等待作为精益实践中努力消除的一种浪费,DevOps实践同样会努力消除。因为消除这种等待之后,即使一分钟的改进,在每个开发者的每一次提交中都会从中获益,所以速度是持续集成实践中非常之重要的一个因素。

    原则 1:并行构建提高速度

    保证构建的速度,整理组件的依赖关系,对没有依赖关系的进行并行构建以保证速度

    原则 2:全部构建和差分构建

    保证构建的速度,整理组件依赖关系,设定全部构建策略和差分构建策略以保证大型项目的构建速度。

    原则 3:分阶段测试策略

    分阶段测试,对持续集成需要测试通过的测试内容进行自定义裁剪,以确保在速度和质量之间的平衡,整体测试质量通过后续的测试保证。

    原则 5:限时结束的单体测试策略

    类似XP的10分钟构建实践那样,使用限时结束策略,通过使用stub或者mock方式,保证单体级别的测试速度10分钟或者5分钟执行完毕。

    原则 6:持续集成的缓冲策略

    使用分支进行缓冲,使用二级结构保证持续集成的Trunk无论何时都可用。

    原则 7:使用API方式进行测试而不是GUI

    虽然使用Selenium和Robot框架能够简单地实现GUI的测试,考虑到维护的成本以及耦合的程度等因素,在有选择的情况下推荐API方式进行测试。

    质量保证
    DevOps打通了开发和运维之间那堵无形的”墙”,敏捷使得开发的效率提高可供部署的软件速度加快,而追求稳定的运维则会压低速度,这是表面上的矛盾之一,其实更多在于相互之间的信任,而信任的来源之一是稳定的质量,质量的保证通过测试,完整有效的测试非常重要。而在测试上所消耗的成本与时间与质量之间的平衡也需要Dev和Ops进行权衡和调整。

    原则 1:单体级别保证测试驱动

    使用xunit工具保证代码是通过单体级别的测试,覆盖率根据组织情况进行自主设定。

    原则 2:已知缺陷覆盖策略

    在实际的项目中,很难保证每次修订缺陷的时候都能很好地进行系统级的重构,所以在在出错的地方再次发生问题的可能性很大,因此至少保证对已知缺陷的覆盖能够保证一定程度上的质量不会发生倒退。每产生一个bug,在自动化测试中增加关于覆盖这个bug的测试用例。

    原则 3:测试用例的持续更新

    随着代码的不断更新,自动化测试用例也需要不断的更新和维护以保证

    原则 4:测试驱动与ROI的折衷策略

    单元测试以及功能性或者非功能性的自动化验收测试是DevOps推荐的方式,每个组织都知道TDD能够带来很多好处,但是在实际项目中,测试用例测更新和维护所带来的成本压力也是TDD推行受阻的最重要原因。根据业务功能的重要性以及能够带来的价值,来判断投入测试用例所能带来的收益,根据受益程度逐步推行TDD,则是一种行之有效的方法。

    精益&敏捷&服务连续性
    提高速度,消除浪费,保证服务连续性,持续集成在实践时也需要参考这些重要的来自于精益/敏捷/ITSM的着眼点。

    原则 1:使用单件流原则

    单件流的推动能够带来很多收获,在单件流的推动上,推荐以下策略:

    在流程清晰化和合理化之后再推动自动化,单件流要建立在清晰合理的流程之上
    不要在失败的持续集成状态上进行代码提交
    原则 2:使用JKK原则

    结合JKK进行实践,确保持续集成的100%标准达成,一旦失败立即限时修复或者回滚

    原则 3:透明原则

    透明/可视能降低沟通成本,使得每个人都能清楚的了解发生的现状,主要的原则如下:

    每个人无需过多说明即可清楚知道自己要做什么,整体的状态如何,建议推动看板的实践
    持续集成的整体过程应该被实时监控的,可以通过整合DevOps工具链实现
    持续集成整体的KPI能够进行监视,以提供现状把握能力以及趋势判断能力
    原则 4:测试环境的按需分配

    测试环境往往是开发中的瓶颈之一,结合其他原则,保证每个人都可以进行按需取得环境和按需进行部署,具体如下:

    每个人都能按需进行环境的创建或者申请到可以使用的测试环境
    出错之后每个人都可以取到最新可以执行的二进制制品和上一次可以正常执行的二进制制品,并将其发布到自己可以使用的测试环境中,以便快速地辅助问题定位。
    原则 5:恢复策略

    DevOps提倡Fail Fast, 但是并不意味着毫无策略,相反,JKK的引入使得质量得到保证,自动化测试使得质量和速度得到平衡,敏捷的小步快跑降低了每次部署的影响程度,在这些前提之上的Fail Fast,已经是有了很大的信心的基础了,而恢复策略则是保证服务连续性的另外一条安全带,每次部署都需要考虑回滚的情况才不至于意外频发,具体可以参照如下原则:

    每次部署都附带回滚或者恢复方案
    每次部署都考虑到所有可能出现的问题并给出想定的对应策略
    回滚和恢复方案在生产环境使用之前需要在测试环境进行可行性验证
    可以参照Capistrano的实现方式实现自己的回滚,结合对资源的消耗以及维护的复杂程度,建议能回滚至上次可运行版本即可
    在恢复策略得到验证的基础之上,推行Fail Fast, 并且视每次发生的问题作为一次组织可以学习和成长的机会。
    总结
    持续集成不是简单的一种工具的引入,就像Martin Fowler所说的那样,持续集成是一种实践,而Jim Shore也认为持续集成是一种态度,而不是一种工具。甚至,对持续集成在自动还是手动方面,Jim认为手动可能更为有效,他在2005年就曾整理过持续集成中可能会出现的问题,至今读来仍然有很多借鉴之处。
    但是,随着各项技术和实践的成熟以及随着持续集成等观念的深入人心,数字转型的企业通过思考和定位组织目标,结合DevOps实践原则,在实施的过程中保持学习和成长,最终会完善出一条属于自己的DevOps之路。

    参考文献
    https://www.martinfowler.com/articles/continuousIntegration.html
    http://www.jamesshore.com/Blog/Continuous-Integration-is-an-Attitude.html
    ————————————————
    版权声明:本文为CSDN博主「淼叔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/liumiaocn/article/details/76488044