究竟什么是replication?
在ZFS中,这意味着在其他地方制作文件系统的精确副本。另一个位置可以在池中的另一个数据集、系统上的第二个池、外部驱动器、远程系统、磁带或只是一个文件。
你可以声明”我希望这个文件系统在那个地方“,并实现它。ZFS复制具有一些设计特性,使其特别强大。
像dump和rsync这样的程序希望接收器以某种方式确认收到数据。
ZFS复制过程是单向的,发送方不需要接收方的任何反馈。
由于复制不需要任何确认,ZFS接收者不需要任何情报,它只需要接受字节流并对其进行处理。
复制系统与快照集成在一起。快照是一个静态的、不变的实体,这意味着传输的ZFS数据集是完全一致的,与转储或同步实时文件系统不同。基于快照的复制还意味着可以进行增量复制,只发送在两个快照之间发生更改的块。使用增量复制,不必发送相同的数据两次。
ZFS的复制功能旨在充分利用所有的磁盘。在机器之间复制数据的速度的唯一限制是网络连接的速度。
ZFS复制由两部分组成:zfs send,它将快照或一系列快照序列化为单个数据流;zfs receive,它把该流转换回ZFS文件系统。
几十年来,rsync一直是机器之间同步文件的标准工具。为了同步文件,rsync遍历目录树,计算每个文件的时间戳和加密校验和,并将其与远程端的文件进行比较。许多组织已经部署了广泛的基于rsync的基础设施。
ZFS从磁盘开始设计,以实现最佳性能。ZFS维护磁盘上每个快照之间不同的块列表。复制过程不需要确定哪些文件已经更改——文件系统本身已经有了这些信息。复制过程立即开始以尽可能快的速度发送这些块。由于更改的块包含重新组装文件的所有元数据,复制过程甚至不需要知道这些块属于哪些文件。
如果有10TB数据,但只有1GB发生了变化,rsync需要检查每一个文件(时间戳、计算校验和,并将其与另一端的版本进行比较),而ZFS则只抓取1GB的更改块并发送它们。
需要更快rsync同步的系统管理员可以告诉rsync作弊,并假设如果本地和远程文件的最后修改时间相同,则认为文件没有更改。这实际上并不总是正确。
当文件的时间戳发生变化时,rsync会计算文件两侧块的校验和,并比较校验和。如果发现差异,它就会计算一个增量并将其发送出去。这意味着,如果你的大文件进行了一个小更改,rsync必须在本地和远程读取并校验整个文件。使用rsync在备份机器上维护500GB VM磁盘映像的副本会占用大量磁盘带宽和处理器时间。
ZFS中的每个块都有一个诞生时间,即创建块时的事务组ID。复制过程发送的任何块都比上次运行复制时更新。无论这些块来自一个新文件,还是来自一个巨大文件的中间,都没关系。
当定期同步文件系统时,基于快照的复制的优势就会真正发挥作用。假设在远程备份服务器上复制快照,一小时后,创建了一个新的快照,并逐步将该快照发送到备份服务器上。ZFS将在几秒钟内完成,而rsync可能仍在第一级目录钟运行。
rsync支持snapshot备份模式。然而,rsync中的快照与ZFS快照完全不同。使用rsync快照,如果修改1GB文件的1个字节,rsync将保留该文件的两个完整副本。而另一方面,ZFS保留单个块的两个不同副本,文件的两个版本共享其余块。
rsync的优势在于,它是一个跨平台、跨文件系统的工具。可以使用rsync在操作系统之间和文件系统之间同步目录树。
但是,如果使用的是ZFS,复制非常适合处理ZFS。复制理解并复制ZFS属性。它可以保持克隆与其父级之间的关系,而rsync将失去此链接并放弃所有节省的空间。
rsync在原始块设备上工作不太好,但ZFS复制在zvols上工作不太好。复制也使用文件系统的集成校验和,因此受到的文件与原始文件不会有差异。
ZFS复制也是版本无关的。只有当你有意添加命令行标志以请求新池功能时,才会启用这些功能。这使得ZFS复制能轻松地在不同版本的池之间移动数据。
复制在许多方面发挥作用:最明显的是在备份中,但也在测试、虚拟化和数据迁移中。
RAID不是备份,即使是RAID-Z3也不是一个合适的备份。RAID无法拯救意外删除的重要数据集。将重要池复制到外部驱动器、备份机或磁带库。现在即使硬件丢失也可以取回数据。ZFS的优点在于,在初始复制之后,每个备份都可以是增量的。
更进一步,在需要24X7X365可用的系统上,将数据复制到第二台、第三台服务器,每隔几分钟进行一次增量快照。现在,数据始终可以在n+2台热备服务器上运行。将其中一台备份服务器放在远程位置,甚至可以免受设施的全面破坏。
当你有一个很好的客户数据集,你想用它测试新版的计费系统,你需要在开发环境中有一个完全独立的副本,而不是在同一台机器上克隆数据。无论目标是远程计算机还是同一池上的新数据集,ZFS复制都是复制数据最快、最可靠的方法。
你是否有数十甚至数百台相同的机器、虚拟机或容器?使用ZFS复制将精心制作的镜像部署到任何地方。如果正确设计了系统,甚至可以使用增量复制来部署更新。
复制可以大大简化数据迁移,即使是远距离的大量数据。对于庞大的数据,即使是同一数据中心的10Gb/s以太网似乎也太慢了。假设你有许多TB的数据一直在使用中,并且正在经历持续的小规模变动,例如客户数据库。通过互联网将这些数据复制到全球的另一端需要几天的时间,但数据又是随时变动的。基于rsync的复制过程耗时太长,可能永远无法赶上。可以通过一些复杂的数据分割来移动数据,这些计划可能会成功,但会增加风险。
如果可用于同步的带宽超过了数据的变化率,请改用ZFS。第一次ZFS包括整个庞大的数据集中的每一条数据,这可能会需要几天或几周的时间。不过,当同步完成后,从新快照进行第二次复制所需的时间不会那么长。通过几次迭代,只要更改的速度低于可用于备份的带宽,ZFS复制就会接近实时。
在大切换日(Big Switch Day),在复制最新更改集的同时,将数据集冻结一会儿,可能会对负载均衡器、防火墙、新服务器以及支持如此庞大数据集所需的所有其他小工具感到恐慌,但数据本身不会是问题所在。
ZFS不复制数据集,它复制快照。快照的数据是不变的,因此可以保证它们在内部的一致性。
首先创建快照:
xxxxxxxxxx
# zfs snapshot mypool/somedata@snappycomeback
现在让我们把快照复制到本地和远程主机。
ZFS复制是单向的,这意味着它不需要来自接收端的任何反馈。同时意味着可以使用标准的Unix shell重定向和管道将快照转储到任何其他程序中。
本例中将zfs send的输出馈送到一个常规的文件中。(如果添加-v选项,zfs send将每秒打印一次进度摘要。)
xxxxxxxxxx
# zfs send mypool/somedata@snappycomeback > backup_file
这个文件是我们首次使用ZFS复制,因此它不是增量的。它包含快照中的所有内容。它的大小于数据集大致相同。然而,它并不像现在这样有用——很少有人能在不将其转换回文件系统的情况下读取流式文件系统。因此,我们使用zfs receive将此数据集反馈给ZFS:
xxxxxxxxxx
# zfs receive mypool/copy < backup_file
zfs从backup_file读取复制流,并从中创建一个新的数据集,这是原始数据集的精确副本。
不需要在本地复制过程中有一个文件。这是个类Unix系统。可以使用管道。此主机在根池上有主目录,但我们正在将副本移动到新池中:
xxxxxxxxxx
# zfs send zroot/home@weds | zfs receive mypool/home
现在我们可以整理几个数据集挂载点,并将主目录移动到新池中。
ZFS的单向特性可以复制到任何可以瞄准命令的对象,比如磁带。使用管道,可以通过SSH将zfs send注入zfs receive,从而在远程计算机上复制数据集。
zstreamdump实用程序检查流并公开其详细信息。可以用它检查文件,或直接从zfs send读取。
xxxxxxxxxx
# zstreamdump < backup_file
BEGIN record
hdrtype = 1
features = 4
magic = 2f5bacbac
creation_time = 56a53713
type = 2
flags = 0x0
toguid = 424654598740125b
fromguid = 0
toname = mypool/somedata@snappycomeback
END checksum = 14035a747cefd2/65f5a463eb5427f0/3e70de6ff7d7456/497949c053fadcb3
...
此输出包含大量关于zfs发送流的信息。最难的是,这些都没有以人性化的方式呈现。即使如此,依然能从中提取一些信息。
creation_time段给出zfs send运行的时间,以Unix纪元的秒为单位,当然是16进制。使用以下命令(date -r 0x——0x表示后面的值是16进制的)可将其转换为人类可读的数值:
xxxxxxxxxx
$ date -r 0x56a53713
Sun Jan 24 15:41:55 EST 2016
在非FreeBSD主机上,data命令可能不接受16进制值。可以尝试以下方法:
xxxxxxxxxx
$ printf "%d\n" 0x56a53713 | xargs date -r
每个快照都有一个人类可读的名称,以toname的形式给出。此例中快照称为mypool/somedata@snappycomeback,并阐明了数据集和快照使用有意义的名称的重要性。
流中的每个快照都有一个全局唯一的标识符或GUID。此快照的GUID显示toguid字段中。
fromguid字段用于增量ZFS发送,仅包括两个快照之间的更改。此例中其值为0,这意味着这个zfs send流包含一个完整的快照。这不是一个增量。由于这是一个完整的快照,将此zfs send流恢复到实时数据集是有意义的。恢复增量zfs流需要它所基于的快照的副本。
如果一个流中有多个快照,比如来自增量或递归zfs send,你可以使用toguid和fromguid值来拼凑快照的组合方式。然而,将zfs send流恢复到数据集并以这种方式查看可能更容易。
每个部分都以校验和结束。
在每个快照的详细信息之后,zstreamdump会打印一个摘要:
xxxxxxxxxx
SUMMARY:
Total DRR_BEGIN records = 5
Total DRR_END records = 6
…
Total records = 170
Total write size = 10523136 (0xa09200)
Total stream length = 10554216 (0xa10b68)
摘要包括一大堆ZFS内部元数据。最有用的部分是DRR_BEGIN records,它对应于此流中的快照数量。
末尾的数字以字节为单位。write size是流中包含的数据的大小;stream length是流本身的大小。
为了将ZFS复制到远程主机,远程主机上需要一个可以接收复制的用户和一个到该远程主机的安全管道。
最常见的安全复制管道类型是SSH,因此我们假定使用SSH。从长远来看,使用SSH最简单的方法是基于密钥的身份验证。可参阅SSH Mastery (Tilted Windmill Press, 2012)
不要使用root账户接收复制流,因为这样做需要使用root身份进行SSH登录。
建议创建一个无特权用户并为其分配复制权限。
同样,虽然可以以root身份发送ZFS数据集,但接收者最好是普通用户。
在发送给和接收主机上,我们都创建了一个专门用于复制的用户。以下示例中,用户名为replicator,他需要一个shell,但不需要特殊的组成员资格。
xlocal# pw user add replicator -m -s /bin/sh
remote# pw user add replicator -m -s /bin/sh
在发送主机上,用户replicator需要对发送的数据集具有发送和快照权限。此处,我们给replicator用户主目录上的这些权限:
xxxxxxxxxx
# zfs allow -u replicator send,snapshot zroot/usr/home
发送用户需要SSH密钥对。在发送端和接收端都使用自己账户的用户可以使用自己的SSH密钥,对于专用账户,使用ssh-keygen生成密钥:
xxxxxxxxxx
# su replicator
$ ssh-keygen
ssh-keygen程序希望你提供密码短语。如果使用此账户发送ZFS数据集,应使用密码短语;如果这是个自动化脚本,建议使用空密码短语。
密钥文件为.ssh/id_rsa.pub,保存在用户的主目录中。我们还建议限制哪些主机可以使用此密钥登录到远程计算机,以帮助在密钥被盗时保护远程主机和您的备份。
现在让这个无特权用户在远程计算机的账户中安装公钥。此处我们将新密钥发送到主机热备盘上具有相同用户名称的账户:
xxxxxxxxxx
$ ssh-copy-id -i .ssh/id_rsa.pub hotspare
验证你是否可以用此用户身份登录到接收主机。
在接收机上,用户必须拥有接收方数据集的挂载点。系统还必须允许无特权用户使用vsf.unsermount sysctl将此文件系统挂载到它们自己的目录上。
xxxxxxxxxx
# zfs create -o mountpoint=/backup remotepool/backup
# chown replicator:replicator /backup
# sysctl vfs.usermount=1
我们的无特权用户需要对目标数据集有一系列ZFS权限:compression、create、mount、mountpoint、receive。
此处,我们为用户replicator在remotepool/backup数据集上分配权限。如果你打算自动化ZFS复制,包括销毁过期的快照,则需要添加destroy权限。
xxxxxxxxxx
# zfs allow -u replicator compression,mountpoint,create,mount,receive remotepool/backup
此无特权用户现在可以复制此数据集。
如果要复制数据集的所有属性,则必须允许replicator用户设置所有这些属性。
要复制ZFS数据集,首先创建快照。我们授予了那个无特权用户权限,可以专门为此目的创建快照,开始吧:
xxxxxxxxxx
$ zfs snapshot zroot/usr/home@monday
现在使用zfs send命令传输快照,使用SSH管道,转储到zfs receive。
记住,ssh允许在远程主机上运行命令。
首次传送数据集时,发送流包含快照中的所有块。
xxxxxxxxxx
$ zfs send zroot/usr/home@monday | ssh user@host zfs receive remotepool/backup
除了复制到另一台计算机上的池外,还可以复制到同一台机器上的同一个池或第二个池、文件或管道。
复制到文件或管道对于备份非常有用,例如复制到磁带或其他文件系统。
如果愿意,可以让一个主机登录到另一个主机以触发zfs send,将zfs复制从推送模式更改为拉取模式。
xxxxxxxxxx
$ ssh user@host zfs send zroot/usr/home@monday | zfs receive remotepool/backup
本书假设你从本地数据集发送数据以保持一致性,但所有内容都可以在另一个方向上工作。
真正的力量来自增量复制。
现在我们已经复制了截至周一的数据集中的所有数据,周二的复制只需要发送已更改的块。
命令的所接收端根本不会改变,但在发送端,我们使用-i选项来指示最近发送的快照:
xxxxxxxxxx
$ zfs snapshot zroot/usr/home@tuesday
$ zfs send -i @monday zroot/usr/home@tuesday | ssh user@host zfs receive remotepool/backup/home
ZFS只发送@monday和@tuesday快照之间发生变化的块,节省了时间和带宽。
现在看看接收端上的数据集:
xxxxxxxxxx
# zfs list -t snap -r zroot/backup
NAME USED AVAIL REFER MOUNTPOINT
zroot/backup/usrhome@monday 8K - 49.0M -
zroot/backup/usrhome@tuesday 0 - 49.0M -
显示了两个快照。
增量备份的一个常见错误是没有指定远程系统上存在的最后一个快照。如果不指定传输的最新快照,则会出现错误。
xxxxxxxxxx
# zfs send zroot/usr/home@tuesday | ssh hotspare zfs recv remotepool/backup/usrhome
cannot receive new filesystem stream: destination remotepool/backup/usrhome' exists
must specify -F to overwrite it
这样的错误消息的危险在于,它提供了错误的方法,而不是建议解决潜在问题的方法。-F选项强制覆盖远程数据集会清除旧快照并重新发送所有数据。
使用-i时,可以跳过快照名称前面的@符号,-i选项意味着“这是一个快照”,因此它可以安全地假设你想把@放在前面,但就是不想被打搅。
磁带的增量备份几乎是固定的:你可能会覆盖它们,但在21世纪,你不会直接在磁带上编辑文件。不过,写入磁盘的增量备份很容易更改。
增量复制要求接收方的数据集副本在复制运行之间不会更改。副本的更改会破坏整个过程。
如果编辑数据集的备份副本,则下一次增量更新将不再插入备份副本。如果要在备份计算机上编辑数据集副本,应创建接收到的数据集的克隆并进行编辑。
如果有人意外或无知地编辑了副本,请将更改回滚到最后一个常用快照。让接收器通过zfs receive命令添中添加-F选项来强制回滚到匹配的远程快照:
xxxxxxxxxx
# zfs send -i @monday zroot/usr/home@tuesday | ssh user@hotspare zfs receive -F remotepool/backup/home
通过将复制数据集的ZFS属性设置为只读,防止对复制数据集进行更改。使用前面给出的权限,用户可以添加快照以只写入数据集:
xxxxxxxxxx
# zfs set readonly=on remotepool/backup
仍然可以在remotepool/backup下添加快照。可以检测数据集中的文件。但是,如果不更改ZFS只读属性,任何人都无法编辑文件。任何拥有这种访问权限的人都应该更好地了解,或者迫切需要立即使该数据集上线。
ZFS复制可以在同一数据集上的任意两个快照上完成,因此您也可以进行差异备份。使用-I标志(大写I)而不是-i发送存在于两个快照之间的所有快照。
假设周二的快照复制因随机网络问题失败。在周三,我们希望同时发送周二和周三的快照:
xxxxxxxxxx
# zfs send -I @monday zroot/usr/home@wednesday | ssh hotspare zfs recv remotepool/backup/usrhome
接收人现在拥有周二的快照,尽管从未明确发送过。
有运行备份经验的人会意识到,增量(incremental)和差异(differential)这个两个词的方式与大多数备份软件略有不同。
备份软件旨在将块发送到磁带,并最大限度减少恢复文件所需的磁带数量。
ZFS的复制近义套用了这两个词。
SSH连接可能不够快,无法满足需求。那些需要复制速度超过每秒几百兆字节的人可能应该考虑使用外部安全解决方案,例如专用VPN。
如果不进行非常具体的修改,SSH将无法快速传输数据,此时可考虑mbuffer。
ZFS复制是单向的,从发送方到接收方。发送方不会从接收方得到任何反馈,允许将流转储到几乎任何东西。在决定增量复制和差异复制时,这一点变得很重要。
增量备份(-i)发送第一个快照和最后一个快照的生成时间之间发生更改的所有块,而不发送期间存在的任何快照。如果数据集一周中的每天都有快照,则zfs send -i monday zroot/usr/home@thursday生成一个依赖于接收端存在的@monday快照的流,并在那里创建@thursday快照。任何中间的快照都不会备复制。
差异备份(-I)的工作方式与增量备份完全相同,但它们会创建任何中间快照。像zfs send -I @monday zroot/usr/home@thursday这样的命令要求@monday快照存在于远程端,并顺便创建缺失的@tuesday和@wednesday快照。
假设每天自动对数据集进行快照,并希望将其发送到远程服务器。将@monday快照复制到远程池:
xxxxxxxxxx
# zfs send zroot/usr/home@monday | zfs receive hotspare remotepool/backup/usrhome
检查远程主机以验证@monday快照的存在:
xxxxxxxxxx
# zfs list -t all -r remotepool/backup/usrhome
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backup/usrhome 19.5K 472M 19.5K /remotepool/weekday
remotepool/backup/usrhome@monday 0 - 19.5K -
现在增量复制@tuesday快照:
xxxxxxxxxx
# zfs send -i monday remotepool/backup/usrhome@tuesday | zfs receive remotepool/weekday
检查远程主机,可以找到两个快照:
xxxxxxxxxx
# zfs list -t all -r remotepool/backup/usrhome
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backup/usrhome 29K 472M 19.5K /remotepool/weekday
remotepool/backup/usrhome@monday 9.50K - 19.5K -
remotepool/backup/usrhome@tuesday 0 - 19.5K -
周三没有复制快照,周四,对@thursday快照进行差异复制:
xxxxxxxxxx
# zfs send -I tuesday zroot/usr/home@thursday | zfs receive remotepool/backup/usrhome
我们的热备盘主机有四个快照:
xxxxxxxxxx
# zfs list -t all -r remotepool/backup/usrhome
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backup/usrhome 48K 472M 19.5K /remotepool/weekday
remotepool/backup/usrhome@monday 9.50K - 19.5K -
remotepool/backup/usrhome@tuesday 9.50K - 19.5K -
remotepool/backup/usrhome@wednesday 9.50K - 19.5K -
remotepool/backup/usrhome@thursday 0 - 19.5K -
周五,在设置当天的复制时,意外地尝试从@monday到@friday执行增量(-i)zfs send:
xxxxxxxxxx
# zfs send -i monday mypool/weekday@friday | zfs receive remotepool/weekday
cannot receive incremental stream: destination remotepool/weekday has been modified since most recent snapshot.
你可能很清楚你没有修改这些快照。没有人能登录到那台机器。但它已经被修改了,@tuesday、@wednesday、@thursday快照挡住去去路。
如果你不确定远程端可能存在哪些快照,可以使用-I发送所有中间快照。或者,可以在zfs send命令中使用-F选项,以强制它删除任何阻碍。
xxxxxxxxxx
# zfs send -i @monday zroot/usr/home@friday | zfs receive -F remotepool/backup/usrhome
使用zfs receive -F“删除任何阻碍“的不愉快的副作用是它会破坏中间快照。
xxxxxxxxxx
# zfs list -t all -r remotepool/weekday
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backup/usrhome 29K 472M 19.5K /remotepool/weekday
remotepool/backup/usrhome@monday 9.50K - 19.5K -
remotepool/backup/usrhome@friday 0 - 19.5K -
我们想要回哪些快照。所以我们再试一次。
在热备盘主机上,删除最新的快照:
xxxxxxxxxx
# zfs destroy remotepool/backup/usrhome@frida
现在,让发送方重新传输所有这些快照,一次一个或全部。此处,我们发送了一个快照,以确保没有破坏任何东西:
xxxxxxxxxx
# zfs send -i monday mypool/weekday@tuesday | zfs receive -F remotepool/weekday
复制是单向的这个事实意味着,在差异备份中,可以发送重叠,传输远程端已经存在的快照。如果我们在@monday和@friday之间发送所有快照,而@tuesday快照已经存在与远程池中,则源会发送所有更改的数据,甚至是远程端已经拥有的块。远程端快速转发它拥有的块,然后创建它没有的快照——此处是@wednesday到@friday。
ZFS也支持递归复制(recursive replication),即在一个命令中复制数据集及其所有子数据集。
这是一个包含三个数据集的本地池:
xxxxxxxxxx
NAME USED AVAIL REFER MOUNTPOINT
mypool 401M 3.09T 192K /mypool
mypool/family 50.2M 3.09T 50.2M /mypool/family
mypool/home 150M 3.09T 150M /mypool/home
mypool/work 200M 3.09T 200M /mypool/work
对数据集及其子数据集进行递归快照:
xxxxxxxxxx
# zfs snapshot -r mypool@first
# zfs list -t all -r mypool
NAME USED AVAIL REFER MOUNTPOINT
mypool 401M 3.09T 192K /mypool
mypool@first 0 - 192K -
mypool/family 50.2M 3.09T 50.2M /mypool/family
mypool/family@first 0 - 50.2M -
mypool/home 150M 3.09T 150M /mypool/home
mypool/home@first 0 - 150M -
mypool/work 200M 3.09T 200M /mypool/work
mypool/work@first 0 - 200M -
现在使用递归发送同时复制该快照的所有子快照:
xxxxxxxxxx
# zfs send -Rv mypool@first | zfs receive remotepool/backup
send from @ to mypool@first estimated size is 9.50K
send from @ to mypool/work@first estimated size is 200M
send from @ to mypool/family@first estimated size is 50.1M
send from @ to mypool/home@first estimated size is 150M
total estimated size is 401M
…
递归发送也适用于增量(-i)和差异(-I)备份,方式完全相同。
发送方可以通过多种方式更改其发送数据集的方式。
要发送数据集属性和实际数据,请添加-p选项。
当接受到的数据的属性与数据集上已有的属性不同时,zfs会受到更改属性以匹配发送的属性的尝试。也就是说,如果要将使用lz4压缩的数据集的属性复制到已经使用lz4的数据集,zfs receive不会对该属性进行任何操作。但如果发送方使用gzip-9,接收方会更改以匹配原始压缩。
接收数据集的用户必须具有设置要复制的属性的权限。如果用户有权复制部分而非全部属性,则将设置允许的属性,并拒绝不允许的属性。
假设我们的源数据集已将dedup设置为on,compression设置为gzip-9。而接收数据集已将dedup设置为off,压缩设置为lz4。我们希望接收数据集使用相同的压缩,但不使用去重设置。我们允许用户replicator更改压缩。
xxxxxxxxxx
# zfs allow -u replicator compress zroot/backup
当我们发送数据集时,受到错误提示:
xxxxxxxxxx
cannot receive compression dedup on remotepool/backup: permission denied
还好,我们不希望在副本上设置dedup属性。不过,压缩属性会根据需要进行复制。
为什么要复制属性,而不仅仅是将其设置在目标上?手动配置可能适用于压缩和去重等简单属性,但不太适合sharenfs或者任何配额等复杂属性。
数据是否已经进行去重?它是否适合去重?ZFS允许你对zfs send的数据流进行去重。使用-D选项,每个唯一块仅发送一次。它不会改变接收者写入磁盘的内容,只会影响数据流。
重复数据流使用的去重消除内存与磁盘去重使用的内存不同。如果数据可以有效地进行去重,但去重使用了数GB的内存,则发送和接收主机都需要类似数量的内存来对数据流进行去重。在zfs send中不要轻易使用去重。
zfs send支持几个选项,可以帮助调试、测试和监控。
较新版本的ZFS可以支持大于128KB的磁盘块,并具有large_blocks zpool功能。-L标记允许zfs发送包含大块的数据,而不是将其分解为小块。接收者也必须支持大块。
对于块非常小的主机,-e通过使用embedded_data特性缩小了数据流的大小。目标池必须支持embedded_data功能标志。
接收方可以通过zfs receive的参数调整其存储转入ZFS流的方式。
接收到的ZFS流包括源的池和数据集路径,可以保留或剔除此路径。
通过添加-d选项,可以告诉zfs receive使用源的完整路径(池名称除外)作为目标数据集的路径,而不需要系统管理员指定目标数据集。
早些时候,我们将zroot/usr/home复制到remotepool/backup/use/home,通过在这里使用-d选项,我们告诉zfs receive使用目标上的源路径:
xxxxxxxxxx
$ zfs send zroot/usr/home@monday | ssh hotspare zfs receive -d remotepool/backup
接收者创建remotepool/backup/use/home并且将@monday快照粘贴在那里。在复制多层数据集时,此功能非常有用。
或者,可以删除大部分路径信息,通过添加-e选项,可以指示zfs receive使用路径的最后一部分来命名此数据集。现在我们运行相同的备份,但去掉大部分路径:
xxxxxxxxxx
$ zfs send zroot/usr/home@monday | ssh zfs2 zfs receive -e remotepool/backup
zfs receive命令查看路径zroot/usr/home,并丢弃最后一个块或home之外的所有内容。接收到的数据流进入remotepool/backup/home中。
最后,-u选项告诉zfs receive不要挂载接收到的快照。如果系统管理员需要,数据可以挂载在那里,但挂载数据集可能会导致数据更改。
如果有人更改了接收到的数据集,则尝试向该副本增量添加新快照将失败。接收到的快照的数据集必须是原始的,zfs receive才能进行增量或差异更新。
-F选项告诉zfs receive回滚任何阻止接收此快照的更改。
与zfs send非常相似,zfs receive支持冗长和无效果选项。
-v选项使zfs receive详细输出信息。它打印有关接收到的数据的信息,并添加定期状态更新。
-n选项阻止zfs receive实际将任何数据写入磁盘。相反,当与-v结合时,它提供了zfs receive在实际使用时将会受到什么的统计信息。
虽然冗长可能有用,但-n选项在接收数据方面的实用性有限。主机将通过网络向该主机发送数据,接收方将进行一些数值分析并丢弃数据。要将数据写入磁盘,必须重新发送。
你可能需要发送一个你知道你会想处理的数据集。从FreeBSD 10.3开始,您可以告诉zfs receive将传入的增量复制流存储为克隆而不是快照。收据克隆仅适用于增量复制。
要让zfs接收并创建克隆,请添加-o标志并将源定义为要克隆的数据集。zfs receive命令获取该数据集,将传入的快照添加到其中,并将克隆从原始数据集中分叉出来。
在本章中,我们一直在将zroot/usr/home备份到远程服务器。假设我们想要一个周三快照的读写克隆。我们将从克隆周二快照开始。
xxxxxxxxxx
$ zfs send -i @tuesday zroot/usr/home@wednesday | ssh hotspare zfs receive -o origin=remotepool/usr/home@tuesday remotepool/usr/wedshome
-o origin语句告诉zfsreceive我们从快照远程池/usr开始/home@tuesday并创建克隆。最后一个参数给出了我们的克隆的名称,remotepool/usr/wedshome。我们现在可以进入/remotepool/usr/wedshome,进行任何我们想要的更改,而不会干扰进一步的复制。
但是,请记住,创建此克隆不会将传输的快照添加到原始目标中的快照中。如果我们还想创建remotepool/usr/home@wednesday,我们必须在不使用-o origin选项的情况下重新传输它。
快照可能会占用大量空间,尤其是在繁忙的文件系统上。如果你让用户构建新软件,下载ISO然后丢弃它们,并将核心文件转储到任何地方,快照可能会变得相当大。不幸的是,增量复制是基于快照构建的。书签是一种避免保留最旧快照的方法,同时仍在执行增量复制。
增量复制不需要知道已经发送的所有块。它必须知道已经发送的最年轻区块的出生时间,这样它才能发送所有更年轻的区块。书签是一个精简的快照,只保留快照中最新块的出生时间。您可以将书签用作增量复制的起点。
为工作日数据集的@friday快照创建书签。书签名称以哈希标记(#)开头。
xxxxxxxxxx
# zfs bookmark zroot/usr/home@friday zroot/usr/home#bm-friday
使用zfs list查看书签:
xxxxxxxxxx
# zfs list -t all -r mypool/weekday
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/home 360K 13.5G 96K /mypool/weekday
zroot/usr/home@monday 64K - 96K -
zroot/usr/home@tuesday 64K - 96K -
zroot/usr/home@wednesday 64K - 96K -
zroot/usr/home@thursday 64K - 96K -
zroot/usr/home@friday 8K - 96K -
zroot/usr/home#bm-friday - - - -
现在从源池中删除所有快照:
xxxxxxxxxx
# zfs destroy -v zroot/usr/home@%
will destroy zroot/usr/home@monday
…
现在就只剩下书签:
xxxxxxxxxx
# zfs list -t all -r mypool/weekday
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/home 96K 13.5G 96K /mypool/weekday
zroot/usr/home#bm-friday - - - -
周六,拷贝一个新的快照:
xxxxxxxxxx
# zfs snapshot zroot/usr/home@saturday
# zfs list -t all -r zroot/usr/home
NAME USED AVAIL REFER MOUNTPOINT
mypool/weekday 96K 13.5G 96K /mypool/weekday
mypool/weekday@saturday 0 - 96K -
mypool/weekday#bm-friday - - - -
毫无帮助的是,书签列在快照之后,即使它们比快照旧。然而,ZFS命令行非常有表现力。为了使事情更容易,可以使用-t选项来告诉它只列出快照和书签。-s属性告诉zfs如何对输出进行排序,因此我们将数据creation属性进行排序,添加最大递归深度-d 1以忽略子数据集的快照:
xxxxxxxxxx
# zfs list -t snapshot,bookmark -s creation -d 1 zroot/usr/home
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/home#bm-friday - - - -
zroot/usr/home@saturday 64K - 96K -
现在使用bm-friday书签作为fromsnap,将@shaurday快照复制到远程主机:
xxxxxxxxxx
# zfs send -i #bm-friday zroot/usr/home@saturday | ssh hotspare zfs receive remotepool/backup/usrhome
远程主机将@shaurday快照捕获为增量快照,即使源主机不再有Friday的快照。
xxxxxxxxxx
# zfs list -t all -r remotepool/usrhome
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backup/usrhome 58.5K 472M 19.5K /remotepool/weekday
remotepool/backup/usrhome@monday 9.50K - 19.5K -
remotepool/backup/usrhome@tuesday 9.50K - 19.5K -
remotepool/backup/usrhome@wednesday 9.50K - 19.5K -
remotepool/backup/usrhome@thursday 9.50K - 19.5K -
remotepool/backup/usrhome@friday 1K - 19.5K -
remotepool/backup/usrhome@saturday 0 - 19.5K -
书签允许我们从源池中删除快照,节省空间,但将它们保留在目标池中,这样我们仍然可以引用旧版本的文件。
类似断点续传,FreeBSD10.3开始实现的功能。
使用-s选项启用ZFS发送流可恢复。
以下例子创建一个数据集的快照,并将它发送到目标主机,在发送过程中按CTRL-C中断它:
xxxxxxxxxx
local# zfs snapshot mypool/from@resumeme
local# zfs send -v mypool/from@resumeme | ssh hotspare zfs receive -s remotepool/to
full send of mypool/from@resumeme estimated size is 2.00G
total estimated size is 2.00G
TIME SENT SNAPSHOT
17:12:43 279M mypool/from@resumeme
17:12:44 529M mypool/from@resumeme
17:12:45 786M mypool/from@resumeme
^C
当然,由于zfs复制是单向的,发送方不知道是接收方实际捕获了哪些块并写入磁盘。如果没有可恢复的发送,就不得不重新开始整个传输。
在接受主机上,部分接收到的数据集有一个新属性receive_resume_token。发送者需要此属性的值才能从它停止的地方继续。
xxxxxxxxxx
remote# zfs get -H -o value receive_resume_token remotepool/to
1-db9a171a3-c8-789c636064000310a500c4ec50360710e72765a5269740d80cd8e4d3d28a534b40320b4c61f26c48f2499525a9c540da20ecb02936fd25f9e9a599290c0cae0dba41478e981fb24092e704cbe725e6a6323014a5e6e697a416e4e7e7e8a715e5e73a14a51697e6a68264100000648b1d2c
现在,使用-t选项将该令牌提供给发送方。不需要包含源数据集,因为令牌包含zfs发送所需的一切。不过,添加-v显示更多关于传输的细节:
xxxxxxxxxx
local# zfs send -v -t 1-db9a171a3-c8-789c636064000310a500c4ec50360710e72765a5269740d80cd8e4d3d28a534b40320b4c61f26c48f2499525a9c540da20ecb02936fd25f9e9a599290c0cae0dba41478e981fb24092e704cbe725e6a6323014a5e6e697a416e4e7e7e8a715e5e73a14a51697e6a68264100000648b1d2c | zfs receive -s remotepool/to
resume token contents:
nvlist version: 0
object = 0x8
offset = 0x35a00000
bytes = 0x35c35630
toguid = 0xc237c4c4522d8045
toname = mypool/from@resumeme
full send of mypool/from@resumeme estimated size is 1.16G
TIME SENT SNAPSHOT
17:38:20 263M mypool/from@resumeme
17:38:21 472M mypool/from@resumeme
…
17:42:04 1.16G mypool/from@resumeme
当zfs receive完成后,这个属性receive_resume_token会变为空。
xxxxxxxxxx
# zfs get receive_resume_token remotepool/to
NAME PROPERTY VALUE SOURCE
remotepool/to receive_resume_token - -
如果你不继续发送,目标池上会有一个不可用的、不完整的数据集,占用了更好地被任何东西使用的空间。用-A删除。
xxxxxxxxxx
remote# zfs receive -A remotepool/to
这将删除部分接收到的流并释放该空间。
必须手动运行的备份不是备份。可靠的备份需要自动化和测试。测试始终是你的工作,但对于自动化,我们使用zxfer。
zxfer检查选定的本地和远程数据集,确定必须复制哪些快照以同步两组数据,并发送数据集。从本地池中删除快照后,它还可以删除远程端的快照。
FreeBSD中默认不包含zxfer,可以使用pkg安装它。由于zxfer可以在推送和拉取模式下工作,只需要在其中一个系统上安装它。为了演示,这里在两个节点上安装它。
zxfer命令当前不支持书签或可恢复的复制。它也不设置复制用户账户。必须自己配置这些账户,创建SSH密钥,并设置权限,就像正常复制一样。
(这个功能暂且用不到,以下均为机翻)
我们所有的复制示例都使用了推送模式;具有当前数据集的主机将它们推送到复制目标。我们将以类似的方式使用zxfer。使用-T启用推送模式,并登录远程主机。 使用zxfer需要声明是要复制单个数据集,还是复制数据集及其所有子数据集。-R标志启用递归,而-N表示单个数据集及其快照。对于备份,递归模式几乎总是正确的。 您还可以添加-v,用于详细模式。
xxxxxxxxxx
$ zxfer -v -T user@host -R localpath remotepath
如果双方的用户帐户相同,则可以跳过识别用户。 要记住的一件事是,有自己论点的论点不能与不需要的论点结合在一起。您可以使用-v-Tuser@host,但是-vTuser@host让zxfer怨声载道。(由于zxfer是一个shell脚本,它使用getopt(1)来处理命令行参数,而不是更复杂语言中的花哨选项处理。) 在这里,我们将zroot/somedata数据集复制到主机远程上的池remotepool/backups。
xxxxxxxxxx
local$ zxfer -T replicator@remote -R mypool/somedata remotepool/backups
如果为详细模式添加-v,您将看到zxfer传输每个快照。
xxxxxxxxxx
Sending zroot/somedata@snappycomeback to remotepool/backups/somedata.
Sending zroot/somedata@reply to remotepool/backups/somedata.
(incremental to zroot/somedata@snappycomeback.)
Sending zroot/somedata@more to remotepool/backups/somedata.
(incremental to zroot/somedata@reply.)
一旦zxfer退出,主机远程就会拥有所有快照。
xxxxxxxxxx
remote$ zfs list -t all -r remotepool/backups/somedata
NAME USED AVAIL REFER MOUNTPOINT
remotepool/backups/somedata 10.1M 15.0G 10.1M /remotepool/backups/somedata
remotepool/backups/somedata@snappycomeback 8K - 10.1M -
remotepool/backups/somedata@reply 8K - 10.1M -
remotepool/backups/somedata@more 8K - 10.1M -
在这些快照之上的进一步复制可以在推送模式或拉取模式下运行。接下来让我们尝试拉取模式。
在拉取模式下,zxfer通过SSH登录到远程计算机,并运行zfssend将快照传输回运行zxfer的主机。在登录名和主机上使用-O标志表示拉取模式。其他一切都是一样的。
xxxxxxxxxx
$ zxfer -v -O user@host -R localpath remotepath
现在在源主机上创建一个额外的快照。
xxxxxxxxxx
local$ zfs snapshot mypool/somedata@new
在目标计算机上,运行zxfer以获取快照。在这里,我们添加了-v来显示实际情况的更多细节:
xxxxxxxxxx
remote# zxfer -v -O replicator@local -R zroot/somedata remotepool/backups
Sending mypool/somedata@new to zroot/backups/somedata.
(incremental to zroot/somedata@more.)
装载的数据集将使用新快照进行更新。
如果您继续发送快照,最终您的远程池将填满。您可能希望销毁源计算机上不再存在的远程快照。使用-d来完成此操作。首先删除旧快照。
xxxxxxxxxx
ocal# zfs destroy zroot/somedata@reply
使用-d标志从目标中删除所有已删除的快照:
xxxxxxxxxx
local$ zxfer -vd -T remote -R zroot/somedata remotepool/backup
在详细模式下,zxfer显示它销毁的每个数据集以及它创建的数据集。
xxxxxxxxxx
Destroying destination snapshot remotepool/backup/somedata@reply.
你可以看到你匆忙输入的错误命令会破坏你心爱的数据。
一个常见的快照方案要求每15分钟制作一次快照,然后每小时、每天、每周和每月制作一次。(我们在FreeBSD Mastery:ZFS中讨论了这样的轮换方案。)主机在几个小时后丢弃15分钟的快照,在几天后丢弃每小时的快照,以此类推。您当然希望在远程主机上丢弃这些15分钟的Snapshot,但您可能希望远程主机在源销毁它们后保留一些快照。我们中的许多人希望将每周或每月的快照作为长期备份。
-g标志允许您使用天数参数保护最旧的备份。例如,-g 375告诉zxfer不要删除375天或更早的快照。
假设您想保留所有月度快照,但会自动删除从源中删除的任何其他快照。源在三个月后删除每月快照,但在六周后删除每周快照。六周等于42天。为系统异常增加一周,即49天。使用-g 50将告诉zxfer不要删除任何50天或更早的快照。
xxxxxxxxxx
local$ zxfer -vd -g 50 -T remote -R zroot/somedata remotepool/backup
最终,您的备份池将填满。你必须进去清理那些太旧而不再有用的快照。这和清理公司的磁带柜没什么不同。
您通常希望复制复杂的属性,如sharenfs和任何配额。-P参数告诉zxfer将目标中的属性设置为与源匹配。 在某些情况下,您想知道属性是什么,但不想在复制后立即还原它们。zxfer命令可以将数据集属性复制到文本文件中,以便稍后从该文件进行还原。 -k标志告诉zxfer在复制副本的根目录中创建属性文本文件。该文件名为.zxfer_backup_info,后跟句点和池名。如果要将整个主机web5的zroot池复制到备份主机上的remotepool/web5,则属性备份文件将位于remotepool/wbe5/.zxfer_backup_info.zroot中。 使用-e标志从该文本文件还原数据集和池属性。 一种常见的配置是,主机留出一个池来接受备份,然后留出一个池来进行灾难恢复。如果源计算机死机,您可以在本地使用zxfer将最新备份复制到灾难恢复池。您可以在zxfer(8)手册页中找到确切的示例,但这里有一个常见的示例,包括恢复属性。我正在将主机web5的备份还原到一个也称为web5的池中。
xxxxxxxxxx
# zxfer -deFPv -R remotepool/web5/ web5
从新的web5池启动,您已恢复服务!
zxfer程序有很多选项可以在恼人的情况下进行复制。 如果你有一个复杂的SSH设置,你可能需要在zxfer用户的$HOME/.SSH/config中设置一些客户端选项。或者,您可以将这些选项添加到-O和-T的单引号中。 -O和-T标志也可用于注入SSH选项以及命令行参数。用户和主机之前的附加参数被馈送到SSH,而用户和主机之后的任何命令都以zfs(8)命令为前缀。(不过,配置ZFS数据集权限比使用sudo更好。) -O'-oPort=1022-i/path/to/key/filereplication@hotsparesudo' -F标志告诉zfsreceive回滚任何阻止复制的数据集。如果你更改了复制的数据集,-F会清除这些更改。 您可以让zxfer在运行前自动拍摄快照。它不会删除旧快照,因此它不是一个合适的快照轮换方案。然而,它适用于即时备份,将清理问题留到另一天。添加-s使zxfer为每个复制的数据集拍摄快照。 最后,-n标志会触发无操作模式。zxfer程序不会传输或删除任何快照。相反,它会执行分析,并打印出如果你没有设置-n,它会做什么。 现在您已经完成了复制,相比之下,ZFS卷的问题似乎很容易解决。或者,也许不是…