Jenkins使用及插件開(kāi)發(fā)介紹
介紹
Jenkins是一個(gè)廣泛用于持續(xù)構(gòu)建的可視化web工具,就是各種項(xiàng)目的的“自動(dòng)化”編譯吝沫、打包呻澜、分發(fā)部署,將以前編譯惨险、打包羹幸、上傳、部署到Tomcat中的過(guò)程交由Jenkins辫愉,Jenkins通過(guò)給定的代碼地址栅受,將代碼拉取到j(luò)enkins宿主機(jī)上,進(jìn)行編譯恭朗、打包和發(fā)布到web容器中窘疮。Jenkins可以支持多種語(yǔ)言(比如:java、c#冀墨、php等等)闸衫,也兼容ant、maven诽嘉、gradle等多種第三方構(gòu)建工具蔚出,同時(shí)跟git、svn無(wú)縫集成虫腋,也支持直接與github直接集成骄酗。
WiseBuild也是基于jenkins進(jìn)行的開(kāi)發(fā),在下面會(huì)看到很多和WiseBuild相似的地方
安裝
- 到j(luò)enkins官網(wǎng)http://jenkins.io/下載war包
使用
java -jar jenkins.war
或者
將war放到web容器中悦冀,啟動(dòng)web容器
啟動(dòng)war包趋翻,會(huì)自動(dòng)將war包解壓到~/.jenkins目錄下,并且生成一些目錄和配置文件盒蟆,我們?cè)趈enkins中配置的job也會(huì)保存到這個(gè)目錄下
打開(kāi)瀏覽器踏烙,輸入localhost:8080 就可以訪問(wèn)到j(luò)enkins的web界面了
新建項(xiàng)目
用個(gè)小例子簡(jiǎn)單示范一下jenkins的使用
源碼配置
將源碼信息配置上去,我選擇一個(gè)github上面的項(xiàng)目历等,如果源碼管理中沒(méi)有g(shù)it這個(gè)選項(xiàng)讨惩,只需要到系統(tǒng)管理中添加git這個(gè)插件即可
構(gòu)建命令
在構(gòu)建階段輸入以下命令:
cd ${WORKSPACE} && ./gradlew build && mv ${WORKSPACE}/docker/jpetstore.war /usr/local/tomcat9/webapps
該命令分為三個(gè)部分:
-
cd ${WORKSPACE}
WORKSPACE是jenkins的定義的環(huán)境變量,代表該項(xiàng)目對(duì)應(yīng)的文件路徑寒屯,該項(xiàng)目檢出的源碼也是該目錄栅迄。類似的環(huán)境變量還有BUILD_NUMBER, BUILD_ID, JOB_NAME, JENKINS_HOME等等 -
./gradlew build
使用gradle 執(zhí)行構(gòu)建命令,將檢出的源碼編譯打包為war包奢米,這里我們使用的構(gòu)建工具是gradle痴怨,如果是使用maven,可以mvn clean package
-
mv {WORKSPACE}/docker/jpestore.war /usr/local/tomcat9/webapps/
將打包好的war包手動(dòng)放到tomcat的webapps目錄下,以便Tomcat能啟動(dòng)該項(xiàng)目了
最后,點(diǎn)擊保存回到主面板上。
構(gòu)建
點(diǎn)擊右邊的立即構(gòu)建
開(kāi)始執(zhí)行構(gòu)建农渊,可以看見(jiàn)構(gòu)建的進(jìn)度,旁邊的#12 就是本次構(gòu)建的構(gòu)建號(hào)(BUILD_NUMBER)
也可以在查看console output
console output 會(huì)顯示出本次構(gòu)建的一些日志信息
這里我們web容器和jenkins都是在同一臺(tái)服務(wù)器上或颊,可以利用shell命令來(lái)進(jìn)行手動(dòng)部署砸紊,如果jenkins的宿主機(jī)和web服務(wù)器不是同一臺(tái),我們也可以利用gradle和maven的部署功能囱挑,例如使用mvn deploy來(lái)將項(xiàng)目部署到遠(yuǎn)程服務(wù)器上
到此醉顽,我們的一個(gè)持續(xù)集成的一個(gè)項(xiàng)目就已經(jīng)搭建好了,現(xiàn)在一旦我們對(duì)代碼修改進(jìn)行提交平挑,然后jenkins就會(huì)獲取最新的代碼然后按照我們上面配置的命令進(jìn)行構(gòu)建和部署游添。
jenkins插件
在前面我們看見(jiàn)jenkins可以支持git, svn, maven等很多功能,這些都是Jenkins的插件通熄,jenkins本身不提供很多功能唆涝,我們可以通過(guò)使用插件來(lái)滿足我們的使用,接下來(lái)就介紹一下插件的原理以及我們?cè)趺赐ㄟ^(guò)寫(xiě)一個(gè)自己的插件來(lái)滿足我們的需求唇辨。
擴(kuò)展點(diǎn)
但是jenkins有很多的擴(kuò)展點(diǎn)(ExtensitonPoint)廊酣,它是Jenkins系統(tǒng)的某個(gè)方面的接口或抽象類。這些接口定義了需要實(shí)現(xiàn)的方法赏枚,而Jenkins插件需要實(shí)現(xiàn)這些方法亡驰,也可以叫做在此擴(kuò)展點(diǎn)之上進(jìn)行擴(kuò)展Jenkins。有關(guān)擴(kuò)展點(diǎn)的詳細(xì)信息饿幅,請(qǐng)參閱Jenkins 官方ExtentionPoints文檔凡辱。通過(guò)這些擴(kuò)展點(diǎn)我們可以寫(xiě)插件來(lái)實(shí)現(xiàn)自己的需求。
下面是一些常用的擴(kuò)展點(diǎn):
-
Scm :代表源碼管理的一個(gè)步驟栗恩,如下面的Git透乾,Subversion就是擴(kuò)展的Scm
-
Builder : 代表構(gòu)建的一個(gè)步驟,如下圖中在構(gòu)建過(guò)程中磕秤,我們可以增加一個(gè)構(gòu)建步驟乳乌,而每一個(gè)選項(xiàng)都是對(duì)應(yīng)一個(gè)Builder,在每一個(gè)Builder中都有自己不同的功能亲澡。如Execute shell钦扭,這就是一個(gè)ShellBuilder,意味著在構(gòu)建過(guò)程中會(huì)執(zhí)行一個(gè)shell命令
-
Trigger:代表一個(gè)構(gòu)建的觸發(fā)床绪,當(dāng)滿足一個(gè)什么樣的條件時(shí)觸發(fā)這個(gè)項(xiàng)目開(kāi)始構(gòu)建。比較常用的觸發(fā)就是當(dāng)代碼變更時(shí)觸發(fā),如果我們需要實(shí)現(xiàn)一些比較復(fù)雜的觸發(fā)邏輯癞己,就需要擴(kuò)展Trigger這個(gè)擴(kuò)展點(diǎn)
-
Publisher:Publisher代表一個(gè)項(xiàng)目構(gòu)建完成后需要執(zhí)行的步驟膀斋,如選項(xiàng)中的E-Mail Notifaction就是一個(gè)Publisher插件,選擇這個(gè)選項(xiàng)后痹雅,當(dāng)項(xiàng)目構(gòu)建完成仰担,就會(huì)使用email來(lái)通知用戶,假如想要在項(xiàng)目構(gòu)建完成后將構(gòu)建目標(biāo)產(chǎn)物發(fā)送到服務(wù)器上绩社,則可以擴(kuò)展此擴(kuò)展點(diǎn)摔蓝。
上面簡(jiǎn)單描述了一下插件和擴(kuò)展點(diǎn),接著我們可以搭建一個(gè)插件的開(kāi)發(fā)環(huán)境
插件開(kāi)發(fā)環(huán)境搭建
首先需要安裝:
- maven3
- jdk6+
安裝完成后愉耙,修改maven目錄下的settings.xml文件
- linux :
~/.m2/settings.xml
- windows :
%USERPROFILE%\.m2\setttings.xml
<settings>
<pluginGroups>
<pluginGroup>org.jenkins-ci.tools</pluginGroup>
</pluginGroups>
<profiles>
<!-- Give access to Jenkins plugins -->
<profile>
<id>jenkins</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- change this to false, if you don't like to have it on per default -->
</activation>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<mirrors>
<mirror>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
<mirrorOf>m.g.o-public</mirrorOf>
</mirror>
</mirrors>
</settings>
使用如下命令創(chuàng)建一個(gè)新的插件
mvn org.jenkins-ci.tools:maven-hpi-plugin:create (或者 mvn hpi:create)
需要輸入插件的groupId贮尉,artifactId, 然后會(huì)在當(dāng)前目錄創(chuàng)建一個(gè)jenkins插件的骨架目錄(熟悉maven的同學(xué)知道這個(gè)一個(gè)標(biāo)準(zhǔn)的maven項(xiàng)目目錄結(jié)構(gòu))
插件目錄結(jié)構(gòu):
- pom.xml: maven使用這個(gè)文件來(lái)構(gòu)建插件,所有的插件都是基于Plugin Parent Pom
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.2</version>
</parent>
- src/main/java:java源碼
- src/main/resources:jelly視圖文件朴沿,用于在web界面上顯示
- src/main/webapp: 靜態(tài)的資源文件猜谚,例如圖片和html文件
導(dǎo)入到IDE
- intellij idea:直接在ide中導(dǎo)入pom文件就能導(dǎo)入
- eclipse:運(yùn)行如下命令
mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes -Declipse.workspace=/path/to/workspace eclipse:eclipse eclipse:configure-workspace
調(diào)試插件
- linux
export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
mvn hpi:run
- windows
set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
mvn hpi:run
輸入命令過(guò)后可以打開(kāi)瀏覽器,輸入:http://localhost:8080/jenkins,就可以看見(jiàn)你的插件在jenkins中運(yùn)行起來(lái)了赌渣,現(xiàn)在就可以開(kāi)始進(jìn)行調(diào)試了魏铅。
修改端口
mvn hpi:run -Djetty.port =8090
設(shè)置上下文路徑
mvn hpi:run -Dhpi.prefix=/jenkins
打包發(fā)布插件
mvn package
該命令會(huì)在target目錄創(chuàng)建出 ‘插件名稱’.hpi 文件,其他用戶可以直接將這個(gè)插件上傳安裝到Jenkins中使用(或者放到$JENKINS_HOME/plugins目錄中)坚芜。
Jenkins插件之HelloWorld
在之前我們使用
mvn hpi:create
創(chuàng)建插件目錄時(shí)览芳,Jenkins在我們的項(xiàng)目中生成了一個(gè)HelloWorldBuilder的插件,這是一個(gè)官方示例鸿竖,下面帶大家分析一下這個(gè)插件的示例源碼
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
}
首先創(chuàng)建一個(gè)類繼承于Builder路操,代表使用這個(gè)插件是一個(gè)構(gòu)建插件(如果繼承于Scm,代表這個(gè)插件是一個(gè)源碼插件千贯,例如Git屯仗,Svn插件),然后實(shí)現(xiàn)SimpleBuildStep接口
在Jenkins的插件中搔谴,每一個(gè)插件類中都必須要有一個(gè)Descriptor內(nèi)部靜態(tài)類魁袜,它代表一個(gè)類的’描述者‘,用于指明這是一個(gè)擴(kuò)展點(diǎn)的實(shí)現(xiàn)敦第,Jenkins是通過(guò)這個(gè)描述者才能知道我們自己寫(xiě)的插件
每一個(gè)‘描述者’靜態(tài)類都需要被@Extension注解峰弹,Jenkins內(nèi)部會(huì)掃描@Extenstion注解來(lái)知道注冊(cè)了有哪些插件。
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
private boolean useFrench;
public DescriptorImpl() {
load();
}
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return true;
}
public String getDisplayName() {
return "Say hello world";
}
@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
save();
return super.configure(req,formData);
}
public boolean getUseFrench() {
return useFrench;
}
}
在Desciptor類中有兩個(gè)方法需要我們必須要進(jìn)行重寫(xiě)
public boolean isApplicable(){
return true;
}
這個(gè)方法的返回值代表這個(gè)Builder在Project中是否可用芜果,我們可以將我們的邏輯寫(xiě)在其中鞠呈,例如判斷一些參數(shù),最后返回true或者false來(lái)決定這個(gè)Builder在此處是否可用
public String getDisplayName(){
return "Say hello world";
}
這個(gè)方法返回的是一個(gè)String類型的值右钾,這個(gè)名稱會(huì)用在web界面上顯示的名稱
如果我們?cè)诓寮行枰@取一些系統(tǒng)設(shè)置參數(shù)蚁吝,我們可以在Descriptor中獲取
一個(gè)參數(shù)對(duì)應(yīng)Descriptor中的一個(gè)屬性旱爆,其中的userFrench屬性是一個(gè)全局配置,可以在系統(tǒng)設(shè)置里面看到這個(gè)屬性
private boolean useFrench;
public DescriptorImpl() {
load();
}
在Descirptor構(gòu)造函數(shù)中使用load()
進(jìn)行加載全局配置窘茁,然后我們就可以在插件中獲取到配置信息
@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
useFrench = formData.getBoolean("useFrench");
save();
return super.configure(req,formData);
}
當(dāng)在全局配置修改屬性后怀伦,需要在configure()
方法中調(diào)用save()
將全局配置信息持久化到xml,我們可以在workspace的插件名.xml中看到持久化的數(shù)據(jù)
在每個(gè)插件的
perform()
方法中山林,是perform真正開(kāi)始執(zhí)行的地方房待,我們?nèi)绻诓寮型瓿墒裁词拢a邏輯也是寫(xiě)在perform方法中驼抹,perform方法參數(shù)中build代表當(dāng)前構(gòu)建桑孩,workspace代表當(dāng)前工作目錄,通過(guò)workspace可以獲取到當(dāng)前工作目錄的信息框冀,并可以做些操作流椒,如workspace.copyTo("/home")
,launcher代表啟動(dòng)進(jìn)程左驾,可以通過(guò)launcher執(zhí)行一些命令镣隶,如launcher.launch().stdout(listener).cmds("pwd").start();
,listener代表一個(gè)監(jiān)聽(tīng)器诡右,可以將運(yùn)行的內(nèi)容信息通過(guò)listener輸出到前臺(tái)console output安岂。
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
@Override
public void perform(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) {
//yuor code...
listener.getLogger().println("Hello World" + name);
}
}
如上面的代碼所示,在perform方法中我們通過(guò)listener打印了一行”Hello World“ + name帆吻,name是一個(gè)變量域那,這個(gè)變量的值從哪里來(lái)下面我會(huì)介紹一下給大家。在web界面上的控制臺(tái)可以看見(jiàn) Hello World kinder猜煮,而kinder這個(gè)值是由我們自己定義的次员。
在jenkins插件中,如果我們需要一些自定義的參數(shù)信息王带,如構(gòu)建時(shí)執(zhí)行一些命令淑蔚,命令的內(nèi)容是由用戶輸入,這個(gè)時(shí)候需要一個(gè)變量來(lái)記錄用戶輸入的信息
所以在HelloWorkdBuilder中定義一個(gè)屬性與用于輸入的信息相對(duì)應(yīng)愕撰,如上面的name屬性
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
private final String name;
....
}
這個(gè)屬性的值是在job的配置過(guò)程中輸入刹衫,由Jenkins從web前端界面?zhèn)鬟f過(guò)來(lái)的值,我們還需要在HelloWorldBuilder的構(gòu)造方法中進(jìn)行參數(shù)的注入
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
private final String name;
@DataBoundConstructor
public HelloWorldBuilder(String name) {
this.name = name;
}
類似于Spring的依賴注入搞挣,在這里Jenkins要求進(jìn)行參數(shù)注入的構(gòu)造方法需要用@DataBoundConstructor
注解標(biāo)注带迟,以便Jenkins可以找到這個(gè)構(gòu)造函數(shù),并且調(diào)用這個(gè)構(gòu)造函數(shù)囱桨,將web界面上配置的參數(shù)傳遞進(jìn)HelloWorldBuilder仓犬,這樣就可以在HelloWorldBuilder中使用這個(gè)屬性了。
到此舍肠,這個(gè)插件的后臺(tái)代碼就已經(jīng)搞定了搀继,現(xiàn)在給大家講講怎么樣編寫(xiě)這個(gè)前端配置的視圖
窘面。
Jenkins中的視圖
Jenkins 使用jelly來(lái)編寫(xiě)視圖,Jelly 是一種基于 Java
技術(shù)和 XML
的腳本編制和處理引擎律歼。Jelly 的特點(diǎn)是有許多基于 JSTL (JSP 標(biāo)準(zhǔn)標(biāo)記庫(kù)民镜,JSP Standard Tag Library)啡专、Ant险毁、Velocity 及其它眾多工具的可執(zhí)行標(biāo)記。Jelly 還支持 Jexl(Java 表達(dá)式語(yǔ)言们童,Java Expression Language)畔况,Jexl 是 JSTL 表達(dá)式語(yǔ)言的擴(kuò)展版本。Jenkins的界面繪制就是通過(guò)Jelly實(shí)現(xiàn)的
在Jenkins 中的視圖的類型有三種
- global.jelly 全局的配置視圖
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:section title="Hello World Builder">
<f:entry title="French" field="useFrench"
description="Check if we should say hello in French">
<f:checkbox />
</f:entry>
</f:section>
</j:jelly>
- config.jelly Job的配置視圖
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="Name" field="name">
<f:textbox />
</f:entry>
</j:jelly>
在定義一個(gè)屬性時(shí)慧库,使用
<f:entry>
標(biāo)簽代表這是一個(gè)屬性跷跪,其中title
是指在界面上顯示的字段名,而field
是指這個(gè)屬性在HelloWorldBuilder中對(duì)應(yīng)的屬性名齐板,jenkins通過(guò)這個(gè)名稱來(lái)與HelloWorldBuilder中的屬性相對(duì)應(yīng)吵瞻,從而使用@DataBoundConstructor
標(biāo)注的構(gòu)造函數(shù)將這些變量注入到HelloWorldBuilder類中。
- help-屬性名.html 幫助視圖 html片段
<div>
Help file for fields are discovered through a file name convention. This file is
help for the "name" field. You can have <i>arbitrary</i> HTML here. You can write
this file as a Jelly script if you need a dynamic content (but if you do so, change
the extension to <tt>.jelly</tt>).
</div>
這是Jenkins 中的三種視圖甘磨,上面也介紹了兩個(gè)簡(jiǎn)單的控件textbox和checkbox的使用橡羞,更多的關(guān)于Jelly的視圖使用可以查看jelly官網(wǎng)。
Jenkins 數(shù)據(jù)持久化
我們之前在web界面上輸入了name济舆,這個(gè)信息在下一次構(gòu)建的時(shí)候仍然存在卿泽,說(shuō)明jenkins中需要使用數(shù)據(jù)持久化來(lái)將我們配置的信息保存下來(lái),而Jenkins 使用文件來(lái)存儲(chǔ)數(shù)據(jù)(所有數(shù)據(jù)都存儲(chǔ)在$JENKINS_HOME)滋觉,有些數(shù)據(jù)签夭,比如 console 輸出,會(huì)作為文本文件存儲(chǔ)椎侠;大多數(shù)的結(jié)構(gòu)數(shù)據(jù)第租,如一個(gè)項(xiàng)目的配置或構(gòu)建(build)記錄信息則會(huì)通過(guò) XStream 持久化為一個(gè)xml文件,如下圖所示
而在需要信息的時(shí)候,jenkins又從xml文件中讀取到相應(yīng)的數(shù)據(jù)我纪,返回給應(yīng)用程序慎宾。
總結(jié)
在本文,主要介紹了Jenkins的簡(jiǎn)單使用宣羊,以及Jenkins的插件開(kāi)發(fā)環(huán)境璧诵,以及Jenkins插件結(jié)構(gòu)的一些介紹。本文主要還是做一個(gè)簡(jiǎn)單入門(mén)介紹仇冯,如果想要了解更多的關(guān)于Jenkins的東西之宿,還是需要去看Jenkins的官方wiki, 上面有詳細(xì)的關(guān)于每個(gè)擴(kuò)展點(diǎn)已經(jīng)Jenkins的api的使用介紹苛坚,同樣比被,你也可以下載Jenkins的源碼來(lái)查看內(nèi)部的一些實(shí)現(xiàn)方式色难。
在Github Jenkinci也有很多的關(guān)于Jenkins插件的源碼,我們可以通過(guò)源碼了解一些擴(kuò)展點(diǎn)是怎樣使用等缀,參照別人的源碼來(lái)寫(xiě)出自己的插件枷莉。