DevOps现代化

 


从持续构建到持续集成

遗留系统的构建方式

在遗留系统中,一次上线前构建过程可能是这样的:一位打包专员,在本地或远程的机器上拉取代码,完成集成、打包和测试的工作,并准备手动部署。这时,即使再紧急的代码提交都会被拒绝,因为一个干净的打包环境来之不易,新引入的代码会导致所有的流程重来一遍。

Kent Beck 早在上世纪 90 年代就提出了持续集成的概念,即集成的频率越高越好,极限情况下就是持续地每时每刻都在集成。这就是极限编程的理念:越是痛苦的事情,就越要频繁地去做,这样每次做的时候就没有那么痛苦。

持续构建

持续构建是指,每次代码提交都会触发一次构建工作,并执行一些相关任务。这个过程由持续集成工具自动化完成,大致过程如下:

1. 开发人员将代码提交(PUSH)到远程代码仓库;

2. 持续集成服务器按一定时间间隔(比如 1 分钟)轮询代码仓库,以便及时发现代码变更;

3. 如果发现了代码变更,持续集成服务器就将代码拉取(PULL)到自己本地;

4. 持续集成服务器按照指定的构建脚本执行各项任务,包括编译、单元测试、代码扫描、安全扫描、打包等;

5. 任务执行完毕后,会把结果(成功或失败)反馈给开发团队。

有了持续构建,就能解决遗留系统最头疼的问题之一:不知道从哪去找可以部署到各个环境的软件包。打包部署不再依赖于打包专员的手工操作,大大缩短和简化了部署流程。

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

建议选用 Atlassian 公司的三件套:BitBucket、Jira 和 Confluence。可以将代码管理、需求管理和知识管理打通,补齐遗留系统中缺失的这些内容。

遗留系统要做到单次构建(如每日构建)还是相对容易的,但要想做到“持续”构建,开发人员就必须改变之前的一些工作习惯。就拿提交代码来说,很多开发人员会等到所有的代码都编写完成,才进行一次提交。这样,代码冲突的风险非常高,很可能出现代码写了两天,合并就用了半天的尴尬情况。如果每个人都这样,就无法做到每天构建多次的目标。

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

做好任务分解,做到小步提交。小步提交不是指每次commit尽量少的代码,如果提交的内容很少,但却破坏了编译和测试,这样的提交也是不合格的。

真正的小步提交是指,每个commit都完成了一个端到端的功能点,因此都是可以PUSH到代码仓库的。可以针对这个commit进行验证,测试甚至是部署。

七步提交法

1.PULL 最新代码,确保在最新的代码基础上开始开发;

2. 本地编写代码;

3. 本地构建:本地执行编译和单元测试等,以确保新编写的代码是可以工作的;

4.PULL 最新代码:需要先检查 CI 状态,如果是绿色则可以 PULL;

5. 本地构建:再次执行编译和单元测试,以确保新编写的代码和最新代码可以成功集成;

6.PUSH 代码:将本地修改 PUSH 到远端服务器;

7. 流水线构建:触发 CI 流水线进行构建,并监控流水线状态,直到通过。

持续集成

持续集成包含持续构建的所有步骤,并且在它后面还增加了部署到某个环境的流程,比如部署到测试环境,并且进行冒烟测试、接口测试等。同时,在这个阶段,你还可以优化一下流水线,进一步提升效率。

持续集成流水线的重要作用之一就是快速反馈。对于编译不通过、测试失败、代码风格不符合标准等问题,我们都希望第一时间看到流水线失败,继而根据日志去分析失败原因,快速修复。但遗留系统的代码库往往很庞大,仅仅编译可能就会很长时间,如果再跑测试和代码扫描,我们得到反馈的时间就会大大拉长。

对构建进行分级,把那些执行速度快、反馈质量高的步骤放到一级构建中,将执行速度慢的步骤放到次级构建环节。比如单元测试执行的速度很快,就可以放到较早的构建环节;而集成测试很慢,就可以放到次级构建;对于代码扫描这种更慢的构建,甚至可以使用单独的流水线,在每天晚上执行一遍。只有把不同构建过程按执行速度和反馈效果拆分为不同阶段,整个构建或集成过程才更像是一条真正的流水线。

小结

从持续集成到持续部署

最理想的分支策略:基于主干开发

基于主干开发的一般流程:

持续提交:开发人员每日持续提交当天开发的代码,持续构建和集成;

冲突处理:每次提交代码,都会先 rebase 远端的 master 代码,这让开发人员有机会在本地解决当前的冲突;

制品晋级:提交的代码经过持续集成流水线产生制品,该制品不断晋级,最终成为生产环境的部署候选。

延迟发布的特性处理:在一开始就预警这种风险,并使用特性开关(Feature Toggle)来进行控制,如果需要延迟,就将开关关闭,只部署但并不交付这个特性,由于开关关闭的场景早就在多个环境下验证过了,测试人员也不需要加班。

DevOps文化

如果当前 CI 的状态是红色,则禁止提交新的代码如果

