持续交付——发布可靠软件的系统方法
断断续续看完了《持续交付——发布可靠软件的系统方法》一书,中间有几次由于事情太忙而中断,直到今天才把书读完。书的内容很精彩,大致分持续集成,测试和部署三大块,还有介绍包括软件开发、项目管理等其他内容。我下面介绍几个我印象比较深刻的内容。
预测试提交(pretested commit)
简单来说,预测试提交就是在将代码提交到代码库之前,先在本地跑一遍持续集成上的操作,比如编译,单元测试等,保证提交的代码不会破坏基本的构建,然后再将代码提交到代码库。有一些持续集成的框架已经提供了这样的功能,可以在开发人员提交代码之后先执行基本构建,如果有问题,则通知开发人员且不会提交代码到代码库;如果没问题再提交代码到代码库,在CI上跑真正的构建。如下图所示:
![](/images/post/04143846-935bd06aa7d74c8cba067610a4f2143a.gif)
![](/images/post/04143855-576fc9a7738e410d8de01ee7a9212723.gif)
这样的好处是,如果有人的提交了破坏构建的代码(比如编译不通过,单元测试失败等),没有预测试提交的话,只能等到破坏构建的人修复了代码后,其他人才能继续提交代码;而使用了预测试提交,破坏构建的提交根本不会提交到CI服务器上去,所以也就省去了等待其他人修复的时间了。
但是预测试提交只是做简单的本地构建,其他比较复杂的比如UAT测试,性能测试等,可能不会放到预测试提交中执行,因为如果要执行这些操作的话,开发人员在本地提交要等待的时间就会比较长,从而影响开发效率。
部署流水线:一键部署,一键回退
所谓的部署流水线,就是从提交代码开始,CI服务器进行代码编译、单元测试、验收测试,然后自动化部署,接着执行集成测试,UAT测试,性能(容量)测试等操作,一般是由多个CI任务来执行,每个CI任务都有上下游关系。比如,任务A执行代码编译,单元测试操作,任务B执行自动化部署操作,任务C执行集成测试,UAT测试等操作,这3个任务间的关系是:先执行任务A,执行完了再执行任务B,最后执行任务C,像生产流水线一样,组成了这样的一个部署流水线。最后产生的结果可能是一个完成了完整测试的一个产品交付物,或是将交付物部署到了生产环境上。
一般的持续集成只是在开发环境使用(像我目前所在的公司),而书中提到的部署流水线还包括了测试环境和类生产环境的部署。测试环境和类生产环境的部署不是由CI服务器自动触发的,而是有相关的操作人员(比如部署人员或者运营人员)通过点击相应的按钮来完成自动部署的操作,即一键化操作。这里的自动部署还可以选择产品交付物(ear、war或jar包)的某一个版本来进行部署,而不是简单的部署最新版本。
除了一键部署,还有一键回退。如果自动部署过程中有异常问题,或者是部署到指定环境后验证出现问题,操作人员可以通过点击相应按钮进行版本回退的操作,即一键回退。同样的,回退也可以选择相应要回退的交付物版本来进行回退,而不是简单的回退到上一个版本。
目前有一些CI工具已经支持了这些操作,比如ThoughtWorks的Go系统等。
蓝绿发布,无停机发布
无停机发布版本有很多种方法,这里只介绍蓝绿发布的方法。所谓的蓝绿发布,指的是系统的两个版本,蓝色的表示已经在生产上运行的版本,绿色表示即将发布的新版本。首先将两套版本的系统都启动起来,现有的用户请求连接的还是旧的蓝色版本,而新的绿色版本启动起来后,观察有没有启动的异常,或者其他一些异常,如果没有问题的话,再将现有的用户请求连接到新的绿色版本。对于用户来说,版本的切换是透明的,系统在发布过程中没有中断过。这样就达到了无停机发布的目的。
如果切换到新的绿色版本后,发现有故障或者异常,可以随时切换到旧的蓝色版本,这样同样可以达到无停机回滚的目的。但是要注意的是,在使用新的绿色版本过程中发现异常需要回滚,这时用户已经使用系统而产生了一些数据,这时候需要在回滚的同时,将数据同步到旧的蓝色版本环境中,或者根据判断是否放弃这些数据。也可以在绿色版本发布后,先用小众的用户做一些操作,如果发现没有问题,再将所有用户请求切换到绿色版本中来。
坚持主干修改,不推荐分支操作
在日常的开发中,经常会遇到这样一种情况。系统的一个版本即将发布生产,这个时候只能做一些测试工作和严重Bug的修复,而新的功能需求又需要继续开发,如果在现有的代码中继续修改的话,可能会影响新版本的发布。通常在这种情况下,很多开发团队会选择在版本控制管理器上拉一个分支出来,在新的分支上做新需求的开发,而在老的分支上做系统测试和bug修复,老的分支最终会发布到生产上。
首先说下这种做法有什么问题。新拉出来的分支最终是要合并到老的主干分支上去的,如果合并的间隔时间很长,到了合并代码的时候就会出现很多问题,甚至有些功能可能需要在老的分支上重新实现。第二个问题,新分支如何确保没有问题?如果已经有持续集成环境的话,对于新的分支就需要重新再搭建一套持续集成环境来跑每天提交的代码,增加了人力和资源的消耗。最终分支被合并到主干后,分支是要被废弃掉的,所以可以说分支的代码是一种开发上的浪费,即使它最终被合并到主干上。总的来说,分支操作跟持续集成是对立的,如果是只有一套持续集成环境,就必须频繁地合并分支代码到主干才不会有问题,但一般合并的时间都会比较长,因为要发布的主干代码要等到发布后才允许合并;如果有多套持续集成环境,又需要增加相关的人力去维护,等分支被合并到主干后,新增的持续集成环境也不需要了,这又是一种资源的浪费。
所以推荐的做法是坚持在主干上做修改,而不是拉分支。如何在主干上做大动作的修改,而又能确保不会破坏即将发布的功能,书上介绍了很多方法,包括“提取抽象层”、将大的修改分解成多而小的修改等,后者可以让每一次提交的小修改都可以通过持续集成上面的各种测试,从而保证主干上的功能不被破坏。这样做的好处是,你始终只需要维护一套代码,不需要做额外的合并工作,不会产生合并代码后的问题,而在持续集成上始终对主干代码进行构建,一旦有问题可以马上发现,缺陷发现的越早修复的成本也就越低。
小结
此书是TW公司的人出的,是很多战斗在一线的开发人员的经验总结,也是获得 jolt 大奖的书籍。有些内容比较深,需要有多年开发经验才好理解,正在实施敏捷或者持续集成的同学可以找来看看,一定会对你的工作有所帮助的,谢谢。