Restic多目标增量备份:香港服务器数据同时备份到Cloudflare R2 + B2 + 本地完整方案

Restic多目标增量备份:香港服务器数据同时备份到Cloudflare R2 + B2 + 本地完整方案

传统备份脚本只写一个目标,一旦该目标出问题(R2 账号被封、B2 收费突然涨价),备份就全部失效。Restic 是目前最优秀的开源备份工具:支持同时备份到多个目标(S3/R2/B2/本地/SFTP)、增量去重(相同内容只存一份)、端对端加密(服务商无法看到内容)、自动剪枝(按保留策略删除旧快照)。本文实现香港服务器数据同时备份到三个目标。


一、Restic 核心优势

特性Resticrsyncmysqldump+tar
增量去重✅(内容寻址,只传变化块)△(文件级增量)❌(每次全量)
端对端加密✅(AES-256)
多目标支持✅(S3/B2/SFTP/本地/Azure)△(需多次运行)
快照管理✅(按时间点恢复任意版本)△(手动管理)
完整性验证✅(restic check)
存储节省约 80~90%(去重+压缩)约 50%(仅差异)0%

二、安装 Restic

<code"># 方法一:apt 安装(版本可能较旧)
apt install -y restic

# 方法二:官方二进制(最新版本,推荐)
wget https://github.com/restic/restic/releases/latest/download/restic_0.17.3_linux_amd64.bz2
bunzip2 restic_0.17.3_linux_amd64.bz2
mv restic_0.17.3_linux_amd64 /usr/local/bin/restic
chmod +x /usr/local/bin/restic
restic version

三、初始化备份仓库

<code"># 设置环境变量(写入 /etc/restic/env,通过 source 加载)
mkdir -p /etc/restic
cat > /etc/restic/env << 'EOF'
# 备份加密密码(请妥善保存!丢失则无法解密备份)
export RESTIC_PASSWORD="YourVeryStrongBackupPassword2026"

# Cloudflare R2 配置
export R2_ACCOUNT_ID="your_cf_account_id"
export AWS_ACCESS_KEY_ID="your_r2_access_key"
export AWS_SECRET_ACCESS_KEY="your_r2_secret_key"

# Backblaze B2 配置
export B2_ACCOUNT_ID="your_b2_account_id"
export B2_ACCOUNT_KEY="your_b2_application_key"
EOF
chmod 600 /etc/restic/env

source /etc/restic/env

# 初始化 Cloudflare R2 仓库
restic -r "s3:https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/hk-backups" init

# 初始化 Backblaze B2 仓库
restic -r "b2:hk-server-backups:/restic" init

# 初始化本地仓库(USB 硬盘或 NAS 挂载点)
restic -r /mnt/external-backup/restic init

四、核心备份脚本(三目标并行)

<code"># /opt/backup/restic-backup.sh
#!/bin/bash
set -euo pipefail
source /etc/restic/env

LOG="/var/log/restic-backup.log"
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/your_webhook"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

log() { echo "[$DATE] $*" | tee -a "$LOG"; }
alert() {
    curl -s -X POST "$FEISHU_WEBHOOK" \
      -H "Content-Type: application/json" \
      -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"$*\"}}" > /dev/null
}

log "===== 开始 Restic 备份 ====="

# ── 备份目录定义 ──
BACKUP_PATHS=(
    "/var/www"           # Web 应用文件
    "/etc/nginx"         # Nginx 配置
    "/etc/supervisor"    # Supervisor 配置
    "/opt"               # 自定义应用
)

EXCLUDE_PATTERNS=(
    "--exclude=/var/www/*/node_modules"
    "--exclude=/var/www/*/.git"
    "--exclude=/var/www/*/vendor"
    "--exclude=*.log"
    "--exclude=*.tmp"
)

# ── 函数:备份到指定仓库 ──
backup_to_repo() {
    local repo_name="$1"
    local repo_url="$2"

    log "备份到 $repo_name..."
    if restic -r "$repo_url" backup \
        "${BACKUP_PATHS[@]}" \
        "${EXCLUDE_PATTERNS[@]}" \
        --tag "server=$(hostname)" \
        --tag "type=files" \
        --compression max \
        >> "$LOG" 2>&1; then
        log "✅ $repo_name 备份成功"
    else
        log "❌ $repo_name 备份失败!"
        alert "🚨 Restic备份失败\n目标:$repo_name\n服务器:$(hostname)\n时间:$DATE"
        return 1
    fi
}

# ── 函数:数据库备份(先导出,再让 Restic 备份导出文件)──
backup_databases() {
    local dump_dir="/tmp/restic-db-dumps"
    mkdir -p "$dump_dir"

    log "导出 MySQL 数据库..."
    mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" \
        --single-transaction --quick --all-databases \
        | gzip -9 > "$dump_dir/mysql-all-$(date +%Y%m%d).sql.gz"

    log "导出 PostgreSQL 数据库..."
    su - postgres -c "pg_dumpall" \
        | gzip -9 > "$dump_dir/postgres-all-$(date +%Y%m%d).sql.gz"

    echo "$dump_dir"
}

