个人数据的 3-2-1 备份实践

备份的价值不在“有备份”,而在“能恢复”。这篇写我用 restic 和对象存储做 3-2-1 的具体配置,以及每季度一次的恢复演练是怎么做的。

3-2-1 到底指什么

  • 3 份数据:一份在用的原件,加至少两份备份。
  • 2 种介质:比如本机磁盘和移动硬盘,或者本地 NAS 与对象存储。
  • 1 份异地:火灾、被盗、勒索软件都可能同时影响同一房间里的设备。

对个人站点来说,我的组合是:服务器本地磁盘、家里的 NAS、异地对象存储。三者使用同一套 restic 仓库格式,恢复命令完全一致。

为什么选 restic

它是一个单文件二进制,支持本地目录、SFTP、S3 兼容存储;备份是增量且去重的,仓库本身加密。相比直接 rsync,多出来的能力主要是快照历史和加密。

sudo apt install restic
restic version

初始化仓库

密码单独存放,不要和备份数据放在同一台机器上,也不要提交到代码仓库。

export RESTIC_REPOSITORY="s3:https://s3.example.com/backup"
export RESTIC_PASSWORD_FILE="/root/.config/restic/password"

sudo install -d -m 700 /root/.config/restic
sudo sh -c 'openssl rand -base64 32 > /root/.config/restic/password'
sudo chmod 600 /root/.config/restic/password

restic init

第一次备份

把配置、站点文件和数据库导出放在一起。日志和缓存目录排除掉,否则每次快照都会变大。

restic backup /srv/notes /etc/nginx /var/www \
  --exclude-caches \
  --exclude '*.log' \
  --exclude '/var/www/*/cache' \
  --tag daily

数据库不要直接拷数据目录,先用 pg_dump 或 mysqldump 导出一份一致的快照,再让 restic 备份导出文件。

保留策略

快照太多会拖慢 check,太少又不够回溯。forget 定义保留规则,prune 真正删除不再被引用的数据块。

restic forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune

这套规则大约保留半年的历史,磁盘占用主要取决于数据变化量,而不是快照数量——去重让重复内容只存一份。

校验与恢复演练

只备份不校验,等于把问题推迟到需要恢复的那天。check 会验证仓库结构,加上 --read-data-subset 可以抽样读取数据块。

restic check
restic check --read-data-subset=5%
restic snapshots --compact

每季度做一次完整恢复,恢复到临时目录即可,不要覆盖线上文件:

restic restore latest \
  --target /tmp/restore \
  --include /srv/notes

diff -r /srv/notes /tmp/restore/srv/notes | head

演练时记录三件事:恢复耗时、缺失的文件、以及恢复后服务能否直接启动。第三点最重要,也最容易被忽略。

把备份做成定时任务

用 systemd timer 比 cron 更容易看到日志。unit 文件里设置 OnFailure= 可以在失败时触发告警,哪怕只是发一封邮件。

# /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup to object storage
OnFailure=notify-admin@%n.service

[Service]
Type=oneshot
Environment=RESTIC_REPOSITORY=s3:https://s3.example.com/backup
Environment=RESTIC_PASSWORD_FILE=/root/.config/restic/password
ExecStart=/usr/local/bin/restic-backup.sh
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Run restic backup daily

[Timer]
OnCalendar=*-*-* 04:15:00
Persistent=true
RandomizedDelaySec=600

[Install]
WantedBy=timers.target
密码就是数据 restic 仓库没有密码无法恢复,也没有找回渠道。把密码和恢复步骤离线保存一份,放在与服务器不同的地方。这是整个方案里最脆弱的一环。

最后

备份系统不需要复杂,但需要三样东西同时成立:按计划运行、失败会告警、恢复被验证过。缺任何一项,都只是心理安慰。