rsync over ZFS:用 snapshot 做"事务性"备份(避免备份到一半客户端写改)

起因

我们的备份服务器收 rsync from 多台业务机器。问题:

  • rsync 跑到一半,业务机继续写新文件 → 备份目录里出现"part-A from 12:00,
    part-B from 12:15" 时间错乱的版本
  • 备份过程中如果客户端机器挂了 → 备份目录是半新半旧
  • 想"备份这一刻的整盘快照" 而不是"几小时内分散的快照"

如果备份目标是 ZFS 文件系统,可以利用 ZFS snapshot 做"事务性"备份:

  1. rsync 完整跑完
  2. 跑完后立即 zfs snapshot
  3. 之后业务继续 rsync 上新数据
  4. snapshot 是这一刻完整一致的副本

完整流程

setup

备份服务器 NAS 上:

# 创建一个 ZFS dataset 收备份
sudo zfs create backuptank/clients/server-foo
sudo zfs set compression=zstd backuptank/clients/server-foo
sudo zfs set atime=off backuptank/clients/server-foo

# 给 rsync 用户写权限
sudo zfs allow rsync-user create,destroy,snapshot,mount backuptank/clients/server-foo

业务机推送 + snapshot 脚本

#!/usr/bin/env bash
# /usr/local/sbin/snap-rsync.sh
set -euo pipefail

REMOTE_USER=rsync-user
REMOTE_HOST=nas.local
REMOTE_DATASET=backuptank/clients/server-foo
REMOTE_MOUNT=/backuptank/clients/server-foo

SRC="/etc /home /srv"
HOSTNAME=$(hostname -s)
TS=$(date +%Y-%m-%dT%H-%M-%S)

# 1. 同步过去(增量;--delete 保镜像)
rsync -aAXH --numeric-ids --delete --info=stats2,progress2 \
    -e ssh \
    $SRC "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_MOUNT}/${HOSTNAME}/"

# 2. 完成后远程 zfs snapshot 标记这一刻
ssh "${REMOTE_USER}@${REMOTE_HOST}" \
    "sudo zfs snapshot ${REMOTE_DATASET}@${HOSTNAME}-${TS}"

echo "snapshot: ${REMOTE_DATASET}@${HOSTNAME}-${TS}"

# 3. retention:保留最近 30 个 snapshot
ssh "${REMOTE_USER}@${REMOTE_HOST}" "
    sudo zfs list -H -o name -t snapshot ${REMOTE_DATASET} \
    | grep '@${HOSTNAME}-' \
    | sort -r \
    | tail -n +31 \
    | xargs -r -n 1 sudo zfs destroy
"

每天 cron / timer 跑这个。

浏览 / 还原历史快照

NAS 上 ZFS snapshot 默认可访问:

ls /backuptank/clients/server-foo/.zfs/snapshot/
# server-foo-2024-05-24T03-00-00
# server-foo-2024-05-23T03-00-00
# ...

# 还原某个文件
cp /backuptank/clients/server-foo/.zfs/snapshot/server-foo-2024-05-24T03-00-00/etc/nginx/nginx.conf /tmp/

或者 zfs clone 把 snapshot 挂成可写副本:

sudo zfs clone backuptank/clients/server-foo@server-foo-2024-05-24T03-00-00 \
    backuptank/restore/server-foo-2024-05-24
mount | grep restore

任意时间点的完整目录树都能挂载浏览。

ZFS dedup(可选)

如果多机器备份内容重叠多(OS 文件大体相同):

sudo zfs set dedup=on backuptank/clients

代价:每 1 TB dedup 数据约需 5 GB RAM 维护 dedup table。
所以只在高 dedup ratio + 充裕 RAM 时开。

测:

sudo zdb -S backuptank
# 输出 estimated dedup ratio:1.5x 以上才值得开

ZFS send:异地同步备份

# 第一次全量 → 异地 NAS
sudo zfs send backuptank/clients/server-foo@latest \
  | ssh remote-nas "sudo zfs receive backuptank-mirror/server-foo"

# 后续增量
sudo zfs send -i @prev-snap backuptank/clients/server-foo@new-snap \
  | ssh remote-nas "sudo zfs receive backuptank-mirror/server-foo"

或者 syncoid 自动:

syncoid backuptank/clients/server-foo remote-nas:backuptank-mirror/server-foo

每天本地 rsync + snapshot;每周 syncoid 异地。两层保护

与"直接备到 ZFS snapshot" 的对比

替代方案:客户端不 rsync,业务机自己 ZFS snapshot + zfs send 给 NAS。

# 业务机
sudo zfs snapshot tank/data@$(date +%F)
sudo zfs send -i @prev tank/data@new \
  | ssh nas "sudo zfs receive backuptank/clients/foo"

优点:

  • 原生 ZFS 一致性快照(毫秒级 frozen)
  • 增量 send 只传变化的 block(比 rsync 比 file 快很多)
  • 文件系统级,不漏 metadata / xattr / hardlink

要求:业务机文件系统是 ZFS。

如果业务机是 ext4 / xfs → rsync + 备份目标 ZFS snapshot 是 fallback。

monitoring

# 看 snapshot 数 + 大小
zfs list -t snapshot backuptank/clients/server-foo

# Prometheus exporter (node_exporter zfs collector)
# 暴露:node_zfs_zpool_state{pool="backuptank"} = 1 (ONLINE)
#       node_zfs_zfs_dataset_used_bytes
#       node_zfs_zfs_dataset_available_bytes

仪表盘看:

  • snapshot 数量是否正常增长
  • 各客户端的备份目录大小变化
  • 最新 snapshot 时间戳(确认 cron 跑了)

告警:snapshot 超过 26 小时没新 = 备份链断了。

retention 策略:sanoid 风格

手写 tail -n +31 简单但只按计数。sanoid 风格按时间精细:

# 保留:
# - 最近 7 个 hourly
# - 最近 30 个 daily
# - 最近 12 个 monthly

snapshots=$(ssh nas "sudo zfs list -H -o name -t snapshot backuptank/clients/foo")
# 分组按时间删
# (实际写起来复杂;建议用 sanoid 现成工具)

sanoid 配 retention policy + 跨多 dataset 统一管。装好后写一个
sanoid.conf 完事。

实战效果

我们 20 台业务机每天备份到一台 NAS:

rsync only rsync + ZFS snapshot
单次备份耗时 30 min 30 min
一致性 半天的飘移 时刻精确
历史版本 没有 30 天日级
占用空间 增量 增量 + dedup 后接近
还原历史文件 困难 cp /.zfs/...

ZFS snapshot 几乎免费给增量备份"加时间维度"。

与商业方案对比

rsync + ZFS Duplicati restic to S3 Veeam
价格 免费 免费 免费 + 存储费 商业
客户端加密 否(依靠传输 ssh)
时间点恢复 ✅(snapshot)
Web UI 第三方
学习曲线

家用 / 小公司 + 有 ZFS NAS → 这套最便宜 + 最快。

踩过的坑

  1. sudo zfs snapshot 没权限:rsync 用户没 zfs allow。
    sudo zfs allow rsync-user create,destroy,snapshot ...

  2. snapshot 名字有冲突:同一秒两次备份生成同名 snapshot 失败。
    用毫秒级或随机 suffix。

  3. --delete 删了重要文件:业务机误删 → rsync 同步删 → 但
    ZFS snapshot 保住前一刻状态。snapshot 是真正救命

  4. 未控制保留 → snapshot 爆:1 年 = 365 个 daily snapshot,
    每 snapshot 元数据几 MB → 几 GB 元数据。
    sanoid 策略限制数量。

  5. NAS 空间满 → 客户端 rsync 失败:监控 ZFS pool 利用率,
    超过 80% 告警。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。