我有一个 zfs 池,其中包含多个 zvol 和数据集,其中一些还嵌套。所有数据集和 zvol 都会由 zfs-auto-snapshot 定期生成快照。所有数据集和 zvol 也有一些手动创建的快照。
我设置了一个远程池,由于时间不足,通过 zfs send -R 通过本地高速网络进行的初始复制未完成(某些数据集丢失,某些数据集已过时或丢失快照)。
现在,池通过低速连接在物理上处于远程状态,我需要定期将远程池与本地池同步,这意味着本地池中存在的数据必须复制到远程池,来自本地池的数据必须从远程池中删除,并且远程池中存在但本地池中不存在的数据必须从远程池中删除,数据含义为“zvols”、“数据集”或“快照”。
如果我使用 rsync 在两个常规文件系统之间执行此操作,则将是“-axPHAX --delete”(这就是我备份某些系统的实际操作)。
如何设置同步任务,以便远程池 zvol 和数据集(包括其快照)可以与本地 zvol、数据集和快照同步?
我想避免通过 ssh 传输,因为 ssh 的吞吐量性能较低;我更喜欢 mbuffer 或 iscsi。
答案1
免责声明:由于我从未使用过 zvols,因此我不能说它们在复制方面与普通文件系统或快照是否有任何不同。我认为是的,但不要相信我的话。
你的问题其实是多个问题,我尝试分开回答:
如何将完整池复制/镜像到远程位置
您需要将任务分为两部分:首先,必须完成初始复制,然后可以进行增量复制,只要你不弄乱你的复制快照。要启用增量复制,您需要保留最后的复制快照,之前的所有内容都可以删除。如果删除以前的快照,zfs recv
将会抱怨并中止复制。在这种情况下你必须重新开始,所以尽量不要这样做。
如果您只需要正确的选项,它们是:
zfs send
:-R
:发送给定池或数据集下的所有内容(递归复制,一直需要,包括-p
)。此外,接收时,所有已删除的源快照都会在目标上删除。-I
:包括最后一个复制快照和当前复制快照之间的所有中间快照(仅在增量发送时需要)
zfs recv
:-F
:扩展目标池,包括删除源上已删除的现有数据集-d
:丢弃源池的名称并将其替换为目标池名称(其余文件系统路径将被保留,如果需要的话还可以创建)-u
:不要在目标上挂载文件系统
如果您喜欢完整的示例,这里有一个小脚本:
#!/bin/sh
# Setup/variables:
# Each snapshot name must be unique, timestamp is a good choice.
# You can also use Solaris date, but I don't know the correct syntax.
snapshot_string=DO_NOT_DELETE_remote_replication_
timestamp=$(/usr/gnu/bin/date '+%Y%m%d%H%M%S')
source_pool=tank
destination_pool=tank
new_snap="$source_pool"@"$snapshot_string""$timestamp"
destination_host=remotehostname
# Initial send:
# Create first recursive snapshot of the whole pool.
zfs snapshot -r "$new_snap"
# Initial replication via SSH.
zfs send -R "$new_snap" | ssh "$destination_host" zfs recv -Fdu "$destination_pool"
# Incremental sends:
# Get old snapshot name.
old_snap=$(zfs list -H -o name -t snapshot -r "$source_pool" | grep "$source_pool"@"$snapshot_string" | tail --lines=1)
# Create new recursive snapshot of the whole pool.
zfs snapshot -r "$new_snap"
# Incremental replication via SSH.
zfs send -R -I "$old_snap" "$new_snap" | ssh "$destination_host" zfs recv -Fdu "$destination_pool"
# Delete older snaps on the local source (grep -v inverts the selection)
delete_from=$(zfs list -H -o name -t snapshot -r "$source_pool" | grep "$snapshot_string" | grep -v "$timestamp")
for snap in $delete_from; do
zfs destroy "$snap"
done
使用比 SSH 更快的东西
如果您有足够安全的连接,例如 IPSec 或 OpenVPN 隧道以及仅存在于发送方和接收方之间的单独 VLAN,您可以从 SSH 切换到未加密的替代方案,例如 mbuffer如此处所述,或者您可以使用具有弱/无加密和禁用压缩的 SSH,详细信息请参见此处。还有一个关于重新编译 SSH 以使其速度更快的网站,但不幸的是我不记得 URL - 如果我找到它,我会稍后编辑它。
对于非常大的数据集和缓慢的连接,通过硬盘第一次传输也可能有用(使用加密磁盘存储zpool并通过快递、邮件或亲自以密封包装传输)。由于传输方法对于发送/接收来说并不重要,因此您可以将所有内容通过管道传输到磁盘,导出池,将磁盘发送到其目的地,导入池,然后通过 SSH 传输所有增量发送。
快照混乱的问题
如前所述,如果您删除/修改复制快照,您将收到错误消息
cannot send 'pool/fs@name': not an earlier snapshot from the same fs
这意味着您的命令错误或者您处于不一致的状态,您必须删除快照并重新开始。
这有几个负面影响:
- 在成功传输新的复制快照之前,您无法删除复制快照。由于这些复制快照包括所有其他(较旧)快照的状态,因此只有在复制完成后才会回收已删除文件和快照的空白空间。这可能会导致池中出现临时或永久的空间问题,您只能通过重新启动或完成完整的复制过程来修复该问题。
- 您将拥有许多额外的快照,这会减慢 list 命令的速度(Oracle Solaris 11 除外,该问题已修复)。
- 您可能需要保护快照免遭(意外)删除,脚本本身除外。
这些问题有一个可能的解决方案,但我自己还没有尝试过。您可以使用zfs bookmark
OpenSolaris/illumos 中专门为此任务创建的新功能。这将使您摆脱快照管理的束缚。唯一的缺点是目前它仅适用于单个数据集,而不是递归。您必须保存所有旧数据集和新数据集的列表,然后循环它们、添加书签、发送和接收它们,然后更新列表(或小型数据库,如果您愿意)。
如果您尝试书签路线,我很想知道它对您来说效果如何!
答案2
就我个人而言,我会在远程服务器上为自己制作一个 zvol、数据集等的列表不拥有最新的快照,然后使用 更新这些快照zfs send
,即使这非常耗时且占用大量带宽。
然后我就可以继续使用zfs send
,而不必通过编写自己的同步代码来重新发明轮子。rsync
对于较旧的文件系统很好,但zfs send
对于 zfs 更好 - 它知道确切地快照中哪些块已更改并发送仅有的它们,而 rsync 必须比较本地和远程服务器之间的单个文件和/或时间戳。这同样适用于btrfs send
btrfs 池。
如果您只有少量快照需要更新,则可以手动完成此操作。否则,要自动执行此操作,您需要最新的本地快照与远程快照的列表,以及用于比较版本和zfs send
rmeote 服务器上过期的本地快照的脚本。
如果您只关心每个数据集的最新快照,这就足够了。如果您关心所有以前的快照,显然您的脚本也必须处理它们......这会变得更加复杂。在某些情况下,您可能必须在远程服务器上回滚,以便可以重新发送中间/丢失的快照。
如果您想要与远程服务器建立安全连接,您实际上别无选择,只能使用ssh
- 或者可能使用 或其他东西建立隧道openvpn
并使用netcat
。
答案3
看看 FreeBSD 上的“zrepl”,它可以让您以及任何人的生活变得更加轻松。它于几天前在渥太华举行的 BSDCan2018 期间发布。它看起来很有前途,可能会解决您的问题
答案4
zrep 是一个很好的一体化解决方案,并且有文档+挂钩如何获得比普通 SSH 传输更快的传输
https://github.com/bolthole/zrep
它也是跨平台的:支持 linux、freebsd 和Solaris/illumos