目录

    使用 Jenkins 总是离不开各种各样的插件,为了更好的实践 DevOps ,我们也应该具备开发插件的能力,使整个流程都能够在 Jenkins 中汇合。

    1. Jenkins 插件

    1.1 插件的生态

    Jenkins 前身 Hudson 始于 2004 ,历经 16 年,依然作为主流的 CI/CD 引擎。除了,Jenkins 提供了 Master-Agent 分布式构建、Pipeline 编排的功能,另外一个很重要的原因就是强大的插件生态。

    Jenkins 插件官网,显示目前插件数量达到 1500 +,涵盖拉取代码、构建、测试、部署、工具集成等方方面面。

    这些开源的插件,基本能够满足功能需求。但为了对接某些定制的系统,我们又不得不开发新的插件。新的插件可以开源给 Jenkins 社区,提供给其他人使用。事实上,大部分的插件也是这么产生的。

    1.2 插件的生命周期

    Jenkins 的执行,具有自己的运行周期:

    1. checkout ,check out出源码
    2. Pre-build , 预编译
    3. Build wrapper , 准备构建的环境,设置环境变量等
    4. Builder runs , 执行构建,比如调用calling Ant, Make 等等
    5. Recording , 记录输出,如测试结果
    6. Notification , 通知成员

    开发插件就是以 Jenkins 的运行周期为切入点,对其进行扩展。

    具体到实现,首先根据需要扩展的功能,在 Jenkins Packages 文档中,找到扩展的类。然后,在插件的主类中 extends 扩展类:

    package mygroup.myauth;
    
    import hudson.Extension;
    import jenkins.security.BasicHeaderAuthenticator;
    
    @Extension
    public class MyAuthenticator extends BasicHeaderAuthenticator {
    
    }
    

    MyAuthenticator 中实现自己的业务逻辑即可。

    在开发测试完成之后,需要将插件托管到线上。可以是私有的 Nexus Server ,也可以是 Jenkins 官方提供的公共仓库。如果希望托管到 Jenkins 官方仓库,需要按照 文档 进行操作,去 https://issues.jenkins-ci.org/browse/HOSTING 按照模板,创建 issue 。在开发过程中碰到的任何问题,可以在 [email protected] 邮件组中,进行交流。下面我们来了解一下插件的开发流程。

    2. 基础环境搭建

    大部分 Jenkins 插件使用 Maven 进行构建。Maven 3.3 以上需要 JDK 1.7 以上版本,下面以 CentOS 7 为例进行安装。

    • JDK
    yum install -y java-1.8.0-openjdk
    
    • Maven

    Maven 下载页面 ,这里下载 3.6.3 版本。

    wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz -P /tmp
    tar xf /tmp/apache-maven-3.6.3-bin.tar.gz -C /opt
    

    编辑 /etc/profile 新增如下内容:

    M2_HOME="/opt/apache-maven-3.6.3"
    export M2_HOME
    
    M2="$M2_HOME/bin"
    MAVEN_OPTS="-Xms256m -Xmx512m"
    export M2 MAVEN_OPTS
    
    PATH=$M2:$PATH
    export PATH
    

    source 一下,使之生效。

    source /etc/profile
    mvn -version
    

    3. 生成插件框架

    mvn -U archetype:generate -Dfilter="io.jenkins.archetypes:"
    
    …
    Choose archetype:
    1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.)
    2: remote -> io.jenkins.archetypes:global-configuration-plugin (Skeleton of a Jenkins plugin with a POM and an example piece of global configuration.)
    3: remote -> io.jenkins.archetypes:global-shared-library (Uses the Jenkins Pipeline Unit mock library to test the usage of a Global Shared Library)
    4: remote -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.)
    5: remote -> io.jenkins.archetypes:scripted-pipeline (Uses the Jenkins Pipeline Unit mock library to test the logic inside a Pipeline script.)
    Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 4 
    Choose io.jenkins.archetypes:hello-world-plugin version:
    1: 1.1
    2: 1.2
    3: 1.3
    4: 1.4
    5: 1.5
    6: 1.6
    Choose a number: 6: 6
    …
    [INFO] Using property: groupId = unused 
    Define value for property 'artifactId': demo 
    Define value for property 'version' 1.0-SNAPSHOT: : 
    [INFO] Using property: package = io.jenkins.plugins.sample
    Confirm properties configuration:
    groupId: unused
    artifactId: demo
    version: 1.0-SNAPSHOT
    package: io.jenkins.plugins.sample
     Y: : y 
    

    其中 package 、groupId 根据需要填写,artifactId 就是插件的 ID 。

    最终会看到提示:

    [INFO] ----------------------------------------------------------------------------
    [INFO] Using following parameters for creating project from Archetype: hello-world-plugin:1.6
    [INFO] ----------------------------------------------------------------------------
    [INFO] Parameter: groupId, Value: unused
    [INFO] Parameter: artifactId, Value: demo
    [INFO] Parameter: version, Value: 1.0-SNAPSHOT
    [INFO] Parameter: package, Value: io.jenkins.plugins.sample
    [INFO] Parameter: packageInPathFormat, Value: io/jenkins/plugins/sample
    [INFO] Parameter: package, Value: io.jenkins.plugins.sample
    [INFO] Parameter: version, Value: 1.0-SNAPSHOT
    [INFO] Parameter: groupId, Value: unused
    [INFO] Parameter: artifactId, Value: demo
    [INFO] Project created from Archetype in dir: /root/java/demo
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    

    根据交互提示,很容易创建一个插件框架。下面查看一下生成的文件:

    tree demo/
    
    demo/
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   │   └── io
        │   │       └── jenkins
        │   │           └── plugins
        │   │               └── sample
        │   │                   └── HelloWorldBuilder.java
        │   └── resources
        │       ├── index.jelly
        │       └── io
        │           └── jenkins
        │               └── plugins
        │                   └── sample
        │                       ├── HelloWorldBuilder
        │                       │   ├── config_de.properties
        │                       │   ├── config_es.properties
        │                       │   ├── config_fr.properties
        │                       │   ├── config_it.properties
        │                       │   ├── config.jelly
        │                       │   ├── config.properties
        │                       │   ├── config_pt_BR.properties
        │                       │   ├── config_sv.properties
        │                       │   ├── config_tr.properties
        │                       │   ├── config_zh_CN.properties
        │                       │   ├── help-name_de.html
        │                       │   ├── help-name_es.html
        │                       │   ├── help-name_fr.html
        │                       │   ├── help-name.html
        │                       │   ├── help-name_it.html
        │                       │   ├── help-name_pt_BR.html
        │                       │   ├── help-name_sv.html
        │                       │   ├── help-name_tr.html
        │                       │   ├── help-name_zh_CN.html
        │                       │   ├── help-useFrench_de.html
        │                       │   ├── help-useFrench_es.html
        │                       │   ├── help-useFrench_fr.html
        │                       │   ├── help-useFrench.html
        │                       │   ├── help-useFrench_it.html
        │                       │   ├── help-useFrench_pt_BR.html
        │                       │   ├── help-useFrench_sv.html
        │                       │   ├── help-useFrench_tr.html
        │                       │   └── help-useFrench_zh_CN.html
        │                       ├── Messages_de.properties
        │                       ├── Messages_es.properties
        │                       ├── Messages_fr.properties
        │                       ├── Messages_it.properties
        │                       ├── Messages.properties
        │                       ├── Messages_pt_BR.properties
        │                       ├── Messages_sv.properties
        │                       ├── Messages_tr.properties
        │                       └── Messages_zh_CN.properties
        └── test
            └── java
                └── io
                    └── jenkins
                        └── plugins
                            └── sample
                                └── HelloWorldBuilderTest.java
    

    还可以执行 verify 命令进行插件的验证。

    cd demo
    mvn verify
    
    [INFO] Fork Value is true
    [INFO] Done SpotBugs Analysis....
    [INFO] 
    [INFO] <<< spotbugs-maven-plugin:3.1.12.2:check (spotbugs) < :spotbugs @ demo <<<
    [INFO] 
    [INFO] 
    [INFO] --- spotbugs-maven-plugin:3.1.12.2:check (spotbugs) @ demo ---
    [INFO] BugInstance size is 0
    [INFO] Error size is 0
    [INFO] No errors/warnings found
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  05:10 min
    [INFO] ------------------------------------------------------------------------
    

    从 Java 类可以得知,这个 Hello World 插件针对 Builder 进行了扩展。在代码中,可以进一步得到验证:

    public class HelloWorldBuilder extends Builder implements SimpleBuildStep {
    
        private final String name;
        private boolean useFrench;
    }
    

    这里就不对 Hello World 插件做修改,直接运行看看。

    4. 运行调试

    • 运行

    Maven Hpi Plugin 提供了非常方便的调试方法,在 demo 目录中执行命令:

    mvn hpi:run
    

    会运行一个带插件的 Jenkins 服务,访问地址为 http://127.0.0.1:8080/jenkins 。通过参数 -Djetty.port=1000 -Djenkins.version=2.176.2 -Djenkins.install.runSetupWizard=true 可以指定访问的端口、Jenkins 版本、是否需要安装插件的向导等。

    • 调试

    如果需要断点调试,可以运行如下命令:

    mvnDebug hpi:run
    

    export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
    mvn hpi:run
    

    这会在 8000 端口建立监听 ,可以在 IDE 中添加一个 8000 端口的调试会话进行 Debug。

    • 打包

    最后就是生成 hpi 包,执行命令:

    mvn package
    
    [INFO] Generating hpi /root/java/demo/target/demo.hpi
    

    编译完成后会生成一个 hpi 文件,也就是插件,可以直接在 Jenkins 后台上传安装。

    当然,也可以直接将插件安装在本地。

    mvn clean install
    
    [INFO] Installing /root/java/demo/target/demo.hpi to /root/.m2/repository/io/jenkins/plugins/demo/1.0-SNAPSHOT/demo-1.0-SNAPSHOT.hpi
    
    • 测试

    http://127.0.0.1:8080 页面上,

    新建一个流水线,可以看到 Build 中,新增了一个 Step ,Hello World 。

    运行输出: Hello, biubiu!

    5. 参考