Published on

饥荒联机版服务器自动备份监控脚本配置笔记

Authors
  • avatar
    作者
    子君
饥荒联机版服务器自动备份监控脚本配置笔记

饥荒联机版服务器自动备份监控脚本配置笔记

背景

在 Linux 云服务器上使用一个名为 z.sh 的二进制程序(实际为 ELF 可执行文件)来启动《饥荒联机版》(Don't Starve Together)专用服务器。z.sh 提供了一个交互式菜单,可以手动启动/停止服务器。需求是:当服务器进程结束时,自动备份存档,并按存档名称分类保存,保留最近 10 个备份,同时记录清晰的日志。

解决方案

编写一个独立的 Bash 监控脚本 monitor_and_backup.sh,配合启动脚本 start_monitor.sh,实现以下功能:

  • 等待服务端进程出现,记录其 PID 和 -conf_dir 参数(如 DST_1
  • 监控该 PID 是否退出
  • 进程退出后,从 cluster.ini 中读取存档名(cluster_name),清理非法字符后与时间戳组成备份文件名
  • 将整个存档目录打包压缩到 /root/.klei/backup/DST_X/
  • 自动删除旧备份(保留最近 10 个)
  • 日志输出到 /root/.klei/backup.log,每次运行用分隔线包裹,便于查阅
  • 使用文件锁防止同一时间多个监控实例运行
  • 启动脚本具备“清理旧实例”功能,避免残留进程导致新监控无法启动

文件结构

/root/
├── monitor_and_backup.sh      # 监控脚本
├── start_monitor.sh            # 启动监控的辅助脚本(带清理)
└── .klei/
    ├── DST_1/                  # 存档目录(示例)
    │   └── Cluster_1/
    │       └── cluster.ini     # 包含 cluster_name
    ├── backup/                 # 自动备份存放目录
    │   └── DST_1/
    │       └── 存档名_时间戳.tar.gz
    └── backup.log              # 日志文件

脚本内容

1、监控脚本:/root/monitor_and_backup.sh

#!/bin/bash
# 饥荒服务端监控脚本 - 最终版
# 功能:等待服务端启动 → 等待服务端退出 → 自动备份(按 cluster_name 命名)→ 日志区块化

LOCK_FILE="/tmp/monitor_dst.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - 已有监控脚本在运行,退出" >> /root/.klei/backup.log
    exit 1
}

# 配置
DST_PROCESS_NAME="dontstarve_dedicated_server_nullrenderer"
KLEI_BASE="/root/.klei"
BACKUP_BASE="$KLEI_BASE/backup"
LOG_FILE="$KLEI_BASE/backup.log"
SLEEP_INTERVAL=5
KEEP_BACKUPS=10

# 初始化
mkdir -p "$BACKUP_BASE"
touch "$LOG_FILE"

# 日志函数(自动加时间)
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# 清理旧备份(保留最近 KEEP_BACKUPS 个)
clean_old_backups() {
    local backup_dir="$1"
    local prefix="$2"
    [ -d "$backup_dir" ] || return
    cd "$backup_dir" || return
    ls -t ${prefix}_*.tar.gz 2>/dev/null | tail -n +$((KEEP_BACKUPS+1)) | while read -r old; do
        rm -f "$old"
        log "删除旧备份: $backup_dir/$old"
    done
}

# ================== 脚本开始 ==================
log ""
log "================== $(date '+%Y-%m-%d %H:%M:%S') =================="
log ""
log "监控脚本启动,等待进程 $DST_PROCESS_NAME 出现..."

# 等待进程启动,并记录 PID 和 conf_dir
while true; do
    PID=$(pgrep -f "$DST_PROCESS_NAME" | head -1)
    if [ -n "$PID" ]; then
        log "检测到进程 PID=$PID"
        CMDLINE=$(cat /proc/$PID/cmdline | tr '\0' ' ')
        CONF_DIR=$(echo "$CMDLINE" | grep -oP '(?<=-conf_dir )\S+')
        if [ -z "$CONF_DIR" ]; then
            log "警告:未能从命令行提取 -conf_dir,使用默认 DST_1"
            CONF_DIR="DST_1"
        fi
        log "当前存档目录: $CONF_DIR"
        break
    else
        log "未检测到进程,继续等待..."
        sleep $SLEEP_INTERVAL
    fi
done

# 等待该特定 PID 退出
while kill -0 $PID 2>/dev/null; do
    log "进程 $PID 仍在运行,继续监控..."
    sleep $SLEEP_INTERVAL
done

# 进程已退出,执行备份
log "进程 $PID 已结束,开始备份存档..."

SRC_DIR="$KLEI_BASE/$CONF_DIR"
if [ ! -d "$SRC_DIR" ]; then
    log "错误:源目录 $SRC_DIR 不存在,无法备份"
    log ""
    log "================== $(date '+%Y-%m-%d %H:%M:%S') =================="
    log ""
    exit 1
fi

# 读取 cluster_name(固定路径:Cluster_1/cluster.ini)
CLUSTER_INI="$SRC_DIR/Cluster_1/cluster.ini"
if [ -f "$CLUSTER_INI" ]; then
    CLUSTER_NAME=$(grep -E '^cluster_name\s*=' "$CLUSTER_INI" | head -1 | sed -E 's/^cluster_name\s*=\s*//' | sed 's/^"//;s/"$//' | sed "s/^'//;s/'$//")
fi

if [ -z "$CLUSTER_NAME" ]; then
    log "警告:未找到 cluster_name,使用 $CONF_DIR 作为备份名称"
    CLUSTER_NAME="$CONF_DIR"
fi

# 清理非法字符(删除 ? * : " < > | \ / 等,空格转为下划线)
SAFE_NAME=$(echo "$CLUSTER_NAME" | tr ' ' '_' | sed 's/[][\\/*?:"<>|/]//g')
log "备份名称: $SAFE_NAME"

# 时间戳格式:年-月-日_时-分-秒
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")

BACKUP_DIR="$BACKUP_BASE/$CONF_DIR"
mkdir -p "$BACKUP_DIR"
BACKUP_FILE="$BACKUP_DIR/${SAFE_NAME}_${TIMESTAMP}.tar.gz"

log "正在备份 $SRC_DIR$BACKUP_FILE"
tar -czf "$BACKUP_FILE" -C "$KLEI_BASE" "$CONF_DIR"
if [ $? -eq 0 ]; then
    log "备份成功: $BACKUP_FILE"
    clean_old_backups "$BACKUP_DIR" "$SAFE_NAME"
else
    log "备份失败: $SRC_DIR"
    log ""
    log "================== $(date '+%Y-%m-%d %H:%M:%S') =================="
    log ""
    exit 1
fi

log "备份任务完成,监控脚本退出。"
log ""
log "================== $(date '+%Y-%m-%d %H:%M:%S') =================="
log ""
exit 0

2、启动脚本:/root/start_monitor.sh(带自动清理旧实例)

#!/bin/bash
# 启动监控脚本(带清理旧实例功能)

MONITOR_SCRIPT="/root/monitor_and_backup.sh"
LOG_FILE="/root/.klei/backup.log"
LOCK_FILE="/tmp/monitor_dst.lock"

# 1. 检查监控脚本是否存在并可执行
if [ ! -x "$MONITOR_SCRIPT" ]; then
    echo "错误:监控脚本 $MONITOR_SCRIPT 不存在或不可执行"
    exit 1
fi

# 2. 查找正在运行的监控脚本进程(排除 grep 自己)
OLD_PIDS=$(pgrep -f "$MONITOR_SCRIPT" | grep -v $$ || true)
if [ -n "$OLD_PIDS" ]; then
    echo "发现已有监控脚本进程: $OLD_PIDS,正在终止..."
    echo "$OLD_PIDS" | xargs kill -9 2>/dev/null
    sleep 1
fi

# 3. 清理锁文件(如果存在)
if [ -f "$LOCK_FILE" ]; then
    echo "清理残留锁文件: $LOCK_FILE"
    rm -f "$LOCK_FILE"
fi

# 4. 启动新的监控脚本(后台运行)
nohup "$MONITOR_SCRIPT" >> "$LOG_FILE" 2>&1 &
NEW_PID=$!
echo "监控脚本已启动,PID=$NEW_PID"
echo "日志文件: $LOG_FILE"

使用方法

准备工作

1、确保服务器上已安装 util-linux(提供 flock 命令):

yum install -y util-linux   # CentOS
# 或 apt install -y util-linux  # Debian/Ubuntu

2、创建上述两个脚本文件,并赋予执行权限:

chmod +x /root/monitor_and_backup.sh
chmod +x /root/start_monitor.sh

3、确保服务端存档目录结构符合预期:/root/.klei/DST_X/Cluster_1/cluster.ini(其中 cluster.ini 包含 cluster_name = 你的世界名

日常操作流程

1、启动游戏服务器:运行 ./z.sh(或你的启动命令),在菜单中选择“启动服务器”,然后按 Ctrl+C 退出 z.sh 界面(服务端进程会继续在后台运行)。

2、启动监控脚本(每次开服后执行一次):

/root/start_monitor.sh

3、关闭游戏服务器:可以通过 z.sh 菜单关闭,或使用命令:

pkill -f dontstarve_dedicated_server_nullrenderer

4、查看日志

tail -f /root/.klei/backup.log

5、查看备份文件

ls -lh /root/.klei/backup/DST_1/

备份文件明明规则

  • 格式:{cluster_name}_{年-月-日_时-分-秒}.tar.gz
  • 示例:今年花谢,明年花谢,白了人头_2026-05-20_03-34-24.tar.gz
  • 非法字符(? * : " < > | \ /)会被自动删除,空格转为下划线。

日志示例

================== 2026-05-20 03:33:59 ==================

2026-05-20 03:33:59 - 监控脚本启动,等待进程 dontstarve_dedicated_server_nullrenderer 出现...
2026-05-20 03:33:59 - 检测到进程 PID=8201
2026-05-20 03:33:59 - 当前存档目录: DST_1
2026-05-20 03:33:59 - 进程 8201 仍在运行,继续监控...
...
2026-05-20 03:34:24 - 进程 8201 已结束,开始备份存档...
2026-05-20 03:34:24 - 备份名称: 今年花谢,明年花谢,白了人头
2026-05-20 03:34:24 - 正在备份 /root/.klei/DST_1 到 /root/.klei/backup/DST_1/今年花谢,明年花谢,白了人头_2026-05-20_03-34-24.tar.gz
2026-05-20 03:34:26 - 备份成功: /root/.klei/backup/DST_1/今年花谢,明年花谢,白了人头_2026-05-20_03-34-24.tar.gz
2026-05-20 03:34:26 - 备份任务完成,监控脚本退出。

================== 2026-05-20 03:34:26 ==================

常见问题及解决

问题原因解决方法
启动监控提示“已有监控脚本在运行”上次监控异常退出,锁文件残留启动脚本已自动清理,或手动 rm -f /tmp/monitor_dst.lock
服务端关闭后没有备份监控脚本未正确运行(没有 nohup 或前台断开)使用 start_monitor.sh 启动,它使用 nohup 保证后台运行
备份文件名中问号等特殊字符cluster_name 包含非法字符脚本已自动删除 ? * : " < > \ / 等,如需保留可修改清理规则
备份目录不存在未初始化脚本会自动创建 mkdir -p

高级定制

  • 修改保留备份数量:更改脚本中的 KEEP_BACKUPS=10
  • 修改备份压缩格式:将 tar -czf 改为 tar -cjf(bzip2)或 tar -cJf(xz)。
  • 修改检查间隔:调整 SLEEP_INTERVAL=5(秒)。
  • 添加备份前的自定义操作:在 while kill -0 循环之后、备份之前插入代码。

注意事项

  1. 该监控脚本不会在服务器启动时自动运行,需要每次开服后手动执行 /root/start_monitor.sh。如有需要,可以将该命令与开服命令合并为一个包装脚本。
  2. 确保服务器磁盘空间充足,定期检查 /root/.klei/backup 目录大小。脚本会自动保留最近 10 个备份,但您也可以手动删除旧备份。
  3. 如果使用 z.sh 菜单关闭服务器,请确认它是否真正杀死了服务端进程(某些菜单选项可能只是停止游戏逻辑而不退出进程)。建议使用 pkill -f dontstarve_dedicated_server_nullrenderer 或提供的 stop_dst 命令。
  4. 日志文件会持续增长,建议定期轮转或清理(例如使用 logrotate)。