Day964.从持续构建到持续集成 -遗留系统现代化实战

news2024/12/21 10:25:41

从持续构建到持续集成

Hi,我是阿昌,今天学习记录的是关于从持续构建到持续集成的内容。

如何修改后的代码可以“火速”部署到生产环境里,这样才能提高整个端到端的交付效率,让每次改动工作都能及时得到反馈,尽快验证效果。


一、遗留系统的构建方式

遗憾的是,遗留系统的特点之一就是 DevOps 相当落后,甚至可以说完全没有。

在遗留系统中,一次上线前构建过程可能是这样的:

一位打包专员,在本地或远程的机器上拉取代码,完成集成、打包和测试的工作,并准备手动部署。

这时,即使再紧急的代码提交都会被拒绝,因为一个干净的打包环境来之不易,新引入的代码会导致所有的流程重来一遍,对打包专员来说是相当痛苦的。

这样的过程死板、低效、容易出错,一点也体现不出软件中“软”的特点(即灵活)。

所以,后来市面上出现了越来越多的自动构建工具,可以自动拉取代码、构建、集成、打包和部署,甚至还能运行自动化测试,这简直就是打包专员们的福音。

但是,如果只是在打包和部署时使用这些工具,那真是大材小用了。

而且,这样也没有解决软件开发中最难解决的问题之一,就是多人协作的情况下,代码集成的问题。

这里的集成是指,将多个人的工作成果合并在一起,并验证这些合并后的代码是否达到了一定的质量要求,是否可以工作的过程。

Kent Beck 早在上世纪 90 年代就提出了持续集成的概念,即集成的频率越高越好,极限情况下就是持续地每时每刻都在集成。

举个例子。相信肯定有类似的经历,如果家里一周不扫地,再扫地时就会发现很多尘土,每次大扫除也变得十分辛苦。

但如果你每天都坚持扫地,每次的工作就不会有很多,因为灰尘的积累时间只有短短的一天。

这其实就是极限编程的理念:越是痛苦的事情,就越要频繁地去做,这样每次做的时候就没有那么痛苦。

集成是软件开发中很痛苦的事情,因为会发生很多不可预知的情况,很容易就要花费比原始编程更多的时间。

越长时间不集成,不可预知的事情就越多,消耗的时间就越长。

因此我们就要更加频繁地去集成,这样每次集成就不会那么痛苦了。

对于遗留系统来说,如果连自动化的流水线都还不存在,那么你的首要任务,就是先打造这样一条流水线,先做到持续构建。


二、持续构建

持续构建是指,每次代码提交都会触发一次构建工作,并执行一些相关任务。

这个过程由持续集成工具自动化完成,大致过程如下:

  1. 开发人员将代码提交(PUSH)到远程代码仓库;
  2. 持续集成服务器按一定时间间隔(比如 1 分钟)轮询代码仓库,以便及时发现代码变更;
  3. 如果发现了代码变更,持续集成服务器就将代码拉取(PULL)到自己本地;
  4. 持续集成服务器按照指定的构建脚本执行各项任务,包括编译、单元测试、代码扫描、安全扫描、打包等;
  5. 任务执行完毕后,会把结果(成功或失败)反馈给开发团队。

有了持续构建,就能解决遗留系统最头疼的问题之一:不知道从哪去找可以部署到各个环境的软件包。

打包部署不再依赖于打包专员的手工操作,大大缩短和简化了部署流程。


三、在遗留系统中引入持续构建

可以使用 Jenkins 或 GoCD 这样的开源持续集成工具,来搭建持续集成服务器。

也可以搭配上 BitBucket 或 GitLab 之类的源代码管理工具,来提供 Pull Request 和 Code Review 等其他功能。

建议选用 Atlassian 公司的三件套:BitBucket、Jira 和 Confluence。

可以将代码管理、需求管理和知识管理打通,补齐遗留系统中缺失的这些内容。其他替代工具也是完全没有问题的。

很多时候,遗留系统的代码体量过于庞大,想要在本地构建一次需要很长时间,甚至会内存溢出。

