Jenkins简介
Jenkins
一
Jenkins是一个CI工具。它可以持续编译,运行你的代码;运行UT或集成测试;将运行结果发送至邮件,或展示成报告。。。
这样做的最终目的是:
让项目保持健康的状态。如果任何checkinbreak了build,每个人都会在最短的时间内通知到,然后问题被fix。接下来的开发将建立在一个健康正确的基础上,而不是任由问题累积,最后失控。
最后,你的项目随时可以被deliver给用户,因为,你的项目每一天都在健康,茁壮的生长。这就是CI的意义所在。
Jenkin和cruisecontrol
Jenkins和cruisecontrol都是CI工具,二者在CI中发挥的作用完全一致。
而Jenkins作为新一代的CI工具,渐渐开始取代cruisecontrol。二者都是java程序,但:
1,Jenkins提供更为友好的用户界面,而cruisecontrol在界面方面糟糕的几乎等于没有。
2,Jenkins内置的功能提供了极大的便利,不论是新建一个build,还是日常使用,你需要做的大部分时候仅仅是在用户界面上点击而已。
在cruisecontrol新建build是通过创建config.xml来完成的。它仅仅提供非常有限的功能,很多时候你会发现,需要自己完成很多工作。
3,Jenkins作为一个欣欣向荣的开源项目,有大批的plugin。当你发现需要一个Jenkins本身并不提供的功能是,搜索一下plugin,总会有收获。非常多的流行工具如JBehave,cobertura都提供jenkins插件。
而针对cruisecontrol的plugin却很少。
4,Jenkins友好的用户界面让学习成本很少,你可以在最短的时间内开始你的工作。
事实上,Jenkins是我见过的最好的开源项目之一,它简洁实用的用户界面设计,完善的文档,丰富的插件。当你开始使用它,你就会爱上它。
当你需要一个build工具时,Jenkins几乎是当下的不二选择。
Jenkins和Hudson
Jenkins起源于Hudson。Hudson在商业软件的路上继续前行,而Jenkins则作为开源软件,从hudson分支出来。
因此现在的jenkins和hudson非常类似,但是随着二者各自的发展,已经有了一些不同。
二
官网首页就提供了windows版本的Jenkins安装包。我们可以下载一个用于学习。安装后自动打开http://localhost:8080,你就能看见Jenkins的界面了。
其他也需要安装的是:
1,Jenkins是java程序,因此需要安装JDK。
2,同时运行job需要提供repository,也就是存放Jenkins定期poll源代码的地方。我们可以去github免费注册一个。
3,如果想在Jenkins中使用ant,maven等,则还需要单独安装。但不是必须的。
启动Jenkins
Jenkins天生支持unix-likesystem。
•直接运行
好吧,Jenkins是一个java程序,所以要运行它,只需要:
$java-jarjenkins.war
我们也可以使用nohup命令,让Jenkins在后台运行。
之后打开URLhttp://myServer:8080就可以方便的操作Jenkins了
官网给了一个sh的例子,用于启动Jenkins。可以参考一下。
•在Servletcontainer中运行
Alternatively,ifyouhaveaservletcontainerthatsupportsServlet2.4/JSP2.0,suchasGlassfishv2,Tomcat5(oranylaterversions),thenyoucanrunthemasservices,anddeployjenkins.warasyouwouldanyotherwarfile.
Forexample,
youcouldsimplyplacethejenkins.warfileinTomcat’swebappsdirectory.此时使用的URL默认就变成:
http://localhost:8080/jenkins
同时Jenkins提供一些默认不会启动的特殊的功能,参考下面的link来enable它们。
https://wiki.jenkins-ci.org/display/JENKINS/Features+controlled+by+system+properties
Jenkins的目录结构
和CruiseControler一样,Jenkins需要一个目录来存储相关文件:JENKINS_HOME。默认为~/.jenkins。即为user的home目录下的一个隐藏目录。我们也可以更改JENKINS_HOME,指向我们希望的地方。
(注意因为是隐藏目录,所以需要使用ls-al才能看到)
JENKINS_HOME
+-config.xml(jenkinsrootconfiguration)
+-*.xml(othersite-wideconfigurationfiles)
+-userContent(filesinthisdirectorywillbeservedunderyourhttp://server/userContent/)
+-fingerprints(storesfingerprintrecords)
+-plugins(storesplugins)
+-jobs
+-[JOBNAME](subdirectoryforeachjob)
+-config.xml(jobconfigurationfile)
+-workspace(workingdirectoryfortheversioncontrolsystem)
+-latest(symboliclinktothelastsuccessfulbuild)
+-builds
+-[BUILD_ID](foreachbuild)
+-build.xml(buildresultsummary)
+-log(logfile)
+-changelog.xml(changelog)
如果有权限管理,则在HOME目录下还会有users目录。
从目录结构来看,和CruiseController非常相似。其中config.xml是Jenkins重要的配置文件。我们都知道Jenkins用于monitor多个build,而jobs这个目录无疑就是存储每个build相关信息的地方。
总的来说,Jenkins目录结构非常直白,简洁。
备份和恢复
备份和恢复非常简单,就是简单的copyJenkins的目录就好了:
Allthesettings,buildlogs,artifactarchivesarestoredundertheJENKINS_HOMEdirectory.Simplyarchivethisdirectorytomakeabackup.Similarly,restoringthedataisjustreplacingthecontentsoftheJENKINS_HOMEdirectoryfromabackup.
移动/拷贝/重命名job
由于每个jobs都有自己单独的目录,我们可以很容易的:
1,moveajobfromoneinstallationofJenkinstoanotherbysimplycopyingthecorrespondingjobdirectory.
,2,makeacopyofanexistingjobbymakingacloneofajobdirectorybyadifferentname.
3,renameanexistingjobbyrenamingadirectory.
修改后执行下面的命令刷新:
http://[jenkins-server]/[command]
在这里[command]可以是:exit退出,restart重启,reload重载。
创建一个Project
因为Jenkins可以用于运行各种CI,测试,批处理任务等等,所以在Jenkins中将这些任务统称为“free-stylesoftwareproject”.
Jenkins也提供了其他类型的jobs,例如:
1,如果项目是Maven,Jenkins还提供了一种仅用于MavenProject的job。但其实free-stylesoftwareprojec仍然可以用于创建Maven项目,只不过这种更适合Maven项目,结合的更好而已。
2,也可以创建一个"Monitoranexternaljob“用于监控外部进程。
3,或者一个Matrixproject,也就是multi-configurationproject。
我不确定是否仅有这4种job,也许使用插件可以创建更多类型的job,大家自己看资料吧。
下面是如何创建一个最常见的“free-stylesoftwareproject"的过程:
GotoJenkinstoppage,select"NewJob",thenchoose"Buildafree-stylesoftwareproject".Thisjobtypeconsistsofthefollowingelements:
•optionalSCM,suchasCVSorSubversionwhereyoursourcecoderesides.指定源代码在哪。
Note:Insoftwareengineering,softwareconfigurationmanagement(SCM)isthetaskoftrackingandcontrollingchangesinthesoftware.
•optionaltriggerstocontrolwhenJenkinswillperformbuilds.指定Jenkins何时触发一次build。
•somesortofbuildscriptthatperformsthebuild(ant,maven,shellscript,batchfile,etc.)wheretherealworkhappens触发build时,使用的脚本文件,例如ant。在这个脚本文件中,我们可以从svn下载最新代码,删除上次build的临时文件,创建必要目录,编译源代码,运行,打包等一系列工作。
•optionalstepstocollectinformationoutofthebuild,suchasarchivingtheartifactsand/orrecordingjavadocandtestresults.收集log信息。
•optionalstepstonotifyotherpeople/systemswiththebuildresult,suchassendinge-mails,IMs,updatingissuetracker,etc.通知相关人员build的结果。
例如我们使用的buildscript就是ant,在Jenkins中运行ant。在脚本文件中,可以直接使用Jenkins提供的一些变量。
同时,每个job可以有多个step。例如:将运行程序定义为step1,运行单元测试定义为step2,生成coverage报告定义为step3。
同时还可以定义post-buildaction。例如:生成javadoc,或清理程序运行的临时文件目录等。
自动运行Build
触发一个build有三种方式:
•BuildsinJenkinscanbetriggeredperiodically(onaschedule,specifiedinconfiguration)这里定义schedule的语法是unix常见的cron语法。
•Orwhensourcechangesintheprojecthavebeendetected
可以设置Jenkins定时检查SVN是否发生了变化,也可以手动检查:http://YOURHOST/jenkins/job/PROJECTNAME/pollong。也可以设置Jenkins为post-commit,这个方式尤其适用于那些检查是否代码改变会花费很长时间的情况。
•OrtheycanbeautomaticallytriggeredbyrequestingtheURL:
http://YOURHOST/jenkins/job/PROJECTNAME/build
Distributedbuilds
Jenkinssupportsthe"master/slave"mode,wheretheworkloadofbuildingprojectsaredelegatedtomultiple"slave"nodes,allowingsingleJenkinsinstallationtohostalargenumberofprojects,orprovidedifferentenvironmentsneededforbuilds/tests.
在现实中需要使用distributedbuilds情况很多,例如:一个webapplication的build,需要分别验证firefox和IE的行为,那么就需要到windows机器上运行IE。
或因为性能问题,将build分布到多个slave节点去。
到Jenkins的管理界面,就可以方便的添加节点。配置节点时,需要提供节点所在的机器,登陆用户名密码,使用的目录等。
但是slave并不需要再安装Jenkins。jenkins会自动启用slaveagent,将build需要tools考到远程机器上。
需要注意的是:thebuildresultsandartifactswillalwaysenduponthemasterserver.因此不需要跑到各个节点去查看build产生的文件,log等。
其实在slave节点,会创建一个本地的workspace,并在运行时使用这个workspace。因为毕竟build运行在slave节点上,所以这个节点肯定要有运行build需要的所有因素。
总之添加节点并远程运行build真是太方便了~
添加节点后,在masterJenkinshome目录下会出现关于该节点的配置文件。
Jenkins将自动决定在哪个节点上运行build,根据下列策略:
Someslavesarefaster,whileothersareslow.Someslavesarecloser(networkwise)toamaster,othersarefaraway.Sodoingagoodbuilddistributionisachallenge.Currently,Jenkinsemploysthefollowingstrategy:
1Ifaprojectisconfiguredtosticktoonecomputer,that'salwayshonored.
2Jenkinstriestobuildaprojectonthesamecomputerthatitwaspreviouslybuilt.
3Jenkinstriestomovelongbuildstoslaves,becausetheamountofnetworkinteractionbetweenamasterandaslavetendstobelogarithmictothedurationofabuild(IOW,evenifprojectAtakestwiceaslongtobuildasprojectB,itwon'trequiredoublenetworktransfer.)Sothisstrategyreducesthenetworkoverhead.
Jenkins通过运行slaveagents来完成分布式build。最常见的情况是:slaveagent运行在各个slave节点。master通过SSH远程启动/停止slaveagent,进而控制各个节点的行为。
一共有下列4种方式启动slaveagent:
•Themasterstartstheslaveagentsviassh
•StartingtheslaveagentmanuallyusingJavaWebStart
•InstallingtheslaveagentasaWindowservice
•Startingtheslaveagentdirectlyfromthecommandlineontheslavemachinefromthecommandline
需要注意的是这4种方式适用于不同的情况,例如slave节点在防火墙后,导致master无法通过SSH启停slaveagent。此时只能用后三种方式,但是往往有一些弊端,比如master无法自动停止/重启slaveagent.
一旦添加node成功,你就可以在job中指定这个job在哪个node运行:
Restrictwherethisprojectcanberun(如果不指定则由Jenkins自行决定,即可以在slave节点运行,也可以在master节点运行,甚至在一次build中就可以自行来回切换)。
配置Jenkins,让它收集更多的log
https://wiki.jenkins-ci.org/display/JENKINS/Logging
我想这对于初学Jenkins的人,用来判断问题所在太有用了。
在流行持续集成的今天,我们在各个环境:alpha,beta和production都部署了唯一的Jenkins服务器。
Jenkins服务器统一负责该环境内所有组件的持续集成。也就是说,一个Jenkins服务器会有很多个build。所以有时会用到上面提到的”Monitoranexternaljob“。
但不是DistributedBuilds。
同时由于Jenkins会进入每个环境,包括production,因此会使用autodeployment的方式,自动完成Jenkins在各个环境的部署。
用户管理
毫无疑问Jenkins中需要有用户管理的功能,因为除开发人员外,有多种角色的人需要查看build的结果。
在Jenkins中的系统管理中,可以设置“任何用户可以做任何事”或“登录用户可以做任何事”。
因此前一个选项意味着,任何浏览JenkinsURL的用户都可以修改Jenkins。或只有登录用户才能做修改。
所以我把Jenkins中的用户划分为两类:可登录用户和不可登录用户。
1,只要是修改过repository,即对build产生过影响的用户,都会被Jenkins记录在本地的database中。这类用户我们可以在Jenkins界面->查看用户中浏览。
但这类用户不是正式的Jenkins用户,也不能登录Jenkins。这类用户的权限由上面说的系统管理中的配置决定。通常只有查看build的权限,没有修改权限。
2,只有在Jenkins中明确注册的用户,才能够登录Jenkins,并且有权限控制。同时注册过的用户,在JENKINS_HOME目录下的users目录下,都有一个单独的目录来存储相关信息。我不太清楚是否能够通过copy/paste将用户部署到其他地方去,回头测试一下。
既然要满足各种人对jenkins使用的各种需求,因此权限管理远没有这么简单。具体大家还得自己去看啊~
JenkinsScriptConsole
Jenkins提供了一个scriptconsoleGroovyscriptwhichallowstorunarbitraryscriptsontheJenkinsserveroronslavenodes.Thisfeaturecanbeaccessedfromthe"manageJenkins"link。
也可以通过URL直接访问:http://myserver:8080/hudson/script
可惜只能用Groovy反证我不懂。
三
在使用Jenkins的过程中,当然也会遇到一些问题。在这里我把我遇到的,比较奇怪的问题列出来,供大家参考。
环境变量
我在一个slavenode上运行job时发现,被启动的程序显示找不到环境变量。
原来,当Jenkins在slave上启动一次build时,不会应用当前用户的profile。因此我们得自己解决这个问题。
解决方式有很多:
1,在创建slavenode时,可以指定要传递的环境变量。这种方法不好的地方就是,相当于hardcode,当实际的环境变量改变时,也需要手工修改slavenode的配置。
2,想办法自己应用:运行shell前运行:./usr/usr_account/.profile(注意第一个点后面有空格),这个是最好的方法,因为所有的环境变量都会生效,而不是仅仅某一个。
或者在调用的ant中,或kshscript中指定某个具体的环境变量。
进程被kill
需求是这样的:在Jenkins中一个job运行完毕时,可以配置它触发下一个job运行。这种情况很常见,例如一个job中启动一个程序并运行了UT,当运行完毕时,我们可能会希望一个集成测试的job被触发。这里隐含的意思就是,我们希望程序能一直运行,直到所有的测试结束,最后再显式的停止这个程序。
然而在实施的过程中,我遇到了一个大问题:在第一个job运行结束后,程序就被杀掉了。第二个job没有运行测试的时间。
来看看这个人遇到的类似的问题:
Iamtryingtorunashellscripttostop/starttheJBossinstance,butwhentheHudsonjobcompletes,theJBossinstanceshutdowns.DoesanybodyhaveanyrecommendationsonhowwecanspawnaninstanceofJBossfromaHudsonjobwithoutusingthedaemonizescript?
由于现实中事情的复杂性(在Jenkins中调用了ant,ant调用了ksh,ksh最终启动了程序),我绕了很大一圈才怀疑到Jenkins上。在这之前,我尝试在ant中使用<parallel>,在调用ksh时使用nohup,但是问题依旧。
而问题的根本在于是Jenkins使用processTreeKiller杀掉了所有子进程,而且这是Jenkins的默认行为。其实回头来看这个问题,就发现Jenkins的做法非常合理。当一次build异常结束,或被人终止时,必然需要结束所有这次build启动的子进程。下面的link提供了更多细节,以及解决方法。
https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller
Jenkinshome目录下的tools目录没有被同步到slavenode
在Jenkins的home目录下,有两个自动生成的目录:toolsandusercontent。
当我在使用slavenode时,我注意到在每个slavenode的上,Jenkins自动建了一个tools目录。因此我想当然的以为,tools目录是用来存放用户自己需要的第三方工具的。
例如,我在project中需要使用ant1.8.2。而tools中则存放了ant1.8.2,同时在建立slavenode时,Jenkins自动将ant1.8.2拷贝到了slavenode的tools下。
然而在之后的使用中,我发现在masternode的tools中放置其他的第三方工具后,slavenode的tools中并没有增加。因此slavenode上的project并不能使用对应的第三方工具。
事实真相是,我应该使用usercontent目录,而不是直接使用tools目录。
当用户需要某个第三方工具时,正确的做法是将安装包放到usercontent目录。然后在Jenkinssystemmanagement中要求安装这个工具。
userContent目录的说明:
YoucanusethisdirectorytoplaceyourowncustomcontentontoyourJenkinsserver.Youcanaccessfilesinthisdirectoryathttp://myserver/hudson/userContent(ifyouarerunning
Jenkinsonanapplicationserver)orhttp://myserver/userContent(ifyouarerunninginstandalonemode).
之后Jenkins会自动将该工具安装到tools目录下,同时也会同步到slavenode的tools下。像我这样直接扔在masternode的tools下,是不会自动同步的。
还有一种解决方案:
Jenkins并没有机制自动将masternode上的tools目录下的内容同步到slavenode上。这意味着如果在slavenode上运行project需要某些特殊工具(或jar包),只能事先在slavenode所在的box上安装好(或准备好)。
但是在某些极端条件下,slavenode并没有准备好这些工具。
例如,我们使用Jenkins来自动deployment最新的release到production,而安装release意味着很多附加功能,例如环境检查,安装程序,自动启停程序,启动cluster等复杂操作。
我们使用Antscript来完成这些操作。但Ant在production环境没有安装。
因此我希望将ant相关的jar放到masterjenkins根目录的tools目录,然后将tools同步到slavenode的tools目录下,之后在slavenode上运行Ant就可以使用这些jar包了。
但是目前我们只能通过使用一个工具rsync来将tools下的内容同步到slavenode:
Onmaster,Ihavealittleshellscriptthatusesrsynctosynchronizemaster's/var/jenkinstoslaves(except/var/jenkins/workspace)Iusethistoreplicatetoolsonallslaves.
如何取得slavenode上的jenkinshome
我在脚本里,或Ant中使用环境变量JENKINS_HOME的时候发现一个问题:尽管限制了job在slavenode上运行,但是这个变量的值还是masternode的JENKINS_HOME。而不是在slavenode的配置中配置的“RemoteFSroot”
一个解决方案是,在创建slavenode时,在配置页面下方的nodeproperties中添加一个环境变量:SLAVE_HOME=上面配置的“RemoteFSroot”目录。这样就可以在slavenode上使用这个变量了。
Youcanalsospecifyenvironmentvariables.Thesewillbepassedintoyourbuildjobs,andcanbeagoodwaytoallowyourbuildjobstobehavedifferentlydependingonwheretheyarebeingexecuted.
比如在ant中,加入:
<propertyenvironment="env"/>来获取所有的环境变量。
之后用<echomessage="homeis${env.SLAVE_HOME}"/>就可以了。
这里顺便提一下,有一个plugin可以显示每次build时,jenkins自动生成的环境变量。在shell,ant中都可以方便的使用这些变量。
在所有的projects中传递参数一个需求:有3个projects,run.program,run.ut,run.integration.test
它们使用在一个project完成后自动trigger另外一个的方式,形成了一个chain。同时第一个project是parameterized-project,即build时需要提供输入参数。其中一个输入参数是,booleanifRunUT,是否运行UT。
现在,如果在第一个project中选择不运行UT,则skip后面的那个project,之后继续运行第三个project,也就是集成测试。
解决方法是安装这个plugin:https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin
同时将后面的run.ut改为parameterized-project,同时有唯一的一个输入参数ifRunUT。
上面的plugin可以将第一个project的输入参数传递到它触发的下一个project。这样当后面的run.ut发现ifRunUT=false时,就自动返回successful,之后该project继续触发之后的run.integration.test。
听上去有点无聊,但是试想一下,如果有两个关于ut的project:run.ut.1run.ut.2。上面的方法就可以一次配置,skip掉两个projects,而不需要配置多次。
更重要的是,这种做法使得整个chain的配置都集中在一个地方,而不是分散在各个project中。每次build都需要去多个projects配置参数是很让人崩溃的。