怎么用Jenkins配置分布式环境的安全发布?

842 阅读4分钟

我本是Java高级开发,去年换了家公司,当时没有运维,让我来搞搞着试试。结果在运维的道路上越走越远。。。从此兼职了公司的运维,老板可高兴坏了。这次分享一篇当时写发布脚本的经历,希望能你有所帮助。

前言

  • 此配置充分考虑了发布中的各种细节,适用于分布式发布。
  • 此配置中流程模仿自去哪儿网,在此表示感谢。
  • 配置的时候身边没有运维大佬交流(毕竟我是Java开发),如有不足的地方欢迎评论指正。

大致流程

点击查看Jenkins配置截图

擦。。。掘金的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,脚本内容类似。

自我感觉这一套还可以,不知道各路大佬怎么看,还请多多指教,毕竟第一次搞运维。。。我感觉在开发的路上越走越远了。。。。


欢迎关注微信公众号,提供思想和技术类原创文章。微信搜索小兵张健或扫描以下二维码。