当然可以从代码和架构层面通过拆分代码库来解决,但这无法解决燃眉之急。

更快速的方式是,在合并代码的时候,触发 Web Hook,让持续集成服务器在远端的特性分支上先执行一次构建,用这次构建来替代本地的构建。然后合并代码,并在合并后的目标分支上再执行一次构建。

代码的构建解决了,下一步就是解决数据库的迁移脚本。在遗留系统中,往往各个环境中的数据库都不完全相同,因为各个环境都可能有手工改动的痕迹。

因此第一步要做的,就是以生产库为标准,统一所有环境数据库的 DDL,并以此作为基线。然后将后续的所有 DDL 和 DML 都通过 Flayway 管理起来,并版本化。

遗留系统要做到单次构建(如每日构建)还是相对容易的,但要想做到“持续”构建,开发人员就必须改变之前的一些工作习惯。

就拿提交代码来说,很多开发人员会等到所有的代码都编写完成,才进行一次提交。

这样,代码冲突的风险非常高,很可能出现代码写了两天,合并就用了半天的尴尬情况。

如果每个人都这样,就无法做到每天构建多次的目标。


四、任务分解

要避免这种局面,开发人员就要对自己编写的代码做出良好的规划,分解出若干小的任务,每个任务都能在很短的时间内完成(比如 15 分钟、40 分钟,或者是一个番茄钟的时间),而且任务最好是按照一个功能的端到端的场景来划分,而不要按技术层级去划分。

为什么不推荐按技术层级划分呢?

比如在开发一个需求时,大多数开发人员都是这样的:

先修改数据库层,看看是否需要增加表或字段;

再开发数据访问层,对新增加的表或字段编写映射代码;然后编写服务层的代码,这时可能才真正接触业务逻辑;

接下来是 Controller,可能要添加新的 API 或修改已有的 API;最后,可能还会涉及到一些前端的修改。

如果按照这样的顺序去开发,每次完成的小任务都是不能提交的,因为新的场景没有开发完,而旧的场景又可能被新添加的代码破坏。

正确的做法是按端到端的方式去分解任务,一个任务完成一个简单的业务场景。

每个任务开发完毕,一个端到端的小场景就开发完了,你可以针对这个小场景写单元测试,也可以在本地环境进行自测。

然后就可以提交(commit)代码了。

比如你要开发一个简单的登录功能,那么可以这样做任务分解:

  1. 用户可以使用用户名和密码来登录网站
  2. 用户名不存在,则登录失败
  3. 密码错误,则登录失败

从一个端到端的业务场景来描述一个任务的,而不是我要修改 Service 层的那几个方法。

完成第一个任务可能要耗费一些时间,搭建一些基础代码,而后面的任务就像是对第一个任务的增强,就像是一种迭代式的演进过程。

多个小任务完成之后,你感觉差不多了,就可以更新一下远端的代码,解决一下冲突,然后 PUSH。

这时每个 commit 都覆盖了一个小的场景,是系统的一个增量,是可以交付的。

这就好像是创作一幅水粉画,首先需要画出大概的轮廓,再逐层往上叠加各种颜料,直到最后完成。

这个过程中,每一次叠加别人都可能看出整体大致的样子,给出修改意见。

如果一开始就只精雕细琢局部一小部分,别人可能根本不知道你画的是什么。


五、小步提交

上面提到的按分解的任务开发并且提交的方式,就是小步提交。

有些人理解小步提交就是指每次 commit 尽量少的代码,这其实是错误的。

如果提交的内容很少,但却破坏了编译和测试,这样的提交也是不合格的,因为它没有办法 PUSH,没法做到持续提交。

真正的小步提交是指,每个 commit 都完成了一个端到端的功能点,因此都是可以 PUSH 到代码仓库的。

如果愿意,你可以针对这个 commit 进行验证、测试甚至部署。只有这样,最终才能做到真正的持续交付。

