这是老王07年进入腾讯接手的第一个项目---自动化构建AutoBuild(06年就已经在上线运行),当年还不知道有Hudson,以及后来更名的Jenkins。做完这个项目之后再去反思传统的软件工程就有了很多新的感悟,只有从根源上提出解决方案,并配合最佳的实践才能解决软件交付的质量问题。
也因为腾讯web端项目基本上都是使用的CGI,所以花了不少时间写整套的自动化编译脚本(全部是perl写的),当然里面对研发提出了编译文件Makefile的规范(后面有原理)。
在后续的版本演进中,把自动化测试、代码安全扫描、持续发布都加入了整体过程,真正的做到全过程持续交付,直到今天和客户去讲持续交付整体过程的时候,很多实践和感悟都是来源于此。
*****建议在你们的项目中实践持续集成,不要以为这不是运维的职责****
以下过程是我们早期完全自研构建持续集成的过程,仅供参考!
1. 编译环境的搭建
1.1. 整体框图
注:ARS是腾讯的Auto Release System,早期我也是作为运维主要需求方参与这个平台的构建和推广,直到所有的业务页面端发布都接入了这一平台。持续部署的过程也就真正成为现实。
说明:
1编译机器支持每天自动编译,以及手工触发编译两种构建方式.
1.2. SVN Server端的配置
每加入一个工程,需要找PM给自动化编译用户isd_webadmin授权只读访问该工程的权限。具体的申请方法:告诉对方工程和用户即可。
1.3. 公共代码配置
/data/auto_build/ // 主目录
|-- bin // 公共程序目录
| |-- svntools.pl // 拉取svn服务器中代码文件,获取最后更新信息的工具,编译
| |-- confgen.pl // 根据qzone项目源码根目录中的makfile文件,生成构建脚本构造文件(make.conf)的工具
| |-- mkgen.pl // 根据构建脚本构造文件(make.conf)生成全构建脚本build.sh的工具,编译环境的关键脚本
| |-- postmessage.pl // 发送邮件的工具(详见后边说明)
| |-- readlog.pl // 读取编译过程中的日志文件,生成编译结果报告buildres.xml的工具,关键脚本
| `-- writemail.pl // 根据编译结果报告build.sh生成通知邮件的工具
|-- config // 项目工程配置目录
| |-- abs_profile.conf // 所有工程的配置
`-- mbox // 存放每天所有工程编译结果的汇总邮件
1.4. 编译机的配置
编译机的这边需要的软件有:
l Apache (版本没有特殊要求) + PHP模块的支持
l PHP 4.4.2 (或者高版本) + DOM + ICONV
l PERL 5.8 (或者高版本) + DOM + ENCODE + HASHINI
建议使用官方源码包编译安装,编译顺序和选项请参考
编译环境的目录结构如下,我们以/data/项目_build做为编译环境的主目录,实际部署中可以修改脚本中的变量设置改变这个目录,只要相应的分区空间足够大,可以支持整个项目的全编译即可.
/data/qzone_build/ // 主目录
|-- admin // 编译管理目录(详见后边说明)
| |-- bin // 编译控制脚本的存放目录
| |-- cgi-bin // qzone.build.isd.com 的cgi程序目录
| |-- htdocs //qzone.build.isd.com 的页面文件目录
|-- source // 项目代码,编译结果的实际存放位置
| |-- qzone_20060829 // 这些以日期作为后缀的子目录,存放着相应日期参加日构建的源码和编译结果
| `-- qzone_20060831 // 可以根据需要保留近几天的历史结果,太早的通过简单脚本删除或者打包备份
`-- temp // 存放一些临时文件或者动态目录
|-- mbox // 发送编译结果通知邮件的信箱(详见后边说明)
`-- src -> /data/qzone_build/source/qzone_20060831 // 一个到当前代码存放目录的软连接, svntools默认会向这里
// 更新最新的代码 (详见后边说明).
admin目录下的文件有:
/data/qzone_build/admin/
|-- bin
| |-- autobuild.sh // 自动编译的总控制脚本,可以直接写到crontab中定时自动运行
| |--buildall.sh // 完整的一次编译过程的控制脚本, 不包含更新代码,发送邮件的过程
| |-- buildres.xsl // buildres.xml需要的xsl风格表单(详见后边说明)
| |-- svntools -> svntools.pl // 为方便使用做的一个软连接
| |-- svntools.conf // svntools.pl的配制文件,它应该始终和svntools.pl放在同一个目录下
| |-- svntools.pl // 拉取svn服务器中代码文件,获取最后更新信息的工具,编译
| | // 环境的关键控制脚本(详见后边说明)
| |-- confgen.pl // 根据qzone项目源码根目录中的makfile文件,生成构建脚本构造文件(make.conf)的工具
| |-- crlf // 将单个文件中每行的结尾标志字符\n(UNIX习惯),替换为\r \n(WINDOWS习惯)的工具
| |-- crlf.c // crlf工具的源代码
| |-- make.conf.def // 构建脚本构造文件(make.conf)的缺省设置文件,供confgengen.pl参考使用
| |-- message.conf // postmessages.pl的配制文件,它应该始终和postmessages.pl放在同一个目录下
| |-- mkgen.pl // 根据构建脚本构造文件(make.conf)生成全构建脚本build.sh的工具,编译环境的关键脚本
| |-- postmessage.pl // 发送邮件的工具(详见后边说明)
| |-- readlog.pl // 读取编译过程中的日志文件,生成编译结果报告buildres.xml的工具,关键脚本
| |-- setroot.sh // 设置文件更新/编译目录连接的工具,关键脚本(详见后边说明)
| |-- u2wlog.sh // 批量转换文件换行字符 \nà \r \n的工具 (内部循环调用crlf程序)
| `-- writemail.pl // 根据编译结果报告build.sh生成通知邮件的工具
|-- cgi-bin
`-- htdocs
|-- buildres.xsl ->/data/qzone_build/admin/bin/buildres.xsl //xsl表单的软连接,将原始文件放在bin中是为方便编辑
|-- index.php // build.qzone.isd.com的入口页面
|-- mainlist.php // 生成目录列表页面的工具脚本
|--slist.php // 文件列表页面
`--test.php // php测试页面 (非必需)
需要的环境变量:
PATH_QZONE_PRJ="/usr/local/qzone_v3.0"
export PATH_QZONE_PRJ
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/c4a/bin:/usr/lib
PATH_SVNTOOLS="/data/qzone_build/admin/bin"
export PATH=$PATH:$PATH_SVNTOOLS
PATH_QZONE_PRJ是qzone编译时必需的环境变量, PATH_SVNTOOLS加入路径中是为了使用方便(非必要).
整个编译环境中各个工具都可以普通用户执行,比如user_*,但应该保证相应的源码目录有足够的读写权限.
1.5. 编译过程
上边是编译的总体流程图,图中黄色圆形标注是编译过程的顺序,依次说明如下(各个控制脚本都放在admin/bin下):
1. 通过setroot.sh在source目录下选取或建立一个日构建的代码存放目录,比如qzone_20060831,并且将它软连接到/usr/local/qzone_v3.0(编译代码的目录),/data/qzone_build/temp/src(svntools.pl更新代码的目录). (注:将代码保存路径,编译目录,更新目录分开是为了支持历史编译结果备份和灵活更新)
2. 使用svntools.pl拉取远程svn服务器上的项目代码,更新到本地的/data/qzone_build/temp/src目录. svntools.pl更新文件的时候会将文件的最后更新信息(时间,更新人,CC事件)写到它所在的的目录下的ccversion.xml中
3. 自动编译系统一般是使用编译配置文件make.conf (位于源代码根目录)生成全编译脚本,但是如果项目代码根目录中有整体makefile文件,则可以使用confgen.pl读取这个makefile生成编译配制文件make.conf
4. 使用mkgen.pl读取源代码根目录下边的编译配置文件make.conf (这个文件中定义了要参加编译的子目录,以及这些目录间的依赖关系,即编译顺序,它可以通过makefile生成,也可以手工编写),在源代码根目录下生成全编译脚本build.sh
5. 上一步生成的build.sh是个可执行的bash脚本,直接运行就可以编译整个qzone代码,编译过程中的提示信息会被写入每个参加编译的子目录下,保存为build_***.log名字的文件,其中***代表build.sh的时间戳(详见后边说明)
6. 编译结束后,使用readlog.pl读取参加编译的各个子目录中的log文件,生成一份报告文件buildres.xml,记录编译结果(出错/异常/警告信息等)
7. 编译机同时兼做编译结果的发布WEB站点,允许大家查看参加编译的代码目录和各个log文件,因为UNIX环境下文本文件的换行符号\n与windows环境下的\r\n不一致,所以这些log在windows下查看时格式比较乱,使用u2wlog.sh可以对各个log文件做UNIXàWINDOWS的换行符替换
8. writemail.pl可以读取编译结果报告文件builres.xml中的关键信息,生成一封发给qzone开发组的通知邮件build_***.mail(其中***是build.sh的时间戳),邮件会放在约定好的发件箱/data/qzone_build/temp/mbox中
9. 最后调用postmessage.pl,它会将发件箱中的所有邮件(*.mail)发送出去(成功发送后将删除原始邮件).
注: admin/bin下边的autobuild.sh中按照上边的顺序串联了整个编译过程, 可以当作总的控制脚本,直接写到crontab中每天定时运行,实现自动编译; 另一个工具buildall.sh是一次完整的编译过程的控制脚本, 不包含更新代码,发送邮件的过程
1.6. build.qzone.isd.com
这是由几个简单的php页面构成的编译结果发布站点,需要在Apache的httpd.conf中作如下配置:
<VirtualHost *>
DocumentRoot /data/qzone_build/admin/htdocs
ServerName build.qzone.isd.com
DirectoryIndex index.php
ScriptAlias /cgi-bin/ "/data/qzone_build/admin/cgi-bin/"
Alias /src/ "/data/qzone_build/source/"
AddType text/plain .cpp .c .h .sh .js .vbs .css .conf .ini .log
ErrorLog logs/build.qzone.isdcom-error_log
</VirtualHost>
全部的页面都放在/data/qzone_build/admin/htdocs下:
/data/qzone_build/admin/htdocs/
|-- buildres.xsl ->/data/qzone_build/admin/bin/buildres.xsl // xsl表单的软连接,将原始文件放在bin中是为方便编辑
|-- index.php //build.qzone.isd.com的入口页面
|-- mainlist.php //生成目录列表页面的工具脚本
|-- slist.php // 文件列表页面
`-- test.php // php测试页面 (非必需)