使用 rsync 备份 ZFS 池

使用 rsync 备份 ZFS 池

我目前有一个 FreeNAS 盒用于存储我的个人文件。我想进行异地备份,但我不愿意花钱购买能够正常运行 ZFS 的第二台计算机。因此,我计划使用 进行远程备份rsync

我希望备份中的所有文件都保持一致,我以为可以先拍摄递归快照,然后使用 进行传输rsync。但事实证明,每个数据集都会拍摄单独的快照。

现在我想知道是否有任何方法可以查看递归快照,包括所有数据集,或者是否有其他推荐的方法来查看rsync整个zpool。我不认为简单地符号链接到.zfs数据集中的文件夹会起作用,因为我想rsync保留数据集本身中存在的任何符号链接。

根据我收到的评论,我认为我所需配置的一些细节已经到位。我希望在家中拥有一个 NAS,我可以放心地将数据放在上面,并且知道我不太可能丢失它。对我来说,这意味着在现场拥有多个副本,在异地拥有多个副本,在情况真的很糟糕时拥有一个离线副本,在意外删除时定期对数据进行快照,并采取一种防止数据错误(例如位衰减)的方法。事件发生的可能性越小,我在灾难发生后就越放心不拥有多个数据副本,并且我越不在乎快照。此外,我更关心旧数据而不是新数据,因为我通常在另一台设备上有一份副本。最后,我应该指出,大多数文件不会更新太频繁。大多数传输的都是新文件。

我之前的设置是两台 Raspberry Pi,外接 4TB 硬盘。我对这个策略失去了信心,但硬件随时可用。经过一番研究,似乎唯一能防止错误随着时间的推移而潜入的方法就是使用校验和文件系统(如 ZFS)结合服务器级组件(如 ECC RAM 和 UPS)。对于我的本地副本,我选择了这条路线。我在镜像中使用 2x4TB 磁盘并在此处定期制作快照。

这台机器应该可以满足除异地和离线备份之外的所有情况。由于我很可能不需要这些备份,所以我不愿意在它上面投入太多。因此,我认为我可以使用 Raspberry Pi 和我已经闲置的外部磁盘。我可以设置其中一个磁盘始终处于离线状态,而另一个磁盘则接收备份。定期更换磁盘将允许我离线备份我的旧数据。

最直接的方法是使用zfs sendreceive两个池,每个磁盘一个。但是,Raspberry Pi 结合 USB 连接到硬盘驱动器,无法提供zfs(或任何文件系统)非常可靠的操作环境。因此,我预计此设置中会经常发生错误。由于我只使用一个磁盘,因此zfs没有任何可靠的方法来从故障中恢复。

这就是我想使用ext3ext4结合的原因rsync。当然,一些坏位可能会被写入磁盘。对于元数据,有工具可以修复大多数这些问题。对于数据块,这会导致单个文件丢失。此外,可以使用 恢复文件,rsync -c因为这会找到错误的校验和,并从本地机器上已知良好的副本再次传输文件。考虑到不太理想的硬件,这似乎是最好的解决方案。

这就是我使用的理由rsync,这让我想到了最初的问题:如何rsync进行递归zfs snapshot。如果我没有回答您的任何建议,请告诉我,因为我真的很愿意接受替代方案。我只是目前看不出它们能给我带来什么好处。

答案1

您似乎非常想要使用rsyncRaspberryPi,因此这里还有另一个答案,其中包含一些脑力倾泻,希望能帮助您找到解决方案。


现在我想知道是否有任何方法可以查看递归快照(包括所有数据集),或者是否有其他推荐的方法来 rsync 整个 zpool。

据我所知,没有......我希望建议与我的其他答案一致。


如果您满足于rsync在已挂载的 ZFS 池上运行,那么您可以.zfs使用排除目录(如果它们对您可见)rsync --exclude='/.zfs/'或设置snapdir=hidden属性。

但这会导致问题,因为每个数据集都可以安装在任何地方,并且您可能不想错过任何一个......


