我本是Java高级开发,去年换了家公司,当时没有运维,让我来搞搞着试试。结果在运维的道路上越走越远。。。从此兼职了公司的运维,老板可高兴坏了。这次分享一篇当时写发布脚本的经历,希望能你有所帮助。
前言
- 此配置充分考虑了发布中的各种细节,适用于分布式发布。
- 此配置中流程模仿自去哪儿网,在此表示感谢。
- 配置的时候身边没有运维大佬交流(毕竟我是Java开发),如有不足的地方欢迎评论指正。
大致流程
擦。。。掘金的Markdown按照cmdMarkdown中教程画不出流程图,知道怎么画图的大佬救救孩子吧。
gitclone代码-->检测代码-->构建jar包-->上传jar包-->网关下线-->运行jar包-->网关上线-->检测服务
流程细节和Jenkins配置
Jenkins配置部分建议对照截图看
gitclone代码
Jenkins的plugin中安装Git相关插件用于clone代码,下图中${group}等为parameterized的参数,权限没有设置的那么细(其实是偷懒),所以没有按项目配置不同的job。不建议学我。
检测代码
通常可以用sonar检测,配置sonar代码检测规则,代码质量不合格直接打回,避免无效发布。
构建jar包
我用maven构建的jar包;war包配个tomcat还方便控制端口,一样的原理。Jenkins中也要下载maven相关plugins。
这里多了Pre Steps,每次构建前删除部分jar包。因为开发没有规范使用SNAPSHOT,所有依赖都用RELEASE,导致每次改动底层jar包后都让我手动去服务器删除老jar包。所以我机智的在每次构建前把所有的老jar包都删除了。这个操作也不规范,不要学习。
mvn命令是
clean package -Dmaven.test.skip=true -P test
上传jar包
用的插件是Publish over SSH,这里的关键是连接上远程服务器。
shell 脚本
shell脚本实现了网关下线、运行jar包、网关上线、检测服务。先来个总览,再细说。
#!/bin/bash
source /etc/profile
source /home/java/.bash_profile
cd /home/java/jar
#发送eureka下线指令
offline=`curl -X -o /dev/null DELETE "http://192.168.30.230:4011/$project/health/offline/018ee962eab6431393540d5eb33s43hs2" -H "accept: */*" `
echo "请求完成,响应内容是$offline"
#sleep 3s 保证已经打到的请求完整返回
sleep 3s
#下线后停止服务
./springboot.sh stop $project-test
# 构建 or 回滚
echo $action
if [ "$action" == "build" ]
then
#备份之前构建
mkdir -p backup/$BUILD_NUMBER
mv $project-test.jar backup/$BUILD_NUMBER
cp target/$project-test.jar .
elif [ "$action" == "rollback" ]
then
cp backup/$buildId/$project-test.jar .
else
exit 1
fi
#保留Jenkins衍生进程
BUILD_ID=DONTKILLME
./springboot.sh start $project-test
#启动后等待上线时间
sleep 10s
#判断服务是否上线成功
for i in {1..10}
do
code=`curl -I -m 10 -o /dev/null -s -w %{http_code} -X GET "http://192.168.30.230:4011/$project/health/check" -H "accept: */*"`
sleep 3s
if [ "$code" == "200" ];then
echo "第$i次尝试,上线成功,响应码是$code"
exit 0
else
echo "第$i次尝试,上线失败,响应码是$code"
fi
done
exit
网关下线
我在代码中写了让服务器下线gateway的接口,为了安全性,做了key的拦截。
public static final String KEY = "018ee962eab6431393540d5eb337131230a12";
@GetMapping("/check")
public String healthCheck() {
return "service is running health";
}
@DeleteMapping("/offline/{key}")
public String offLine(@PathVariable String key) {
if (ObjectUtils.equals(KEY, key)) {
DiscoveryManager.getInstance().shutdownComponent();
return "下线成功";
} else {
return "下线失败,秘钥错误";
}
}
需要注意的是,服务器下线后不能立马重启,因为下线后新请求不会打过来,但可能有已经打进来的请求还没返回出去,所以要等3s把所有进来请求都安全返回。
#发送eureka下线指令
offline=`curl -X -o /dev/null DELETE "http://192.168.30.230:4011/$project/health/offline/018ee962eab6431393540d5eb33s43hs2" -H "accept: */*" `
echo "请求完成,响应内容是$offline"
#sleep 3s 保证已经打到的请求完整返回
sleep 3s
运行jar包
这里我做了回滚,所以如果发布的话会把jar包保存到backup,是回滚就从构建开始拦截,直接cp backup里指定的jar包,我用$BUILD_NUMBER区分备份。springboot.sh是项目运行脚本。
# 构建 or 回滚
echo $action
if [ "$action" == "build" ]
then
#备份之前构建
mkdir -p backup/$BUILD_NUMBER
mv $project-test.jar backup/$BUILD_NUMBER
cp target/$project-test.jar .
elif [ "$action" == "rollback" ]
then
cp backup/$buildId/$project-test.jar .
else
exit 1
fi
#保留Jenkins衍生进程
BUILD_ID=DONTKILLME
./springboot.sh start $project-test
以下是springboot脚本内容
#!/bin/bash
# RUNNING_USER=jianzhangg
ADATE=`date +%Y%m%d%H%M%S`
APP_NAME=$2
echo $APP_NAME
APP_HOME=`pwd`
dirname $0|grep "^/" >/dev/null
if [ $? -eq 0 ];then
APP_HOME=`dirname $0`
else
dirname $0|grep "^\." >/dev/null
retval=$?
if [ $retval -eq 0 ];then
APP_HOME=`dirname $0|sed "s#^.#$APP_HOME#"`
else
APP_HOME=`dirname $0|sed "s#^#$APP_HOME/#"`
fi
fi
if [ ! -d "$APP_HOME/logs" ];then
mkdir $APP_HOME/logs
fi
LOG_PATH=$APP_HOME/logs/$APP_NAME.log
GC_LOG_PATH=$APP_HOME/logs/gc-$APP_NAME-$ADATE.log
if [ ! -f "$LOG_PATH" ]; then
touch "$LOG_PATH"
fi
if [ ! -f "$GC_LOG_PATH" ]; then
touch "$GC_LOG_PATH"
fi
#JMX监控需用到
JMX="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1091 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
#JVM参数
JVM_OPTS="-Dname=$APP_NAME -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -Xloggc:$GC_LOG_PATH -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
JAR_FILE=$APP_NAME.jar
pid=0
start(){
checkpid
if [ ! -n "$pid" ]; then
nohup java -jar $JVM_OPTS $JAR_FILE > $LOG_PATH 2>&1 &
# JAVA_CMD="nohup java -jar $JVM_OPTS $JAR_FILE > $LOG_PATH 2>&1 &"
# su - $RUNNING_USER -c "$JAVA_CMD"
echo "---------------------------------"
echo "启动完成,按CTRL+C退出日志界面即可>>>>>"
echo "---------------------------------"
sleep 20s
tail -n 500 $LOG_PATH
# sleep 20s
# exit
else
echo "$APP_NAME is runing PID: $pid"
fi
}
status(){
checkpid
if [ ! -n "$pid" ]; then
echo "$APP_NAME not runing"
else
echo "$APP_NAME runing PID: $pid"
fi
}
checkpid(){
pid=`ps -ef |grep "Dname=$APP_NAME" |grep -v grep |awk '{print $2}'`
}
stop(){
checkpid
if [ ! -n "$pid" ]; then
echo "$APP_NAME not runing"
else
echo "$APP_NAME stop..."
kill -9 $pid
fi
}
restart(){
stop
sleep 1s
start
}
case $1 in
start) start;;
stop) stop;;
restart) restart;;
status) status;;
*) echo "require start|stop|restart|status" ;;
esac
网关上线、检测服务
springGateway服务重启后会自动上线;以前在去哪儿里面用的nginx,有个healthcheck.html做上下线,通过ping html判断tomcat是否启动。我这里也是在项目里面写了接口,通过请求接口判断。
#判断服务是否上线成功
for i in {1..10}
do
code=`curl -I -m 10 -o /dev/null -s -w %{http_code} -X GET "http://192.168.30.230:4011/$project/health/check" -H "accept: */*"`
sleep 3s
if [ "$code" == "200" ];then
echo "第$i次尝试,上线成功,响应码是$code"
exit 0
else
echo "第$i次尝试,上线失败,响应码是$code"
fi
done
完结
以上是完整的发布流程,如果分布式环境需要在Jenkins配置post step,脚本内容类似。
自我感觉这一套还可以,不知道各路大佬怎么看,还请多多指教,毕竟第一次搞运维。。。我感觉在开发的路上越走越远了。。。。
欢迎关注微信公众号,提供思想和技术类原创文章。微信搜索小兵张健或扫描以下二维码。