15 分钟内不能快速修复,就 revert 刚才 PUSH 的代码,重新提交

尽量频繁地触发 CI,比如一天 N 次

CI 失败不过夜

一旦提交代码,要监控 CI 状态,直到全部通过(或提交构建通过,次级构建开始),才能着手其他工作

需求管理

一方面,有可能不会上线的需求要提前预警,让开发人员准备特性开关。另一方面,把需求的粒度砍小。

DevOps所提倡的任务分解和小步提交,前提就是需求的粒度足够小,这样代码提交的粒度才能随之变小。这需要需求分析人员配合工作。

再者,如果一个大需求需要几个月开发完成,需求方就只能在最后阶段才能看到做成了什么样子。一旦与想要的不符,就要推倒重来,造成了巨大的浪费。细粒度的需求可以在早期就向客户展示部分已完成的内容,确保方向的正确性。

Code Review

基于极限编程的理念,既然Code Review是好的,那么就频繁的去做。

所以, 可以尝试每日 Code Review。每天一个固定的时间,团队成员围在一台显示器前或者会议室的大电视前,集体 Code Review,每个人都能知道其他人在做的事情,尽早知道是否会影响自己的开发,也能在其他人遇到困难的时候,第一时间伸手帮忙。

为了实现更高效的 Code Review,你还可以在 commit 代码的时候使用一些小技巧。比如在使用并行运行模式的时候,你需要复制代码。你可以在复制完代码后立即 commit 一次,然后再在复制后的代码上修改,继续 commit。这样做的好处是,第二次 commit 和第一次 commit 的代码是可以看出差别的,有利于 Code Review。否则,如果复制出来直接修改,那么就只能看到修改后的代码,无法 diff 你的修改了。

自动化部署

为每个阶段准备部署脚本,使用部署流水线来管理部署的过程,对不同的环境也尽量使用同一套部署脚本,并把脚本纳入到代码版本管理中。

构建低风险发布策略,将发布风险降到最低。低风险发布策略,是指在部署过程中不要影响正常的业务行为,要让用户无感知;一旦部署失败,需要尽快回滚到正常状态,尽量减少对客户的影响。

低风险发布策略包括蓝绿部署、滚动部署、金丝雀发布等。

蓝绿部署(blue-green deployment)是指准备两套完全一样的运行环境,即生产环境(蓝环境)和预生产环境(绿环境)。

在部署时,先在绿环境中部署,并测试验收。在确认没有问题后,再将请求引流到绿环境,而蓝环境则仍然保持旧版本。当确定新版的部署没有问题后,绿环境升级为生产环境,而蓝环境则变为预生产环境,等待下次部署。

由于蓝绿部署并不会造成停机,新的生产数据一直在产生,这样就会给环境切换造成一定的困难。

因此,很多蓝绿部署方案都会采用共享数据库的方式,同时对数据迁移脚本做兼容性处理,让共享的数据库可以应对新旧两个版本的系统。比如在修改字段时使用扩张 - 收缩模式,先增加字段,并做数据迁移。这样,数据库就可以运行在新旧两个版本上了。当新版本确认没问题后,在下次部署的时候再删掉老字段。

 

 滚动部署(rolling deployment),即在服务集群中选择一个或多个服务单元,先对这些服务单元进行部署,然后投入使用,并开始部署其他服务单元。如此循环直到所有单元都部署完毕。

 

 上面的两种部署方式是从物理的角度隔离新旧版本,而金丝雀发布(Canary Release)策略则引入了用户的维度。比如在蓝绿部署或滚动部署中引入了新版本后,并不是将所有流量都引流到新版本,而是只对一小部分用户开放,以快速验证,从而降低发布风险。

 

在实际操作中,可以让生产环境的测试用户作为金丝雀用户,测试人员在生产环境进行测试和验证,这样能在一定程度上做到 QA in Production。

金丝雀发布还可以延伸成为灰度发布,即当金丝雀用户验证通过后,不立即开发给全部用户,而是按照一定阶段逐步开放给所有用户。

应用回滚

哪怕发布风险降到最低,也不代表零风险。当发布出现问题的时候,要及时将系统“回滚”到上一个稳定的版本。

这里说的“回滚”并不是指像数据库回滚事务那样,从逻辑上逆向执行一遍所有代码增量;也不是指 revert 所有这次部署的代码提交,重新走一遍流水线,产生新的制品,进而部署,而是指部署上一个稳定的版本。这个稳定版本是相对可靠的,没必要产生新的制品了。

如果发生问题的部分包含特性开关,也可以关闭开关来规避问题。同时,数据库的结构要做到向下兼容。一般回滚部署时,只回滚应用程序,而不要回滚数据库,否则会造成数据丢失等问题。

小结

对于持续集成的建议:

频繁提交代码。

不要提交无法构建的代码

及时修复持续集成流水线

将需要操作的尽可能自动化,放在持续集成中自动运行

必须通过代码扫描和测试

执行本地构建

避免检出无法构建的代码

posted @   李琦贝尔蒙特  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示