前言

相信大家对Jenkins已经非常了解了,大多数项目都采用了这款开源工具来实现项目的CI/CD。

对于小型项目,我们完全可以采用“融合部署”的模式,即将Jenkins和构建环境部署在同一台服务器上,来实现自动构建和发布,这时Jenkins的环境搭建也比较简单。但有些时候我们需要多个项目并行编译,或者需要适配多种编译环境,这时对于单主机的性能要求就比较高了。

为应对这种情况,通常会采用“代理模式”(也有叫主从模式的):将Jenkins独立部署,负责对多个项目的构建任务进行调度,而不参与(或极少参与)项目的构建发布;然后添加多个子节点,来负责具体的编译构建任务。这种模式的好处是:

  • 构建节点高可用。一个构建节点挂了,其他节点可以接替执行构建任务,保证系统正常运行;
  • 可以配置差异化的构建环境。每个节点可以针对不同语言开发的项目配置不同的构建环境,互不影响;
  • 构建任务可并行。当SCM同时触发了多个构建任务时,可以将这些任务分配到不同节点上执行,提升构建效率。

代理模式的环境搭建比较简单,这里不做讨论。当我们在使用代理模式时构建项目时会出现很多问题,本文主要讨论在这种情况下的一些实践经验和建议。


准备工作

1、环境准备

这一步的工作是必须的,也是“很有学问”的。

对于java项目,我们需要配置的就是jdkMaven。按照传统的思路,我们会这么做:

  1. 上传适配系统和应用的jdk和maven;
  2. 在~/.bash_profile或/etr/profile中配置jdk和maven的环境变量;
  3. 在任意路径下执行java -version和mvn -v验证环境配置

最终配置的结果类似下方截图:

Jenkins代理模式配置Maven工程插图

配置环境变量

但是你会惊奇的发现这样做对Jenkins来说并不没有用,你可能会遇到如下错误:

Jenkins代理模式配置Maven工程插图1

Jenkins报错

相信大家到这里都会有点懵。啥情况?明明配置好了环境变量,在主机上运行也好使,怎么到Jenkins里就不好用呢?

这是因为Jenkins在运行时不会加载用户的环境变量,只会使用自己(在Jenkins里配置)的环境变量。如果没有在Jenkins中配置环境变量,默认会使用系统中(Linux为例)/usr/bin或/bin 目录下的指令

所以,针对上面的问题,有2种解决方案,任选其一即可:

1)在Jenkins的控制台上,对构建节点添加环境变量,在“系统管理->节点管理->选中目标节点->配置从节点”中,添加环境变量,如下图:

Jenkins代理模式配置Maven工程插图2

节点属性配置

说明:

这是节点的个性化配置。

如果所有的构建节点环境配置一致,就可以在“全局配置”中统一配置。

2)也可以在构建节点上建立软连接,链接到我们的可执行程序上,如:

ln -s /home/maven/apache-maven-3.6.2/bin /usr/bin/mvn

ln -s /home/maven/apache-maven-3.6.2/bin /bin/mvn

这样,Jenkins就能找到mvn这个指令了。

JAVA指令也是这样配置。

2、插件准备

在Jenkins中商店中按需安装相关插件,如svn、git、pipeline,等等。

如果你的构建节点和运行节点不在一个机器上,需要安装“Publish Over ssh”这个插件。插件的安装不在本文讨论。

在Jenkins和构建环境中做好上述配置之后,就可以进行下一步了。

Maven项目构建

1、构建一个Maven项目

对于Maven工程,我们会本能的选择“构建一个Maven项目”:

Jenkins代理模式配置Maven工程插图3

选择项目形式

但是这个工程有一个问题,它依赖于Jenkins的全局配置。如果没有配置,现象如下:

Jenkins代理模式配置Maven工程插图4

Jenkins报错

这时可以点击后面的“the tool configuration”进入全局配置界面,配置maven路径。

很多时候各节点的jdk和maven的路径配置不同,或某些节点根本就不需要这2个工具,那么又需要在每个节点上分别修改配置。这种情况下,使用“构建一个Maven项目”就不太合适了。

我们可以使用下面的2种方式。

2、构建一个自由风格的软件项目

这种模式比较简单,只要在“环境准备”步骤中都配置好,接下来就是一些shell指令的编排。

首先,在创建项目时选择“构建一个自由风格的软件项目”:

Jenkins代理模式配置Maven工程插图5

?编辑

其次,在General中勾选“限制项目的运行节点”,填写构建节点的名称,如下:

Jenkins代理模式配置Maven工程插图6

然后,在“源码管理”中选择代码库、在“构建触发器”中设置SCM周期,这里就不展示了。

接下来到核心操作步骤了,在“构建”中增加构建步骤,选“执行 shell”,按照你工程的实际编译指令,编写shell内容,如:

Jenkins代理模式配置Maven工程插图7

执行shell时,默认是在当前工作目录下,在主机上路径为$(JENKINS_HOME)/workspace/工程目录/。如果你的pom文件不在工程的根目录下,你还需要cd到目标路径下。

这一步完成之后,就会在你 “工作目录/target”中生成jar或war包。

