Jenkins部署SpringBoot应用到远程服务器(含脚本)

Java框架

浏览数:395

2019-2-23

使用SpringBoot+SpringCloud写了一套APP后台服务,也引入了当下比较流行的微服务的理念,模块也比较多。为了方便前期测试和后期线上部署更新,使用Jenkins作为持续集成工具。

结构

服务器结构

服务器结构

  • 测试机器:若干台外网测试主机,这里假设其中某台的IP地址为114.114.111.111。
  • Jenkins服务器:搭建在内网的测试主机,Jenkins部署在这台机器上。
  • SVN代码库:公司内部的SVN代码托管服务器(少说都有7,8年了,稳定到出奇)

测试环境目录结构

测试环境目录结构

  • /chl: 项目总目录
  • /chl/exec:执行脚本目录
  • /chl/pid:记录各个程序运行时pid
  • /chl/work:项目应用程序存放目录

代码结构

这里以chl-tss(一个业务系统模块)模块为例

思路

  • 使用maven构建项目
  • 构建后使用Publish Over SSH部署到远程服务器:
  • 关闭应用
  • 备份应用到lastDepoly目录
  • 上传新版本的Jar包(或者war包)
  • 启动应用
  • 其中,关闭、备份、删除和启动应用都是由脚本来完成,所以我写了一个部署脚本 chl-deploy.sh 放在 /chl/exec下(内容见 相关脚本 章节)。
  • Pushlish Over SSH的执行顺序是先上传文件再执行脚本,所以对于每个应用每个服务器我都设置了两个 Transfer Set :
  • 先执行清理脚本:关闭,备份和删除旧版应用jar包

chl-deploy.sh chl-tss clean

  • 再执行启动脚本启动应用

chl-deploy.sh chl-tss start

安装Jenkins

下载Jenkins

可以在https://jenkins.io/download/下载Jenkins的最新版本。建议下载LTS(LongTermSuport)。我下载的是 jenkins.war

选择下载war包

部署到内网服务器

  1. 将下载的jenkins.war包上传到Jenkins服务器上,使用一下命令启动:

nohup java -Xms256m -Xmx1024m -XX:MaxPermSize=512m -jar jenkins.war –ajp13Port=-1 –httpPort=7076 > jenkins.out 2>&1 &

其中 --httpPort=7076指定jenkins启动监听的端口,这里更改为7076(默认是8080)。-Xms256m -Xmx1024m -XX:MaxPermSize=512m设置了JVM参数(需要因环境而异)。

我安装到了/jenkins,目录结构为:

| - /jenkins/
|  + pid
|  + conf
|  + log
|    jenkins.sh
|    jenkins.war

其中,conf为jenkins工作目录,pid目录记录jenkins运行pid。jenkins.sh是为了方便启动和停止jenkins服务器,内容见相关脚本章节。

启动jenkins :

./jenkins.sh start

关闭jenkins:

./jenkins.sh stop

配置Jenkins

  1. 初始化
    1.1 启动Jenkins:
cd /jenkins
./jenkins.sh start;tail -f log/jenkins.out

1.2. 访问Jenkins:http://192.168.0.186:7076 (192.168.0.186是我部署jenkins的服务器)。页面出现:

输入管理员初始化密码

会提示
“Unlock Jenkins”,要求输入密码“Administrator password”,此时可以到控制台上找到这一段:

从控制台获取管理员密码

图中划横线的部分就是管理员密码,辅助粘贴到输入框中即可。另外也可以从/jenkins/conf/secrets/initialAdminPassword文件中得到:

cat /jenkins/conf/secrets/initialAdminPassword

1.3. 选择插件,选择“Install suggest Plugins”

选择默认插件

然后等待吧,安静的等插件安装完成。
此时需要一杯咖啡…

或者去解决一波BUG…

1.4 若干分钟之后,插件安装完成。之后设置管理员后就可以使用了。

  1. 增加 Publish Over SSH 插件
    系统设置->插件管理->可选插件 搜索 Publish Over SSH,选中点击立即安装即可安装。

