分享

镜像、代码分支、版本管理

 pkeel 2012-09-12
本文介绍一下镜像环境的作用,以及版本发布的一些策略

一、镜像环境

所谓镜像环境,是指跟生产环境完全一致的一套环境,包括代码、数据库表结构(或许还包括数据)、配置文件等。镜像环境的作用是用于定位、解决生产环境的问题,或者用于生产环境升级前的验证

当项目实际运行中发现了一个BUG,开发人员就需要一个环境来重现、定位问题。但是一般来说,不可能直接在生产环境上进行定位,以免对业务造成影响。另一方面,如果开发环境的代码和生产环境不一致的话,又无法准确快速地重现、定位问题。所以,镜像环境对系统运维来说,是十分重要的

对于集中部署的WEB应用,或许一套镜像环境就足够了,当然也不一定,根据实际需要,可以搭建任意数量的镜像环境

对于不是集中部署的应用,一般来说需要多套镜像环境。比如说一个系统,假设在局点A和局点B分别部署,那么开发人员就需要有2套镜像环境,分别用来支撑局点A和局点B的运维

二、关于代码分支

代码分支(branch),指的不只是代码,也还包括数据库脚本、配置文件等。代码分支是一把双刃剑,在某些情况下会给开发带来便利,但同时也可能存在很大的维护隐患

举例而言,比如一个项目开发基本完成,处于测试阶段。这个时候在现场有一些定制的需求,那么这种情况怎么办?

如果直接在主干版本上进行定制需求的开发,风险是比较大的。因为这个时候主干版本还处于测试阶段,尚未稳定,所以这个时候在主干上合入定制需求的代码,就有可能发生代码和进度冲突的情况

比如说主干版本计划3月10日转测试,定制需求计划3月15日发布到现场。那么定制需求新合入的代码,就很有可能对主干版本转测试的计划产生冲击。有可能是新代码影响了主版本的功能,也有可能是在3月10日新代码还没有完成(甚至极端一点,还没有办法跑起来或者编译通过),总之定制需求的代码,可能会影响主版本转测试

反过来,如果在修改主版本代码时引入了新问题,也有可能对定制需求的开发产生阻塞,影响定制需求发布的计划。总之,在主版本自身尚未稳定的情况下开发定制需求,是一个高风险的行为

在这种情况下,从主干版本上拉出一个分支,不失为解决问题的一种方法。拉出分支之后,主干版本可以继续进行问题的修改,按照原定的时间表转测试。分支版本上可以进行定制需求新代码的开发,开发完成之后发布到现场

但是,分支版本存在的生命周期一般来说不宜太长。还是用上面的场景作为例子,当主干版本通过测试,已经稳定下来以后,就应该启动定制需求的收编工作,将定制需求的代码合入主干版本中,然后用主干版本替换掉现场的分支版本

因为如果长期同时存在2个版本,随着时间的推移,2套代码就会渐行渐远,到那个时候再进行收编,难度就会非常大,甚至成为不可能完成的任务。总结来说,分支版本存在的时间越长,收编时就需要花费越大的成本

这种情况可以类比代码提交,比如是2个开发人员协作开发一段代码,提交到SVN上。如果2个人都是短周期地更新代码,然后提交,那么代码冲突的可能性就会比较小,即使发生了代码冲突,由于提交的代码不多,要完成代码比对与合并也比较容易。而如果2个人各写各的,半年后再要一起提交,那基本上就是一件不可能完成的事情,至少是很困难的事情

分支版本存在时间太长,除了合并的难度会增大之外,还有一个弊端,就是会增大维护的工作量。在分支版本上发现的BUG,除了需要在分支版本上修改之外,也需要合到主干版本中(这个步骤由于分支代码和主干代码不尽相同,并不是一件很容易的事情),因为以后是需要用主干版本替换分支版本的,如果这时候不合入,那替换的时候,曾经解决的问题就又会重现

或许还有一种方式,就是分支的修改不在主干上同步修改,先用问题列表的方式记录下来,到合入主干的时候再进行修改。不过一般是不推荐这种方式的,因为这只是把修改推迟了,并不会减轻工作量。而且同步修改一般来说,会比事后再修改容易得多。另外这种方式,需要对发现的问题进行持续的记录和跟踪,这也是一个不小的维护成本

另一方面,在主干上发现的问题,一般来说是不需要在分支上也同步修改的。因为以后反正会用主干版本替代分支版本。不过这也不是绝对的,如果主干上的问题,对分支造成了重大的影响,那么一般还是需要同步修改的

总的来说,主干版本和分支版本并存,就需要同时维护2套代码,也就是一个BUG需要在两处修改,维护的工作量相当于是翻倍了

如果从一开始就不打算收编分支版本,而是计划把主版本和分支版本作为两个产品来维护,那么或许情况会好一点,两边发现的问题不需要互相同步

只要有收编分支版本的计划,那么收编版本存在的生命周期就越短越好。一个分支就是一套代码,只要有分支,就有维护成本存在

三、关于版本火车的发布策略

版本火车是我从同事那里听到的一个名词,不知道是不是标准用语,不过我觉得挺形象的。这个词指的是有计划地按批次发布版本,类似于火车的一节节车厢

比如现场提了30个需求,开发人员首先对工作量和开发能力进行评估,然后结合需求的紧急程度,制定出版本发布的计划。比如1月1日交付最紧急的6个需求,然后2月1日交付次要的工作量较小的14个需求,最后在3月1日交付剩余的10个需求

这个和第一点提到的镜像环境有关联。比如1月1日交付了第一批需求之后,研发就需要建立起现场的镜像环境,来定位解决现场发现的BUG,如果之后往现场发布了补丁,镜像环境也需要保持同步更新,总之要保证镜像环境和现场环境的高度一致,这样才方便定位问题

在这个时候,现场跑的代码是代码11,镜像环境也是代码11。开发人员继续在代码11上进行开发(因为后续还要交付代码21),但是镜像环境必须保持稳定,始终保持与现场代码的一致。否则现场发现了一个BUG提交回来,家里的代码已经不一致了,就无法准确的重现及定位问题

然后到了2月1日,将代码21交付到现场之后,家里的镜像环境也需要同步更新到代码21。然后开发人员可以继续在代码21的基础上进行代码31的开发

四、关于升级的方式

升级的方式有两种

比较简单的是全量安装。即每次都将系统的全量安装包发布到现场,现场进行全新安装。这种方式的成本是比较低的,因为不需要考虑升级包的制作,每次装新的就可以了,维护的工作量比较小。不过实际中,这种方式一般是不可行的,因为在两次版本发布之间,现场的系统已经运行了一段时间,肯定已经有大量的业务数据存在,全新安装可能会对业务数据造成破坏,这是不可接受的。当然可以用数据导入导出的方式来进行规避,不过这样就需要对数据进行额外的处理,也是需要工作量的。并且如果2个版本的数据库表结构有变化的话,要进行简单的导入导出就比较困难了

另外一种方式是提供增量补丁。即每次制作升级包,将升级包发布到现场,现场不需要重新安装应用,只需要运行升级包,对应用进行升级。这种方式就需要每次制作一个升级包,将两次版本之间的差异(包括二进制文件、配置文件、数据库脚本、初始化数据等)提取出来

如果多次发布升级包,一般还要考虑把前面的升级包内容,放到后面的升级包里,即所谓的全量升级包。比如说,1月份发布了一个升级包,2月份发布的升级包里,应该把1月份的升级包内容合进来。这样的话,如果需要再搭一套环境,就可以直接跑2月份的升级包,不需要先跑1月份的,再跑2月份的

此外,制作升级包的时候,一般需要考虑升级脚本的容错性、可回滚、可重复执行等

五、一个实际的案例

以我们目前正在开发的系统,结合上文说的4个要素,来综合说明一下

我们的系统三个月前,还处在测试阶段,决定发布到现场进行试用(现在回想起来,我觉得这是一个错误的做法),然后现场提出了一些定制的需求