# ── 主流程 ──

# 1. 数据库导出
DB_DUMP_DIR=$(backup_databases)
BACKUP_PATHS+=("$DB_DUMP_DIR")

# 2. 并行备份到三个目标(&后台执行,wait等待全部完成)
backup_to_repo "Cloudflare R2" \
    "s3:https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/hk-backups" &
PID_R2=$!

backup_to_repo "Backblaze B2" \
    "b2:hk-server-backups:/restic" &
PID_B2=$!

backup_to_repo "本地磁盘" \
    "/mnt/external-backup/restic" &
PID_LOCAL=$!

# 等待全部完成
wait $PID_R2 && R2_OK=true || R2_OK=false
wait $PID_B2 && B2_OK=true || B2_OK=false
wait $PID_LOCAL && LOCAL_OK=true || LOCAL_OK=false

# 3. 清理旧快照(按策略保留)
PRUNE_POLICY="--keep-daily 7 --keep-weekly 4 --keep-monthly 6"

for repo in \
    "s3:https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/hk-backups" \
    "b2:hk-server-backups:/restic" \
    "/mnt/external-backup/restic"; do
    restic -r "$repo" forget $PRUNE_POLICY --prune >> "$LOG" 2>&1 || true
done

# 4. 清理临时数据库导出
rm -rf "$DB_DUMP_DIR"

# 5. 发送备份报告
STATUS="✅ 所有目标备份成功"
[[ "$R2_OK" == false || "$B2_OK" == false || "$LOCAL_OK" == false ]] && \
    STATUS="⚠️ 部分目标备份失败,请检查日志"

alert "${STATUS}\n服务器:$(hostname)\n时间:$DATE\nR2:$R2_OK | B2:$B2_OK | 本地:$LOCAL_OK"
log "===== 备份完成 ====="
<code"># 设置每天凌晨 3 点自动备份
chmod +x /opt/backup/restic-backup.sh
echo "0 3 * * * root /opt/backup/restic-backup.sh" >> /etc/crontab

五、常用 Restic 操作命令

<code"># 加载环境变量
source /etc/restic/env
REPO="s3:https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/hk-backups"

# 查看所有快照
restic -r "$REPO" snapshots

# 查看快照内容(文件列表)
restic -r "$REPO" ls latest

# 恢复最新快照到指定目录
restic -r "$REPO" restore latest --target /tmp/restore/

# 恢复特定快照的特定文件
restic -r "$REPO" restore abc12345 \
    --include /var/www/mysite/wp-config.php \
    --target /tmp/restore/

# 挂载备份仓库(像文件系统一样浏览)
mkdir -p /mnt/restic-backup
restic -r "$REPO" mount /mnt/restic-backup &
ls /mnt/restic-backup/snapshots/
# 可以直接复制任意历史版本的文件
fusermount -u /mnt/restic-backup   # 卸载

六、备份完整性验证(定期执行)

<code"># /opt/backup/restic-verify.sh
#!/bin/bash
source /etc/restic/env

log() { echo "[$(date '+%Y-%m-%d %H:%M')] $*" >> /var/log/restic-verify.log; }

for repo_name in "R2" "B2" "Local"; do
    case $repo_name in
        R2)    REPO="s3:https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/hk-backups" ;;
        B2)    REPO="b2:hk-server-backups:/restic" ;;
        Local) REPO="/mnt/external-backup/restic" ;;
    esac

    log "验证 $repo_name 仓库完整性..."
    if restic -r "$REPO" check --read-data-subset=5% 2>&1; then
        log "✅ $repo_name 验证通过"
    else
        log "❌ $repo_name 验证失败!"
        curl -s -X POST "$FEISHU_WEBHOOK" \
          -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"🚨 备份完整性验证失败!仓库:$repo_name\"}}"
    fi
done

# 每周日执行验证
echo "0 10 * * 0 root /opt/backup/restic-verify.sh" >> /etc/crontab

七、存储成本估算(1TB 原始数据)

目标实际存储(去重后约200GB)月费
Cloudflare R2200GB$3(超出10GB免费后$0.015/GB)
Backblaze B2200GB$1.2($0.006/GB/月,首10GB免费)
本地磁盘200GB含在 VPS 套餐
合计三份备份约¥30/月

八、总结

Restic 将备份这件事做到了极致:增量去重节省 80%+ 存储空间,端对端加密保证备份数据安全,多目标并行确保任意单一目标失效时备份仍然可用。三个目标(R2 + B2 + 本地)月费约 ¥30,却保护着整台服务器的所有数据,是香港服务器运维中性价比最高的风险对冲手段。

Telegram