使用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。
部署到内网服务器
- 将下载的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 启动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”
然后等待吧,安静的等插件安装完成。
此时需要一杯咖啡...
1.4 若干分钟之后,插件安装完成。之后设置管理员后就可以使用了。
- 增加 Publish Over SSH 插件
在 系统设置->插件管理->可选插件 搜索Publish Over SSH
,选中点击立即安装即可安装。
- 系统配置
在新建任务之前,需要做一些配置。
2.1 设置jenkins路径
- Jenkins URL填写jenkins访问路径
- 邮箱地址填写自己的邮箱地址
2.2 邮件通知设置
- 这是用QQ企业邮箱的配置
2.3 设置SSH远程服务器
- 这里点击“增加”即可新增多个远程服务器
- 点击“高级”即可设置Push Over SSH的端口和密码
- 勾选“Use password authentication, or use a different key”
- Passphrase / Password 设置密码
- Port 设置端口
2.4 SVN版本指定
如果使用SVN,则需要注意选择Subversion Workspace Version版本:
- subversion 版本设置一定要正确
2.6 保存后全局配置就已经设置完成了
2.7 项目配置
2.7.1 新建maven项目
源码管理选择 Subversion:
- Credentials:凭证,点击 Add 即可输入用户名和密码
- Local module directory :默认即可(Jenkins工作空间下)。特别是在web
构建环境中勾选 Send files or execute commands over SSH after the build runs,然后点击 Add Server,即可新增目标远程服务器:
- Name:选择 2.3 步骤中新增的远程服务器
- Source files:需要上传的文件,可以使用通配符和Jenkins变量。这一步留空。
- Exec command:在远程服务器上执行的脚本,这里我的想法是,先清理一下远程服务器,关闭服务并备份程序。
cd /chl/exec
sh ./chl-deploy.sh chl-tss clean
点击Add Transfer Set,新增一组设置:
- 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执行参数:
- 这里的 -P dev 是我在maven项目中使用了profile来区分测试和生产配置。
至此,Maven工程配置已基本完成。保存即可返回到工程点击立即构建即可开始构建。
如果构建失败,可以点击 Build History => Console Output 查看构建日志。
相关脚本
- 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
- 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
踩坑记
- 报错: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。改成:
- 上传到远程服务器后文件找不到,即使在transfer set中指定Remote directory 也没用。
解决办法: 在系统设置->全局配置-> SSH Servers中设置 Remote Directory
- 上传到服务器上之后的目录是 target/chl-tss.jar而非期望的jar,多了一层target目录。
解决办法: