原文发表于:https://www.rea-group.com/blog/continuous-integration-and-delivery-pipeline-mistakes/

CI/CD & Pipeline

随着DevOps的理念在众多公司的采纳,CI/CD也渐渐落地。
**CI(**Continuous Integration)持续集成,是把代码变更自动集成到主干的一种实践。CI的出现解决了集成地狱的问题,让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过一系列自动化测试,比如编译、单元测试、lint、代码风格检查。

CD包括持续交付和持续部署。持续交付(Continuous Delivery)指的是团队自动地、频繁地、可预测地交付高质量软件版本的过程,可以看做持续集成的下一个阶段,强调的是无论代码怎么更新,软件都是随时可以交付的;持续部署(continuous deployment)更强调的是使用自动化测试来保证变更的正确性和稳定性,以便在测试通过后立即部署,是持续交付的更进一步。二者的区别是,持续交付需要人为介入,需要确保可以部署到生产环境时,才去进行部署。

图1 持续集成 & 持续交付 & 持续部署

CI/CD Pipeline是软件开发过程中避免浪费的一种实践,展现了从代码提交、构建、部署、测试到发布的整个过程,为团队提供可视化和及时反馈。Pipeline推荐的实施方式是,把软件部署的过程分为不同的阶段(Step),其中任务(job)在每个阶段中运行。在同一阶段,可以并行执行任务,帮助快速反馈,只有一个阶段中所有任务都通过时,下一阶段的任务才可以启动。比如图中,从git push到deploy to production的整个流程,就是一条CD Pipeline。可以利用Pipeline工具,如Jenkins、Buildkite、Bamboo,来帮助我们更方便的实施C/ICD。

图2 CI/CD Pipeline

###CI/CD Pipeline的反模式
虽然有Pipeline广泛的应用,但我们却会听见开发人员抱怨糟糕的Pipeline对他们的伤害,如阻塞开发流程,影响变更的部署效率,降低交付质量。我们收集了项目上经常出现的Pipeline的八大反模式,按照出现频率排序,分别阐述这些坏味道,分析可能产生的原因、影响及解决方式,希望能够减少抱怨,让Pipeline更大程度上提升工作效率。

1. 没有代码化

反模式:Pipeline的定义没有完全代码化,进行版本控制,存储在代码仓库,而是在Pipeline 工具上直接输入shell脚本定义Pipeline的运行过程。

原因:由于早期的CI工具不支持代码化,一直能够保留到现在,没有做重构和升级。

影响:Pipeline的创建和管理都是通过CI工具的界面交互来的,难以维护,因此需要专门的管理员来维护,而有人工操作的部分就会出错,因此会降低Pipeline的可靠性。如果Pipeline因为一些原因丢失就没有办法很快恢复,就会影响交付速率。

解决方案:Pipeline as code这个理念已经提了很多年了,在ThoughtWorks 2016年的技术雷达里就已经采纳了,需要强调的是,用于构建、测试和部署我们应用程序或基础设施的交付Pipeline的配置,都应以代码形式展现。随着组织逐渐演变为构建微服务或微前端的去中心化自治团队,人们越来越需要以代码形式管理Pipeline这种工程实践,来保证组织内部构建和部署软件的一致性。

通常,针对某个项目的Pipeline配置,应和项目代码放在项目的源码管理仓库中。同业务代码一样要做code review。这种需求使得业界出现了很多支持Pipeline工具,它们可以以标准的方式构建、部署服务和应用,如Jenkins、Buildkite、Bamboo。这些工具用大多有一个Pipeline的蓝图,来执行一个交付生命周期中不同阶段的任务,如构建、测试和部署,而不用关心实现细节。以代码形式来完成构建、测试和部署流水线的能力,应该成为选择CI/CD工具的评估标准之一。

2. 运行速度慢

反模式:一条Pipeline的执行时间超过半小时,就属于运行速度慢的Pipeline。(这里的运行速度与交付的产品有关,在不同的项目中,运行时长的限定也有所不同)

很多原因都会导致运行一次Pipeline时间很长,比如:

  • 该并行的任务没有并行执行,等待的任务拉长了执行时间;
  • 执行Pipeline的agent节点太少,或者性能不足,导致排队时间太长,效率太低;
  • 执行的任务太重,相同测试场景被不同的测试覆盖了很多次。比如同样的逻辑在不同测试中都测了一遍;
  • 没有合理利用缓存,比如每个任务里都要下载全部依赖,在构建Dockerfile时没有合理利用layer,每次都会构建一个全新的image。

影响:这是开发人员抱怨最多的一个反模式。敏捷开发模式需要Pipeline快速反馈结果,受这一反模式制约,在特性开发过程中,经常出现开发人员改一行代码,等半天CI的效果。如果出现一个线上事故需要修改一行代码来修复,最终需要很长的周期才能让这一更改应用在生产环境。

解决:不同的原因导致的Pipeline速度慢,有不同的解决方法。比如针对上面的问题,我们可以去:

  • 检查Pipeline的设计是否合理,尽可能让任务并行;
  • 对代码的各种测试深入了解,让测试尽量正交,避免过多的重复;
  • 检查代码中的依赖,合理利用好缓存。包括Docker Image、Gradle、Yarn、Rubygem的缓存,以及Dockerfile是否合理的设计,最大化的将不可变的layer集中的开始阶段;
  • 检查执行构建的节点资源是否充足,能否在任务量大时做弹性伸缩,减少等待和执行时间。

####3. 执行结果不稳定

图3 执行多次结果不稳定

反模式:构建相同代码的Pipeline运行多次,得到结果不同。比如,基于同一代码基线,一条Pipeline构建了5次,只有最后一次通过了。

原因:出现执行结果不稳定的原因也多种多样,比如测试用例的实现不合理,导致测试结果时过时不过;代码中使用了不可靠的依赖源,比如来自国外的依赖源,下载依赖经常超时;由或是在Pipeline运行过程中没有合理设计各个阶段,导致有些任务同时运行冲突了。

影响:Pipeline作为代码发布的最后一道防火墙,最基本的特性是幂等性,即在一个相同的代码基线,执行Pipeline的任意任务,不管是10次、100次,得到的结果都相同。Pipeline不稳定会直接导致代码的部署速率降低。更重要的是,影响开发人员对Pipeline的信任。如果不稳定Pipeline不及时解决,慢慢这条Pipeline会失去维护,开发最后会转向手工部署。

解决:要构建幂等的、可靠的Pipeline,就要分析这些不稳定因素出现的原因。

  • 提升测试的稳定性,比如用mock替代不稳定的源。
  • 采用Pipeline的重试功能,或者采用稳定的镜像源,或者提前构建好基础镜像。
  • 引入Pipeline的插件保证任务不会并行执行。

4. 滥用job处理生产环境数据

反模式:使用Pipeline的定时任务的特性,运行生产环境的负载。比如经常会定期做数据备份、数据迁移,数据抓取。

原因:由于对Pipeline的认识不够清晰,将重要的任务交由Pipeline做。Pipeline一旦有了某个生产环境的访问权限,做这些数据处理相关的任务就很方便,减少了很多人为的操作。

影响:Pipeline是用来做构建、部署的工具,不能用于业务逻辑的执行。由于Pipeline是一个内部服务,他的SLO/SLI必定和生产环境不同,如果强依赖势必影响生产环境的SLO。假如某天Pipeline挂掉了,生产环境就无法得到想要的数据。另外,任务和Pipeline紧密耦合,是我们后面会讨论的另一个反模式。

解决方法:用生产环境自身的工具解决这种数据问题,比如 采用AWS的lambda,定时触发数据处理任务。

5. 复杂难懂

图4 Pipeline的定义逻辑复杂

反模式:Pipeline的定义包含了太多的逻辑,复杂难懂。只有在一条Pipeline运行起来才能知道这里会运行哪些步骤,会将这个版本部署到哪些环境。

原因:Pipeline的代码不够整洁。有人认为Pipeline只是给CI工具提供的,就随意编写,认为能完成指定的工作就够了。

影响:Pipeline的复杂性,会直接提升学习成本。如果想重复执行上一次构建,会花费较长时间。

解决:Pipeline的代码要简洁,把复杂性放在部署脚本或代码侧。通过每个阶段的的标题可以直接了解所要执行的任务。如果存在很多相同的逻辑,可以通过开发Pipeline的Plugin来简化配置。

6. 耦合太高

图5 (左)耦合太高的Pipeline定义 (右)期待的Pipeline定义

反模式:Pipeline跟运行它的CI工具紧密耦合,以至于无法在本地重复相同的步骤。

表现可能多种多样:

  • Pipeline的定义跟构建工具紧密耦合,包含了Pipeline工具特有的参数以及CLI命令。比如在配置中使用BUILDKITE_BUILD_NUMBER,BUILDKITE_QUEUE等等。结果就是本地运行的方式或结果和Pipeline上运行的方式以及结果不一致。
  • 在Pipeline的任务中写了一大段脚本,或者直接使用命令加上一堆参数,以至于在本地想跑测试需要在Pipeline的配置中找命令并且在本地粘贴。
  • 不做环境隔离, 测试,编译,部署等都依赖于运行时环境。可能出现Pipeline 因依赖的软件/库等版本不一致而导致的不一致的情况,通常还很难排查。

影响:因为本地不方便调试,所变更的失败概率会大大增加。如果变更用来修复一个Bug,由于不做环境隔离,会导致故障修复周期拉长。

解决:Pipeline的每个step都用脚本封装起来,脚本里不使用Pipeline工具特有的参数,并且保证本地运行时和Pipeline上保持一致。

7. 僵尸Pipeline

反模式:一条Pipeline年久失修,很久没有执行过,而且最后一次的构建是失败的。

原因:这种反模式通常出现于不再活跃开发的项目上,因此很久没有执行过Pipeline。

影响:Pipeline的结果反应的是一个项目的状态。由于软件产品迭代速度快,这个软件的依赖可能已经发生了巨大的变化,一旦运行,大概率会出错。假如这个项目目前出现了一个事故,需要提交代码,就得先修复项目的Pipeline,才能确保提交修复代码。

解决:针对常年没有提交的Pipeline,我们建议让Pipeline周期的执行,出现问题立即修复。如Github的Dependabot,能保证项目的依赖始终是是最新的,而且能让Pipeline执行,提早发现问题。

8. 需要人工介入

反模式:通常项目上会有一个专职Ops,在项目可以发布的时候手动触发部署流程,或者需要传递很多参数,让Pipeline运行起来。

原因:包括项目的流程繁琐,需要反复确认;DevOps成熟度不够,没有实现持续部署;或者CI的测试覆盖不够,CI通过后还要进行更多的测试才能部署。

影响:这些Pipeline需要专人盯着,去点某些按钮。会直接影响产品的交付速率和代码部署频率。

解决:让项目的运行更加敏捷,减少Pipeline定义中的阻塞按钮,将手工测试自动化后集成到Pipeline中。

最后

希望通过本篇文章,意识到项目中CI/CD Pipeline的问题,使其发挥更大的价值。

文/Thoughtworks冯炜
原文链接:
https://insights.thoughtworks.cn/continuous-integration-and-delivery-pipeline-mistakes/
更多精彩洞见,请关注微信公众号:Thoughtworks洞见

持续集成和交付流水线的反模式相关推荐

  1. 某银行大型管理系统端到端持续集成和交付实践

    背景 传统的银行IT系统研发流程从需求提出到产品交付往往具有较长的研发周期,纵观银行当下面临的市场环境,个人信贷消费升级,资管需求旺盛,普惠金融成为国家战略,来自银行同业和互联网金融的压力扑面而来,谁 ...

  2. 初学者的持续集成和交付(DevOps)

    目录 介绍 背景 先决条件 第1步--创建部署组 步骤2--在内部部署服务器中安装VSTS代理 第3步--配置构建操作 第4步--设置发布 第5步--启用CI和CD 您可能会发现一些有用的提示 1.请 ...

  3. 【持续集成和交付】项目环境配置:在Jenkins中运行项目

    前言 一直想学习自动化测试,但是都没行动,业余时间学习零零碎碎并记录20210424. 11.持续集成和交付 Jenkins环境搭建 项目环境配置 邮件通知 定时项目执行 在Jenkins中运行项目 ...

  4. 持续集成 ci/cd_CI / CD即服务:在云中进行持续集成和交付的10种工具

    持续集成 ci/cd 云和持续集成 (CI)是天生的匹配. 尽管云使我们摆脱了安装和维护物理服务器的痛苦,但持续集成可以自动消除构建,测试和部署代码的痛苦. 如果两家公司都希望把工作从开发团队的肩膀上 ...

  5. 持续交付之三——持续集成

    其他持续交付相关文章:<持续交付>系列文章目录 公众号,欢迎关注 第三章 持续集成 1. 引言 持续集成的目标是让软件一直处于可工作的状态 2. 实现持续集成 2.1. 准备工作 版本控制 ...

  6. svn增量打包部署_持续集成、持续交付、持续部署(CI/CD)简介

    >>>推荐阅读<<< 1.性能测试学习笔记-场景设计 2.性能测试的重要意义 3.性能分析流程及方法 4.应用系统性能调优之性能分析 概述: 软件开发周期中需要一些 ...

  7. Springboot Gitlab Jenkins Maven Docker 持续集成/持续交付

    Gilab安装教程 http://blog.csdn.net/chenhaifeng2016/article/details/78603216 http://blog.csdn.net/chenhai ...

  8. 适用于Java开发人员的微服务:持续集成和持续交付

    1.简介 如果我们回顾与微服务体系结构相关的众多挑战,确保每个微服务都能够与每个对等方说正确的语言可能是最困难的挑战之一. 我们最近谈论了很多关于测试的话题,但是总是有机会让bug潜入.也许是合同的最 ...

  9. 【linux】持续集成与持续发布CICD

    文章目录 DevOps的发展历程 原始开发时代 瀑布开发时代 敏捷开发时代 精益开发时代 DevOps 版本控制概念 什么是版本? 什么是版本控制? 常见的版本控制系统及比较 git的安装 git应用 ...

最新文章

  1. C语言程序设计之编程求鸡和兔的只数,用穷举法解决
  2. nginx 301重定向带www的https链接配置方法
  3. ps -ef和ps aux的区别
  4. UPS不断电割接流程和步骤
  5. jmeter录制 过滤_Jmeter脚本录制
  6. C++获取当前系统时间并格式化输出
  7. webpack基础+webpack配置文件常用配置项介绍+webpack-dev-server - QxQstar - 博客园
  8. 持续更新的Zookeeper知识总结
  9. 从h264码流中获取图像的宽高---版本2(简洁版)
  10. 一个女人如何让自已越来越厉害
  11. 3.手动搭建Maven项目
  12. 数学建模之倾倒的啤酒杯
  13. 汽车使用总结(七)--侧方停车
  14. pandas数据处理之合并与拼接
  15. VB问题——ByRef参数类型不符
  16. 案例分析:FIFA2018球员数据分析
  17. Codeup企业级代码管理平台,我们应该如何使用Codeup-阿里云
  18. 阿里妈妈展示广告召回之多场景建模算法
  19. 原理c语言for循环延时1s,for循环实现C语言精确延时
  20. 基于enc28j60的学习心得

热门文章

  1. 2014年国际注册信息系统审计师CISA考试相关信息
  2. 芯片巨头恩智浦的前世今生
  3. 2019 ICPC 南昌网络赛 H. The Nth Item
  4. caffe把数据转化为lmdb格式zxw.sh
  5. java crc-16校验位 xmodem x16+x12+x5+1(0x11021)实现
  6. 邦纳LTF12KC2LDQ激光传感器
  7. 得物Tech Leader对管理授权的思考是什么?|得物技术管理集锦
  8. 单片机按键设计的四个方案
  9. Python判断奇偶的方法
  10. JavaScript实现商品规格的组合匹配