天天看點

Jenkins使用及插件開發介紹

Jenkins使用及插件開發介紹

介紹

Jenkins是一個廣泛用于[持續建構]()的可視化web工具,就是各種項目的的“自動化”編譯、打包、分發部署,将以前編譯、打包、上傳、部署到Tomcat中的過程交由Jenkins,Jenkins通過給定的代碼位址,将代碼拉取到jenkins主控端上,進行編譯、打包和釋出到web容器中。Jenkins可以支援多種語言(比如:java、c#、php等等),也相容ant、maven、gradle等多種第三方建構工具,同時跟git、svn無縫內建,也支援直接與github直接內建。

WiseBuild也是基于jenkins進行的開發,在下面會看到很多和WiseBuild相似的地方

安裝

  1. 到jenkins官網http://jenkins.io/下載下傳war包

使用

java -jar jenkins.war           

或者

将war放到web容器中,啟動web容器

<!-- more -->

啟動war包,會自動将war包解壓到~/.jenkins目錄下,并且生成一些目錄和配置檔案,我們在jenkins中配置的job也會儲存到這個目錄下

打開浏覽器,輸入localhost:8080 就可以通路到jenkins的web界面了

Jenkins使用及插件開發介紹
建立項目
用個小例子簡單示範一下jenkins的使用
Jenkins使用及插件開發介紹
源碼配置

将源碼資訊配置上去,我選擇一個github上面的項目,如果源碼管理中沒有git這個選項,隻需要到系統管理中添加git這個插件即可

Jenkins使用及插件開發介紹
建構指令
Jenkins使用及插件開發介紹

在建構階段輸入以下指令:

cd ${WORKSPACE} && ./gradlew build && mv ${WORKSPACE}/docker/jpetstore.war /usr/local/tomcat9/webapps           

該指令分為三個部分:

  1. cd ${WORKSPACE}

    WORKSPACE是jenkins的定義的環境變量,代表該項目對應的檔案路徑,該項目檢出的源碼也是該目錄。類似的環境變量還有BUILD_NUMBER, BUILD_ID, JOB_NAME, JENKINS_HOME等等
  2. ./gradlew build

    使用gradle 執行建構指令,将檢出的源碼編譯打包為war包,這裡我們使用的建構工具是gradle,如果是使用maven,可以

    mvn clean package

  3. mv {WORKSPACE}/docker/jpestore.war /usr/local/tomcat9/webapps/

    将打包好的war包手動放到tomcat的webapps目錄下,以便Tomcat能啟動該項目了

最後,點選儲存回到主面闆上。

建構

點選右邊的立即建構

Jenkins使用及插件開發介紹

開始執行建構,可以看見建構的進度,旁邊的#12 就是本次建構的建構号(BUILD_NUMBER)

Jenkins使用及插件開發介紹

也可以在檢視console output

Jenkins使用及插件開發介紹

console output 會顯示出本次建構的一些日志資訊

Jenkins使用及插件開發介紹

這裡我們web容器和jenkins都是在同一台伺服器上,可以利用shell指令來進行手動部署,如果jenkins的主控端和web伺服器不是同一台,我們也可以利用gradle和maven的部署功能,例如使用mvn deploy來将項目部署到遠端伺服器上

到此,我們的一個持續內建的一個項目就已經搭建好了,現在一旦我們對代碼修改進行送出,然後jenkins就會擷取最新的代碼然後按照我們上面配置的指令進行建構和部署。

jenkins插件

在前面我們看見jenkins可以支援git, svn, maven等很多功能,這些都是Jenkins的插件,jenkins本身不提供很多功能,我們可以通過使用插件來滿足我們的使用,接下來就介紹一下插件的原理以及我們怎麼通過寫一個自己的插件來滿足我們的需求。

擴充點

但是jenkins有很多的擴充點(ExtensitonPoint),它是Jenkins系統的某個方面的接口或抽象類。這些接口定義了需要實作的方法,而Jenkins插件需要實作這些方法,也可以叫做在此擴充點之上進行擴充Jenkins。有關擴充點的詳細資訊,請參閱Jenkins 官方ExtentionPoints文檔。通過這些擴充點我們可以寫插件來實作自己的需求。

下面是一些常用的擴充點:

  • Scm :代表源碼管理的一個步驟,如下面的Git,Subversion就是擴充的Scm
    Jenkins使用及插件開發介紹
  • Builder : 代表建構的一個步驟,如下圖中在建構過程中,我們可以增加一個建構步驟,而每一個選項都是對應一個Builder,在每一個Builder中都有自己不同的功能。如Execute shell,這就是一個ShellBuilder,意味着在建構過程中會執行一個shell指令
    Jenkins使用及插件開發介紹
  • Trigger:代表一個建構的觸發,當滿足一個什麼樣的條件時觸發這個項目開始建構。比較常用的觸發就是當代碼變更時觸發,如果我們需要實作一些比較複雜的觸發邏輯,就需要擴充Trigger這個擴充點
    Jenkins使用及插件開發介紹
  • Publisher:Publisher代表一個項目建構完成後需要執行的步驟,如選項中的E-Mail Notifaction就是一個Publisher插件,選擇這個選項後,當項目建構完成,就會使用email來通知使用者,假如想要在項目建構完成後将建構目标産物發送到伺服器上,則可以擴充此擴充點。
    Jenkins使用及插件開發介紹

上面簡單描述了一下插件和擴充點,接着我們可以搭建一個插件的開發環境

插件開發環境搭建

首先需要安裝:

  1. maven3
  2. 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>           

使用如下指令建立一個新的插件

mvn org.jenkins-ci.tools:maven-hpi-plugin:create (或者 mvn hpi:create)           

需要輸入插件的groupId,artifactId, 然後會在目前目錄建立一個jenkins插件的骨架目錄(熟悉maven的同學知道這個一個标準的maven項目目錄結構)

Jenkins使用及插件開發介紹

插件目錄結構:

  • pom.xml: maven使用這個檔案來建構插件,所有的插件都是基于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: 靜态的資源檔案,例如圖檔和html檔案

導入到IDE

  • intellij idea:直接在ide中導入pom檔案就能導入
  • eclipse:運作如下指令
mvn -DdownloadSources=true -DdownloadJavadocs=true -DoutputDirectory=target/eclipse-classes -Declipse.workspace=/path/to/workspace eclipse:eclipse eclipse:configure-workspace           

調試插件

  • 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           

輸入指令過後可以打開浏覽器,輸入:http://localhost:8080/jenkins,就可以看見你的插件在jenkins中運作起來了,現在就可以開始進行調試了。

修改端口

mvn hpi:run -Djetty.port =8090           

設定上下文路徑

mvn hpi:run -Dhpi.prefix=/jenkins           

打包釋出插件

mvn package           

該指令會在target目錄建立出 ‘插件名稱’.hpi 檔案,其他使用者可以直接将這個插件上傳安裝到Jenkins中使用(或者放到$JENKINS_HOME/plugins目錄中)。

Jenkins插件之HelloWorld
在之前我們使用

mvn hpi:create

建立插件目錄時,Jenkins在我們的項目中生成了一個HelloWorldBuilder的插件,這是一個官方示例,下面帶大家分析一下這個插件的示例源碼
public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
    
}           

