构建 VPS → OP8P → WRT 三级联动数据备份自动化方案

2025年12月30日 · 1024 字 · 3 分钟

为了确保数据的安全性和多地冗余,我构建了一套跨服务器的联动备份系统。这套系统将云端服务数据、本地生产数据进行多级汇总,最终统一存储到备灾设备中。

1. 备份架构设计

整个系统由三部分组成,通过 rsync 进行数据流转:

  1. VPS (源端):主要运行 Docker 服务,承载公网业务。
  2. OP8P (汇总端):本地核心服务器,运行本地 Docker 服务,同时拉取 VPS 数据进行第一级汇总。
  3. WRT (备份端):最终的存储设备,挂载大容量硬盘,接收来自 OP8P 的全量数据汇总。

2. 核心备份脚本 (/home/ban/sync.sh)

该脚本部署在 OP8P 上,作为整个流程的控制中心。它具备预检查机制,并能通过钉钉机器人实时反馈备份状态。

脚本实现逻辑

#!/bin/bash

# --- 配置区 ---
RSYNC_BIN="/usr/bin/rsync"
VPS_HOST="vps"
WRT_HOST="wrt"
LOCAL_DOCKER_DIR="/home/ban/Docker"
TOKEN="${ding_token}"

# --- 函数定义:钉钉推送 ---
send_dingtalk() {
    local msg="[HA] $1"
    /usr/bin/curl -s "https://oapi.dingtalk.com/robot/send?access_token=${TOKEN}" \
        -H 'Content-Type: application/json' \
        -d "{\"msgtype\": \"text\",\"text\": {\"content\":\"$msg\"}}" > /dev/null
}

# --- 函数定义:错误处理 ---
error_exit() {
    local err_msg="备份失败: $1"
    echo -e "\033[31m[ERROR] $err_msg\033[0m" >&2
    send_dingtalk "$err_msg (时间: $(date '+%Y-%m-%d %H:%M:%S'))"
    exit 1
}

# --- 任务开始 ---
echo "--- 正在进行预检查 ---"
# 发送开始通知(可选)
# send_dingtalk "数据备份任务开始执行..."

# 1. 检查必要工具和目录
[ ! -x "$RSYNC_BIN" ] && error_exit "找不到 rsync"
[ ! -d "$LOCAL_DOCKER_DIR" ] && error_exit "本地目录 $LOCAL_DOCKER_DIR 不存在"

# 2. 检查网络连通性
for HOST in "$VPS_HOST" "$WRT_HOST"; do
    if ! /usr/bin/ssh -o ConnectTimeout=5 -o BatchMode=yes "$HOST" exit 2>/dev/null; then
        error_exit "无法连接主机: $HOST"
    fi
done

# --- 第一阶段:VPS -> OP8P ---
echo -e "\n>>> Phase 1: VPS -> OP8P"
$RSYNC_BIN -avz \
    --exclude 'blog' \
    --exclude '.*' \
    --exclude 'ttrss/postgres' \
    --exclude 'virt-sysprep-firstboot.log' \
    "$VPS_HOST":/root/ "$LOCAL_DOCKER_DIR/" || error_exit "VPS -> OP8P 同步出错"

# --- 第二阶段:OP8P -> WRT ---
echo -e "\n>>> Phase 2: OP8P -> WRT"

# 2-1. 同步 Docker 核心文件
$RSYNC_BIN -avz \
    --exclude 'photoprism/database' \
    --exclude 'photoprism/storage' \
    --exclude 'gitlab/config' \
    --exclude 'gitlab/logs' \
    --exclude 'gitlab/data' \
    --exclude 'homeassistant/config' \
    "$LOCAL_DOCKER_DIR/" "$WRT_HOST":/opt/Docker/ || error_exit "Docker 核心文件同步出错"

# 2-2. 独立同步 HA Backups
$RSYNC_BIN -avz "$LOCAL_DOCKER_DIR/homeassistant/config/backups/" "$WRT_HOST":/opt/Docker/homeassistant/config/backups/ || error_exit "HA Backups 同步出错"

# 2-3. 资料库循环同步
TASKS=(
    "/home/ban/Pictures/:/opt/Pictures/"
    "/home/ban/Documents/:/opt/Documents/"
    "/home/ban/Music/:/opt/Music/"
    "/home/ban/script/:/opt/script/"
)

for TASK in "${TASKS[@]}"; do
    SOURCE="${TASK%%:*}"
    DEST_PATH="${TASK##*:}"
    DEST="$WRT_HOST:$DEST_PATH"
    if [ -d "$SOURCE" ]; then
        $RSYNC_BIN -av "$SOURCE" "$DEST" || error_exit "资料同步出错: $SOURCE"
    fi
done

# --- 任务完成 ---
FINISH_TIME=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "\n✅ 所有任务完成!"
send_dingtalk "数据备份成功完成。 (时间: $FINISH_TIME)"

3. 部署与自动化

SSH 免密配置

为了让脚本在无人值守下运行,需要在 OP8P 上生成 SSH 密钥并分发到 VPS 和 WRT:

ssh-copy-id vps
ssh-copy-id wrt

Crontab 定时任务

使用 crontab -e 配置每天凌晨自动执行。特别注意在配置中显式定义变量,确保 cron 能够正确调用钉钉 API:

0 3 * * * export ding_token=xxxxxx; /bin/bash /home/ban/sync.sh >> /home/ban/sync.log 2>&1

4. 方案优势

  • 汇总式备份:OP8P 作为中心节点,将云端和本地数据合二为一,简化了最终备份端的同步逻辑。
  • 异常拦截:通过 error_exit 机制,任何一步同步出错都会立即触发钉钉告警,避免出现“备份已死却无人知晓”的情况。
  • 灵活扩展:通过 TASKS 数组可以轻松添加新的同步目录,无需修改核心逻辑。

通过这套方案,我实现了从公网到内网、从服务到资料的全自动化备份闭环。