ZFS最强大的功能之一是快照。文件系统或zvol快照允许您访问数据集在精确时刻存在的副本。快照是只读的,从不更改。快照是文件的冻结映像,您可以在空闲时访问它。虽然备份通常会在几分钟或几小时内捕获系统,但在快照上运行备份意味着备份将获得单个一致的系统映像,从而消除这些 tar: file changed as we read it 消息及其同类。
由于快照为只读,可以将数据集回滚到快照的状态。在升级系统之前拍摄快照,如果升级出现严重错误,您可以返回快照并对供应商大喊大叫。
快照是许多特殊ZFS功能(如克隆)的根源。克隆是基于快照的文件系统的分支。新克隆不占用额外的空间,因为它们与快照共享其所有数据集块。在更改克隆时,ZFS会分配新的存储以适应更改。这允许您旋转数据集的几个稍微不同的副本,而不必为每个副本使用完全比例的磁盘空间。您想知道您的测试环境是否与生产环境紧密镜像?克隆生产文件系统并在克隆上测试。
快照还支持复制,允许您将数据集从一个主机发送到另一个主机。
最重要的是,ZFS的写入时拷贝特性意味着快照是“免费的”。创建快照是即时的,不会消耗额外的空间。
第七章:快照和克隆写时复制(Copy-on-Write)快照如何工作使用快照创建快照数据集变化和快照空间递归(recursive)快照高级数据集和快照查看查看数据集类型修改 zfs list 输出默认列出快照脚本和ZFS每个快照空间使用访问快照秘密的Snapdir挂载快照删除快照销毁预演递归和范围回滚差异(diff)快照自动快照机制轮换(rotation)计划ZFS Toolszfs-auto-snapshot启用自动快照查看自动快照使zfs-auto-snap变得聪明保留书签克隆创建克隆查看克隆删除克隆和快照升级克隆安全管理克隆、快照、递归
在普通文件系统和ZFS中,文件都作为块存在于磁盘上。在传统的文件系统中,更改文件意味着更改文件的块。如果系统在主动更改这些块时系统崩溃或断电,则所产生的剪切写入将创建一个文件,该文件一半是旧版本,一半是新版本,并且可能不可用。
ZFS从不覆盖文件的现有块。当文件发生更改时,ZFS标识必须更改的块,并将它们写入磁盘上的新位置。这称为写时复制(Copy on Write,COW)。旧块保持原状。缩短写入可能会丢失对文件的最新更改,但文件的早期版本仍然完整存在。
永远不会丢失文件是写时复制的一大好处,但COW提供了其他可能性。ZFS通过跟踪更改的块的旧版本来创建快照。听起来很简单,不是吗?是的。但就像所有简单的事情一样,细节是复杂的。我们在第3章中讨论了ZFS如何存储数据,但让我们更深入地讨论。
ZFS几乎是一个面向对象的文件系统。元数据(metadata)、索引(indexing)和数据(data)都是可以指向其他对象的不同类型的对象。ZFS池是一棵巨大的对象树,根植于池标签(pool labels)。
池中的每个磁盘都包含ZFS标签的四个副本:两个在驱动器的前面,两个在末尾。每个标签都包含池名称、全局唯一ID(GUID)和VDEV每个成员的信息。每个标签还包含128 KB的超级块(uberblocks)。
uberblock是一个固定大小的对象,它包含:
MOS记录池中所有内容的顶级信息,包括池中所有根数据集列表的指针。反过来,每个列表都指向其子级的类似列表,以及描述存储在数据集中的文件和目录的块。ZFS根据数据的需要链接这些列表和指针对象。在树的底部,叶块包含存储在池中的实际数据。
每个对象都包含校验和(checksum)和出生时间(birth time)。校验和用于确保对象有效。出生时间是创建块的事务组(txg)编号。出生时间是快照基础架构的关键部分。
修改数据块会触及整个树。修改后的数据块将写入新位置,因此指向它的块将被更新。该指针块也会写入新位置,因此树上的下一个对象需要更新。这会一直渗透到uberblock。
uberblock是树的根。一切都是从它派生出来的。ZFS不能在不违反写时复制规则的情况下修改uberblock,因此它会旋转(rotates)uberblock。每个标签为uberblock保留128 KB。具有512字节扇区的磁盘具有128个uberblock,而具有4 KB扇区的硬盘具有32个uberblock。如果您有一个具有16 KB扇区的磁盘,它将只有8个uberblock。每次文件系统更新都会向该阵列添加一个新的uberblock。当数组填充时,最旧的uberblock将被覆盖。
当系统启动时,ZFS扫描所有超级块,找到具有有效校验和的最新超级块,并使用该超级块导入池。即使最近的更新以某种方式失败,系统也可以在几秒钟之前导入池的一致版本。如果系统在写入过程中发生故障,则最后一个数据将丢失,但该数据永远不会写入磁盘。它不见了,ZFS帮不了你。使用写时复制意味着ZFS不会遇到传统文件系统所需的 fsck(8) 问题。
当管理员告诉ZFS创建快照时,ZFS复制文件系统的顶级元数据块。实时系统使用副本,保留原始副本供快照使用。创建快照只需要复制一个块,这意味着ZFS几乎可以立即创建快照。ZFS不会修改快照内的数据或元数据,使快照成为只读的。ZFS会记录有关快照的其他元数据,如出生时间。
快照还需要一段新的ZFS元数据,即死列表(dead list)。数据集的死列表记录最近的快照使用的所有块,但不再是数据集的一部分。从数据集中删除文件时,该文件使用的块将添加到数据集的死列表中。创建快照时,活动数据集的死列表被传递给快照,而活动数据集将获得一个新的空死列表。
删除、修改或覆盖活动数据集上的文件意味着为新数据分配新块,并断开包含旧数据的块。然而,快照需要一些旧的数据块。在丢弃旧块之前,系统会检查快照是否仍然需要它。
ZFS将旧数据块的出生时间与最近快照的出生时间进行比较。小于快照的块不可能由该快照使用,并且可以扔进回收站。早于快照出生时间的块仍由快照使用,因此被添加到活动数据集的死列表中。
在所有这些之后,快照仅仅是拍摄快照时实时数据集中正在使用的块的列表。创建快照告诉ZFS保留这些块,即使使用这些块的文件已从活动文件系统中删除。
这意味着ZFS不会保留每个文件的每个版本的副本。创建新文件并在创建快照之前删除该文件时,该文件将不存在。每个快照都包含创建快照时存在的每个文件的副本。ZFS不会DragonFly 的 HAMMER 那样保留历史。
删除快照需要比较块的出生时间,以确定哪些块现在可以释放,哪些块仍在使用。如果删除最新的快照,则数据集的当前死列表将更新,以删除仅该快照所需的块。
快照意味着数据可以保留很长时间。如果您在一年前拍摄快照,则任何出生日期超过一年的块都仍在使用中,无论是11个月前删除的还是今天午餐前删除的。如果旧快照需要这些块中的大多数,则删除六个月的快照可能不会释放太多空间。
只有在没有文件系统、卷或快照使用块时,才会释放该块。
以下示例,先创建一个新的文件系统数据集,并用一些文件填充它:
# zfs create -o mountpoint=/sheep mypool/sheep# cd /sheep# dd if=/dev/random of=randomfile bs=1m count=1# fetch -o zfsbook.html http://www.zfsbook.com/# date > date.txt这为我们提供了一些可以使用的数据。
使用 zfs snapshot 命令创建一个快照。指定数据集的完整路径,加上一个@符号,和快照名称:
xxxxxxxxxx# zfs snapshot mypool/sheep@snap1使用 zfs list -t snapshot 命令查看快照, -r 选项可以指定数据集:
xxxxxxxxxx# zfs list -t snapshot -r mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@snap1 0 - 1.11M -注意,快照的USED列表示使用量为0,表示快照中的每个块仍然由实时数据集使用,因此此快照未使用额外的空间。
现在更改数据集,看看它如何影响快照。以下示例将一个1MB的新crud附加到 randomfile 中,并更新 date.txt 文件:
xxxxxxxxxx# dd if=/dev/random of=randomfile bs=1m count=1 oseek=1# date > date.txt回想一下快照是如何工作的。randomfile 增加了1MB,但这不在旧的快照中。date.txt 文件已被替换,因此快照应该保留在旧文件使用的块上。让我们看看这对快照的空间使用有何影响。
xxxxxxxxxx# zfs list -t snapshot -r mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@snap1 72K - 1.11M -快照现在用了72KB。快照消耗的唯一空间是 date.txt 文件被替换的块。快照不会因较大的 randomfile 文件占用的新空间而变大,因为没有覆盖任何块。
现在,创建第二个快照,看看它使用了多少空间:
xxxxxxxxxx# zfs snapshot mypool/sheep@second# zfs list -t snapshot -r mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@snap1 72K - 1.11M -mypool/sheep@second 0 - 2.11M -REFER 列显示,第一个快照允许您访问1.11 MB的数据,而第二个快照则允许您查看2.11 MB数据。第一个快照使用72 KB的空间,而第二个快照不使用任何空间。第二个快照仍然与活动数据集相同。
但不会太久。让我们通过覆盖随机文件的一部分来更改活动数据集,并查看空间使用情况如何变化。
xxxxxxxxxx# dd if=/dev/random of=randomfile bs=1m count=1 oseek=1# zfs list -t snapshot -r mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@snap1 72K - 1.11M -mypool/sheep@second 1.07M - 2.11M -我们已经覆盖了1兆字节的随机数据文件。第一个快照的空间使用情况没有变化。第二个快照显示,它使用1MB的空间来保留覆盖的数据,外加一些元数据开销。
ZFS允许您创建递归快照,该快照获取您指定的数据集及其所有子数据集的快照。所有快照都具有相同的名称(即@后面指定的名称)。使用 -r 标志递归地为系统创建快照。这里,我们用一个命令来快照引导池。
xxxxxxxxxx# zfs snapshot -r zroot@beforeupgrade我们现在为这个池中的每个数据集都有一个单独的快照,每个快照都标记有 @beforeupgrade :
xxxxxxxxxx# zfs list -t snapshotNAME USED AVAIL REFER MOUNTPOINTzroot@beforeupgrade 0 - 144K -zroot/ROOT@beforeupgrade 0 - 144K -zroot/ROOT/default@beforeupgrade 0 - 1.35G -zroot/usr@beforeupgrade 0 - 454M -zroot/usr/local@beforeupgrade 0 - 1.54G -…我们现在可以肆意滥用此系统,因为我们知道快照中存在一个已知的良好版本。
一旦您习惯了ZFS,就会发现您已经创建了许多数据集,并且每个数据集都有一大堆快照。尝试查找所需的精确快照会变得很麻烦。虽然您可以始终使用 grep(1) ,但ZFS命令行工具具有非常强大的功能,可以查看和管理数据集和快照。组合选项可以让您精确地确定要查看的内容。在第4章中,我们从 zfs list 开始,但现在让我们从头开始。
其中许多选项适用于其他类型的数据集和快照。如果将文件系统堆叠19层,则可能希望限制所看到的内容。然而,对于我们大多数人来说,快照是这些选项真正开始有用的地方。许多功能也可以与 zpool(8) 和池一起使用,尽管池不像数据集那样复杂。
普通 zfs list 显示文件系统和zvol数据集,但没有快照。
xxxxxxxxxx# zfs listNAME USED AVAIL REFER MOUNTPOINTmypool 4.62G 13.7G 96K nonemypool/ROOT 469M 13.7G 96K nonemypool/ROOT/default 469M 13.7G 469M /mypool/avolume 4.13G 17.8G 64K -…可以用名称指定单独的数据集:
xxxxxxxxxx# zfs list mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep 2.11M 13.7G 2.11M /mypool/sheep使用 -r 选项和名称,可以查看池或数据集以及它的所有子集:
xxxxxxxxxx# zfs list -r mypool/varNAME USED AVAIL REFER MOUNTPOINTmypool/var 22.6G 854G 1.70G /varmypool/var/crash 355M 854G 355M /var/crashmypool/var/db 224M 854G 187M /var/db…一旦你有了许多数据集,就需要进一步缩小范围。
要仅查看特定类型的数据集,可以使用 -t 选项指定数据集类型。可以查看文件系统、卷、快照和书签:
xxxxxxxxxx# zfs list -t snapshot -r mypoolNAME USED AVAIL REFER MOUNTPOINTmypool@all 0 - 96K -mypool/ROOT@all 0 - 96K -mypool/ROOT/default@all 84K - 419M -mypool/avolume@all 0 - 64K -...可以通过提供完整的快照名称来检查指定快照:
xxxxxxxxxx# zfs list -t snapshot mypool/sheep@allNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@all 0 - 2.11M -应确保提供完整的名称,包括快照部分。以下示例中,我们告诉 zfs list 只显示快照,然后给它一个文件系统数据集的名称,zfs(8) 非常礼貌地告诉我们在要求什么时要始终如一:
xxxxxxxxxx# zfs list -t snapshot mypool/sheepcannot open 'mypool/sheep': operation not applicable to datasets of this type-r 选项不仅可以用了显示数据集和它地子集,也可以在快照上使用:
xxxxxxxxxx# zfs list -r -t snapshot mypool/secondNAME USED AVAIL REFER MOUNTPOINTmypool/second@all 0 - 96K -mypool/second/baby@all 0 - 96K -使用 -t all 选项,可以显示所有内容:
xxxxxxxxxx# zfs list -r -t all mypool/secondNAME USED AVAIL REFER MOUNTPOINTmypool/second 192K 13.5G 96K legacymypool/second@all 0 - 96K -mypool/second/baby 96K 13.5G 96K legacymypool/second/baby@all 0 - 96K -如果有多层数据集,则可能需要一个部分递归视图。虽然 -r 显示所有子项,但 -d 选项限制了查看的层数。将深度限制为1,将只获得单个数据集的快照:
xxxxxxxxxx# zfs list -d 1 -t snapshot mypool/sheepNAME USED AVAIL REFER MOUNTPOINTmypool/sheep@all 0 - 2.11M -mypool/sheep@snap2 0 - 2.11M -mypool/sheep@moresnap 0 - 2.11M -mypool/sheep@evenmore 0 - 2.11M -将深度限制为2将显示指定的数据集、指定数据集中的快照和数据集的子数据集,但不显示其孙文件系统或子数据集的快照。
zfs list 输出可以使用 -o 选项和列或属性列表来控制zfs列表显示的信息。使用 -o 时, zfs list 仅显示您请求的信息。
查看前面的任何 zfs list 输出,您将看到 NAME 列(可以预见)显示了数据集名称。用 -o 仅显示该列。在这里,我们递归地列出 mypool 中的所有快照,仅显示它们的名称。
xxxxxxxxxx# zfs list -r -t snapshot -o name mypoolNAMEmypool@allmypool/ROOT@allmypool/sheep@snap2mypool/sheep@moresnapmypool/sheep@evenmore...也可以将任何属性显示为列。这里我们列出了每个数据集的一些常见文件系统属性。
xxxxxxxxxx# zfs list -o name,atime,exec,setuidNAME ATIME EXEC SETUIDmypool on on onmypool/sheep on on onzroot off on onzroot/ROOT off on on是的,文件系统属性与快照无关。但它们是该功能的一个很好的例子。
最后,您可以更改 zfs list 显示数据集的顺序。使用 -s 和属性按属性的值排序。使用 -S 和属性按属性对输出进行反向排序。按顺序列出多个属性,以逗号分隔。
zfs list 默认隐藏快照和书签。如果想默认显示快照可以设置池的 listsnapshots 属性为 on 。
xxxxxxxxxx# zpool set listsnapshots=on zroot然而,一旦您使用它运行了一段时间,我们非常有信心您会将其关闭。累积的快照很快就会覆盖所有其他内容。
系统管理员喜欢自动化。自动化的一个恼人之处是,您必须运行命令并解析输出。使输出更人性化通常会使其不太利于自动化。ZFS开发人员都非常熟悉这个问题,并包含了命令行选项来消除大部分问题。
-p 选项告诉 zfs(8) 和 zpool(8) 打印精确的值,而不是人类友好的值。一个池实际上并没有2 TB的可用容量—它只是一个四舍五入的数字。使用 ‑p 打印其所有荣耀的实际值。
-H 选项告诉 zfs(8) 和 zpool(8) 不要打印标题,并用单个tab分隔列,而不是像人类喜欢的那样让它们整齐排列。你是人,不是吗?
结合在一起,这些选项将输出从人类容易理解的内容转换为您可以直接输入脚本的内容。
xxxxxxxxxx# zfs list -t all -pH -r mypoolmypool 2670592 96529122918498304 /mypoolmypool/sheep 2351104 965291229184 2273280 /sheepmypool/sheep@snap1 77824 - 1224704 -mypool/sheep@second 0 - 2273280 -...是的,这是真正的间距。整齐的列是给人类的,傻瓜。
快照的 written 属性是一个特别有用的属性,它可以让人了解快照包含多少新的数据块。
xxxxxxxxxx# zfs list -d 1 -t all -o name,used,refer,written mypool/sheepNAME USED REFER WRITTENmypool/sheep 10.3M 6.11M 2.07Mmypool/sheep@all 0 2.11M 2.11Mmypool/sheep@snap2 0 2.11M 0mypool/sheep@evenmore 0 2.11M 0mypool/sheep@later 2.07M 5.11M 4.07Mmypool/sheep@rewrite 1.07M 5.11M 2.07M注意,快照按创建日期顺序显示。实时数据集首先出现——虽然它可能比任何快照都有更新的数据,但它是在任何快照之前创建的。@all 快照是最旧的,然后是 @snap2 ,以此类推。
第一个快照 @all 允许您访问2.11MB的数据(REFER 列)。此快照还包含2.11个新写入的数据。这是此快照与其之前的快照之间的区别。
快照 @snap2 和 @evenmore 没有新数据。它们与第一个快照没有变化。
在 @evenmore 快照和 @later 快照之间的某个时间,数据增长了。快照 @later 允许您访问5.11 MB的数据。它有4.07 MB的新数据。
@rewrite 快照还允许您访问5.11 MB的数据,但它写入了2.07 MB的新数据。由于您可以访问的数据量与上一个快照相同,因此必须覆盖一些旧数据。
实时文件系统还覆盖了1 MB的数据。该数据现在仅包含在 @rewrite 快照中。
访问快照内容的最方便的方法是通过 snapshot directory ,或 snapdir 。每个ZFS数据集的根都有一个隐藏的 .zfs 目录。该目录有一个快照目录,该目录又为每个快照提供一个目录。
xxxxxxxxxx# ls /mypool/sheep/.zfstotal 1dr-xr-xr-x 2 root wheel 2 Mar 29 00:30 sharesdr-xr-xr-x 2 root wheel 2 Mar 30 16:27 snapshot# ls -l /mypool/sheep/.zfs/snapshottotal 1drwxr-xr-x 2 root wheel 5 Mar 29 00:40 snap1drwxr-xr-x 2 root wheel 5 Mar 29 00:40 second进入该目录,您将发现自己在快照的根目录中。快照中的每个文件都将完全保留为创建快照时的状态,直到文件访问时间。要从快照恢复单个文件,请将它们复制回主文件系统。
默认情况下,.zfs 快照是隐藏的。即使运行 ls -lA ,它也不会显示。这可以防止备份程序、rsync 和类似的软件遍历它。如果希望显示 .zfs 目录,请将数据集的 snapdir 属性设置为 visible 。
xxxxxxxxxx# zfs set snapdir=visible mypool/sheep一旦有人在数据集上运行 cp -R ,递归地将所有快照复制到文件系统上,并破坏所有内容,通过将 snapdir 属性设置为 hidden 再次隐藏它。
可以将快照挂载到系统,就像其文件系统一样:
xxxxxxxxxx# mount -t zfs mypool/sheep@snap1 /mnt手动挂载快照时,无法通过隐藏的 .zfs 目录访问快照。即使挂载的快照也是只读的。
快照防止释放它们使用的块。这意味着只有通过删除引用这些块的所有快照,才能恢复这些空间。
创建新快照,然后删除它:
xxxxxxxxxx# zfs snapshot mypool/sheep@deleteme# zfs destroy mypool/sheep@deleteme那并不难,是吗?
您还可以添加冗长标志(-v,verbose),以获得有关被销毁内容的更多详细信息。虽然在销毁单个快照时,详细模式没有多大帮助,但当您销毁更多的数据集时,或者如果您希望查看命令在不实际运行它的情况下将执行的操作,则它变得更有用。
noop 标志 -n 执行删除操作的“试运行”(dry run)。它描述了如果在不实际删除快照的情况下删除快照将发生的情况。让我们返回到我们拍摄的前几个快照,看看如果删除第一个快照会发生什么。
xxxxxxxxxx# zfs destroy -vn mypool/sheep@snap1would destroy mypool/sheep@snap1would reclaim 72K删除此快照将仅回收72 KB的空间。构成此快照的块仍由活动文件系统和/或第二个快照使用。
我们的第二个快照覆盖了第一个快照中的一些数据。这将更改删除快照的效果。
xxxxxxxxxx# zfs destroy -vn mypool/sheep@secondwould destroy mypool/sheep@secondwould reclaim 1.07M我们将释放用于存储文件覆盖版本的空间。
递归地创建快照可能会创建一大堆快照。幸运的是,您也可以递归销毁快照。
xxxxxxxxxx# zfs destroy -rv mypool@allwill destroy mypool@allwill destroy mypool/second@allwill destroy mypool/second/baby@allwill destroy mypool/lamb@allwill destroy mypool/ROOT@all...will reclaim 84K递归销毁快照是在实际销毁任何数据之前使用 -n 的好时机。不止一次,我们意识到在删除快照后的两秒钟内需要它。
另一个方便的功能是销毁一系列快照。您为同一数据集提供两个快照,ZFS会擦除它们,并在它们之间捕获所有快照。运行 zfs destroy ,但提供“from”快照的全名、百分号和“to”快照的名称。那两个快照和它们之间的所有内容都会被销毁。
–n 标志非常方便,可以确保在实际执行之前完成预期的操作。此外,您还可以了解将获得多少空间。
在这里,我们销毁了两个测试快照。注意,第一个快照由其全名给出,包括数据集: mypool/sheep@myfirstsnapshot 。第二个快照必须是同一数据集的一部分,并且它必须是快照,因此您只需要快照的简要名称:second 。
xxxxxxxxxx# zfs destroy -vn mypool/sheep@snap1%secondwould destroy mypool/sheep@snap1would destroy mypool/sheep@secondwould reclaim 1.14M如果确定,请删除 -vn 并真正销毁快照:
xxxxxxxxxx# zfs destroy mypool/sheep@snap1%second快照不见了。您的用户现在可以自由地告诉您,他们需要这些数据。
关于百分号%:
如果%前面没有指定快照,则销毁掉指定快照以及比它更早创建的快照:
xxxxxxxxxx# zfs destroy mypool/data@%d如果%后面没有指定快照,则销毁掉指定快照以及比它更晚创建的快照:
xxxxxxxxxx# zfs destroy mypool/data@d%如果%前后都没有指定快照,则销毁这个数据集的所有快照:
xxxxxxxxxx# zfs destroy mysql/data@%快照不仅显示了文件系统在过去的某个时间点是如何存在的。您可以将整个数据集还原为其快照状态。要升级吗?首先创建快照。如果它不工作,只需回滚。使用 zfs rollback 命令将文件系统还原为快照。但一旦你回去,你就不能再前进了。
在这里,我们创建了一个具有一系列更改的文件系统,对每个更改进行快照。
xxxxxxxxxx# zfs create -o mountpoint=/delorean mypool/delorean# echo “this is the past” > /delorean/timecapsule.txt# zfs snapshot mypool/delorean@thepast# echo “this is the present” > /delorean/timecapsule.txt# zfs snapshot mypool/delorean@thepresent# echo “I broke the future” > /delorean/timecapsule.txt/delorean/timecapsule.txt 文件中有三组不同的文本,快照捕获了该文本的两个版本,第三个不在快照中。
xxxxxxxxxx# cat /delorean/timecapsule.txt“I broke the future”哦,不,未来是破碎的。让我们回到现在。运行 zfs rollback 并给出要使用的快照的名称。
xxxxxxxxxx# zfs rollback mypool/delorean@thepresent这所需的时间比你想象的要少。请记住,所有数据和元数据都已经在磁盘上。ZFS仅切换它使用的元数据集。一旦回滚完成,活动文件系统将包含所选快照中的所有文件。
xxxxxxxxxx# cat /delorean/timecapsule.txt“this is the present”您对数据集的新更改已不存在,并且不可恢复。
虽然这是一个简单的示例,但您可以对软件升级、数据库迁移或任何其他有风险的操作执行完全相同的操作。曾经需要从脱机备份进行恼人的恢复的操作现在可以在单个命令中处理。
您只能将文件系统回滚到最新的快照。不可能像电影中那样前后冲浪。如果要返回到较早的快照“过去”,则必须销毁所有比目标更新的快照。
xxxxxxxxxx# zfs rollback mypool/delorean@thepastcannot rollback to 'mypool/delorean@thepast': more recent snapshots or bookmarks existuse '-r' to force deletion of the following snapshots and bookmarks:mypool/delorean@thepresent如果使用递归(-r)标志,zfs rollback 命令可以为您销毁所有中间快照。这与创建和销毁快照时使用的多数据集递归类型不同。使用 rollback -r 不会回滚子级。必须分别回滚每个数据集。
xxxxxxxxxx# zfs rollback -r mypool/delorean@thepast# cat /delorean/timecapsule.txt“this is the past”您已经回到了过去,现在可以再次尝试风险和痛苦的升级。恭喜你!
有时,您确实想知道在拍摄快照的时间和现在之间发生了什么变化。如果数据库服务器在今天中午开始崩溃,您可能需要将文件系统的状态与11AM快照进行比较,以便查看是否有任何变化。您可以使用 find(1) 来查找自创建快照以来修改的文件,也可以使用 diff(1) 将快照中的文件与活动文件系统中的文件进行比较。然而,ZFS已经有了这些信息,并通过 zfs diff 提供了这些信息。
要查看快照和活动文件系统之间的差异,请运行 zfs diff 并为其指定快照名称。
xxxxxxxxxx# zfs diff mypool/sheep@laterM /mypool/sheep/randomfile
文件可以有四种状态:
我们在这里的示例显示,文件 /mypool/sheep/randomfile 在拍摄快照后被修改。
您还可以比较两个快照。
xxxxxxxxxx# zfs diff mypool/sheep@later @muchlaterM /mypool/sheep/+ /mypool/sheep/newfile- /mypool/sheep/zfsbook.htmlR /mypool/sheep/date.txt -> /mypool/sheep/olddate.txtM /mypool/sheep/randomfile目录 /mypool/sheep 已被修改。添加了文件 /mypool/sheep/newfile ,而删除了文件 /mypool/sheeep/zfsbook.html 。我们对文件进行了重命名,并且再次修改了文件 randomfile 。
您还可以获得更多的细节。如果添加 -t 标志,则输出包括来自inode的更改的时间戳。 -F 标志包括文件的类型,比如:
B 表示块设备C 表示字符设备/ 表示目录>表示通道|表示命名管道@表示符号链接P表示事件端口=表示套接字F表示普通文件查看 zfs(8) 以获取文件类型的完整列表。
即使仅为特殊事件创建快照,快照也很有用。然而,如果按计划自动创建快照,它们将变得非常有用。计划每隔15分钟创建系统的递归快照非常简单。然而,如果保留所有这些快照,则池将填满。自动快照需要像备份磁带一样进行轮换和丢弃(rotating and discarding)。
计划快照的创建和销毁的困难部分是确定如何使用快照。谁是您的用户?哪些应用程序可能需要快照?我们无法为您回答这些问题。
一种常见的设置是围绕每周、每天、每小时和15分钟快照构建的。您每周拍摄一次快照,并保存两个月。每日快照保留两周。您的每小时快照将保留三天。然后,每隔15分钟拍摄一次快照,并将其保存6小时。
也许您只需要四张15分钟的快照。或者,您必须将每月快照保留一年。适合你的规则取决于许多因素。您的数据有多重要?您可能需要回到多远的地方?您的空间限制如何?您的文件多久更改一次,每天写入多少新数据?您是否有规定某些数据必须保留多长时间的IT审计控制?与团队中的其他人交谈,并制定一个适合您的组织的时间表。
一旦您有了所需的时间表,ZFS工具可以帮助您部署它。
许多脚本和软件包可以为您管理ZFS快照。我们建议使用 ZFS Tools(https://github.com/bdrewery/zfstools),因为它不使用配置文件。它确实需要 cron(8) ,但您不必处理任何类型的 zfstools.conf 。ZFS Tools从ZFS中设置的用户定义属性中获取配置。这意味着新数据集将自动从其父数据集继承其快照配置。当系统有几十个数据集,并且您不断地创建和删除它们时,继承的配置可以节省大量时间。
从软件包中安装ZFS Tools。
xxxxxxxxxx# pkg install zfstoolsZFS Tools有很多脚本和应用程序,本书仅关注 zfs-auto-snapshot 命令。
zfs-auto-snapshot Ruby脚本创建和删除快照。它接受两个参数,快照的名称和要保留的快照的数量。例如,运行 zfs-auto-snapshot frequent4 会创建一个名为 frequent 的递归快照,并保留每个数据集的四个快照。
与 cron(8) 相结合,zfs-auto-snapshot 允许您以所需的时间间隔创建任何您喜欢的快照,然后在它们过时时丢弃它们。
ZFS Tools附带了一个默认的crontab,用于按开发人员希望能够满足大多数人需求的时间表创建快照。它首先设置 $PATH ,以便 zfs-auto-snapshot 可以找到Ruby。然后,它有创建15分钟、每小时、每天、每周和每月快照的条目。让我们看看每一个。
xxxxxxxxxx15,30,45 * * * * root /usr/local/sbin/zfs-auto-snapshot frequent 4zfs-auto-snapshot 在每小时的第15、第30、第45分钟运行一次。在每个数据集上创建一个名为 frequent 的快照。保留最新的4个快照,删除旧的。
xxxxxxxxxx0 * * * * root /usr/local/sbin/zfs-auto-snapshot hourly 24每小时的第0分钟,zfs-auto-snapshot 创建一个名为 hourly 的快照。保留最新的24个快照,删除旧的。
xxxxxxxxxx7 0 * * * root /usr/local/sbin/zfs-auto-snapshot daily 7每天0:07,zfs-auto-snapshot 创建一个名为 daily 的快照。保留最新的7个快照,删除旧的。
xxxxxxxxxx14 0 * * 7 root /usr/local/sbin/zfs-auto-snapshot weekly 4每周第7天的0:14,zfs-auto-snapshot 创建一个名为 weekly 的快照。保留最新的4个快照,删除旧的。
xxxxxxxxxx28 0 1 * * root /usr/local/sbin/zfs-auto-snapshot monthly 12每月的第一天的0:28,zfs-auto-snapshot 创建一个名为 monthly 的快照。保留最新的12个快照,删除旧的。
以上这些crontab条目是为 /etc/crontab 设计的。如果在root的crontab中使用它们,则必须从每个条目中删除“root”关键字。
无论哪种情况,都需要包含PATH变量,以使 zfs-auto-snapshot 能找到Ruby。
调整名字和时间表,以适应实际的环境和个人喜好。Lucas总是将 frequent 快照重命名为15min ,因为单词 frequent (频繁)不明确。但他有点痛苦,所以不管他怎么想。
zfs-auto-snapshot 仅对 com.sun:auto-snapshot 属性为 true 的数据集进行快照。没有此属性或此属性值为 true 以外的数据集不会被快照。在数据集上设置此属性可以让其子数据集继承它。
以下示例设置 mypool 池的根数据集的 com.sun:auto-snapshot 属性:
xxxxxxxxxx# zfs set com.sun:auto-snapshot=true mypool当 zfs-auto-snapshot 运行时,它为 mypool 中的每一个数据集创建快照,并按照 /etc/crontab 中的设置命名这些快照。
有些数据集可能不需要快照。可以将这些数据集和它们的子集的 com.sun:auto-snapshot 属性设置为 false :
xxxxxxxxxx# zfs set com.sun:auto-snapshot=false mypool/usr/ports也可以仅禁用特定类别的快照。不经常更改的数据集可能不需要频繁或每小时快照。zfs-auto-snapshot 检查 com.sun:auto-snapshot 以快照周期命名的自动快照。例如,控制每小时快照的属性名为 com.sun:auto-snapshot:hourly 。将这些属性设置为 false 以禁用这些快照。
xxxxxxxxxx# zfs set com.sun:auto-snapshot:frequent=false mypool/delorean# zfs set com.sun:auto-snapshot:hourly=false mypool/delorean以上示例禁用了 mypool/delorean 和其子集的 frequent 和 hourly 快照,仅进行 daily 、weekly、monthly 快照。也可以重新设置 mypool/delorean 的某个子集进行 frequent 快照。
您还可以决定,虽然您需要 /usr/src 的 frequent 快照,因为您正在处理一些重要的代码,但您不需要保留源树的几个月前的副本:
x# zfs set com.sun:auto-snapshot:monthly=false mypool/usr/srcZFS Tools的ZFS自动快照为您处理所有快照旋转。
自动快照的名称以 zfs-auto-snap 为开头,后面跟句点和时间戳:
xxxxxxxxxx# zfs list -t all -r db/dbNAME USED AVAIL REFER MOUNTPOINTdb/db 587M 13.5G 561M /db/db@zfs-auto-snap_hourly-2015-04-08-16h00 224K - 561M -db/db@zfs-auto-snap_hourly-2015-04-08-17h00 220K - 561M -db/db@zfs-auto-snap_hourly-2015-04-08-18h00 200K - 561M -db/db@zfs-auto-snap_frequent-2015-04-08-18h45 188K - 561M -db/db@zfs-auto-snap_hourly-2015-04-08-19h00 172K - 561M -db/db@zfs-auto-snap_frequent-2015-04-08-19h15 172K - 561M -db/db@zfs-auto-snap_frequent-2015-04-08-19h30 180K - 561M -db/db@zfs-auto-snap_frequent-2015-04-08-19h45 180K - 561M -db/db@zfs-auto-snap_hourly-2015-04-08-20h00 180K - 561M -zfs-auto-snap 使用的快照名称或时间表没有什么神奇之处。Lucas曾经在命令行上运行 zfs-auto-snap hourly 2 ,并销毁了许多每小时的快照(仅保留最后两个hourly 快照)。您可以将hourly快照命名为 monthly ,也可以将yearly快照命名为 daily 。如果你缺少讨厌你的人,你所代表的一切,这是一个很好的方法来解决这个问题。
有时,您希望保留特定的快照,尽管有任何自动保留计划或在深夜拼命清理池。可能是发生了事件,或者这是一些复制的起点。如果您需要保留快照,请暂停它,就像您的银行不希望您花钱时所做的那样。
使用 zfs hold 、标记名和快照名称。标记名是此特定保留的人类可读标签。
xxxxxxxxxx# zfs hold tag dataset@snapshot这将锁定快照并分配标记名。一个快照可以有多个保留,因此可以为不同的目的创建保留。
保留也可以是递归的。要使用公共标记锁定子数据集上同名的所有快照,请使用 -r 。
xxxxxxxxxx# zfs hold -r hostages mypool/test@holdmezfs holds 命令列出快照上的保留,或递归列出快照层次结构上的所有保留。
xxxxxxxxxx# zfs holds -r mypool/test@holdmeNAME TAG TIMESTAMPmypool/test@holdme hostages Fri Apr 3 19:13 2015mypool/test/sub1@holdme hostages Fri Apr 3 19:13 2015mypool/test/sub2@holdme hostages Fri Apr 3 19:13 2015无法销毁具有保留的快照。
xxxxxxxxxx# zfs destroy mypool/test@holdmecannot destroy snapshot mypool/test@holdme: dataset is busy使用 zfs release 命令,给出标记和数据机名称,可以释放保留:
xxxxxxxxxx# zfs release hostages mypool/test@holdme现在可以销毁快照。要是让银行释放你的资金就这么简单了!
然而,释放快照的保持不会释放其子级的任何保持。
# zfs destroy -r mypool/test@holdmecannot destroy snapshot mypool/test/sub1@holdme: dataset is busycannot destroy snapshot mypool/test/sub2@holdme: dataset is busy要递归释放快照及其子级上的所有保持,请使用 -r 标志。
xxxxxxxxxx# zfs release -r hostages mypool/test@holdme# zfs destroy -r mypool/test@holdme现在可以销毁子数据集。
较新版本的ZFS支持书签(bookmarks)。书签类似于快照,只是它们不保留旧数据。书签只是从中创建它的快照的时间戳。书签构建在新的 extensible_dataset 功能标志上。
ZFS需要时间戳来执行增量复制。ZFS可以轻松收集自书签的时间戳以来更改的每个块。这允许增量复制,而不必像以前那样保留旧快照。
书签是与快照相关的数据集类型,因此我们在这里提到它们。他们在《FreeBSD Mastery:Advanced ZFS》中获得全面报道。
克隆是从快照创建的新文件系统。最初,它不使用新空间,与创建它的快照共享其所有块。虽然快照是只读的,但克隆像任何正常的文件系统一样是可写的。
克隆可以被认为是文件系统的“fork”或“branch”(这两个单词都是“分支”的意思)。如果您有包含web应用程序的文件系统,则可以创建快照并克隆该快照。克隆的文件系统可以是应用程序的测试实例,使您能够在不接触生产实例和不消耗额外磁盘空间的情况下应用修补程序和更改。您可以在克隆版本上运行测试,使其与活动版本一起运行。
克隆不会接收在原始数据集中进行的更新。它们基于静态快照。如果希望克隆具有对原始数据集的最新更新,则必须拍摄新快照并创建新克隆。
克隆最初不使用磁盘空间。当克隆与快照分离时,对克隆文件系统所做的任何更改都存储为克隆的一部分,并且它开始消耗空间。对于大型企业资源规划(ERP)应用程序,您可能有一个多TB的数据集,但该数据集的完全可写副本除了您更改的内容外,根本不占用任何空间。
磁盘空间已经很便宜了,但克隆使其更便宜。
使用 zfs clone 命令创建克隆。此命令需要两个参数:源快照和目的地。如果池没有挂载点,则需要在克隆上设置一个以便访问其内容。
xxxxxxxxxx# zfs clone mypool/sheep@evenmore mypool/dolly现在查看数据集:
xxxxxxxxxx# zfs listNAME USED AVAIL REFER MOUNTPOINTmypool 4.74G 13.5G 96K none...mypool/sheep 10.3M 13.5G 6.10M /mypool/sheepmypool/dolly 8K 13.5G 2.11M /mypool/dollymypool/second 192K 13.5G 96K legacymypool/second/baby 96K 13.5G 96K legacy...除了空间使用情况外,数据集 dolly 看起来就是个普通的数据集。REFER 列显示它有2MB的数据,但 USED 下它只占用8KB。它包含的数据来自原始快照。克隆只会为新写入的数据占用空间,无论是新文件还是覆盖旧文件。
克隆看起来与常规数据集相同。在 zfs list 命令的输出结果中,克隆没有特别的地方。
但克隆会在其 origin (原始)属性中记录其源快照:
xxxxxxxxxx# zfs get type,origin mypool/dollyNAME PROPERTY VALUE SOURCEmypool/dolly type filesystem -mypool/dolly origin mypool/sheep@evenmore -因此,从各个方面来看,克隆似乎只是一个常规数据集。origin 属性是表示这是克隆的唯一方法。原点是从中创建此克隆的快照。
要跟踪系统上的所有克隆,请使用 zfs list 并检查 origin 属性。我们正在检查没有以破折号结尾的条目。
xxxxxxxxxx# zfs list -o name,origin | grep -ve '-$'NAME ORIGINmypool/dolly mypool/sheep@evenmore这列出了所有源自快照的所有数据集。
克隆依赖于存储在源快照中的块。克隆的存在会阻止删除源快照。如果尝试删除快照, zfs destroy 会告诉您存在问题。
xxxxxxxxxx# zfs destroy mypool/sheep@evenmorecannot destroy 'mypool/sheep@evenmore': snapshot has dependent clonesuse '-R' to destroy the following datasets:mypool/dolly@zfs-auto-snap_frequent-2015-04-08-16h15mypool/dolly添加 -R 标志,销毁快照会带走所有依赖的克隆。可以像删除任何其他文件系统数据集一样删除克隆本身。
xxxxxxxxxx# zfs destroy mypool/dollycannot destroy 'mypool/dolly': filesystem has childrenuse '-r' to destroy the following datasets:mypool/dolly@zfs-auto-snap_frequent-2015-04-08-16h15哦,等等。克隆从其父级继承了 zfs-auto-snapshot 属性,因此我们的快照自动化会捕获它。如果您不希望克隆被快照,则应该关闭该属性。您可以手动删除克隆的快照,但 zfs-auto-snapshot 会不断创建新的快照。您还可以使用 -r(递归)标志销毁克隆及其所有快照。
xxxxxxxxxx# zfs destroy -rv mypool/dollywill destroy mypool/dolly@zfs-auto-snap_frequent-2015-04-08-16h15will destroy mypool/dolly现在我们可以删除源快照了:
xxxxxxxxxx# zfs destroy -v mypool/sheep@evenmorewill destroy mypool/sheep@evenmorewill reclaim 0克隆很强大,但它会使快照管理复杂化。
现在,您已经完成了对web应用程序的开发版本的测试,您希望将克隆制作为活动版本,并放弃以前的版本。但这导致了问题。无法销毁原始数据集,因为克隆依赖于该数据集中的快照。
为了解决这个问题,您可以“升级”(promote)克隆,告诉ZFS反转原始数据集和克隆之间的父/子关系。克隆成为文件系统。上一个父级将成为克隆。学生成为老师(master)。克隆需要的任何快照都将移动,并成为克隆的一部分。在克隆的原始快照之后创建的快照仍然属于原始父级。
一旦克隆成功地与父数据集交换位置,您就可以删除原始数据集。
ZFS还更改新父级和新克隆使用的空间。数据集不占用额外的空间,但对该空间的计算(accounting)发生了变化。克隆仅按其与原始快照不同的空间量计费。新的父数据集几乎所有内容都要付费,就像新的人类父母一样。
让我们浏览一下如何升级克隆。这里,我们将数据集 mypool/wooly 克隆到一个名为 mypool/bonnie 的数据集,并修改克隆。
xxxxxxxxxx# zfs clone mypool/wooly@later mypool/bonnie# date > /mypool/bonnie/date.txt# dd if=/dev/random of=/mypool/bonnie/randomfile bs=1m count=8 oseek=4看看克隆的磁盘使用情况:
xxxxxxxxxx# zfs list mypool/bonnieNAME USED AVAIL REFER MOUNTPOINTmypool/bonnie 8.07M 13.5G 12.1M /mypool/bonnieUSED 列显示8MB的新数据写入到了克隆。REFER 列显示数据集包含12MB数据——4MB来自源快照,另外8MB是我们新添加的数据。
我们要保留 bonnie 数据集,并删除原始的 wooly 数据集:
xxxxxxxxxx# zfs destroy -rv mypool/woolycannot destroy 'mypool/wooly': filesystem has dependent clonesuse '-R' to destroy the following datasets:mypool/bonnie@zfs-auto-snap_frequent-2015-04-08-16h30mypool/bonnieZFS知道数据集 mypool/bonnie 及其原始快照依赖于 mypool/wooly 数据集。因此,我们使用 zfs promote 命令使 bonnie 成为文件系统,并将旧数据集转换为克隆。
在升级克隆之前,运行 zfs list 并检查所涉及的两个数据集的空间使用情况:
xxxxxxxxxx# zfs list -t all -r mypool/wooly mypool/bonnieNAME USED AVAIL REFER MOUNTPOINTmypool/bonnie 8.07M 13.5G 12.1M /mypool/bonniemypool/wooly 10.3M 13.5G 6.10M /mypool/sheepmypool/wooly@all 0 - 2.11M -mypool/wooly@moresnap 0 - 2.11M -mypool/wooly@later 2.07M - 5.11M -mypool/wooly@rewrite 1.07M - 5.11M -mypool/wooly@muchlater 0 - 6.10M -稍后我们将回到这个列表。现在升级 mypool/bonnie :
xxxxxxxxxx# zfs promote mypool/bonnie升级操作静默运行。再次查看这两个数据集:
xxxxxxxxxx# zfs list -t all -r mypool/wooly mypool/bonnieNAME USED AVAIL REFER MOUNTPOINTmypool/bonnie 14.3M 13.5G 12.1M /mypool/bonniemypool/bonnie@all 0 - 2.11M -mypool/bonnie@moresnap 0 - 2.11M -mypool/bonnie@later 1.07M - 5.11M -mypool/wooly 4.14M 13.5G 4.10M /mypool/sheepmypool/wooly@rewrite 1.07M - 5.11M -mypool/wooly@muchlater 0 - 6.10M -mypool/bonnie 所基于的快照,以及所有比原始快照更早的快照,现在都属于 mypool/bonnie 。在创建 mypool/bonnie 快照后拍摄的 mypool/wooly 的较新快照仍然属于 mypool/wooly 。
现在,可以销毁旧数据集及其所有快照:
xxxxxxxxxx# zfs destroy -rv mypool/woolywill destroy mypool/wooly@muchlaterwill destroy mypool/wooly@rewritewill destroy mypool/wooly请记住,一旦克隆从主文件系统分叉(forked),它就不会从父文件系统获得任何更新。应用程序需要的任何持久数据都应该放在不同的数据集中。它可以是子数据集,就像Jode喜欢的那样。Lucas说持久数据应该放在一个完全不相关的数据集中,这样递归删除就不会触及它。
您可以拍摄数据集的快照。您可以基于这些快照创建克隆。然后,您可以拍摄克隆的快照并创建更多克隆。尽管你尽了最大的努力,你很可能会产生大量相互关联的克隆和快照,这些克隆和快照超过了任何人的心理跟踪能力。ZFS为您提供了一大堆功能和便利,但克隆使可能的全新类型的混乱搅动您的大肠。
-nv 标志对于安全系统管理至关重要。任何时候,只要一想到销毁数据集就开始考虑是否有可能出现在您的脑海中,就用 -nv 进行冗长的试运行。看看 destroy 命令实际上会消除什么。阅读列表。您可能会发现递归 destroy 拉动了一个克隆线程,该线程一直延伸到池中。
三思而后行。始终。
ZFS更改了您使用磁盘空间的方式,但管理它仍然是系统管理员的任务。让我们接下来讨论这个问题。