zoukankan      html  css  js  c++  java
  • 记录使用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的本来面目:

    终于解决问题后已经心力交瘁,我先给出我最后的解决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还是很好用的,除了测试它还能做很多事情。上面的不少坑我只是解决了问题,或者绕开了。真正的原因我还没有完全分析完,也希望大家多多指教。

  • 相关阅读:
    Mysql常用函数总结(二)
    mysql百万的数据快速创建索引
    php 中的sprintf 坑
    php5.5之后新特性整理
    mysql实践总结
    php下载远程图片到本地
    搜藏一个php文件上传类
    Attribute基本介绍
    Fiddler4抓包工具使用教程一
    HTTP传输数据压缩
  • 原文地址:https://www.cnblogs.com/lkiversonlk/p/4885565.html
Copyright © 2011-2022 走看看