首先建立一個類繼承于Builder,代表使用這個插件是一個建構插件(如果繼承于Scm,代表這個插件是一個源碼插件,例如Git,Svn插件),然後實作SimpleBuildStep接口

在Jenkins的插件中,每一個插件類中都必須要有一個Descriptor内部靜态類,它代表一個類的’描述者‘,用于指明這是一個擴充點的實作,Jenkins是通過這個描述者才能知道我們自己寫的插件

每一個‘描述者’靜态類都需要被@Extension注解,Jenkins内部會掃描@Extenstion注解來知道注冊了有哪些插件。

@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類中有兩個方法需要我們必須要進行重寫

public boolean isApplicable(){
     return true;
}           

這個方法的傳回值代表這個Builder在Project中是否可用,我們可以将我們的邏輯寫在其中,例如判斷一些參數,最後傳回true或者false來決定這個Builder在此處是否可用

public String getDisplayName(){
    return "Say hello world";
}           

這個方法傳回的是一個String類型的值,這個名稱會用在web界面上顯示的名稱

Jenkins使用及插件開發介紹

如果我們在插件中需要擷取一些系統設定參數,我們可以在Descriptor中擷取

一個參數對應Descriptor中的一個屬性,其中的userFrench屬性是一個全局配置,可以在系統設定裡面看到這個屬性

Jenkins使用及插件開發介紹
private boolean useFrench;

public DescriptorImpl() {
     load();
}           

在Descirptor構造函數中使用

load()

進行加載全局配置,然後我們就可以在插件中擷取到配置資訊

@Override
 public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
      useFrench = formData.getBoolean("useFrench");
      save();
      return super.configure(req,formData);
  }           

當在全局配置修改屬性後,需要在

configure()

方法中調用

save()

将全局配置資訊持久化到xml,我們可以在workspace的插件名.xml中看到持久化的資料

Jenkins使用及插件開發介紹

在每個插件的

perform()

