用 Bash 脚本实现 Jar 包在多环境中的部署与 JVM 的启停管理
故事背景:
- 暂未启用 CI/CD 机制;
- JVM 以 Jar 包方式启动;
- 存在开发、测试、生产三套环境。
解决的痛点:
- 解放劳动力,使 Jar 包从开发、测试到生产环境的部署自动化;
- 友好且优雅地管理 JVM;
- 我要看到更详细的日志。嗯,配合 ELK 简直真香。
实现前提:
- Jar 包中配置文件(数据池、配置项等)剥离,每套环境独立,Jar 包重复利用;
- 各环境中已配置了 FTP 服务,且可访问到 Jar 包所在目录。
好,开始表演:
#!/bin/sh
# author: xuchao_dot_org
DATE_TIME=$(date "+%Y-%m-%d %H:%M:%S");
BASE_DIR=/data/java/jar/myproject
TMP_DIR=/data/java/tmp/myproject
PROJECT_NAME=myproject_name # 同样也是 Jar 包的文件名
CONFIG_FILE=/data/java/conf/myproject/application.yml
LOG_FILE=/data/java/logs/myproject/$(date +%Y_%m_%d_%H_%M_%S).log
DEV_IP=
DEV_PORT=
DEV_USERNAME=
DEV_PASSWD=
DEV_DIR=/jar/myproject
TEST_IP=
TEST_PORT=
TEST_USERNAME=
TEST_PASSWD=
TEST_DIR=/jar/myproject
export JAVA_HOME=/data/java/jdk/jdk1.8.0_191
export PATH=$JAVA_HOME/bin:$PATH
export JAVA=$JAVA_HOME/bin/java
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
function running() {
count=`ps aux | grep java | grep ${PROJECT_NAME}.jar | grep -v grep | wc -l`
procedure=`ps -ef | grep -w "${PROJECT_NAME}.jar" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
if [ ${count} -eq 1 -o "${procedure}" != "" ]; then
return 0
else
return 1
fi
}
function start() {
if running; then
echo "${PROJECT_NAME} is running."
echo "${DATE_TIME} failed to start, ${PROJECT_NAME} is running." 2>&1 >> $LOG_FILE
exit 1
fi
echo "starting ${PROJECT_NAME}..."
echo "${DATE_TIME} starting ${PROJECT_NAME}..." >> $LOG_FILE
echo ${CLASSPATH} >> $LOG_FILE
exec nohup $JAVA -server -Xms128m -Xmx256m -Xss256k -Dspring.config.location=${CONFIG_FILE} -Djava.io.tmpdir=${TMP_DIR} -jar ${BASE_DIR}/${PROJECT_NAME}\.jar >> $LOG_FILE 2>&1 & # 需要调整 JVM 启动参数的选手请看这里
sleep 15
if running; then
echo "${PROJECT_NAME} is successfully started."
echo "${DATE_TIME} ${PROJECT_NAME} is successfully started." 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to start."
echo "${DATE_TIME} ${PROJECT_NAME} is failed to start." 2>&1 >> ${LOG_FILE}
fi
}
function stop() {
if ! running; then
echo "${PROJECT_NAME} is not running." | tee ${LOG_FILE}
echo "${DATE_TIME} failed to stop, ${PROJECT_NAME} is not running." 2>&1 >> ${LOG_FILE}
exit 1
fi
echo "stopping ${PROJECT_NAME}..."
echo "${DATE_TIME} stopping ${PROJECT_NAME}..." >> $LOG_FILE
pid=`ps -ef | grep -w "${PROJECT_NAME}.jar" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
kill -15 ${pid}
sleep 5
if ! running; then
echo "${PROJECT_NAME} is successfully stoped."
echo "${DATE_TIME} ${PROJECT_NAME} is successfully stoped." 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to stop. trying again..."
echo "${DATE_TIME} ${PROJECT_NAME} is failed to stop. trying again..." 2>&1 >> ${LOG_FILE}
kill -15 ${pid}
sleep 5
if ! running; then
echo "${PROJECT_NAME} is successfully stoped. [2nd attempt]"
echo "${DATE_TIME} ${PROJECT_NAME} is successfully stoped. [2nd attempt]" 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to stop. [2nd attempt]"
echo "${DATE_TIME} ${PROJECT_NAME} is failed to stop. [2nd attempt]" 2>&1 >> ${LOG_FILE}
fi
fi
}
function status() {
if running; then
echo "${PROJECT_NAME} is running."
else
echo "${PROJECT_NAME} is stopped."
fi
}
function help() {
echo "script for jvm managerment. author: xuchao_dot_org"
echo "usage: $0 [start|stop|restart|status|get|help]"
echo " start: start the ${PROJECT_NAME} server"
echo " stop: stop the ${PROJECT_NAME} server"
echo " restart: restart the ${PROJECT_NAME} server"
echo " status: get ${PROJECT_NAME} current status, running or stopped"
echo " get: [dev|test] [version] get file from dev or test"
echo " help: show this message"
}
function get_dev() {
[ -z $1 ] && echo "params error. please see [ $0 help ] for more information." && exit 1
echo "geting file [${PROJECT_NAME}-$1.jar] from dev..."
echo "${DATE_TIME} geting file [${PROJECT_NAME}-$1.jar] from dev..." 2>&1 >> ${LOG_FILE}
ftp -inp 2>&1 >> ${LOG_FILE} <<EOF
open ${DEV_IP} ${DEV_PORT}
user ${DEV_USERNAME} ${DEV_PASSWD}
cd ${DEV_DIR}
lcd ${BASE_DIR}
get ${PROJECT_NAME}-$1.jar
close
bye
EOF
if [ -f "${BASE_DIR}/${PROJECT_NAME}-$1.jar" ]; then
echo "get file [${PROJECT_NAME}-$1.jar] from dev successfully."
echo "${DATE_TIME} get file [${PROJECT_NAME}-$1.jar] from dev successfully." 2>&1 >> ${LOG_FILE}
\cp -rf ${BASE_DIR}/${PROJECT_NAME}-$1.jar ${BASE_DIR}/${PROJECT_NAME}.jar
echo "copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully."
echo "${DATE_TIME} copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully." 2>&1 >> ${LOG_FILE}
else
echo "failed to get file [${PROJECT_NAME}-$1.jar] from dev."
echo "${DATE_TIME} failed to get file [${PROJECT_NAME}-$1.jar] from dev." 2>&1 >> ${LOG_FILE}
fi
}
function get_test() {
[ -z $1 ] && echo "params error. please see [ $0 help ] for more information." && exit 1
echo "geting file [${PROJECT_NAME}-$1.jar] from test..."
echo "${DATE_TIME} geting file [${PROJECT_NAME}-$1.jar] from test..." 2>&1 >> ${LOG_FILE}
ftp -inp 2>&1 >> ${LOG_FILE} <<EOF
open ${TEST_IP} ${TEST_PORT}
user ${TEST_USERNAME} ${TEST_PASSWD}
cd ${TEST_DIR}
lcd ${BASE_DIR}
get ${PROJECT_NAME}-$1.jar
close
bye
EOF
if [ -f "${BASE_DIR}/${PROJECT_NAME}-$1.jar" ]; then
echo "get file [${PROJECT_NAME}-$1.jar] from test successfully."
echo "${DATE_TIME} get file [${PROJECT_NAME}-$1.jar] from test successfully." 2>&1 >> ${LOG_FILE}
\cp -rf ${BASE_DIR}/${PROJECT_NAME}-$1.jar ${BASE_DIR}/${PROJECT_NAME}.jar
echo "copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully."
echo "${DATE_TIME} copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully." 2>&1 >> ${LOG_FILE}
else
echo "failed to get file [${PROJECT_NAME}-$1.jar] from test."
echo "${DATE_TIME} failed to get file [${PROJECT_NAME}-$1.jar] from test." 2>&1 >> ${LOG_FILE}
fi
}
function get_file() {
[[ -z $1 || -z $2 ]] && echo "params error. please see [ $0 help ] for more information." && exit 1
case "$1" in
dev)
get_dev $2
;;
test)
get_test $2
;;
*)
help
exit 1
;;
esac
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
$0 stop
sleep 5
$0 start
;;
status)
status
;;
help)
help
;;
get)
get_file $2 $3
;;
*)
help
exit 1
;;
esac
演出结束,下期再见。