在部署应用时,可以部署到容器、k8s、或者独立运行。本文只讨论独立部署运行的情况。可以在“构建后操作”中使用“Publish Over SSH”这个插件,将jar包上传到运行节点(也可以是本机)的目标目录中,然后启动程序。参考如下:

Jenkins代理模式配置Maven工程插图8

Publish Over SSH 插件

需要注意几点:

  • SSH Server:目标节点的登录信息可以在“节点管理”中提前配置好,也可以在SSH Server下面的“高级”中设置;
  • Source files:要上传到目标节点的文件,本例中是可执行jar文件;
  • Remove prefix:去掉目录前缀,这是根据Source files设置的。按照例子中的填写方式,放到目标路径后,rest-1.0.jar这个文件还是在target目录下。这一步的作用就是把target目录去掉;
  • Remote directory:要将源文件放到目标节点上的目标目录。注意:这里是相对路径!你需要提前在“系统配置”中,配置好ssh主机的路径,如下图,系统配置中Remote Directory配置的是 /home/appuser。那么在本例中,rest-1.0.jar 会被放到目标主机的 /home/appuser/test 目录下。
Jenkins代理模式配置Maven工程插图9

系统配置-SSH Server

后续的程序启动就可以在“Exec Command”中完成了,比较简单。

至此,我们就完成了一个Maven项目的自动构建和独立发布。

3、流水线

我们还可以使用Pipeline来构建和运行项目。在创建项目时,选择“流水线”:

Jenkins代理模式配置Maven工程插图10

选择项目形式

在项目中,“General”和“构建触发器”的配置方式跟前面都是一样的。

在后面的“流水线”这里,需要编写Groovy脚本来控制构建和发布的逻辑。我们可以在网上找一些通用的代码模板,也可以选择Jenkins自带的模板,为我们自动生成一些代码,观察代码结构:

Jenkins代理模式配置Maven工程插图11

流水线脚本

看到pipeline里面,指定了agent节点、克隆代码、构建,等等。

不会Groovy怎么办?看到左下角的“流水线语法”了吗?点进去,会为我们自动生成流水线代码!例如,我想指定我的运行节点,就可以按照如下步骤点击、选择:

Jenkins代理模式配置Maven工程插图12

从英文中不难理解每个步骤的含义,最后点击按钮就会为我们生成代码。把这个代码直接贴到Pipeline的适当位置上即可,如下:

pipeline {
    //1.指定标签为 test 的节点 执行该构建任务
   agent {
      label 'test'
    }
    
    //2.声明环境变量
    environment {
      MAVEN_HOME = "/home/appuser/lr/maven/apache-maven-3.6.2"
      JAVA_HOME = "/home/appuser/lr/jdk1.8.0_11"
      JRE_HOME = "${JAVA_HOME}/jre"
      CLASSPATH = ".:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH"
      JAVA_PATH = "${JAVA_HOME}/bin:${JRE_HOME}/bin"
      PATH = "/usr/local/go/bin:$JAVA_HOME/bin:$PATH:$JAVA_HOME/jre/bin:$PATH:${MAVEN_HOME}/bin"
    }


   stages {
      stage('Pull Code') {
         steps {
            // 3.编写拉取代码的指令
            // sh "svn xxxxx"
            // sh "git pull xxxx"
            echo "start"
         }
      }
      stage('Build') {
         steps {
            // 4.在这里添加编译构建指令
            sh "mvn -Dmaven.test.failure.ignore=true clean package"
         }
      }
      stage('Run') {
        // 5.应用发布
        // 这部分代码也是在流水线语法中自动生成的,在“片段生成器”中选择sshPublisher
          steps {
            sshPublisher(publishers: [sshPublisherDesc(configName: '10.4.122.155', sshCredentials: [encryptedPassphrase: '{AQAAABAAAAAQUhudNn9S1kPGyV4eRRGNs649cFzWNnaNJKTG0C0/OGc=}', key: '', keyPath: '', username: 'appuser'], transfers: [sshTransfer(excludes: '', execCommand: 'nohup java -jar /home/appuser/test/rest-1.0.jar &', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'test', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/rest-1.0.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
          }
      }
   }
}

在编写Pipeline时,以上5个步骤是最基本的,即:

  1. 指定任务运行节点
  2. 设置环境变量
  3. 拉取代码
  4. 编译构建
  5. 应用发布

有了“流水线语法”,不会Groovy也可以编写Pipeline!本例中的所有代码都是通过“流水线语法”自动生成的

当然,如果你想实现更多的高级功能,就需要深入研究Groovy了。


总结

本文介绍了在代理模式下,分别通过“构建一个自由风格的软件项目”、“构建一个Maven项目”以及“流水线”3种方式来构建Maven项目,以及每种方式的注意事项,总结如下:

  1. Jenkins在运行任务时,使用的自己的环境变量,而不是操作系统的环境变量。可以在Jenkins的系统设置中进行设置,也可以针对每个节点进行设置;
  2. Jenkins默认使用的是/usr/bin和/bin下的指令,不会读取主机用户的PATH;
  3. “构建一个Maven项目”,更适用于小型项目,单台服务器就能够完成调度和构建任务的情形;
  4. 不熟悉Groovy也可以构建流水线,使用“流水线语法”自动生成代码;

?