方法中,是perform真正開始執行的地方,我們如果要在插件中完成什麼事,代碼邏輯也是寫在perform方法中,perform方法參數中build代表目前建構,workspace代表目前工作目錄,通過workspace可以擷取到目前工作目錄的資訊,并可以做些操作,如

workspace.copyTo("/home")

,launcher代表啟動程序,可以通過launcher執行一些指令,如

launcher.launch().stdout(listener).cmds("pwd").start();

,listener代表一個監聽器,可以将運作的内容資訊通過listener輸出到前台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方法中我們通過listener列印了一行”Hello World“ + name,name是一個變量,這個變量的值從哪裡來下面我會介紹一下給大家。在web界面上的控制台可以看見 Hello World kinder,而kinder這個值是由我們自己定義的。

Jenkins使用及插件開發介紹

在jenkins插件中,如果我們需要一些自定義的參數資訊,如建構時執行一些指令,指令的内容是由使用者輸入,這個時候需要一個變量來記錄使用者輸入的資訊

是以在HelloWorkdBuilder中定義一個屬性與用于輸入的資訊相對應,如上面的name屬性

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {

    private final String name;
    
    ....
}           
Jenkins使用及插件開發介紹

這個屬性的值是在job的配置過程中輸入,由Jenkins從web前端界面傳遞過來的值,我們還需要在HelloWorldBuilder的構造方法中進行參數的注入

public class HelloWorldBuilder extends Builder implements SimpleBuildStep {

    private final String name;
    
    @DataBoundConstructor
    public HelloWorldBuilder(String name) {
        this.name = name;
}           

類似于Spring的依賴注入,在這裡Jenkins要求進行參數注入的構造方法需要用

@DataBoundConstructor

注解标注,以便Jenkins可以找到這個構造函數,并且調用這個構造函數,将web界面上配置的參數傳遞進HelloWorldBuilder,這樣就可以在HelloWorldBuilder中使用這個屬性了。

到此,這個插件的背景代碼就已經搞定了,現在給大家講講怎麼樣編寫這個前端配置的視圖

Jenkins使用及插件開發介紹

Jenkins中的視圖

Jenkins 使用jelly來編寫視圖,Jelly 是一種基于

Java

技術和

XML

的腳本編制和處理引擎。Jelly 的特點是有許多基于 JSTL (JSP 标準标記庫,JSP Standard Tag Library)、Ant、Velocity 及其它衆多工具的可執行标記。Jelly 還支援 Jexl(Java 表達式語言,Java Expression Language),Jexl 是 JSTL 表達式語言的擴充版本。Jenkins的界面繪制就是通過Jelly實作的

在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>           
Jenkins使用及插件開發介紹
  • 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>           
Jenkins使用及插件開發介紹

在定義一個屬性時,使用

<f:entry>

标簽代表這是一個屬性,其中

title

是指在界面上顯示的字段名,而

field

是指這個屬性在HelloWorldBuilder中對應的屬性名,jenkins通過這個名稱來與HelloWorldBuilder中的屬性相對應,進而使用

@DataBoundConstructor

标注的構造函數将這些變量注入到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使用及插件開發介紹

這是Jenkins 中的三種視圖,上面也介紹了兩個簡單的控件textbox和checkbox的使用,更多的關于Jelly的視圖使用可以檢視jelly官網。

Jenkins 資料持久化

我們之前在web界面上輸入了name,這個資訊在下一次建構的時候仍然存在,說明jenkins中需要使用資料持久化來将我們配置的資訊儲存下來,而Jenkins 使用檔案來存儲資料(所有資料都存儲在$JENKINS_HOME),有些資料,比如 console 輸出,會作為文本檔案存儲;大多數的結構資料,如一個項目的配置或建構(build)記錄資訊則會通過 XStream 持久化為一個xml檔案,如下圖所示

Jenkins使用及插件開發介紹

而在需要資訊的時候,jenkins又從xml檔案中讀取到相應的資料,傳回給應用程式。

__

總結

在本文,主要介紹了Jenkins的簡單使用,以及Jenkins的插件開發環境,以及Jenkins插件結構的一些介紹。本文主要還是做一個簡單入門介紹,如果想要了解更多的關于Jenkins的東西,還是需要去看Jenkins的官方wiki, 上面有詳細的關于每個擴充點已經Jenkins的api的使用介紹,同樣,你也可以下載下傳Jenkins的源碼來檢視内部的一些實作方式。

在Github Jenkinci也有很多的關于Jenkins插件的源碼,我們可以通過源碼了解一些擴充點是怎樣使用,參照别人的源碼來寫出自己的插件。