Jenkins
Jenkins是一个CI工具。它可以持续编译,运行你的代码;运行UT或集成测试;将运行结果发送至邮件,或展示成报告。。。
这样做的最终目的是:
让项目保持健康的状态。如果任何checkin break了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非常类似,但是随着二者各自的发展,已经有了一些不同。
要想更多的了解Jenkins,当然最好是访问它的官方网站。也可以参考我的下一篇文章:
Jenkins(二) http://blog.csdn.net/onlyqi/article/details/7076915
至少和官网比起来,它是中文的~
官网:https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins
我的这篇文章仅仅是简单的根据上文,介绍Jenkins提供了哪些功能。具体大家还是要自己学习啦~
官网首页就提供了windows版本的Jenkins安装包。我们可以下载一个用于学习。安装后自动打开http://localhost:8080,你就能看见Jenkins的界面了。
其他也需要安装的是:
1,Jenkins是java程序,因此需要安装JDK。
2,同时运行job需要提供repository,也就是存放Jenkins定期poll源代码的地方。我们可以去github免费注册一个。
3,如果想在Jenkins中使用ant,maven等,则还需要单独安装。但不是必须的。
启动Jenkins
Jenkins天生支持unix-like system。
- 直接运行
好吧,Jenkins是一个java程序,所以要运行它,只需要:
$ java -jar jenkins.war
我们也可以使用nohup命令,让Jenkins在后台运行。
之后打开URL http://myServer:8080 就可以方便的操作Jenkins了
官网给了一个sh的例子,用于启动Jenkins。可以参考一下。
- 在Servlet container中运行
Alternatively, if you have a servlet container that supports Servlet 2.4/JSP 2.0, such as Glassfish v2, Tomcat 5 (or any later versions), then you can run them as services, and deployjenkins.war as you would any other war file.
For example,
you could simply place the jenkins.war file in Tomcat’s webapps directory. 此时使用的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 (jenkins root configuration)
+- *.xml (other site-wide configuration files)
+- userContent (files in this directory will be served under your http://server/userContent/)
+- fingerprints (stores fingerprint records)
+- plugins (stores plugins)
+- jobs
+- [JOBNAME] (sub directory for each job)
+- config.xml (job configuration file)
+- workspace (working directory for the version control system)
+- latest (symbolic link to the last successful build)
+- builds
+- [BUILD_ID] (for each build)
+- build.xml (build result summary)
+- log (log file)
+- changelog.xml (change log)
如果有权限管理,则在HOME目录下还会有users目录。
从目录结构来看,和CruiseController非常相似。其中config.xml是Jenkins重要的配置文件。我们都知道Jenkins用于monitor多个build,而jobs这个目录无疑就是存储每个build相关信息的地方。
总的来说,Jenkins目录结构非常直白,简洁。
备份和恢复
备份和恢复非常简单,就是简单的copy Jenkins的目录就好了:
All the settings, build logs, artifact archives are stored under the JENKINS_HOME directory. Simply archive this directory to make a back up. Similarly, restoring the data is just replacing the contents of the JENKINS_HOME directory from a back up.
移动/拷贝/重命名 job
由于每个jobs都有自己单独的目录,我们可以很容易的:
1,move a job from one installation of Jenkins to another by simply copying the corresponding job directory.
,2,make a copy of an existing job by making a clone of a job directory by a different name.
3,rename an existing job by renaming a directory.
修改后执行下面的命令刷新:
http://[jenkins-server]/[command]
在这里[command]可以是:exit 退出,restart 重启, reload 重载。
创建一个Project
因为Jenkins可以用于运行各种CI,测试,批处理任务等等,所以在Jenkins中将这些任务统称为“free-style software project”.
Jenkins也提供了其他类型的jobs,例如:
1,如果项目是Maven,Jenkins还提供了一种仅用于Maven Project的job。但其实free-style software projec仍然可以用于创建Maven项目,只不过这种更适合Maven项目,结合的更好而已。
2,也可以创建一个"Monitor an external job“用于监控外部进程。
3,或者一个Matrix project,也就是multi-configuration project。
我不确定是否仅有这4种job,也许使用插件可以创建更多类型的job,大家自己看资料吧。
下面是如何创建一个最常见的“free-style software project"的过程:
Go to Jenkins top page, select "New Job", then choose "Build a free-style software project". This job type consists of the following elements:
- optional SCM, such as CVS or Subversion where your source code resides. 指定源代码在哪。
Note: In software engineering, software configuration management (SCM) is the task of tracking and controlling changes in the software.
- optional triggers to control when Jenkins will perform builds. 指定Jenkins何时触发一次build。
- some sort of build script that performs the build (ant, maven, shell script, batch file, etc.) where the real work happens 触发build时,使用的脚本文件,例如ant。在这个脚本文件中,我们可以从svn下载最新代码,删除上次build的临时文件,创建必要目录,编译源代码,运行,打包等一系列工作。
- optional steps to collect information out of the build, such as archiving the artifacts and/or recording javadoc and test results. 收集log信息。
- optional steps to notify other people/systems with the build result, such as sending e-mails, IMs, updating issue tracker, etc. 通知相关人员build的结果。
例如我们使用的build script就是ant,在Jenkins中运行ant。在脚本文件中,可以直接使用Jenkins提供的一些变量。
同时,每个job可以有多个step。例如:将运行程序定义为step1,运行单元测试定义为step2,生成coverage报告定义为step3。
同时还可以定义post-build action。例如:生成javadoc,或清理程序运行的临时文件目录等。
自动运行Build
触发一个build有三种方式:
- Builds in Jenkins can be triggered periodically (on a schedule, specified in configuration) 这里定义schedule的语法是unix常见的cron语法。
- Or when source changes in the project have been detected
可以设置Jenkins定时检查SVN是否发生了变化,也可以手动检查:http://YOURHOST/jenkins/job/PROJECTNAME/pollong。也可以设置Jenkins为post-commit,这个方式尤其适用于那些检查是否代码改变会花费很长时间的情况。
- Or they can be automatically triggered by requesting the URL:
http://YOURHOST/jenkins/job/PROJECTNAME/build
Distributed builds
Jenkins supports the "master/slave" mode, where the workload of building projects are delegated to multiple "slave" nodes, allowing single Jenkins installation to host a large number of projects, or provide different environments needed for builds/tests.
在现实中需要使用distributed builds情况很多,例如:一个web application的build,需要分别验证firefox和IE的行为,那么就需要到windows机器上运行IE。
或因为性能问题,将build分布到多个slave节点去。
到Jenkins的管理界面,就可以方便的添加节点。配置节点时,需要提供节点所在的机器,登陆用户名密码,使用的目录等。
但是slave并不需要再安装Jenkins。jenkins会自动启用slave agent,将build需要tools考到远程机器上。
需要注意的是:the build results and artifacts will always end up on the master server. 因此不需要跑到各个节点去查看build产生的文件,log等。
其实在slave节点,会创建一个本地的workspace,并在运行时使用这个workspace。因为毕竟build运行在slave节点上,所以这个节点肯定要有运行build需要的所有因素。
总之添加节点并远程运行build真是太方便了~
添加节点后,在master Jenkins home目录下会出现关于该节点的配置文件。
Jenkins将自动决定在哪个节点上运行build,根据下列策略:
Some slaves are faster, while others are slow. Some slaves are closer (network wise) to a master, others are far away. So doing a good build distribution is a challenge. Currently, Jenkins employs the following strategy:
- If a project is configured to stick to one computer, that's always honored.
- Jenkins tries to build a project on the same computer that it was previously built.
- Jenkins tries to move long builds to slaves, because the amount of network interaction between a master and a slave tends to be logarithmic to the duration of a build (IOW, even if project A takes twice as long to build as project B, it won't require double network transfer.) So this strategy reduces the network overhead.
Jenkins通过运行slave agents来完成分布式build。最常见的情况是:slave agent运行在各个slave 节点。master通过SSH远程启动/停止slave agent,进而控制各个节点的行为。
一共有下列4种方式启动slave agent:
•The master starts the slave agents via ssh
• Starting the slave agent manually using Java Web Start
• Installing the slave agent as a Window service
• Starting the slave agent directly from the command line on the slave machine from the command line
需要注意的是这4种方式适用于不同的情况,例如slave节点在防火墙后,导致master无法通过SSH启停slave agent。此时只能用后三种方式,但是往往有一些弊端,比如master无法自动停止/重启 slave agent.
一旦添加node成功,你就可以在job中指定这个job在哪个node运行:
Restrict where this project can be run (如果不指定则由Jenkins自行决定,即可以在slave节点运行,也可以在master节点运行,甚至在一次build中就可以自行来回切换)。
配置Jenkins,让它收集更多的log
https://wiki.jenkins-ci.org/display/JENKINS/Logging
我想这对于初学Jenkins的人,用来判断问题所在太有用了。
在流行持续集成的今天,我们在各个环境:alpha,beta和production 都部署了唯一的Jenkins服务器。
Jenkins服务器统一负责该环境内所有组件的持续集成。也就是说,一个Jenkins服务器会有很多个build。所以有时会用到上面提到的”Monitor an external job“。
但不是Distributed Builds。
同时由于Jenkins会进入每个环境,包括production,因此会使用auto deployment的方式,自动完成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使用的各种需求,因此权限管理远没有这么简单。具体大家还得自己去看啊~
Jenkins Script Console
Jenkins提供了一个script console Groovy script which allows to run arbitrary scripts on the Jenkins server or on slave nodes. This feature can be accessed from the "manage Jenkins" link。
也可以通过URL直接访问:http://myserver:8080/hudson/script
可惜只能用Groovy 反证我不懂。
在使用Jenkins的过程中,当然也会遇到一些问题。在这里我把我遇到的,比较奇怪的问题列出来,供大家参考。
环境变量
我在一个slave node上运行job时发现,被启动的程序显示找不到环境变量。
原来,当Jenkins在slave上启动一次build时,不会应用当前用户的profile。因此我们得自己解决这个问题。
解决方式有很多:
1,在创建slave node时,可以指定要传递的环境变量。这种方法不好的地方就是,相当于hard code,当实际的环境变量改变时,也需要手工修改slave node的配置。
2,想办法自己应用:运行shell前运行:. /usr/usr_account/.profile (注意第一个点后面有空格),这个是最好的方法,因为所有的环境变量都会生效,而不是仅仅某一个。
或者在调用的ant中,或ksh script中指定某个具体的环境变量。
进程被kill
需求是这样的:在Jenkins中一个job运行完毕时,可以配置它触发下一个job运行。这种情况很常见,例如一个job中启动一个程序并运行了UT,当运行完毕时,我们可能会希望一个集成测试的job被触发。这里隐含的意思就是,我们希望程序能一直运行,直到所有的测试结束,最后再显式的停止这个程序。
然而在实施的过程中,我遇到了一个大问题:在第一个job运行结束后,程序就被杀掉了。第二个job没有运行测试的时间。
来看看这个人遇到的类似的问题:
I am trying to run a shell script to stop/start the JBoss instance, but when the Hudson job completes, the JBoss instance shutdowns. Does anybody have any recommendations on how we can spawn an instance of JBoss from a Hudson job without using the daemonize script?
由于现实中事情的复杂性(在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
Jenkins home目录下的tools目录没有被同步到slave node
在Jenkins的home目录下,有两个自动生成的目录: tools and usercontent。
当我在使用slave node时,我注意到在每个slave node的上,Jenkins自动建了一个tools目录。因此我想当然的以为,tools目录是用来存放用户自己需要的第三方工具的。
例如,我在project中需要使用ant 1.8.2。而tools中则存放了ant 1.8.2,同时在建立slave node时,Jenkins自动将ant1.8.2拷贝到了slave node的tools下。
然而在之后的使用中,我发现在master node的tools中放置其他的第三方工具后,slave node的tools中并没有增加。因此slave node上的project并不能使用对应的第三方工具。
事实真相是,我应该使用usercontent目录,而不是直接使用tools目录。
当用户需要某个第三方工具时,正确的做法是将安装包放到usercontent目录。然后在Jenkins system management中要求安装这个工具。
userContent目录的说明:
You can use this directory to place your own custom content onto your Jenkins server. You can access files in this directory at http://myserver/hudson/userContent (if you are running
Jenkins on an application server) or http://myserver/userContent (if you are running in standalone mode).
之后Jenkins会自动将该工具安装到tools目录下,同时也会同步到slave node的tools下。像我这样直接扔在master node的tools下,是不会自动同步的。
还有一种解决方案:
Jenkins 并没有机制自动将master node上的tools目录下的内容同步到slave node上。这意味着如果在slave node上运行project需要某些特殊工具(或jar包),只能事先在slave node所在的box上安装好(或准备好)。
但是在某些极端条件下,slave node并没有准备好这些工具。
例如,我们使用Jenkins来自动deployment最新的release到production,而安装release意味着很多附加功能,例如环境检查,安装程序,自动启停程序,启动cluster等复杂操作。
我们使用Ant script来完成这些操作。但Ant在production环境没有安装。
因此我希望将ant相关的jar放到master jenkins根目录的tools目录,然后将tools同步到slave node的tools目录下,之后在slave node上运行Ant就可以使用这些jar包了。
但是目前我们只能通过使用一个工具rsync来将tools下的内容同步到slave node:
On master, I have a little shell script that uses rsync to synchronize master's/var/jenkins to slaves (except/var/jenkins/workspace) I use this to replicate tools on all slaves.
如何取得slave node上的jenkins home
我在脚本里,或Ant中使用环境变量JENKINS_HOME的时候发现一个问题: 尽管限制了job在slave node上运行,但是这个变量的值还是master node的JENKINS_HOME。而不是在slave node的配置中配置的“Remote FS root”
一个解决方案是,在创建slave node时,在配置页面下方的node properties中添加一个环境变量:SLAVE_HOME=上面配置的“Remote FS root”目录。这样就可以在slave node上使用这个变量了。
比如在ant中,加入:
<property environment="env"/> 来获取所有的环境变量。
之后用<echo message="home is ${env.SLAVE_HOME}"/>就可以了。
在所有的projects中传递参数
一个需求:有3个projects,run.program, run.ut, run.integration.test
它们使用在一个project完成后自动trigger另外一个的方式,形成了一个chain。同时第一个project是parameterized- project,即build时需要提供输入参数。其中一个输入参数是,boolean ifRunUT,是否运行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.1 run.ut.2。上面的方法就可以一次配置,skip掉两个projects,而不需要配置多次。
更重要的是,这种做法使得整个chain的配置都集中在一个地方,而不是分散在各个project中。每次build都需要去多个projects配置参数是很让人崩溃的。