您需要管理快照,并需要为“现在”,然后备份,之后可能会删除它。采用这种方法(而不仅仅是使用“居住“已挂载的文件系统”将为您提供某一时间点的一致备份。它还将确保您不会备份任何奇怪的层次结构或错过可能在其他地方挂载的任何文件系统。

$ SNAPSHOT_NAME="rsync_$(date +%s)"
$ zfs snapshot -r ${ROOT}@${SNAPSHOT_NAME}
$ # do the backup...
$ zfs destroy -r ${ROOT}@${SNAPSHOT_NAME}

接下来,您需要通过运行获取要备份的数据集的完整列表zfs list -Hrt filesystem -o name ${ROOT}。例如,我可能想备份我的users树,下面是一个例子:

$ zfs list -Hrt filesystem -o name ell/users
ell/users
ell/users/attie
ell/users/attie/archive
ell/users/attie/dropbox
ell/users/attie/email
ell/users/attie/filing_cabinet
ell/users/attie/home
ell/users/attie/photos
ell/users/attie/junk
ell/users/nobody
ell/users/nobody/downloads
ell/users/nobody/home
ell/users/nobody/photos
ell/users/nobody/scans

这将为您提供您感兴趣的文件系统的递归列表......

不过,您可能希望跳过某些数据集,我建议使用属性来实现这一点 - 例如,rsync:sync=false将阻止同步该数据集。这是我最近添加到syncoid

下面的字段由制表符分隔。

$ zfs list -Hrt filesystem -o name,rsync:sync ell/users
ell/users   -
ell/users/attie -
ell/users/attie/archive -
ell/users/attie/dropbox -
ell/users/attie/email   -
ell/users/attie/filing_cabinet  -
ell/users/attie/home    -
ell/users/attie/photos  -
ell/users/attie/junk    false
ell/users/nobody    -
ell/users/nobody/downloads  -
ell/users/nobody/home   -
ell/users/nobody/photos -
ell/users/nobody/scans  -

您还需要了解,由于 ZFS 数据集可以挂载任何地方(如上所述),按照它们在 VFS 中呈现的方式去思考它们是不对的……它们是独立的实体,您应该这样处理它们。

/为了实现这一点,我们将用三个下划线___(或其他通常不会出现在文件系统名称中的分隔符)替换任何正斜杠,从而使文件系统名称扁平化。

$ filesystem="ell/users/attie/archive"
$ echo "${filesystem//\//___}"
ell___users___attie___archive

这些都可以组合成一个简单的 bash 脚本……就像这样:

笔记:我只是对此进行了简单的测试...并且应该有更多的错误处理。

#!/bin/bash -eu

ROOT="${ZFS_ROOT}"
SNAPSHOT_NAME="rsync_$(date +%s)"
TMP_MNT="$(mktemp -d)"

RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}"

# take the sanpshots
zfs snapshot -r "${ROOT}"@"${SNAPSHOT_NAME}"

# push the changes... mounting each snapshot as we go
zfs list -Hrt filesystem -o name,rsync:sync "${ROOT}" \
    | while read filesystem sync; do
        [ "${sync}" == "false" ] && continue
        echo "Processing ${filesystem}..."

        # make a safe target for us to use... flattening out the ZFS hierarchy
        rsync_target="${RSYNC_TARGET}/${filesystem//\//___}"

        # mount, rsync, umount
        mount -t zfs -o ro "${filesystem}"@"${SNAPSHOT_NAME}" "${TMP_MNT}"
        rsync -avP --exclude="/.zfs/" "${TMP_MNT}/" "${rsync_target}"
        umount "${TMP_MNT}"
    done

# destroy the snapshots
zfs destroy -r "${ROOT}"@"${SNAPSHOT_NAME}"

# double check it's not mounted, and get rid of it
umount "${TMP_MNT}" 2>/dev/null || true
rm -rf "${TMP_MNT}"

答案2

我强烈建议使用zfs send-zfs receiversync会更快并且还具有其他主要优点(例如:不会错过更改,无需密钥即可加密)。

有些存储服务可以为您提供向其推送数据集的功能(类似于使用支持的服务rsync)。

甚至还有一个不错的工具 - syncoid萨诺德项目)——我强烈推荐。它管理快照并允许推送或者拉动操作。

这次谈话zfs send/recv讨论了和之间的区别rsync


作为后续行动,我刚刚从奥布南(现已退役),并已在 ZFS 上安装快照。我还刚刚调查了异地存储服务,并且(对于我所需的存储量)得出结论,在远程位置构建和托管机器比在大约 1 年之前使用专用存储服务更便宜……当然,请自行做出决定。


针对你的一些言论:

我不愿意花钱购买第二台能够正常运行 ZFS 的计算机。

值得注意的是,ZFS 没有必须使用 ECC RAM,并且您可以轻松地在单个磁盘上运行 ZFS- 这是一个异地备份,因此您可能会接受。

对于我来说,构建自己的机器的价格与云存储的价格大致相同。

正如我上面提到的,我进行了一些计算,得出的结论是,建造一台便宜的异地机器比支付一年的“云储存“来自服务提供商......所以我通过构建这样的机器预先付款,一年之内我就会开始看到节省。”云储存“不是你购买的东西——你必须继续为它付钱。

还有进一步的好处 - 我可以向托管我的机器的人提供服务和异地备份......在这种情况下他们根本没有这些。

答案3

我同意其他答案,一般来说最好使用zfs send

但是,如果您决定使用rsync,并且您想要的只是整个池的一致快照,则可以使用递归来实现zfs snapshot。尽管快照在输出中分别显示在zfs list每个受影响的数据集/卷中,但它们是在一致的时间点拍摄的(即它们是“原子“—txg在 ZFS 内部术语中,全部都具有相同的功能)。

相关内容