守护进程
概述
一次为云服务器上的 FineReport 制作守护进程时使用 systemd 出现问题,启动命令执行要等很长时间,结果是失败的,后来使用 rc.local 就可以快速成功的制作开机启动。不过 ubuntu22server 开始就已经弃用 rc.local 了,需要手动制作该文件并启用
# 制作文件
sudo vim /etc/rc.local
# 黏贴如下代码,注意第一行的感叹号后面不要有空格,否则报错
# ==== 代码区域 start====
#!/bin/bash
sleep 10
/home/chanchaw/zipkin/start.sh
# 把所有自定义启动项都写在这一行的上面
# 保证本行是最后一行
exit 0
# ==== 代码区域 end ====
# 赋予可执行权限
sudo chmod +x /etc/rc.local
# 启用后台服务
sudo systemctl enable rc-local.service
# 手动启动服务
sudo systemctl enable rc-local.service
systemd 不好用
为 FineReport 制作守护进程,创建文件 fine-report.service
不可行!!!
# 创建文件
sudo vim /etc/systemd/system/fine-report.service
# 黏贴下面内容
[Unit]
Description=Fine Report Server
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/projs/barrel/report/server
ExecStart=/usr/lib/jvm/java8/bin/java -Xms512m -Xmx512m -jar fine-report.jar --spring.config.location=application.yml
ExecStop=/bin/kill -15 $MAINPID
SuccessExitStatus=143
Restart=always
RestartSec=10
# 设置JAVA_HOME环境变量
Environment="JAVA_HOME=/usr/lib/jvm/java8"
Environment="SPRING_CONFIG_LOCATION=./application.yml"
# 安全设置
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/projs/barrel/report/server
[Install]
WantedBy=multi-user.target
# 刷新守护进程
sudo systemctl daemon-reload
sudo systemctl start fine-report
sudo systemctl enable fine-report
sudo systemctl status fine-report
自启动 - 已验证
上面通过 systemctl 的方式管理进程,制作 FineReport 时出现问题,在浏览器中访问速度非常慢(输入URL后要等待很长时间出现页面),输入账号密码登录后没有反应,不跳转进入系统。后来修改为使用脚本启动项目,并使用 /etc/rc.local 设置自启动可以成功访问并且速度很快。下面是启动项目的脚本:
#!/bin/bash
# 启动脚本配置
APP_NAME="barrel-fine-report"
JAR_FILE_NAME="barrel-fine-report.jar"
JAR_FILE_PATH="/projs/barrel/report/server"
JAR_PATH="/projs/barrel/report/server/barrel-fine-report.jar"
CONFIG_FILE="/projs/barrel/report/server/application.yml"
LOG_FILE="/var/log/barrel-fine-report.log"
STARTUP_LOG="/var/log/barrel-fine-report-startup.log"
PID_FILE="/var/run/barrel-fine-report.pid"
JAVA_OPTS="-Xms512m -Xmx512m -Dspring.config.location=$CONFIG_FILE"
# 检查必要文件是否存在
if [ ! -f "$JAR_PATH" ]; then
echo "$(date): 错误: 找不到 JAR 文件: $JAR_PATH" >> "$STARTUP_LOG"
exit 1
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "$(date): 警告: 找不到配置文件: $CONFIG_FILE" >> "$STARTUP_LOG"
fi
# 检查 Java 是否安装
if ! command -v java &> /dev/null; then
echo "$(date): 错误: Java 未安装" >> "$STARTUP_LOG"
exit 1
fi
# 检查是否已在运行
if [ -f "$PID_FILE" ]; then
OLD_PID=$(cat "$PID_FILE")
if ps -p "$OLD_PID" > /dev/null 2>&1; then
echo "$(date): 警告: $APP_NAME 已在运行 (PID: $OLD_PID)" >> "$STARTUP_LOG"
exit 1
else
echo "$(date): 清理陈旧的 PID 文件" >> "$STARTUP_LOG"
rm -f "$PID_FILE"
fi
fi
# 切换到项目目录
cd $JAR_FILE_PATH
# 启动应用
echo "$(date): 启动 $APP_NAME 服务..." >> "$STARTUP_LOG"
nohup java $JAVA_OPTS -jar $JAR_FILE_NAME >> "$LOG_FILE" 2>&1 &
# 记录进程 ID
PID=$!
echo $PID > "$PID_FILE"
echo "$(date): $APP_NAME 服务启动完成,PID: $PID" >> "$STARTUP_LOG"
# 验证进程是否在运行
sleep 3
if ps -p "$PID" > /dev/null 2>&1; then
echo "$(date): 验证: 进程 $PID 正在运行" >> "$STARTUP_LOG"
else
echo "$(date): 错误: 进程 $PID 启动后立即退出" >> "$STARTUP_LOG"
rm -f "$PID_FILE"
exit 1
fi
下面是关闭项目的脚本,要配套使用,启动脚本中指定了 PID 文件所在的路径,关闭脚本中要用到
#!/bin/bash
# 关闭脚本配置
APP_NAME="barrel-fine-report"
PID_FILE="/var/run/barrel-fine-report.pid"
LOG_FILE="/var/log/barrel-fine-report.log"
SHUTDOWN_LOG="/var/log/barrel-fine-report-shutdown.log"
# 记录关闭开始时间
echo "$(date): 开始停止 $APP_NAME 服务..." >> "$SHUTDOWN_LOG"
# 方法1: 通过 PID 文件停止进程
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p "$PID" > /dev/null 2>&1; then
echo "$(date): 通过 PID 文件找到进程 $PID,正在停止..." >> "$SHUTDOWN_LOG"
kill "$PID"
# 等待进程结束,最多30秒
TIMEOUT=30
while [ $TIMEOUT -gt 0 ] && ps -p "$PID" > /dev/null 2>&1; do
sleep 1
TIMEOUT=$((TIMEOUT-1))
done
# 如果进程还在,强制杀死
if ps -p "$PID" > /dev/null 2>&1; then
echo "$(date): 正常停止失败,强制杀死进程 $PID..." >> "$SHUTDOWN_LOG"
kill -9 "$PID"
sleep 2
fi
# 确认进程已停止
if ps -p "$PID" > /dev/null 2>&1; then
echo "$(date): 错误: 无法停止进程 $PID" >> "$SHUTDOWN_LOG"
exit 1
else
echo "$(date): 成功停止进程 $PID" >> "$SHUTDOWN_LOG"
rm -f "$PID_FILE"
fi
else
echo "$(date): 警告: PID 文件存在但进程 $PID 未运行" >> "$SHUTDOWN_LOG"
rm -f "$PID_FILE"
fi
else
echo "$(date): 警告: 未找到 PID 文件 $PID_FILE" >> "$SHUTDOWN_LOG"
fi
制作统一管理脚本 barrel-fine-report-service.sh
#!/bin/bash
# 服务管理脚本
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
START_SCRIPT="/projs/barrel/report/bin/start-barrel-fine-report.sh"
STOP_SCRIPT="/projs/barrel/report/bin/stop-barrel-fine-report.sh"
PID_FILE="/var/run/barrel-fine-report.pid"
APP_NAME="barrel-fine-report"
JAR_FILE_NAME="barrel-fine-report.jar"
case "$1" in
start)
echo "启动 $APP_NAME 服务..."
$START_SCRIPT
;;
stop)
echo "停止 $APP_NAME 服务..."
$STOP_SCRIPT
;;
restart)
echo "重启 $APP_NAME 服务..."
$STOP_SCRIPT
sleep 3
$START_SCRIPT
;;
status)
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p "$PID" > /dev/null 2>&1; then
echo "$APP_NAME 正在运行 (PID: $PID)"
exit 0
else
echo "$APP_NAME PID 文件存在但进程未运行"
exit 1
fi
else
# 检查是否有通过其他方式启动的进程
PROCESSES=$(ps aux | grep "$JAR_FILE_NAME" | grep -v grep)
if [ -n "$PROCESSES" ]; then
echo "$APP_NAME 在运行但没有 PID 文件:"
echo "$PROCESSES"
exit 0
else
echo "$APP_NAME 未运行"
exit 3
fi
fi
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
对于进程管理的命令如下
# 查看进程状态
sudo ./barrel-fine-report-service status
# 启动项目
sudo ./barrel-fine-report-service start
# 停止项目
sudo ./barrel-fine-report-service stop
创建两个脚本文件后记得使用 sudo chmod +x start-barrel-fine-report.sh 赋予可执行的权限,下面通过 rc.local 设置开机启动
sudo vim /etc/rc.local
# 在该文件的末尾添加下面几行代码
# 等待系统基本服务启动完成
sleep 10
# 启动 barrel 服务
/projs/barrel/report/bin/start-barrel-fine-report.sh
# 创建符号链接
sudo ln -s /projs/barrel/backend/bin/barrel-service.sh /usr/local/bin/barrel-service
# 此后在任意路径都可执行下面命令
barrel-service status
barrel-service start
barrel-service stop
# 按照名称模糊查找软连接
# 查找包含 barrel 的软连接
find / -type l -name "*barrel*" 2>/dev/null
非java管理脚本
2026年1月26日 在群晖安装部署 万吉 时使用 Grok 制作的管理脚本如下
#!/bin/bash
# 服务配置
SERVICE_NAME="lucky"
BIN_DIR="/opt/wanji"
EXECUTABLE="$BIN_DIR/lucky"
PIDFILE="/var/run/${SERVICE_NAME}.pid"
LOGFILE="/var/log/${SERVICE_NAME}.log" # 可选日志文件,如果不需要可改为 /dev/null
start() {
if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "$SERVICE_NAME is already running."
return 1
fi
echo "Starting $SERVICE_NAME..."
cd "$BIN_DIR" || exit 1
nohup ./"$(basename "$EXECUTABLE")" >> "$LOGFILE" 2>&1 &
echo $! > "$PIDFILE"
if kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "$SERVICE_NAME started."
else
echo "Failed to start $SERVICE_NAME."
rm -f "$PIDFILE"
return 1
fi
}
stop() {
if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "$SERVICE_NAME is not running."
return 1
fi
echo "Stopping $SERVICE_NAME..."
kill $(cat "$PIDFILE")
rm -f "$PIDFILE"
echo "$SERVICE_NAME stopped."
}
status() {
if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "$SERVICE_NAME is running (PID: $(cat "$PIDFILE"))."
else
echo "$SERVICE_NAME is stopped."
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
;;
esac
exit 0
按照下面步骤设置文件可执行权限以及设置开机自启动
sudo cp lucky_manager.sh /usr/local/bin/luckyctl
sudo chmod +x /usr/local/bin/luckyctl
# 测试命令
luckyctl status # 在任何路径下运行
# 启动
luckyctl start
# 设置开机自启动
sudo cp lucky_manager.sh /usr/local/etc/rc.d/S99lucky.sh
sudo chmod +x /usr/local/etc/rc.d/S99lucky.sh
# 重启NAS测试自启动
上面脚本都是测试通过的
管理脚本生成器
在为群晖制作 万吉 的管理脚本时还让 Grok 制作了生成管理脚本的脚本,制作文件 generate_service_script.sh 的内容如下
#!/bin/bash
# 默认值
SERVICE_NAME=""
BIN_DIR=""
EXECUTABLE_NAME=""
OUTPUT_FILE=""
PID_DIR="/var/run"
LOG_DIR="/var/log"
usage() {
echo "Usage: $0 -n <service_name> -d <bin_dir> -e <executable_name> -o <output_file>"
echo "Example: $0 -n filebrowser -d /opt/filebrowser -e filebrowser -o filebrowser_manager.sh"
exit 1
}
while getopts ":n:d:e:o:" opt; do
case $opt in
n) SERVICE_NAME="$OPTARG" ;;
d) BIN_DIR="$OPTARG" ;;
e) EXECUTABLE_NAME="$OPTARG" ;;
o) OUTPUT_FILE="$OPTARG" ;;
*) usage ;;
esac
done
if [ -z "$SERVICE_NAME" ] || [ -z "$BIN_DIR" ] || [ -z "$EXECUTABLE_NAME" ] || [ -z "$OUTPUT_FILE" ]; then
usage
fi
EXECUTABLE="$BIN_DIR/$EXECUTABLE_NAME"
PIDFILE="$PID_DIR/${SERVICE_NAME}.pid"
LOGFILE="$LOG_DIR/${SERVICE_NAME}.log"
cat << EOF > "$OUTPUT_FILE"
#!/bin/bash
# 服务配置
SERVICE_NAME="$SERVICE_NAME"
BIN_DIR="$BIN_DIR"
EXECUTABLE="$EXECUTABLE"
PIDFILE="$PIDFILE"
LOGFILE="$LOGFILE" # 可选日志文件,如果不需要可改为 /dev/null
start() {
if [ -f "\$PIDFILE" ] && kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
echo "\$SERVICE_NAME is already running."
return 1
fi
echo "Starting \$SERVICE_NAME..."
cd "\$BIN_DIR" || exit 1
nohup ./"\$(basename "\$EXECUTABLE")" >> "\$LOGFILE" 2>&1 &
echo \$! > "\$PIDFILE"
if kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
echo "\$SERVICE_NAME started."
else
echo "Failed to start \$SERVICE_NAME."
rm -f "\$PIDFILE"
return 1
fi
}
stop() {
if [ ! -f "\$PIDFILE" ] || ! kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
echo "\$SERVICE_NAME is not running."
return 1
fi
echo "Stopping \$SERVICE_NAME..."
kill \$(cat "\$PIDFILE")
rm -f "\$PIDFILE"
echo "\$SERVICE_NAME stopped."
}
status() {
if [ -f "\$PIDFILE" ] && kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
echo "\$SERVICE_NAME is running (PID: \$(cat "\$PIDFILE"))."
else
echo "\$SERVICE_NAME is stopped."
fi
}
case "\$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
*)
echo "Usage: \$0 {start|stop|status}"
exit 1
;;
esac
exit 0
EOF
echo "Generated service script: $OUTPUT_FILE"
chmod +x "$OUTPUT_FILE"
使用命令 chmod +x generate_service_script.sh 赋予可执行权限,如果要声明软件名称是 filebrowser 的管理脚本,则
# 生成管理脚本
./generate_service_script.sh -n filebrowser -d /opt/filebrowser -e filebrowser -o filebrowser_manager.sh
# 制作软连接
sudo cp filebrowser_manager.sh /usr/local/bin/filebrowserctl
sudo chmod +x /usr/local/bin/filebrowserctl
# 测试命令
luckyctl status # 在任何路径下运行
# 设置开机自启动
sudo cp filebrowser_manager.sh /usr/local/etc/rc.d/S99filebrowser.sh
sudo chmod +x /usr/local/etc/rc.d/S99filebrowser.sh
# 重启NAS测试自启动
