网游开发中的持续部署问题

在做《梦幻诛仙》的时候,曾有那么一段时间我虽名为主程序但实际不过是一个发版本的。互联网公司的人可能难以想象,一个项目,就为了发版本,完完全全占去了3-4个全职员工。我若不是自己写了大量自动化脚本,我的境地会更凄惨。09年和上海的同事一起吃饭的时候,说起svn merge的事情,得知他们的主程序也是在全职干这个,为这个占去了绝大多数时间。

就我观察而言,程序员大多数都是自我中心的,除非他是架构师他是主程序,否则他根本不会考虑他的行为会对项目发布进度造成什么影响。策划完全不懂软件工程,虽然他们指挥着一个高达4000多人月的大项目。而我的软件工程理论是在高中时自学的,泪奔……那时候的教科书是90s年写的,哪有什么敏捷开发、持续集成啊。

我们来看网游的开发模式是怎样?三四个人凑一起,拿着一个想法,迅速做出一个能演示的demo给领导看,然后立项,拉更多的人进来一起干。在后续的开发过程中,程序的整体框架不会做改动,只是策划不停的提新需求,然后不停的往里添新模块。然后编译,发给策划测试。然后策划给反馈BUG以及新的功能需求。《梦幻诛仙》在上线之后依然很长的一段时间内保持着一周4次的发布频率,发布周期之短令人乍舌。每次发版本都拖到下班以后加班才能完成,每天早上上班第一件事"SVN UP"然后立马有人吼:"TMD,谁提的代码,编不过"。你想,一大早起来,兴致勃勃的想干活但是发现代码编不过于是只能嘬着咖啡等着别人改代码再提交多沮丧啊!遇上脾气不好的,就开始吵了。

这矛盾的核心是,Trunk到底是什么,它有多稳定?最初,他们完全不懂SVN的分支怎么用。一年后,形成两派观点。一派是希望每人一个分支,每次有新功能就新开分支,稳定后合并到trunk上,每次发布都是从trunk上发代码(先提交到分支上后merge到trunk上)。我是另一个观点,所有人都在Trunk上工作,我根据项目发布需求新开分支并把bug fix合并过去(先提交到trunk后merge到其它分支)。无论是哪种方案,最终的目标是要能随时发布,所以用于发布的分支的代码一定要是稳定的,随时可以编译的,随时可以跑完所有自动化测试的。我经常会遇见这样的情况,谁谁新写的代码因为缺这个缺那个导致服务器起不来(很可能代码是没问题的,但是策划数据还没到位)。所以新加的功能最好是能做成开关项,这就要求模块化要清晰。我有段时间把自动编译脚本放到crontab中自动执行,如果出错,那么crontab就会产生mail,然后我就知道了。这个方法很土,因为实在是没有时间去做图形化监控界面。

然后就是部署的问题。我们从一开始就考虑到开发环境和运营环境应当是区别对待的。每个人都可以用非root身份以一条命令启动一套测试环境。他只需要根据配置文件的模板稍微修改一行即可(cp conf.m4.sample conf.m4),把其中的STARTPORT(开始端口号)改成和其它人不重复的。程序中所有的路径都是相对路径,庆幸的是JAVA不能更改workdir,于是问题变得很简单,所有路径根据当前路径+相对路径计算即可。后来运营的人问我"干嘛你这个项目的配置文件中的端口号都是算出来的而不是写死的",我解释了很久,因为需要考虑开发环境。部署的时候其实有两个截然不同的阶段,一是怎么从干净的操作系统上把基础设施搭好(如mysql、JDK),另一个是如何把自己的程序部署上去并搜集错误日志。前一个无非就是写好脚本一劳永逸,后一个是我最近需要很深的学习和考虑的问题。比如如何借助于RPM这样的系统自带的包管理器。我之前在开发阶段所采用的部署方法很土,如果编译成功了它会提交到另外一个SVN中,然后策划可以使用工具更新SVN并重启服务器。在服务器中可以通过GM命令查到SVN版本号。

之所以回忆这些问题是因为今天看到了一篇很好的文章,http://prettyprint.me/prettyprint.me/2011/01/24/continuous-deployment-at-outbrain/,作者是一个很有经验的程序员讲他们如何做持续部署的。着重推荐的是这张图:

我觉得,如何在迭代的过程中有效的持续集成持续部署是每个PM应当花时间好好思考的问题。就我所呆过的公司来看,我们为这个问题付出的代价太大了!

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