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

使用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官网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 个人学习批处理的初衷来源于实际工作;在某个迭代版本有个BS(安卓手游模拟器)大需求,从而在测试过程中就重复涉及到...
    Luckykailiu阅读 4,527评论 0 11
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • 本文章仅作为个人笔记 CDH官网地址:https://www.cloudera.com/ CDH镜像地址: c...
    草丛里的黄盖阅读 1,863评论 2 2
  • 一、物理层:将数据转化为物理信号 设备:中继器、集线器 二、数据链路层:用MAC地址来寻址 每个主机都有一个,全球...
    xuyouxin1阅读 282评论 0 3