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配置参数是很让人崩溃的。

相关推荐