产品研发生命周期演化史:
1 纯人肉构建
这是发生在我身上的7年前的故事,我们的项目每周四会发布一个新版本,大家在每周四的晚上买好干粮饮料熬夜苦战。研发人员先提交代码,你merge我我merge,忙得不可开交;测试人员们则无事可做耐心等待。夜晚10点钟,研发人员终于憋出来一个build的过的版本,你方唱罢我登场,测试人员接手下一棒,研发人员开始扯皮打游戏修bug。凌晨3点,整个研发部门终于发布出一个看似还能用的部署包,现场的运维人员被唤醒接手部署的事情,身为研发的我终于可以回家睡大觉了。第二天早8点,被电话吵醒,哪个天杀的写了个bug把系统跑崩了,此处省略百万草泥马……
2 引入构建工具
后来发现老这么熬夜身体吃不消,而且质量也得不到保障,于是Jenkins等CI工具闪亮登场,而且不再规定具体的哪个时间点build版本,而是要求凡是提交好的版本都应该满足发布现场的要求。管理上要求研发人员要写单元测试、测试人员要写集成测试、现场工程人员拿到后要做冒烟测试等等,整体感觉虽然大家的工作量要比以前大很多,但是返工率大大降低,有了明显的进步。
3 引入指标
人员素质和能力参差不齐,并不是每个人都能写好单元测试和集成测试,必须来点可量化的考核制度来干预管理,一旦跟KPI和收入挂钩,大家总归是会收敛点。于是Sonar等统计工具干预进来,通过技术手段来尽早地发现问题,特别是对研发团队的要求,Sonar榜单上那几位每天会通报批评。
4 持续集成
互联网竞争惨烈,质量不再是评价一个产品好坏的唯一标准,响应速度甚至变成更重要的一个要求。特别是某些需求客户自己也不见得自己想要什么,他不会等你忙活一个月发布出来然后问“你在弄啥嘞”。于是有了Devops的出现,要求自动化的CI+CD,以最短的时间内上线一个可用的新版本然后慢慢优化。而且中间要省去大量沟通成本,全部交给系统去处理,并且可以快速回滚。
5 To be continue
虽然现阶段看似完美了,但我们还是有实打实的问题需要优化。微服务节点太多,走脚本控制总会有手误的地方,如何提高运维成本?数据库如何CI+CD,特别是回滚?部署到云平台后,如果供应商有变动如何迁移?这些问题有的已经有了解决思路,有的还是世界难题,文章结尾会给出答案。
CIcontinuous integration的三大环节:版本控制、Jenkins、交付物管理
版本控制工具从古至今已经出现过很多,早期的CVS,到中期的SVN,再到现在的Git系列。当然后生代的肯定要比老古董们优秀,我们拿github来说,第一:可以通过webhock触发jenkins实现自动化;第二:丰富的API可以与其他项目集成;第三:对于以软件研发为重要业务的公司还可以用github做为自己的认证中心。这里有个细节需要点明,我自己也踩过坑,github与gitlab虽然功能上是同一个东西内外网的两种实现形式,但是细节上差别巨大,特别是API做集成方面,url设计根本不一样,所以一个系统如果可以集成github可不要轻易的拿来与gitlab对接。
Jenkins就不多说了,目前CI界排名No.1的神器,前身是hudson,被Oracle收购后又另起炉灶。(我发现老外还真是有股子初恋情怀,这种类似的分道扬镳的例子屡见不鲜,两口子本来好好的过日子,特然某一天你喜欢你的刘三姐我喜欢我的赵飞燕,审美观不同散伙了,activiti与JBoss也是这股子破事)。Jenkins支持Maven、Gradle、Ant等build和测试工具,还可以支持容器。Jenkins输入是代码版本,输出是发布包,中间主要有两类build方式。第一种是利用Jenkins的插件,需要提前配置好;第二种是用shell脚本调本机的mvn等命令,这种方式更灵活,当然对使用者要求更高。
交付物管理,我知道的有Nexus和Artifactory这两种,主要为了维护发布包的版本。当然并不是一定要用这种方式,自己搭建个仓库用ftp或scp把交付包上传上去也不是不可以,就看你是怎么用了。而且什么是交付物?这个问题随着技术的更新也出现了不同的理解,例如一个jar包或者war包是交付物,一个.deb是交付物,甚至一个image也是交付物。
测试三大类:单元测试、集成测试、性能测试
这三类测试出现在研发周期的三个阶段,也是由不同角色的人来负责的。
单元测试,由研发人员自己编写,并在maven、gradle等在build版本时执行。一般会有通过率和测试覆盖率的要求,经验值覆盖率70%,通过率95%,只有满足这些指标,才会进行CICD的下一步。常用的单元测试有最流行的XUnit,个人比较推崇Spock。
集成测试,由测试人员编写,在单元测试通过后进行。因为集成测试需要把发布版本部署好后进行,所以集成测试的成本要比单元测试高的多。如果你是云平台上租赁服务器,那一次部署会产生流量,这就是经济成本。而上传交付物,启动,运行集成测试节点间的网络通信这些都是时间成本。常用的集成测试是selenium,用代码操作浏览器去跑case。
性能测试,由测试人员通过工具去执行,发生在某一个产品第一次或者大版本发布时。它的意义在于对产品QPS等做评估,能否在代码上有优化的空间,对物理资源或者云平台资源做预算。这方面我接触不多,接触过的只有LoadRunner,测试人员录入case,设定好QPS后工具重复执行。
除开这三大类测试,还有冒烟测试,或者交付测试,我觉得它们都是属于集成测试里的,只是出现的时间节点不同。
代码质量控制三大类:CheckStyle、PMD、FindBugs
CheckStyle:定制书写规范,形成统一的代码编写风格,防止研发人员freestyle,提高代码的可读性,当开发人员发生变动或者后期维护时可以看起来专业些、舒服些。
PMD:静态分析工具,从源码帮你进一步查问题,什么空try啊,什么没有使用到的变量啊。
FindBugs:从.class文件帮你分析潜在的bug,什么空指针啊,IO流未关闭啊等。
Jenkins里有一个报表统计类插件Violations,如果你的产品是maven项目还可以用独立的sonar,这些会把对代码的分析以面板的方式展现给研发团队。
CI结束CD登场:
前面介绍的都是CI相关的内容,下面开始说下CD,我个人觉得在持续集成中CD比CI更难,因为CI技术已经区域成熟,而CD的方案仍在探索中,现阶段比较流行的有Ansible,我有写过一篇介绍该技术的博客,但是Ansible适用于面向实例的部署,更优秀的CD方式是面向Image,也叫做Immutable方式,我现在正在研究的NetFlix的Spinnaker就是这种方式,Spinnaker方面想后续专门开一个专题大约10来节课程来介绍,这部分国内的资料基本是空白,我也是github上80W行代码一点点看过来的。总之我觉得Immutable方式是正确的发展方向,因为它在伸缩性、可维护性、移植性等方面更加优秀。
我们狭义上说的CD是Continuous Deployment持续部署,但广义上讲还有Continuous Delivery持续交付这层意义,持续部署是可以全自动化的,而持续交付由于业务上的要求往往需要人工干预。整个过程应该是:研发开发--持续集成--持续部署--持续交付,而且一定要记住一个原则,交付之前不再build!因为build就意味版本变动的风险,而你前面所有的测试环节都通过了,一定要保证交付出去的版本就是你测试通过的这个版本。
一图解天下:
说了这么多,上一张图吧,把本篇的内容归纳下。
仍然不完美:
产品的发布不仅仅是代码的升级,还会涉及到数据库的变更,虽然在数据库方面的CICD已经有了一些成熟的工具,但是在生产场景中,特别涉及到版本回退和自动化时,却显得很乏力。目前我接触过的有Liquibase,数据库集成这块先留白,等我真正在这块探索出一些成果时再来与大家分享。
作者:牛麦康纳