在提交代码时,总结了一套行之有效的七步提交法,它的过程是这样的:

  1. PULL 最新代码,确保在最新的代码基础上开始开发;
  2. 本地编写代码;
  3. 本地构建:本地执行编译和单元测试等,以确保新编写的代码是可以工作的
  4. PULL 最新代码:需要先检查 CI 状态,如果是绿色则可以 PULL;
  5. 本地构建:再次执行编译和单元测试,以确保新编写的代码和最新代码可以成功集成;
  6. PUSH 代码:将本地修改 PUSH 到远端服务器;
  7. 流水线构建:触发 CI 流水线进行构建,并监控流水线状态,直到通过。

对于第二步编写的代码,既可以是与一个需求相关的若干 commit,也可以小到仅仅是一个 commit。

在这个 commit 中,既要包含功能代码,也要包含测试。

不要害怕编写测试,如果任务分解是基于业务场景的,而测试是根据分解的任务编写的,会发现这一切就会变得很容易,甚至测试驱动开发也是很自然而然的事情。


六、质量门禁

持续构建的过程不仅仅是对最新的代码进行编译和打包,还要进行一定的质量检查,也就是质量门禁。

效率最高的检查手段就是单元测试,它运行快,反馈快,可以在本地运行,能让你在第一时间知道自己的代码是否破坏了其他功能,或者是否达到了单元测试覆盖率的要求。

遗留系统很可能没法直接做单元测试。可以先对代码进行可测试化重构,再添加单元测试。

但这将是一个十分漫长的过程,因此在搭建遗留系统的持续集成流水线时,可以先跳过这一步骤,等有了单元测试之后,再加到流水线中来。除了单元测试,代码扫描也是一种检查质量的有效方式。

可以使用 SonarQube 这样的工具对代码进行静态扫描,检查代码的规范和各种潜在的错误。

一方面它能为我们敲响警钟,提升代码质量,另一方面也可以让 Code Review 更专注在代码的设计上。

一般来说,代码扫描的结果如果超过某个配置的阈值,就会阻断整个持续集成流水线。

但对于遗留系统来说,可能会扫出成千上万个代码漏洞。

这时可以选择不阻断流水线,只将扫描结果作为参考,也可以将某个结果作为基线,来验证新提交的代码是否包含新的漏洞。

如果遗留系统在 DevOps 方面还是一张白纸,建议你先引入工具和平台,做到持续构建。

在持续构建时,也可以考虑自身情况,进行一些剪裁,比如去除单元测试覆盖率检查,去除代码扫描,只做代码的编译和打包。

这样虽然看上去很单薄,但引入工具本身已经前进了一大步。

对很多遗留系统来说,能做到这一步,已经相当不容易了。

等到团队适应了新的工作方式,再逐步添加质量门禁和其他 DevOps 实践,做到持续集成。


七、持续集成

持续集成包含持续构建的所有步骤,并且在它后面还增加了部署到某个环境的流程,比如部署到测试环境,并且进行冒烟测试、接口测试等。

同时,在这个阶段,可以优化一下流水线,进一步提升效率。


八、分级构建

持续集成流水线的重要作用之一就是快速反馈。

对于编译不通过、测试失败、代码风格不符合标准等问题,都希望第一时间看到流水线失败,继而根据日志去分析失败原因,快速修复。

但遗留系统的代码库往往很庞大,仅仅编译可能就会很长时间,如果再跑测试和代码扫描,得到反馈的时间就会大大拉长。

Martin Fowler 在《持续集成》一文中提出了次级构建的概念,即对构建进行分级,把那些执行速度快、反馈质量高的步骤放到一级构建中,将执行速度慢的步骤放到次级构建环节。

比如单元测试执行的速度很快,就可以放到较早的构建环节;

而集成测试很慢,就可以放到次级构建;

对于代码扫描这种更慢的构建,甚至可以使用单独的流水线,在每天晚上执行一遍。

只有把不同构建过程按执行速度和反馈效果拆分为不同阶段,整个构建或集成过程才更像是一条真正的流水线。

在真正的工业流水线上,工人们围绕一个制品(artifact)进行组装,一个工人完成工作后,就把制品传递给下一个工人。