鉴于当时主版本还处在测试阶段,开发人员还在修改代码,每天都有很多代码合入,在主干上进行定制需求的开发相当困难,所以就决定拉出一个分支,在分支上进行定制需求的开发,然后将分支发到现场。待主版本稳定以后,再把定制需求的代码合入主版本,最后用主版本替换现场的分支版本

对需求进行评估之后,我们决定每30天往现场发布一个版本,分3个批次发布。发布完成之后,主版本的测试阶段就结束了,届时再将定制需求合入主版本,然后用主版本替换掉分支版本(现在看来,分支版本的存在时间达到了3个月,已经是相当长了,合入主版本的时候十分困难)

目前前2个批次的需求已经发到现场,并且这2批新增的代码已经收编到主干版本中。主版本计划于本月底结束测试阶段,然后在下个月替换掉现场的分支版本

所以现在正在测试的,是已经包含前2批需求的主版本,但是现场正在跑的还是分支版本

因此现在我们就面临2个问题:

1、第3批的需求,在分支还是在主干上开发?
2、修改的BUG,应该提交到主干还是分支上?是否需要发补丁到现场?

对于第一个问题:

我们可以在分支上开发,然后用分支的升级包替换现场的分支版本。接下来将第3批需求也合并到主版本上,再用主版本替换分支版本

也可以直接在主版本上开发,届时直接用主版本替换掉分支版本

看起来当然是第2种方式简单很多,但是这样就面临着和3个月前同样的问题,主版本月底才结束测试,我们如果直接在主版本上开发,势必会影响到主版本的测试

所以权衡之后,决定采用一个折中的方案。首先在分支上开发,验证;然后主版本月底测试结束之后,把分支上的代码合入到主版本,最后直接用主版本替换现场的分支版本

对于第二个问题:

首先需要明确一个原则:鉴于很快会用主版本替代分支版本(同时这也意味着分支版本生命周期的结束),分支版本上的修改,一定要在主版本上同步修改;而主版本上的修改,如果不是确实必要,则不应该继续在分支版本上同步修改

这个原则,前者是为了避免替换后已解决的问题重现,后者是为了避免毫无意义的重复劳动

明确这个原则之后,就需要对BUG的来源进行分类:

第一类是在主版本测试中发现的问题,经过上面的分析,显然,这类问题是不需要在分支上同步修改的,只需要保证在主版本中修改通过即可,也不需要发补丁到现场

第二类是现场发现的问题(既包括现场发回的BUG,也包括在现场镜像环境里测出的BUG),对于这类问题,就要复杂一些:

首先可以明确的一点是,问题肯定是要在主干上修改的,否则到主干版本替换之后,问题依然不能解决

那么现在就有2个问题,这类问题是否需要在分支上修改,是否需要发补丁到现场?

仔细分析一下,会发现这2个问题实际上是同1个问题,关键在于:在主版本替换分支版本之前,是否发补丁到现场。

因为如果要发补丁到现场,那么分支必须修改,因为现在现场跑的是分支版本代码,主版本的补丁发不出去;如果不发补丁到现场,那么这段时间在分支上的修改就毫无意义

那么现在就只需要思考一下,在版本替换之前,是否发布补丁到现场?

如果仍然坚持发补丁到现场,那么所有现场问题(包括现场发回的BUG,和镜像环境上测出的BUG),都需要在主干和分支上各修改一遍,维护成本非常高。考虑到很快就要做版本替换,我不认可这种策略的价值

如果不发补丁到现场,那么直到版本替换结束之前,现场的问题就无法解决,这同样也是一个问题,如果阻塞了现场业务,这是不可接受的

综合考虑,我认为采用如下的策略是最优的:

1、所有上述两类问题,都在主干上进行修改
2、第一类问题,不在分支上修改,也不发补丁到现场
3、第二类问题,如果是非阻塞业务的问题,不在分支上修改,也不发补丁到现场
4、第二类问题,如果是阻塞业务的问题,则在分支上修改,并制作补丁发到现场(这部分工作,在一个月内将作废,但是这是保障这个月业务顺利进行必须付出的代价)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多