Restic多目标增量备份:香港服务器数据同时备份到Cloudflare R2 + B2 + 本地完整方案
传统备份脚本只写一个目标,一旦该目标出问题(R2 账号被封、B2 收费突然涨价),备份就全部失效。Restic 是目前最优秀的开源备份工具:支持同时备份到多个目标(S3/R2/B2/本地/SFTP)、增量去重(相同内容只存一份)、端对端加密(服务商无法看到内容)、自动剪枝(按保留策略删除旧快照)。本文实现香港服务器数据同时备份到三个目标。
一、Restic 核心优势
| 特性 | Restic | rsync | mysqldump+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 R2 | 200GB | $3(超出10GB免费后$0.015/GB) |
| Backblaze B2 | 200GB | $1.2($0.006/GB/月,首10GB免费) |
| 本地磁盘 | 200GB | 含在 VPS 套餐 |
| 合计 | 三份备份 | 约¥30/月 |
八、总结
Restic 将备份这件事做到了极致:增量去重节省 80%+ 存储空间,端对端加密保证备份数据安全,多目标并行确保任意单一目标失效时备份仍然可用。三个目标(R2 + B2 + 本地)月费约 ¥30,却保护着整台服务器的所有数据,是香港服务器运维中性价比最高的风险对冲手段。