这就好像是一个构建阶段结束后,把制品传递给下一个构建阶段。


九、制品晋级

持续集成流水线的产物也叫做制品(artifact),有时也翻译为工件。

一次代码轮询所触发的流水线构建,只会产生一个制品。在持续集成的时候,可以把构建阶段产生的软件包作为制品,存入制品库中。

在需要部署的时候,会从制品库中抽取最新的制品。

通常来说,不同的测试环境有各自的制品库,当一个制品满足了相应环境的要求后,就可以晋级到这个环境中。

举个例子,一次提交在成功构建后产生的制品,经历了单元测试、代码扫描等质量门禁,才会进入 QA 制品库。

这时这个制品就可以部署到 QA 环境中。在 QA 环境通过 QA 测试后,方能进入 UAT 制品库,进而部署到 UAT 环境。

以此类推,如果该制品通过了所有非生产环境的验证,就可以进入 PROD 制品库,作为部署到生产环境的候选制品了。

这种制品晋级的机制,是持续集成和持续交付的基础。很多号称做到持续交付的项目,其实只不过是交付的频率高一些而已,根本没有制品晋级的机制,并不是持续交付。

遗留系统在一开始做持续构建时,可以先不做制品晋级,只把制品作为部署的候选包。

等其他 DevOps 实践慢慢丰富以后,再考虑实现制品晋级。


十、总结

如何在一个没有持续集成流水线的遗留系统中,逐步搭建基础设施,从持续构建开始,慢慢做到持续集成的初级阶段。

这其中包含很多工作习惯的改变,比如任务分解、小步提交等,一开始你可能并不适应,但要知道,只有做好任务分解,才能做到小步提交,才能做到持续提交代码并持续构建。

这些都是 DevOps 文化的一部分。一个遗留系统要想真正做好 DevOps 现代化,就必须转变思想,摒弃成见,彻底拥抱 DevOps。

七步提交法,图解,供参考。

在这里插入图片描述

在做到持续构建之后,可以逐步引入分级构建、制品晋级等实践,慢慢向持续集成演进。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/505285.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

看火山引擎DataLeap如何做好电商治理(二):案例分析与解决方案

接上篇,以短视频优质项目为例,火山引擎DataLeap平台治理团队会去对每天发布的这种挂购物车车短视频打上标签,识别这些短视频它是优质的还是低质的,以及具体原因。一个视频经过这个模型识别之后,会给到奖惩中心去做相应…

聊一聊 用 dotnet-trace 调查 lock锁竞争

一:背景 1. 讲故事 最近在分析一个 linux 上的 dump,最后的诱因是大量的lock锁诱发的高频上下文切换,虽然问题告一段落,但我还想知道一点信息,所谓的高频到底有多高频?锁竞争到底是一个怎样的锁竞争&…

将训练好的模型保存在服务端的三种办法

刚刚在完善我书中第七章案例的文档时,需要将训练好的模型存储在服务端,方便小伙伴们来使用该模型,这里我提供三种办法: 直接从我的个人网站中加载;通过python启动一个文件下载服务器;使用微信小程序云存储…

windows10+detectron2完美安装教程

文章目录 前言下载detectron2安装Visual Studio 2019修改代码 前言 需要下载detectron2的github项目,安装vs2019 (强烈建议这个版本,其他的版本需要做更多地操作才能成功安装),默认其他环境没问题。 下载detectron2 链接:https…

【来点小剧场--爪哇岛寻宝】java实现网络编程,写一个简易的回显服务程序

作者:困了电视剧 专栏:《JavaEE初阶》 文章分布:这是一篇关于网络编程的文章,在这篇文章中我会剖析一段回显服务程序的执行步骤和代码编程,希望对你有所帮助! 目录 客户端 服务端 总结 客户端 现在我们…

Postman完全卸载步骤

一、卸载应用程序 我们首先正常右键点击卸载通过windows程序卸载功能来卸载postman应用程序。 二、删除文件 (1)删除AppData》Roadming下的postman文件夹 (2)删除AppData》Roadming》\Microsoft\Windows\Start Menu\Programs下的p…

