用 Jenkins 自动化构建 Git 中 Maven 项目

一、环境

系统 CentOS 7.2
Java OpenJDK 1.8.0_101(yum 安装)
Java 容器 Apache Tomcat 8.5.4
Maven 3 Apache Maven 3.0.5(yum 安装)
Jenkins Jenkins 2.7.2
Java 环境变量

OpenJDK 我是用 yum 安装的,yum install java-1.8.0-openjdk-devel.x86_64

# ~/.bashrc
# JAVA_HOME
export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-3.b13.el7_2.x86_64"  
export PATH="$PATH:$JAVA_HOME/bin"  
export CLASSPATH=".:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar;$JAVA_HOME/bin"  
export JRE_HOME="$JAVA_HOME/jre"  
Tomcat 环境变量

Tomcat 只需要 $CATALINA_HOME 指向 Tomcat 根目录即可,$CATALINA_BASE 是可选的。

因项目目前处于初始阶段,所以我将 Production & Test & jenkins 放到了同一台服务器上,所以并不涉及 slave jenkins 的问题。

二、安装 Jenkins

首先你需要一个 Tomcat 容器来跑 Jenkins,我在 /opt/ 下建立了一个新的 Tomcat: apache-tomcat-jenkins

安装步骤:

  1. 复制一个新的 tomcat 程序:/opt/apache-tomcat-jenkins
  2. Jenkins 官网 下载最新的 jenkins.war 文件到 /opt/apache-tomcat-jenkins/webapps/ 下;
  3. 启动 tomcat,访问 http://host:8080/jenkins
  4. 创建用户,并进行初始化配置;
    Customize Jenkins Getting Started
  5. 去项目名,详见下方「坑2」。

Tomcat 这块我踩了几个坑,有必要列出来一下。

坑1. 多实例 Tomcat 启动 & 关闭

要准备一个生产服,测试服,还有一个 Jenkins 服务器,开始,我复制了三份儿 Tomcat:

  1. /opt/apache-tomcat-8.5.4 (Production)
  2. /opt/apache-tomcat-jenkins (Jenkins)
  3. /opt/apache-tomcat-test (DevTest)

直接跑他们的 startup.sh,结果每次都只有第一个跑起来了,然后看到他们在启动时输出的内容都一样:

$ sh /opt/apache-tomcat-jenkins/bin/startup.sh
Using CATALINA_BASE:   /opt/apache-tomcat-8.5.4  
Using CATALINA_HOME:   /opt/apache-tomcat-8.5.4  
Using CATALINA_TMPDIR: /opt/apache-tomcat-8.5.4  
Using JRE_HOME:        /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-3.b13.el7_2.x86_64/jre  
Using CLASSPATH:       /opt/apache-tomcat-8.5.4/bin/bootstrap.jar:/opt/apache-tomcat-8.5.4/bin/tomcat-juli.jar  
Tomcat started.  

查看 tomcat/bin/catalina.sh 后发现,不同实例的 Tomcat 启动时,是需要使用环境变量中的 $CATALINA_BASE & $CATALINA_HOME
而我在 ~/.bashrc 中只添加了 export CATALINA_HOME="/opt/apache-tomcat-8.5.4",因此导致了所有的启动脚本都只能启动同一个 tomcat。

解决办法
只要能在 Tomcat 启动前修改 $CATALINA_BASE & $CATALINA_HOME当前 Tomcat 的路径即可。

我的方法是直接修改 tomcat/bin/ 下启动脚本 startup.sh & shutdown.sh(shutdown 也需要)的内容

# startup.sh
...
# -----------------------------------------------------------------------------
# Start Script for the CATALINA Server
# -----------------------------------------------------------------------------

# Change $CATALINA_HOME & $CATALINA_BASE
# 除非变量用 export 命令进行导出,否则变量是不会被子进程继承的
export CATALINA_HOME=/opt/apache-tomcat-jenkins。  
export CATALINA_BASE=/opt/apache-tomcat-jenkins  
...

shutdown.sh 同理,把变量加到最前面就可以了。

坑2. Tomcat 项目应用重复启动

按照这种配置方法,将 Tomcat 项目名去掉:

<!-- tomcat/conf/server.xml -->  
<Host name="localhost" appBase="webapps"  
      unpackWARs="true" autoDeploy="true"  
      xmlValidation="false" xmlNamespaceAware="false">
    <Context path="" docBase="prjName" debug="0" reloadable="true" />
</Host>  

如果将应用的 war 包直接放到 webapps 下,Tomcat 在启动后,会将 jenkins.war 包解成 jenkins & ROOT 两个项目文件夹,并且都会运行,导致占用巨多内存(700MB)。其实就是跑了两个 Jenkins 实例。

double jenkins instance

无论是 Jenkins 还是自己的项目,只要把 war 包放到 webapps 下,并按上面的配置跑 Tomcat 都会出现多运行一个实例的问题。

解决办法:
参考了这篇文章(war包部署到tomcat的疑问)后,解决方法如下:

  1. 建立 war 包文件夹:/opt/war/,war 包全部放到这里,
  2. 修改每个 tomcat 程序的 server.xml 配置为(具体 war 包要基于项目改名字):
