需求背景
- 我使用本地编译,复制到 linux 上运行的方式部署 go 项目
- 单体部署,如果是集群部署可以依赖 nginx 做无感切换
痛点
- 正在运行的 go 项目文件不能被覆盖,我需要先使用脚本停止项目,然后覆盖,然后再用脚本运行起来。 这会造成显著的服务中断。
- 单体运行多个 go项目时,需要明确区分项目名称,不然终止脚本可能会误杀
原脚本
启动
bash
nohup /全路径/项目名称 >> nohup.out &
tail -f /全路径/nohup.out
终止
bash
kill -15 `ps -ef | grep 项目名称 | grep -v grep | awk '{print $2}'`
新思路
- 每次还是编译成相同名称的文件。
- 脚本将新的可执行文件复制为一个新的名称,此名称确保和原来名称不重复。
- 如果需要更新项目包,脚本先复制新项目,然后终止原项目,然后运行新项目,可以最大程度缩小服务中断时间。
- 需要有个文件存储上一次运行的项目被改成了什么名字,以方便需要终止他的时候能找到他。
新脚本
代码如下
bash
#!/bin/bash
#项目路径
PROJECT_PATH="/opt/my_project/"
# 配置文件名称
CONFIG_FILE="shell_info.ini"
# 目标文件路径
TARGET_FILE="my_golang_app"
# 日志文件路径
LOG_FILE="nohup.log"
# 生成随机字符串
generate_random_string() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 6 | head -n 1
}
# 根据日期和随机数生成新文件名
generate_new_filename() {
local date_str=$(date +"%Y%m%d")
local random_str=$(generate_random_string)
echo "${date_str}_${random_str}"
}
# 启动服务
start_service() {
local new_filename=$(generate_new_filename)
cp $PROJECT_PATH$TARGET_FILE "$PROJECT_PATH$new_filename"
echo "Starting service with filename: $PROJECT_PATH$new_filename"
nohup "$PROJECT_PATH$new_filename" >> $PROJECT_PATH$LOG_FILE 2>&1 &
echo "current=$PROJECT_PATH$new_filename" > $PROJECT_PATH$CONFIG_FILE
tail -f $PROJECT_PATH$LOG_FILE
}
# 重新加载服务
reload_service() {
local current=$(grep '^current=' $PROJECT_PATH$CONFIG_FILE | cut -d'=' -f2)
if [ -z "$current" ]; then
echo "No current service to reload."
return
fi
local new_filename=$(generate_new_filename)
cp $PROJECT_PATH$TARGET_FILE "$PROJECT_PATH$new_filename"
echo "Reloading service from $current to $PROJECT_PATH$new_filename"
local pid=$(ps -ef | grep "$current" | grep -v grep | awk '{print $2}')
if [ -n "$pid" ]; then
kill -9 $pid
echo "Service $current stopped."
fi
nohup "$PROJECT_PATH$new_filename" >> $PROJECT_PATH$LOG_FILE 2>&1 &
rm "$current"
echo "current=$PROJECT_PATH$new_filename" > $PROJECT_PATH$CONFIG_FILE
tail -f $PROJECT_PATH$LOG_FILE
}
# 停止服务
stop_service() {
local current=$(grep '^current=' $PROJECT_PATH$CONFIG_FILE | cut -d'=' -f2)
if [ -z "$current" ]; then
echo "No current service to stop."
return
fi
local pid=$(ps -ef | grep "$current" | grep -v grep | awk '{print $2}')
if [ -n "$pid" ]; then
kill -9 $pid
echo "Service $current stopped."
fi
}
case $1 in
start)
start_service
;;
reload)
reload_service
;;
stop)
stop_service
;;
*)
echo "Usage: $0 {start|reload|stop}"
exit 1
;;
esac
exit 0
使用方法:
bash
#运行项目
sh runner.sh start
#使用新包覆盖原运行项目
sh runner.sh reload
#终止运行
sh runner.sh stop
使用说明:
如果你第一次查阅相关内容,就看到了我的文章,那么你在复制 linux 可执行文件后,可能需要修改其权限。建议将脚本和 linux 可执行文件放在同一文件夹下。
bash
#赋予当前路径下所有文件执行权限
chmod 777 ./*
修改脚本头部配置,其中项目路径填你存放脚本和 linux 可执行文件的路径,配置文件不用管,不要手欠误删了就没事。删了也没事,只是你得自己终止进程了。
目标文件路径填 linux 可执行文件的名称即可。.
脚本会复制原可执行文件,生成的新文件名称是:原名称+时间日期+乱码
在使用 reload 命令时,会自动清除上次复制出来的文件。