记录使用Buildbot遇到的坑
Buildbot Tips
Buildbot也是个大坑。。我并不熟悉python,偏偏文档又少。这几天使用buildbot出了不少坑。有的解决了,有的绕过去,这里都把它们一一记下来。
Force Build
第一个坑就是False Build,正常情况下在Web页面上的builder栏里,会有一个Force Build按钮。点击按钮会强制开始Build,这对于调试Buildbot非常重要。但是我的页面上没有。。。
这个坑还算小,其实是自动生成的master.cfg文件中设置了只有通过认证的才能Force Build,改法也简单,如下:
authz_cfg=authz.Authz(
forceBuild = True,
cancelPendingBuild = False,
)
c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
有关Buildbot的ShellCommand
我做的是一个web server,为了做测试我需要在build结束后启动它,然后用client给它发消息进行测试。server基于nodejs,因此启动命令是npm start。
我在slave的build里添加ShellCommand,然后slave会一直停留在这一步不往下走。OK,这个可以理解,server启动后是会一直在等待,我加个&把它后台运行吧,于是加上&,结果不行,还是等待。然后我试了setsid, nohup全部不行。。。
受不了了,我最后跑去看buildbot的源码。原来它的shellcommand都是通过spawn一个新进程的方式来执行的,然后通过回调的形式获取执行结果。参见Twistd SpawnProcess,回调的IProcessProtocol, 接收执行结果的hook描述如下:
def processEnded(reason): (source)
Called when the child process exits and all file descriptors associated with it have been closed.
Parameters reason A failure giving the reason the child process terminated. The type of exception for this failure is either twisted.internet.error.ProcessDone or twisted.internet.error.ProcessTerminated. (type: twisted.python.failure.Failure )
Anyway,我试了各种办法也没法让buildbot在启动web server后继续往下走。只能考虑绕过去了,有两个饶的思路:
- 把WebServer做成Service,比如利用Supervisor之类的,这样slave只需要做一个client发出一个启动通知。
- 我采用的办法是另外写了一个slave,这个slave就负责build和start web server,另一个slave来做测试。
这个坑已经很磨人了。。。但是我决定另外用一个Slave之后遇到了一个更大的坑。。。。
Codebase
我在看Buildbot的文档的时候就看到过好多次Codebase的概念,但是文档里都是一笔带过。当时心里就嘀咕这玩意儿看起来会很麻烦。果然就遇上了!
我用一个Slave从一个Repository去拉下我的Web Server源代码,然后用另一个Slave在另一个Repository拉下测试数据和测试框架代码。谋算着第一个Slave把Web Server启动起来,然后另一个Slave启动测试程序把测试请求一个一个发出去做测试,我很快写完了Buildbot配置文件,然后噩梦来了。
第二个Slave拉代码会出错!
fatal: Could not parse object '9810ad5734d29523739206a28042fae87344c19b'.
啥?从Git上拉下来出错?我完全没搞明白,Google到的结果完全不像是一回事。这个看起来是Git的问题,我把Repository从Github移到Bitbucket也还是一样,我又换了几个Git Repository做实验,都会出错,但是只要两个Slave拉的是同一个仓就OK,搞毛线?
怎么办,只有老老实实的读了一下slave的那个繁琐的log。终于看到一行:
Cloning into '.'...
program finished with exit code 0
elapsedTime=14.558712
git reset --hard 9810ad5734d29523739206a28042fae87344c19b --
这不就是那个没法parse的号么,看起来就是那个reset --hard的问题。两个slave拉同一个仓就ok,拉不同的仓就出错,我推断是Buildbot的代码版本管理出了问题,貌似用来处理多仓库的关键就是Codebase了,好吧,那Codebase是什么?
坑爹的Buildbot对于Codebase基本啥都没写,完全不知道那是个什么东西。
我拼命的Google之后,从以下几个链接中终于推断出来了Codebase的本来面目:
- http://markmail.org/thread/ne6hdbf4wyhx25cj#query:+page:1+mid:ilx7ahbyzu4jdm64+state:results
- https://github.com/ethereum/ethereum-buildbot
- http://docs.buildbot.net/latest/manual/concepts.html
- http://docs.buildbot.net/latest/manual/cfg-global.html#cfg-codebaseGenerator
终于解决问题后已经心力交瘁,我先给出我最后的解决code,具体的讲解以后再补上。。
repositories = {
r'repository url of module1' : 'module1',
r'repository url of module2' : 'module2',
}
def codebaseGenerator(chdict):
return repositories[chdict['repository']]
module1_codebases = {
'module1' : {
'repository' : 'repository url',
'branch' : 'master',
'revision' : None
}
}
module2_codebases = {
'module2' : {
'repository' : 'repository url',
'branch' : 'master',
'revision' : None
}
}
...
c['schedulers'].append(schedulers.SingleBranchScheduler(
name="***",
change_filter=util.ChangeFilter(branch='master'),
treeStableTimer=None,
codebases = module1_codebases,
builderNames=["builder-***"]))
c['schedulers'].append(schedulers.Triggerable(
name="scheduler-***",
builderNames=["builder-***"],
codebases = module2_codebases))
c['schedulers'].append(schedulers.ForceScheduler(
name="force",
codebases = ["module1", "module2"],
builderNames=["builder-exchange"]))
...
module1Factory.addStep(steps.Git(
repourl="*****"
, name="pull code"
, description="git codes"
, descriptionDone="code pulled"
, mode='full'
, codebase="module1"
, method='clobber'))
module2Factory.addStep(steps.Git(
repourl="****"
, name="pull data"
, description="git pulling test data"
, descriptionDone="test data pulled"
, mode='full'
, codebase='module2'
, method='clobber'))
Hosts
这也是一个小坑,我本来在Dockerfile里写了
RUN echo -e "127.0.0.1 dsp" >> /etc/hosts
然而,启动image之后,根本就没有作用。。不过当我用docker exec直接在运行中的Container修改/etc/hosts,又是可以工作的。这是为啥?。。
终于找到了原因:
Docker will generate /etc/hosts dynamically every time you create a new container. So that it can link others. You can use --add-host option:
docker run --add-host www.domain.com:8.8.8.8 ubuntu ping www.domain.com
OMG,每次新Container的/etc/hosts是会自动生成的。。。这跟Docker的Volumn一样,是个大坑啊!因为Dockfile生成Image的机制是每行命令都会用一个新的Container来运行,然后Commit Image,所以我在这一行做的修改再下一行立刻就被新的Container覆盖了。。
解决方案也很简单,使用--add-host就行了。
Supervisor控制Nodejs程序
这个坑其实跟Docker没什么关系,但是因为Docker会经常需要运行Supervisor,所以暂且先放在这里。
我在docker里用supervisor反复跑tests时我启动的nodejs server不稳定,总是突然就挂掉了,一直不明白是什么原因。反复测试之后发现,即使我用Supervisorctl stop去停掉了nodejs server,它也根本没停,端口仍然在被占用,所以反复跑时之后的启动server命令就会出错。
奇怪,原因是什么呢?
我勘察了一下我的supervisor conf文件
[program:exchange]
command=npm start
directory=/data/buildbot/exchange/builder-exchange/exchange
autostart=false
对于Supervisor的具体工作原理我并不了解,不过我猜想是它每次启动一个service后,通过父进程或者记录pid的方式“控制”这个service,需要停止时把它kill掉。那么问题可能就是出在这个npm start上面了。在Express 4里,启动脚本默认为bin/www,那么正常的启动命令应该是node bin/www。因为npm会读取package.json中设置的start项运行方式,调用npm start,也会最终运行node bin/www,但是因为这一个中间层,可能导致supervisor无法准确的记录service了,我估计npm的工作方式是另外spawn一个进程来运行最后对应的命令,这样当我们调用supervisor stop时,它会试着去杀掉npm。。。导致最后的Nodejs程序仍然在运行中。
fix也很简单:
[program:exchange]
command=node bin/www
directory=/data/buildbot/exchange/builder-exchange/exchange
autostart=false
把command改成正确的就可以了。
这个坑好难绕,我还不确定我现在的推断是否正确,不过80%可能是这样吧。
结束语
还会不停的添加新的坑。。肯定的。。Buildbot还是很好用的,除了测试它还能做很多事情。上面的不少坑我只是解决了问题,或者绕开了。真正的原因我还没有完全分析完,也希望大家多多指教。
记录使用Buildbot遇到的坑的更多相关文章
- CozyRSS开发记录0-RSS阅读器开坑
CozyRSS开发记录0-RSS阅读器开坑 1.RSS RSS,全名是Really Simple Syndication,简易信息聚合. 关于RSS相关的介绍,网上可以很容易的找到.RSS阅读器是我几 ...
- 记录Jackson和Lombok的坑
记录Jackson和Lombok的坑 今天遇到Jackson反序列化json缺少了字段,后来研究下发现是Jackson的机制和Lombok生成的setter不一致,导致没有正确调用setter. 复现 ...
- 【bug记录】OS Lab4 踩坑记
OS Lab4 踩坑记 Lab4在之前Lab3的基础上,增加了系统调用,难度增加了很多.而且加上注释不详细,开玩笑的指导书,自己做起来困难较大.也遇到了大大小小的bug,调试了一整天. 本文记录笔者在 ...
- 【bug记录】OS Lab3 踩坑记
OS Lab3 踩坑记 Lab3在之前Lab2的基础上,增加了进程建立.调度和中断异常处理.其中测试包括进程建立以及进程调度部分. 由于是第一次做bug记录,而且是调试完bug后再做的记录,所以导致记 ...
- ios 记录支付宝集成遇到的坑及解决方法
今天项目中要开始动手集成支付宝支付,在此小结一下.(目前新版的支付宝SDK有较大改版,去集成还需要自己去开发平台详细的按照集成步骤来完成https://doc.open.alipay.com/docs ...
- 记录pageHelper分页orderby的坑
pageHelper的count查询会过滤查询sql中的order by条件! pageHelper分页功能很强大,如果开启count统计方法,在你执行查询条件时会再执行一条selet count(* ...
- wrk 使用记录及踩过的坑
wrk是什么?https://github.com/wg/wrk wrk 是一个非常小巧高效的开源性能测试工具,支持lua脚本来创建复杂的测试场景.wrk 的一个很好的特性就是能用很少的线程压出很大的 ...
- .NET Core 从1.1升级到2.0记录(Cookie中间件踩坑)
.NET Core 2.0 新时代 万众瞩目的.NET Core 2.0终于发布了,原定于9.19的dotnetconf大会的发布时间大大提前了1个月,.NET Core 2.0/.NET Stand ...
- UWP开发细节记录:DirectX::XMMATRIX 的坑
这两天写的代码概率性的崩溃在 XMMatrixMultiply() 函数,XMMatrixMultiply() 本身是 inline 函数可以看到崩溃处的代码: vX = _mm_mul_ps(vX, ...
随机推荐
- dubbo 序列化 问题 属性值 丢失 ArrayList 解决
参考文章:http://blog.csdn.net/wanyanxgf/article/details/6944733 http://tianya23.blog.51cto.com/1081650/5 ...
- 【BZOJ4384】[POI2015]Trzy wieże 树状数组
[BZOJ4384][POI2015]Trzy wieże Description 给定一个长度为n的仅包含'B'.'C'.'S'三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符 ...
- 170215、监听器(HttpSessionListener)在开发中的应用
这个监听器主要用于网站在线超时退出的功能. OnlineUserListener实现了HttpSessionListener定义的两个方法: sessionCreated()和sessionDestr ...
- 160624、Spark读取数据库(Mysql)的四种方式讲解
目前Spark支持四种方式从数据库中读取数据,这里以Mysql为例进行介绍. 一.不指定查询条件 这个方式链接MySql的函数原型是: 1 def jdbc(url: String, table: S ...
- 客户也可以申请它使用的最后一个IP地址。如果该客户所在的网络中此IP仍然可用,服务器就可以准许该申请。
http://baike.baidu.com/item/IP地址 公有地址 公有地址(Public address)由Inter NIC(Internet Network Information Ce ...
- vim_action
读取文件,显示行号 nl -a.txt brace expansion 花括号扩展 echo a{A{1,2},B{3,4}}b mkdir {2009...2011}-0{1...9} {2009. ...
- Spring4学习笔记-AOP(基于配置文件的方式)
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://shamrock.blog.51cto.com/2079212/1557743 引 ...
- JQuery基础与事件和动画
JQuery语法 1.JQuery("选择器").action; 通过选择器调用时间函数 但Jquery可以用$符号代替,即$("选择器").action; ① ...
- Redis资料汇总(转)
原文:Redis资料汇总专题 很多朋友反映,说NoSQLFan上的资料不少,但是要找到自己实用的太难,于是萌生做这样一个专题的想法.通过将不同NoSQL产品从入门到精通的各种资料进行汇总,希望能够让大 ...
- 23种设计模式UML图