【干货】一次早期自动化构建的搭建过程

斯年java 2016-08-11 12:35:19
这是老王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测试页面 (非必需)

...全文
385 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
斯年java 2016-08-11
  • 打赏
  • 举报
回复
发布页面的入口如下: 这个页面的信息主要来自/data/qzone_build/source下的每个目录下的buildres.xml, 通过第一列各个日期的连接可以进入查看相应的编译目录,通过查看详细信息,可以了解具体的编译情况: 4 上图页面的信息主要来自各个目录的ccversion.xml文件,ccversion.xml文件本身不会出现在列表中. 上图页面的信息来自相应的buildres.xml 注:由于源码目录下文件可能会被手工增删或改动,有时有些信息文件无法正确读取,这时php会发出警告,这些都是可以忽略的,可以通过/usr/local/lib/php.ini改变php的告警级别(仅显示错误): error_reporting = E_ERROR 2. 关键工具的使用说明 2.1. svntools.pl的用法 用途: 拉取svn工程源代码的工具脚本. 配置文件: svntools.conf, 需要和svntools.pl放在同一个目录下, 其中设置远程SVN服务器的连接信息(IP,USER,PASSWD等). 使用方法: svntools [[-i|-u] local_directory|local_file]|[-e]|[-h] -i local_directory|local_file 显示目录或文件在远程和本地的版本信息; -u local_directory|local_file 更新本地目录或文件; -e 查看或编辑配制文件; -h 显示帮助信息. 注意: 命令行中的目录和文件参数支持相对和绝对路径;路径字符串中不支持通配符(*)和正则表达式. 其中srcroot定义了本地的源代码更新位置和远端svn地址, -i, -u选项后边的文件路径都应该在srcroot以内. 使用[svntools.pl -u 目录名]更新时不会删除服务器上不存在而在本地存在的目录内部文件;但使用[svntools.pl -u 文件名]时会做删除. 如果文件以及父目录都在服务器上不存在,只会删除文件而不会删除父目录. cctool.pl –i 文件名可以比较本地文件和服务器上相应文件的CC信息,输出内容分两行显示,分别以L和S开头,L (local)代表本地文件信息,S (server)代表服务器端文件信息 2.2. setroot.sh的用法 用途: 设置更新和(或)编译目录的软连接 使用方法: setroot.sh[-b][-u][-d date|-p directory [-f]][-h] -b set build source root -u set update source root -ddate use /data/qzone_build/source/qzone_$dateas base dir (fmt: YYYYMMDD) -pdir use specified directory as basedir -f use with -d or -p, force createdirectory if not exists -h show help information 说明: 如果没有(用-d或者-p)指定具体目录,则会使用当天的日期当作-d的参数,即缺省采用–d + 当天日期 -f选项会在 –d或者-p指定的目录不存在时创建目录 -b会将指定的目录软连接到/usr/local/qzone_v3.0; -u会将指定的目录软连接到/data/qzone_build/temp/src 2.3. confgen.pl的用法 用途: 根据makefile生成make.conf 使用方法:confgen.pl [-i original_makefile|-o outputfile_confile] [-h hint_file] -i original_makefile, original makefile tobuild conf file; -o outputfile_confile, name of output makeconfig file; -f hint_file, a file in which includedefault conf values; -h, print help messages. 说明: 缺省的输入文件是/usr/local/qzone_v3.0/makefile, 但可以通过-i选项指定输入文件;缺省的输出文件是/usr/local/qzone_v3.0/make.conf, 但可以通过-o选项指定输出文件; -f 用来指定缺省配置所在的文件,如果不指定的话,则会尝试使用confgen.pl同一目录下的make.conf.def,其中可以指定编译目录的依赖关系,以及包括,排除列表的缺省值,这些值会合并到最终生成的make.conf文件中(参见mkgen.pl的用法说明) 2.4. mkgen.pl的用法 用途: 根据make.conf生成全构建脚本build.sh 使用方法: mkgen.pl [-i include_list|-e exclude_list][-h] [-f hint_file] source_directory -i include_list, ';' splited list forinclude sub_dirs; -e exclude_list, ';' splited list forexclude sub_dirs; -f hint_file, a file in which listinginclude/exclude sub_dirs; -h, print help messages. 说明: 不一定要使用make.conf文件,(但是如果在没有用-f 明确指定配制文件名,而/usr/local/qzone_v3.0/make.conf存在,则会尝试使用它), 可以在命令行中通过 –i,-e指定参加构建的或者不参加构建的子目录(如果-i,-e指定了同一个目录,则以-e为准) make.conf一个很重要的用途是用来指明编译中的依赖关系,帮助确定编译顺序,下边是一个例子: [order] platform/src/comm=16 comm=8 platform=4 main=2 [include] portal/src/fcg blog/src/blog music/src/discuss/fcg main/src/mall izone/src/favor 其中order一节定义了不同目录的优先级,数值越大越先编译,(也就是说,越多的其他的代码依赖于它). 优先级的设置使用目录关键字=级别的形式定义,当目录中包含关键字时(同一个关键字多次出现仅计算一次),会将相应的级别加到该目录的总级别,最终的编译脚本build.sh会按照各个目录的级别由高到低依次编译(级别相同的目录意味着他们之间的编译顺序不影响结果,因此只按照目录字母顺序做简单排序), 不含关键字的目录级别为0, 比如: platform/src/comm 级别 16 platform/src/c5a/c5atool 级别 4 main/src/comm 级别 10 blog/src/comm 级别 8 izone/src/favor 级别 0 music/src/comm 级别 8 建议使用2的幂做为级别的数字,以便拉开差距,防止低级别的目录级别相加后超过更优先的级别. mkgen.pl每次生成的build.sh都有一个不同的时间戳(选用当前系统时间),这个时间戳相当于build.sh的版本,不同版本的build.sh生成的log文件名不同,这些log的名称格式是: build_***.log其中***就是它的时间戳 2.5. writemail.pl,postmessage.pl的用法 用途: 这两个工具脚本联合起来完成邮件通知编译结果的功能.它们应该放在同一个目录下,并且还会在同一目录下寻找message.conf文件,其中包含发送邮件配制信息,例如: [server] ip=192.168.?.? user=qzonebuilder@tencent.com port=25 [local] msgbox=/data/qzone_build/temp/mbox 其中msgbox指定了发件箱的位置,writemail.pl缺省会将新建的邮件放进这里. l writemail的使用方法: writemail.pl [-i build resultfile] [-h] [-o output mail file] -i build result file's path & name -o output mail file's path & name -h print help messages 说明: 如果没有用-i 指定编译结果文件,则尝试使用/usr/local/qzone_v3.0/buildres.xml,如果没有用-o指定生成的邮件的存放位置,则会根据build.sh的时间戳生成一封名为build_$timestamp.mail的文件放进message.conf中定义的msgbox中. l postmessage.pl的使用方法: postmessage.pl [-fmessage_box|message_file ]|[-a]|[-e]|[-h] -f message_box|message_file 指定邮箱路径或者邮件文件名,邮件文件必须以.mail做为名称后缀 -a 自动发送缺省配置的邮箱内的所有邮件和消息; -e 查看或编辑配制文件; -h 显示帮助信息. 3. crontab使用说明 3.1. crontab脚本 工程编译crontab脚本: 工程清理crontab脚本(保留一天的工程): 每天的译结果汇总邮件: 至此整个过程结束!

590

社区成员

发帖
与我相关
我的任务
社区描述
提出问题
其他 技术论坛(原bbs)
社区管理员
  • community_281
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