搜索安装Publish Over SSH

  1. 系统配置
    在新建任务之前,需要做一些配置。
    2.1 设置jenkins路径

设置jenkins路径

  • Jenkins URL填写jenkins访问路径
  • 邮箱地址填写自己的邮箱地址

2.2 邮件通知设置

图片.png

  • 这是用QQ企业邮箱的配置

2.3 设置SSH远程服务器

SSH Servers

  • 这里点击“增加”即可新增多个远程服务器

设置远程服务器SSH(1)

  • 点击“高级”即可设置Push Over SSH的端口和密码

设置端口和密码

  • 勾选“Use password authentication, or use a different key”
  • Passphrase / Password 设置密码
  • Port 设置端口

2.4 SVN版本指定
如果使用SVN,则需要注意选择Subversion Workspace Version版本:

Subversion Workspace Version

  • subversion 版本设置一定要正确

2.6 保存后全局配置就已经设置完成了

2.7 项目配置
2.7.1 新建maven项目

构建一个maven项目

源码管理选择 Subversion

使用SVN源码管理

  • Credentials:凭证,点击 Add 即可输入用户名和密码
  • Local module directory :默认即可(Jenkins工作空间下)。特别是在web

构建环境中勾选 Send files or execute commands over SSH after the build runs,然后点击 Add Server,即可新增目标远程服务器:

构建环境增加SSH远程部署(1)

  • Name:选择 2.3 步骤中新增的远程服务器
  • Source files:需要上传的文件,可以使用通配符和Jenkins变量。这一步留空。
  • Exec command:在远程服务器上执行的脚本,这里我的想法是,先清理一下远程服务器,关闭服务并备份程序。
cd /chl/exec
sh ./chl-deploy.sh chl-tss clean

点击Add Transfer Set,新增一组设置:

构建环境增加SSH远程部署(2)

  • Source files:需要上传的文件。target/chl-tss.jar。即maven打包生成后的jar包文件。路径相对于maven工程的根目录。
  • Exec command:在远程服务器上执行的脚本。这里需要先上传文件到服务器,再执行启动脚本
cd /chl/exec
sh ./chl-deploy.sh chl-tss start
  • 点开“高级”之后勾选上 Flatten files,扁平化文件。只上传文件,不上传文件所属文件夹。否则上传到远程文件夹之后就会多一级 target 目录。

设置maven执行参数:

maven执行参数

  • 这里的 -P dev 是我在maven项目中使用了profile来区分测试和生产配置。

至此,Maven工程配置已基本完成。保存即可返回到工程点击立即构建即可开始构建。
如果构建失败,可以点击 Build History => Console Output 查看构建日志。

相关脚本

  1. jenkins.sh
#!/bin/sh
## java env
export JAVA_HOME=/usr/local/jdk1.8
export JRE_HOME=$JAVA_HOME/jre
export JENKINS_HOME=/jenkins/conf
## exec shell name
EXEC_SHELL_NAME=jenkins\.sh
## service name
SERVICE_NAME=jenkins
SERVICE_DIR=/jenkins
JAR_NAME=$SERVICE_NAME\.war
PID=pid/$SERVICE_NAME\.pid
cd $SERVICE_DIR
case "$1" in
    start)
        nohup java -Xms256m -Xmx1024m -XX:MaxPermSize=512m  -jar $JAR_NAME --ajp13Port=-1 --httpPort=7076 > jenkins.out 2>&1 &
        echo $! > $SERVICE_DIR/$PID
        echo "#### start $SERVICE_NAME"
        ;;

    stop)
        kill `cat $SERVICE_DIR/$PID`
        rm -rf $SERVICE_DIR/$PID
        echo "#### stop $SERVICE_NAME"
        sleep 8
        TEMP_PID=`ps -ef | grep -w "$SERVICE_NAME" | grep  "java" | awk '{print $2}'`
        if [ "$TEMP_PID" == "" ]; then
            echo "#### $SERVICE_NAME process not exists or stop success"
        else
           echo "#### $SERVICE_NAME process pid is:$TEMP_PID"
           kill -9 $TEMP_PID
        fi
        ;;

    restart)
        $0 stop
        sleep 2
        $0 start
        echo "#### restart $SERVICE_NAME"
        ;;