态势感知与信质、信量

未来的新智能是人机环境系统智能,而人机融合的态势感知是其关键,简单地说,态势感知(situation awareness)就是智能体在“一定时间和空间环境中的元素的感知,对它们的含义的理解,并对他们稍后状态…

uni——调用子组件失败解决方法($nextTick)

案例说明 调用子组件内的方法或者属性赋值报错的 this.$refs.goodsOrder.list [ ]解决代码 在外层包裹this.$nextTick(() > { 数据 }) this.$nextTick(() > {this.$refs.goodsOrder.list [] })

【JAVA面试】JVM

提示:文章先作为初版,等后续时间充足后,补充更深的内容 文章目录 JVM一、垃圾回收算法二、什么是STW三、JVM参数四、JVM内存模型 JVM 一、垃圾回收算法 JVM中的垃圾回收算法可以分为两种类型:基于引用计数的垃圾回收算法和基于可…

国考省考行测:数字推理题,趋势平缓作差,趋势陡峭看平方乘积,根号数列平方,分数小数拆开看

国考省考行测:数字推理题,趋势平缓作差,趋势陡峭看平方乘积,根号数列平方,分数小数拆开看 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能,附带行测和申论,而常规国考…

【Unity3D】Shader变体管理流程-变体剔除

一、什么是Shader变体管理 想要回答这个问题,要看看什么是Shader变体。 1. 变体 我们用ShaderLab编写Unity中的Shader,当我们需要让Shader同时满足多个需求,例如说,这个是否支持阴影,此时就需要加keyword(…

强化学习p1-基本概念

Terminologies(名词) 状态(State) 每个时刻,环境有一个状态 (state),可以理解为对当前时刻环境的概括 状态(State) 有时也被称为观测(Observation),因为有时智能体并不能观测到环境改变后的全部,只能观测到部分。 环境(Environm…

计算机毕业论文内容参考|软件工程|网络流量异常信息分析方法研究

文章目录 导文文章重点摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍技术分析技术设计技术实现总结与展望导文 网络流量异常信息分析方法研究 文章重点 摘要 本课题针对网络安全中网络流量异常的分析方法进行研究,提出一种基于机器学习和深度学习的异常检…

OpenCV教程——处理图像像素及图像掩膜

1.像素值 像素值是图像被数字化时由计算机赋予的值,代表了图像中某一小方块(即【像素点】)的平均亮度信息。 灰度图像通常用8位表示一个像素,这样总共有256个灰度等级(像素值在0~255之间)。 …

最值得推荐的免费分区管理软件

磁盘管理是 Windows 的内置工具,用于操作硬盘上的分区,但自 Windows XP 以来直到Windowa11 该程序几乎没有变化。个人测试了五个备选方案,以了解它们之间的比较。 奇客分区 默认的分区个数根据磁盘大小?需要重新划分分区&#xf…

2023/5/8总结

JAVA基础知识(2) 1.方法 1、方法定义 格式:public static void 方法名(){ //方法体 } 2、方法调用 格式:方法名(); 3、方法的通用格式 public static 返回值类型方法名&…

Camtasia2023官方中文版免费下载

在现在的网络互联网时代,越来越多的人走上了自媒体的道路。有些自媒体人会自己在网络上录制精彩视频,也有一些人会将精彩、热门的电影剪辑出来再加上自己给它的配音,做成大家喜欢看的电影剪辑片段。相信不管大家是自己平时有独特的爱好也好、…

【机组组合】基于数据驱动的模型预测控制电力系统机组组合优化【IEEE24节点】(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

MySQL ---- 事务

事务 1、事务简介 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撒销操作请求,即这些操作要么同时成功,要么同时失败。 在实际的开发过程中,一个业务操作如&am…

如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)

如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows) 如何制作 ESXi USB 启动盘 请访问原文链接:https://sysin.org/blog/create-bootable-esxi-usb-installer/,查看最新版。原创作品,转载请保留出处。 作者主页:sysi…