<!-- tomcat/conf/server.xml -->  
<Host name="localhost"  appBase="webapps"  
      unpackWARs="true" autoDeploy="true">
  <Context path="" docBase="/opt/war/jenkins.war" debug="0" privileged="true" />
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
         prefix="localhost_access_log" suffix=".log"
         pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>  

三、配置 Jenkins

在创建任务前,我们需要配置一下相关的工具参数,和邮件设置。

1. 系统设置 - 邮件设置

进入系统设置后,先填写 Jenkins Location 中的「系统管理员邮件地址」,否则无法发件。
Jenkins URL 就填写你的 Jenkins 访问路径,IP/域名都可以。
Jenkins Location

然后在邮件通知中,配置 SMTP 服务器和认证信息 SMTP

可以勾选「通过发送测试邮件测试配置」用上述配置向指定的邮箱发送测试邮件,测试成功便可以顺利地在后面的任务创建中使用邮件通知了。

2. Global Tool Configuration

这里配置全局的工具环境/路径,这里我配置了 JDK,Git,Maven。

Global Tool Configuration

四、创建 Maven 项目构建任务

注意:创建 Maven 项目任务,需要先安装 Maven Integration plugin 等 Maven 相关的插件。
我还安装了 Email Extension Plugin 和一些 Git 相关的插件。

创建步骤:

1. 新建任务,选择「构建一个 maven 项目」

new maven job

2. 配置任务

我的项目是托管在 Coding.net 上的,所以当做 Gitlab 托管项目来进行配置。

1) Git Repository 配置

Repo URL 我这里填写的是 https 类型的
Chose Git 添加 Credentials,填写 Git 服务的用户名密码即可,确认后要看是否能通过 Git Repo 的验证。 Credentials

2) 构建设置,添加邮件提醒

Email Notification

3) 构建触发器配置

Build Trigger Config

4) Post Steps (构建后的操作)

选择 Run only if build succeeds or is unstatble,构建成功/不稳定时运行。因为有可能需要前端文件更新,而 Java 有报错,这时候也是需要构建的。
我们需要添加一个 Execute shell 脚本,这个脚本会在构建后运行,脚本名字为 hudson + 时间戳。

重点在下面:

#!/bin/bash  
#copy file and restart tomcat

tomcat_path=/opt/apache-tomcat-project  
war_name=project.war  
# job-name 就是你的 jenkins 任务名
file_path=/root/.jenkins/workspace/job-name/target/  
war_path=/opt/war/

now=$(date +"%Y%m%d%H%M%S")  
echo "the shell execute time is ${now}"

# 获取 tomcat_path 下 tomcat 进程 PID
echo "lsof -n -P -t ${tomcat_path}/bin/tomcat-juli.jar"  
tomcat_pid=`lsof -n -P -t ${tomcat_path}/bin/tomcat-juli.jar`  
echo "the tomcat_pid is ${tomcat_pid}"

# 杀死当前 tomcat 进程
if [ "${tomcat_pid}" != "" ]; then  
   kill -9 $tomcat_pid
   echo "kill the server, PID:${tomcat_pid}"
fi 

# 删除 tomcat/webapps/ROOT
echo "rm -rf ${tomcat_path}/webapps/ROOT/"  
rm -rf ${tomcat_path}/webapps/ROOT/

# 覆盖 war 包到 `/opt/war/${war_name}`
echo "cd $file_path"  
cd $file_path  
ls -al  
if [ -f ${war_name} ]; then  
   echo "cp ${war_name} ${war_path}${war_name}"
   cp ${war_name} ${war_path}${war_name}
else  
   echo "${file_path}${war_name} unexists"
fi

# 定义环境变量
export CATALINA_HOME=${tomcat_path}  
export CATALINA_BASE=${tomcat_path}

# 启动服务
echo "BUILD_ID=dontKillMe bash ${tomcat_path}/bin/startup.sh"  
# 防止 hudson 脚本在运行后,kill 掉他调用的脚本,即 kill startup.sh
BUILD_ID=dontKillMe bash ${tomcat_path}/bin/startup.sh  
echo "server restarted"  

这个脚本做的流程就是:先杀死当前项目 Tomcat 进程,复制最新构建的 war 包到指定位置,再启动 Tomcat 进程。

不过上面的构建后执行脚本,有几个要注意的点:

a). 获取 Tomcat 进程 PID 用 lsof + 运行程序路径会靠谱些;
b). war_name 即构建打包后的 war 包文件名;
c). 构建打包的 war 包会保存到 ~/.jenkins/workspace/job-name/target/${war_name},这个目录保存的永远是最新成功构建的 war 包;
d). 在调用 Tomcat startup.sh 脚本时,需要先 export CATALINA_HOME,别问我为什么,这是个坑;
e). 最重要的一点,在运行 startup.sh 命令前一定要加上 BUILD_ID=dontKillMe,否则 hudson 脚本会在执行结束后,会结束由它调用的脚本,结果就是 Tomcat 跑一半被 kill,这也是个坑。(其实 BUILD_ID 内容改成别的也行)

4). 配置完成,进行测试

在首页点击「立即构建」,测试任务是否能顺利完成。 如果失败,或 Tomcat 没有更新内容,可以在构建记录中的 Console Output,看详细内容。

最后

参考文章:

  1. 「Jenkins+Git+Maven+Shell+Tomcat持续集成」经典教程
  2. Jenkins 持续集成之 Git Hooks 触发构建任务
正在加载 Disqus 评论组件...