Restic 加密备份是当前开源方案里少数同时满足「客户端加密、内容去重、快照式恢复、仓库可设为只追加」四项要求的工具。它把备份数据切块后按内容哈希存储,相同数据只存一份,再用 AES-256 加密后写入本地磁盘、SFTP、S3 或兼容对象存储,服务端拿到的永远是密文。本文给出从仓库初始化、备份脚本、保留策略、恢复演练到完整性校验的完整配置,重点解决「备份跑成功了但恢复不出来」这个最常见的问题。
备份系统的验收标准只有一个:能不能在规定时间内把指定时间点的数据恢复到可用状态。快照数量、去重率、增量速度都是过程指标,恢复演练才是结果指标。
适用场景
- 单机或多机的文件级备份,尤其是需要长期保留多个时间点版本的业务目录。
- 勒索软件防护:仓库设为只追加后,即使主机被完全控制,攻击者也无法删除历史快照。
- 异地容灾:同一份数据同时备份到本地磁盘与对象存储两个仓库,互为副本。
- 数据库的二级保护:先用逻辑导出生成一致性备份文件,再用 restic 把这些文件纳入加密快照。
- 成本敏感场景:去重后实际占用通常只有原始数据的 30% 到 60%,比整盘镜像便宜得多。
前置条件
- restic 0.16 以上版本,
--read-data-subset等校验参数在 0.14 之后才稳定。 - 一个仓库位置:本地目录、SFTP 主机或 S3 兼容对象存储均可。
- 密码文件,权限 600,且不与仓库放在同一台机器上。
- 系统时间已同步(NTP 或 chrony),否则快照时间戳错乱,按时间点恢复会选错版本。
- 磁盘空间预估:仓库所在分区至少留出源数据 1.2 倍空闲,prune 期间会有临时占用。
原理说明
restic 的仓库由四类对象构成:keys 存放加密后的主密钥,config 记录仓库参数与分块大小,data 是实际数据块并按内容哈希命名,snapshots 是快照元数据。备份时文件被切成可变长度块,对每块计算哈希作为 ID,已存在则直接跳过——这就是去重的来源,也是「第二次备份明显更快」的原因。
加密全部在客户端完成:主密钥由密码经 scrypt 派生后再加密存储,所有数据块用 AES-256 加密并附加认证码。服务端只能看到块 ID 和块大小,看不到文件名、目录结构或内容。代价非常明确——密码一旦丢失,数据完全不可恢复,没有后门,也没有厂商能协助找回。
快照之间是指针关系而非全量复制,所以同一份数据保留几十个快照的额外开销极小。但删除快照不等于释放空间:forget 只删元数据,prune 才真正清理无引用的数据块。这两个命令必须配对使用,且 prune 期间不能有其他备份任务在跑,否则可能把正在写入的块误判为无引用。
操作步骤
步骤 1:安装与初始化仓库
# 安装;发行版仓库版本偏旧时改用官方二进制
sudo apt install -y restic || {
curl -L -o /tmp/restic.bz2 \
https://github.com/restic/restic/releases/download/v0.17.3/restic_0.17.3_linux_amd64.bz2
bunzip2 -c /tmp/restic.bz2 | sudo tee /usr/local/bin/restic >/dev/null
sudo chmod 755 /usr/local/bin/restic
}
restic version
# 密码文件:独立目录、权限 600,不要与仓库同机同盘
sudo mkdir -p /etc/restic
sudo sh -c 'head -c 32 /dev/urandom | base64 > /etc/restic/password'
sudo chmod 600 /etc/restic/password
# 初始化本地仓库;对象存储把路径换成 s3:https://endpoint/bucket/prefix
export RESTIC_REPOSITORY=/backup/restic-repo
export RESTIC_PASSWORD_FILE=/etc/restic/password
restic init
密码文件必须异地留存一份。最常见的自伤操作是把密码文件和仓库放在同一块盘上,磁盘故障时两者一起丢,备份等于从未存在。
步骤 2:编写备份脚本(含数据库前置导出)
sudo tee /usr/local/bin/restic-backup.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY=/backup/restic-repo
export RESTIC_PASSWORD_FILE=/etc/restic/password
export RESTIC_CACHE_DIR=/var/cache/restic
DUMP=/var/backups/dump
mkdir -p "$DUMP"
# 数据库逻辑备份先落盘,再纳入快照,保证一致性
mysqldump --single-transaction --quick --all-databases \
| gzip -6 > "$DUMP/mysql-$(date +%F).sql.gz"
# 排除清单:缓存、临时文件、仓库自身
cat > /etc/restic/excludes.txt <<'EX'
/var/cache/**
/tmp/**
/backup/restic-repo/**
**/*.sock
**/node_modules/**
EX
restic backup /etc /var/www /var/backups/dump /root \
--exclude-file=/etc/restic/excludes.txt \
--exclude-caches \
--one-file-system \
--tag daily \
--compression max
# 保留策略:日 7、周 4、月 6
restic forget --tag daily \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
--prune
restic check --read-data-subset=5%
EOF
sudo chmod 755 /usr/local/bin/restic-backup.sh
几个参数值得单独说明:--one-file-system 防止备份时跨进挂载的 NFS 或容器卷,否则容易把整个共享目录一并拖进来,既慢又危险;--exclude-caches 依据目录内的 CACHEDIR.TAG 标记自动跳过缓存;--compression max 对文本类数据压缩比明显,对已压缩的图片视频基本无效,可按业务取舍。
步骤 3:执行首次备份并查看快照
sudo /usr/local/bin/restic-backup.sh
# 查看快照列表
restic snapshots
# 查看某次快照的文件树(不落盘,只在内存展开)
restic ls latest /var/www | head -n 20
# 统计仓库实际占用与去重效果
restic stats --mode raw-data
restic stats latest
stats --mode raw-data 反映仓库实际占用,stats latest 反映最新快照代表的数据量,两者比值就是去重率。如果这个比值长期接近 1,说明源数据本身几乎没有重复块,属于正常情况,不代表去重失效。
步骤 4:恢复演练(关键步骤,不要跳过)
# 整快照恢复到临时目录
restic restore latest --target /tmp/restore-test
# 只恢复单个文件
restic restore latest --target /tmp/one \
--include /var/www/html/config.php
# 对比恢复结果与源目录(日志类易变文件会有差异,属正常)
diff -rq /var/www /tmp/restore-test/var/www | head -n 20
# 演练完成后清理,避免占满磁盘
rm -rf /tmp/restore-test /tmp/one
--include 里必须写快照内的绝对路径,不是宿主机的当前路径;--target 才是恢复到本地的目标目录。这两个参数写反了会恢复到一个空目录,然后被误判成「备份损坏」。
步骤 5:远端仓库与只追加模式
# 方式一:SFTP 到远端主机(需先配置免密登录)
export RESTIC_REPOSITORY=sftp:backup@10.0.0.20:/data/restic-repo/frontend
restic init
# 方式二:rest-server 开启 append-only,客户端无法删除任何快照
rest-server --path /data/restic --append-only --listen 0.0.0.0:8000
# 客户端连接
export RESTIC_REPOSITORY=rest:http://10.0.0.20:8000/frontend
# 方式三:S3 兼容对象存储
export RESTIC_REPOSITORY=s3:https://oss-cn-hangzhou.aliyuncs.com/my-bucket/restic
export AWS_ACCESS_KEY_ID=CHANGE_ME
export AWS_SECRET_ACCESS_KEY=CHANGE_ME
只追加是抵御勒索软件的核心配置:攻击者即使拿到仓库凭据,也只能新增对象,不能删除历史快照。但只追加模式下 prune 会失败,需要用独立的管理凭据在维护窗口内临时放开权限,清理完立即收回。不要为了省事给日常备份开删除权限,那等于把唯一的安全边界拆掉。
步骤 6:systemd 定时任务
sudo tee /etc/systemd/system/restic-backup.service >/dev/null <<'EOF'
[Unit]
Description=Restic encrypted backup
After=network-online.target mysqld.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF
sudo tee /etc/systemd/system/restic-backup.timer >/dev/null <<'EOF'
[Unit]
Description=Run restic backup daily
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=600
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer --no-pager
RandomizedDelaySec=600 让多台机器错开执行,避免所有主机在同一秒涌向同一个仓库;Persistent=true 保证关机期间错过的时间点会在开机后补跑一次。
配置验证
# 1. 仓库完整性:抽查 5% 数据块
restic check --read-data-subset=5%
# 2. 快照链是否完整(父子关系无断裂)
restic snapshots --compact
# 3. 恢复可读性:从快照直接取文件比对哈希
restic dump latest /etc/hosts | sha256sum
sha256sum /etc/hosts
# 4. 保留策略预演,不实际删除
restic forget --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6
# 5. 定时任务与执行日志
systemctl list-timers restic-backup.timer
journalctl -u restic-backup -n 30 --no-pager
第 3 步两个哈希必须完全一致。如果不一致,说明数据块损坏或密钥异常,应立即执行 restic check --read-data 做全量校验,并从副本仓库重新拉取。
常见问题
FAQ 1:忘记仓库密码还能恢复数据吗?
不能。restic 采用客户端端到端加密,密码只用于解密主密钥,没有找回机制、没有后门,官方也无法协助。可行的冗余手段只有一种:初始化后立刻用 restic key add 为同一仓库添加第二个密码,把新密码离线保存到与主密码不同的位置。这件事必须在第一次备份前做完,事后补做没有意义。
FAQ 2:prune 会不会删掉我需要的快照?
不会。prune 只清理没有被任何现存快照引用的数据块,快照本身的去留由 forget 决定。真正需要小心的是 forget 的保留策略,规范做法是先用 --dry-run 跑一遍看会删掉哪些快照,确认无误后再去掉该参数正式执行。另外 prune 期间不要并行跑备份任务,也不要在只追加模式的仓库上直接执行。
FAQ 3:只追加模式下备份脚本报 403 怎么办?
典型报错是 unexpected HTTP response (403),原因就是脚本里的 forget --prune 需要删除权限。正确做法是拆成两个脚本:日常备份用只追加凭据,只跑 backup;清理用管理凭据,在维护窗口单独跑 forget --prune,跑完立即收回权限。两台机器、两套凭据、两条链路,互不影响。
FAQ 4:restic 能直接备份运行中的数据库吗?
不建议。restic 做的是文件级快照,不理解数据库的页结构与事务日志,直接备份数据目录可能得到崩溃不一致的状态。可行路径有两条:一是像本文脚本那样先做逻辑导出(mysqldump --single-transaction)再纳入快照;二是借助文件系统快照(LVM、ZFS)先冻结再备份。两条路径都比直接拷数据目录可靠。
总结
Restic 把加密、去重、快照三件事做进了同一个二进制里,部署成本很低,但它无法替你保证「恢复得出来」。真正决定备份有效性的三件事是:密码文件异地留存、仓库设为只追加防篡改、定期做恢复演练并留下记录。把这三件事写进运维规程,备份才算真正可用;否则它只是一个占着磁盘、看起来很安全的目录。