esac
exit 0
  1. chl-deploy.sh
 #!/bin/sh
## java env
export JAVA_HOME=/usr/local/jdk1.8
export JRE_HOME=$JAVA_HOME/jre

## exec shell name
EXEC_SHELL_NAME=$1\.sh
## service name
SERVICE_NAME=$1
SERVICE_DIR=/chl
JAR_NAME=$SERVICE_NAME\.jar
PID=pid/$SERVICE_NAME\.pid
WORK_DIR=$SERVICE_DIR/work

#function start
start(){
    cd $WORK_DIR
   if [ ! -d "log" ]; then
        mkdir log
    fi
   nohup $JRE_HOME/bin/java -Xms256m -Xmx512m -jar $JAR_NAME >log/$SERVICE_NAME.out 2>&1 &
        echo $! > $SERVICE_DIR/$PID
        echo "#### start $SERVICE_NAME"
}

# function stop
stop(){
    cd $WORK_DIR
   if [ -f "$SERVICE_DIR/$PID" ]; then
                kill `cat $SERVICE_DIR/$PID`
                rm -rf $SERVICE_DIR/$PID
        fi
        echo "#### stop $SERVICE_NAME"
        sleep 6
        TEMP_PID=`ps -ef | grep -w "$SERVICE_NAME" | grep "java" | awk '{print $2}'`
        if [ "$TEMP_PID" == "" ]; then
            echo "#### $SERVICE_NAME process not exists or stop success"
        else
            echo "#### $SERVICE_NAME process pid is:$TEMP_PID ."
            kill -9 $TEMP_PID
        fi
}

# function clean
clean(){
    cd $WORK_DIR
        if [ ! -d "lastDeploy" ]; then
           mkdir lastDeploy
        else
           rm lastDeploy/$SERVICE_NAME*
        fi
        if [ -f "$JAR_NAME" ]; then
           mv $JAR_NAME lastDeploy
        fi 
}

case "$2" in

    start)
   start
        ;;

    stop)
   stop
        ;;

    restart)
        stop
        sleep 2
        start
        echo "#### restart $SERVICE_NAME"
        ;;
   
    clean)
   stop
    sleep 2
   clean
    echo "#### clean $SERVICE_NAME"
   ;;

    *)
       ## restart
       stop
       sleep 2
       start
       ;;

esac
exit 0

踩坑记

  1. 报错:Exec exit status not zero. Status [-1]
ERROR: Exception when publishing, exception message [Exec exit status not zero. Status [-1]]
Finished: UNSTABLE

这个错误是我费时解决最长的一个,现象很奇怪,手动执行脚本没问题,其他环境也没问题,单独使用Jenkins在本地构建也没问题,偏偏用Jenkins+Publish Over SSH部署远程就会出现问题。
解决办法:检查了许久发现原因是因为之前的 chl-depoly.sh 关闭程序时使用了(35行),是这样写的:

错误程序

这会导致SSH当前的执行进程也被kill掉(因为只过滤grep),从而返回-1,而非正常结束的0。改成:

改正后的脚本

  1. 上传到远程服务器后文件找不到,即使在transfer set中指定Remote directory 也没用。
    解决办法: 在系统设置->全局配置-> SSH Servers中设置 Remote Directory

设置Remote Directory

  1. 上传到服务器上之后的目录是 target/chl-tss.jar而非期望的jar,多了一层target目录。
    解决办法:

勾选“Flatten files”

参考:

  1. Publish Over SSH插件
  2. Jenkins+Maven进行Java项目持续集成
  3. Jenkins官网