第十二章:Z文件系统

从计算的角度来看,大多数文件系统都很古老。我们丢弃了5年前的硬件,因为它非常慢,但我们用40年前的文件系统格式化了替换的硬盘。虽然我们改进了这些文件系统并使其更加健壮,但它们仍然使用相同的基本架构。每次文件系统崩溃时,我们都会诅咒并争先恐后地修复它,同时拼命希望得到更好的东西。

ZFS是更好的选择。

这并不是说ZFS使用了革命性的技术。ZFS的所有单独部分都得到了很好的理解。哈希、数据树或索引并不神秘。但ZFS将所有这些易于理解的原则组合成一个有凝聚力、设计良好的整体。它的设计考虑到了未来。今天的哈希算法在15年后还不够用,但ZFS的设计是为了在不失去向后兼容性的情况下将新的算法和技术添加到新版本中。

本章不会涵盖有关ZFS的所有知识。ZFS几乎是一个独立的操作系统,或者可能是一个专用数据库。整本书都是关于使用和管理ZFS的。不过,您将充分了解ZFS的工作原理,以便在服务器上使用它,并了解其最重要的功能。

虽然ZFS希望直接安装在磁盘分区上,但您可以使用其他GEOM提供程序作为ZFS存储。最常见的例子是使用加密磁盘进行安装。FreeBSD在磁盘上放置一个 geli(8) geom,并在该geom上安装ZFS。本章将任何存储提供者称为“磁盘”,即使它可能是文件、加密提供者或其他任何东西。

如果您以前从未使用过ZFS,请在虚拟机上安装一个基于ZFS的FreeBSD系统,然后按照以下步骤操作。安装程序会自动处理先决条件,例如在 loader.conf 中设置 zfs_lod=YES ,在 rc.local 中设置 zfSenable=YES;您只需要关心文件系统。

ZFS代表什么? Z File System。是的,说真的。很久以前,它的意思是Zettabyte File System ,但这个首字母缩略词已经被替换掉了。

ZFS将一系列易于理解的技术融合到卷管理器和文件系统的组合中。它希望处理从文件权限到跟踪哪个存储提供程序上的哪些块获取哪些信息的所有内容。作为系统管理员,您告诉ZFS您拥有哪些硬件以及您希望如何配置,ZFS将从那里获取这些硬件。

ZFS有三个主要组件:数据集、池和虚拟设备。

数据集

dataset ——数据集,被定义为ZFS数据的块(chunk)。最常见的数据集类似于分区文件系统(partitioned filesystem),但ZFS支持其他类型的数据集用于其他用途。快照是一个数据集。ZFS还包括用于虚拟化和SCSI目标、克隆等的块设备,所有这些都是数据集。本书重点介绍文件系统数据集。像UFS这样的传统文件系统有各种小程序来管理文件系统,但也可以使用 zfs(8) 管理所有ZFS数据集。

使用 zfs list 命令查看当前存在的数据集。输出资料看起来像 mount(8)

每一行都以数据集名称开头,从数据集所在的存储池(或 zpool )开始。第一个条目称为zroot➊。此条目表示池的根数据集。数据集树的其余部分悬挂在这个数据集上。

接下来的两列显示了已使用和可用的空间量。池 zroot 已使用4.71GB,有894GB可用。虽然可用空间肯定是正确的,但4.71GB比看起来更复杂。数据集在USED下显示的空间量包括该数据集及其所有子数据集上的所有内容。根数据集的子数据集包括该zpool中的所有其他数据集。

REFER 列是ZFS特有的。此列显示了此特定数据集上可访问的数据量,这不一定与使用的空间量相同。一些ZFS功能(如快照)在它们之间共享数据。该数据集使用了4.71GB的数据,但只引用了88KB。如果没有子数据集,这个数据集上只有88KB的数据。

最后,我们得到了数据集的挂载点。此根数据集没有挂载点;它没有挂载。

看看下一个数据集 zroot/ROOT➋。这是为根目录和相关文件创建的数据集。这似乎是合理的,但如果你看看 REFER列,你会发现它里面也只有88KB的数据,而且没有挂载点。根目录不应该存在吗?

接下来的两行解释了为什么。某种程度上。数据集 zroot/ROOT/2018-11-17➌的挂载点为/,因此它是一个真正的根目录。下一个数据集,zroot/ROOT/default➍,也有一个挂载点/。不,ZFS不允许您在同一挂载点挂载多个数据集。ZFS数据集记录了数据集中的一大堆设置。挂载点就是这些设置之一。

考虑一下这四个数据集。zroot/ROOT数据集是zroot数据集的子数据集。zroot/ROOT/2018-11-17zroot/ROUT/default 数据集是 zroot/ROOT 的子数据集。每个数据集都有其子数据集的空间使用费用。

为什么这么做?当你启动FreeBSD ZFS主机时,你可以很容易地在多个根目录之间进行选择。每个可引导的根目录都称为 boot environment ——引导环境。假设您应用了补丁并重新启动了系统,但新系统无法启动。通过引导到备用引导环境,您可以轻松访问有缺陷的根目录并尝试找出问题。

下一个数据集 zroot/usr➎,是zroot的一个完全不同的子数据集。它有自己的子目录 zroot/usr/home➏。zroot/usr/home 中使用的空间由 zroot/usr 收费,两者都由其父目录收费,但它们的分配不会影响 zroot/ROOT

数据集属性

除了一些会计技巧外,到目前为止,数据集看起来很像分区。但是分区是磁盘的逻辑细分,填充存储设备上非常特定的LBA。分区不知道分区上的数据。更改分区意味着破坏其上的文件系统。

ZFS紧密集成了文件系统和较低的存储层。它可以根据需要在各种文件系统之间动态划分存储空间。在分区控制可用块的数量以限制磁盘使用的情况下,数据集可以使用配额来达到同样的效果。不过,如果没有这些配额,如果一个池有空间,你就可以使用它。

数据集可以使用的空间量是ZFS property ——属性。ZFS支持数十个属性,从控制数据集可以增长多大的配额属性到显示数据集是否已装载的装载属性。

查看和更改数据集属性

使用 zfs set 命令更改属性:

使用 zfs get 命令查看属性。你可以指定特定属性,也可以使用 all 查看所有属性。你可以通过逗号分隔来列出多个属性。如果指定数据集名称,则仅影响该数据集。

在这里,我们有数据集的名称、属性、属性值和称为源的东西。(我们将在第261页的“属性继承”中讨论最后一个。)

我真正的问题是,哪个数据集被挂载为根目录?我可以用/作为挂载点来检查这两个数据集,但当我得到几十个启动环境时,这会让我发疯。通过添加 -r 标志检查数据集及其所有子数据集的属性。

在这三个数据集中,只有 zroot/ROOT/default➊被挂载。这是我们的主动启动环境。

属性继承

许多属性都是可继承的。您将它们设置在父数据集上,它们会向下渗透到子数据集中。继承对于像挂载点这样的属性没有意义,但对于某些更高级的功能来说是正确的。虽然我们将在后面的“compression”中查看 compression (压缩)属性的作用,但我们将在这里将其用作继承的示例。

根数据集zroot的 compression 属性设置为lz4。SOURCElocal 的,这意味着此属性是在此数据集上设置的。现在看看 zroot/ROOT 。压缩属性也是lz4,但 SOURCE 继承自zroot。此数据集从其父级继承了此属性设置。

管理数据集

ZFS使用数据集的方式与传统文件系统使用分区的方式非常相似。使用 zfs(8) 管理数据集。您需要创建、删除和重命名数据集。

创建数据集

使用 zfs create 创建数据集。通过指定池和数据集名称来创建文件系统数据集。在这里,我为我的包创建了一个新的数据集。(请注意,这会破坏引导环境,我们将在本章稍后看到。)

每个数据集都必须有一个父数据集。默认的FreeBSD安装有一个 zroot/usr 数据集,所以我可以创建一个 zrot/usr/local 。我想有一个 /var/db/pkg 的数据集,但是FreeBSD附带了 zroot/var 数据集,但没有 zroot/var/db。我需要创建 zroot/var/db,然后创建 zroot/var/db/pkg

请注意,数据集是可堆叠的,就像UFS一样。如果我的 /usr/local 目录中有文件,并且我在该目录上创建了一个数据集,ZFS将把数据集挂载到该目录上。我将无法访问这些文件。您必须洗牌(shuffle——变换位置)文件以复制(duplicate)现有目录。

销毁和重命名数据集

我创建的新的 zroot/usr/local 数据集?它隐藏了我的 /usr/local 目录的内容。用 zfs destroy 销毁它,然后重试。

/usr/local 的内容重新出现。或者,我可以使用 zfs rename 来重命名该数据集。

不过,我喜欢引导环境,所以我将保持 /usr/local 不变。不过,有时你真的需要一个 /usr/local 数据集。

未挂载的父数据集

作为Postgres用户,我希望为我的Postgres数据提供一个单独的数据集。FreeBSD的Postgres 9.6软件包使用 /var/db/pgsql/data96 。如果没有 /var/db 的数据集,我就无法创建该数据集,而且如果不破坏包的启动环境支持,我也无法创建它。怎么办?

解决方案是通过设置 canmount 数据集属性为 /var/db 创建数据集,但不使用它。此属性控制是否可以装载数据集。FreeBSD为 /var/var 使用了一个未挂载的数据集,正是出于这个原因。新数据集自动将 canmount 设置为 on,因此您通常不必担心。在数据集创建时使用 -o 标志设置属性。

/var/db 的数据集存在,但无法挂载。检查 /var/db 目录的内容,以验证所有内容是否仍然存在。现在,您可以为 /var/db/postgres 甚至 /var/db/pgsql/data96 创建数据集。

您的数据库有一个数据集,/var/db 中的文件仍然是根数据集的一部分。现在初始化新的Postgres数据库并开始!

当您探索ZFS时,您会发现许多情况下,您可能希望在数据集创建时设置属性或使用未装载的父数据集。

将文件移动到新数据集

如果需要为现有目录创建新的数据集,则需要复制文件。我建议您创建一个名称略有不同的新数据集,将文件复制到该数据集,重命名目录,然后重命名数据集。在这里,我想要一个 /usr/local 的数据集,所以我用不同的名称创建了它。

使用 tar(1) 复制文件,就像复制新的UFS分区一样(见第11章)。

完成后,将旧目录移开并重命名数据集。

我的Postgres数据现在存储在自己的数据集中。

ZFS 池

ZFS将其底层存储组织在池中,而不是按磁盘。ZFS存储池或 zpool 是底层存储设备的抽象,允许您将物理介质和其上的用户可见文件系统分开。

使用 zpool(8) 查看和管理主机的ZFS池。在这里,我使用 zpool list 查看我的一个主机的池。

这个主机有三个池:zrootjailscratch 。每个池的条目占一行。

【14版FreeBSD的 zpool list 还有一列 CKPOINT

池详情

通过运行 zpool status,您可以获得有关池或单个池的更多详细信息。如果省略池名称,您将看到所有池的此信息。在这里,我检查了我的 jail 池的状态。

我们从池名开始。该段很像 HEALTH 专栏;它显示池的任何问题。scan 字段显示有关scrubs的信息(请参阅第273页的“池完整性和修复”)。

然后我们有了池配置。该配置显示了池中虚拟设备的布局。当我们创建游泳池时,我们将深入探讨这一点。

池属性

与数据集非常相似,zpools具有控制和显示池设置的属性。有些属性本质上是信息性的,例如表示池有多少可用空间的自由属性。你可以改变别人。

查看池属性

要查看池的所有属性,请使用 zpool get 。添加属性 all 以查看每个属性。您可以添加一个池名称,仅包含该池。

其中一些信息会被拉入 zpool statuszpool list 等命令中。您还可以使用属性名称查询所有池中的单个属性。

与数据集属性不同,大多数池属性是在创建或导入池时设置的。

虚拟设备

virtual device ——虚拟设备(VDEV)是一组存储设备。您可能会将VDEV视为RAID容器:一个大型RAID-5在操作系统中表现为一个巨大的设备,即使系统管理员知道它实际上是一堆较小的磁盘。虚拟设备是ZFS神奇之处。您可以为不同级别的冗余安排池,也可以放弃冗余并最大限度地利用空间。

ZFS的自动纠错发生在VDEV级别。ZFS中的所有内容,从znode(索引节点)到数据块,都会进行校验和以验证完整性。如果你的池有足够的冗余,ZFS会注意到数据已损坏,并从一个好的副本中恢复。如果您的池缺乏冗余,ZFS将通知您数据已损坏,您可以从备份中恢复。

zpool由一个或多个相同的VDEV组成。该池在所有VDEV上条带化数据,没有冗余。VDEV的丢失意味着池的丢失。如果您有一个包含大量磁盘的池,请确保使用冗余的VDEV。

VDEV 类型和冗余

ZFS支持几种不同类型的VDEV,每种VDEV都根据其提供的冗余程度和风格而有所不同。常见的镜像磁盘是VDEV的一种类型,其中每个磁盘都复制另一个磁盘上的内容。没有冗余的磁盘堆是VDEV的另一种类型。ZFS包括三种不同类型的复杂的基于奇偶校验的冗余,称为RAID-Z

在池中使用多个VDEV可以创建类似于高级RAID阵列的系统。RAID-Z2阵列看起来很像RAID-6,但具有两个RAID-Z2 VDEV的ZFS池类似于RAID-60。镜像VDEV的工作方式类似于RAID-1,但池中的多个镜像的行为类似于RAID-10。在这两种情况下,ZFS在VDEV上对数据进行条带化处理,没有冗余。单个VDEV提供冗余。

仔细选择VDEV类型。

Striped VDEVs

由单个磁盘组成的VDEV称为 stripe ——条带,没有冗余。丢失磁盘意味着丢失数据。虽然一个池可以包含多个条带VDEV,但每个磁盘都是自己的VDEV。与RAID-0非常相似,丢失一个磁盘意味着丢失整个池。

Mirror VDEVs

镜像VDEV在每个磁盘上存储所有VDEV数据的完整副本。您可能会丢失VDEV中除一个驱动器外的所有驱动器,但仍然可以访问您的数据。镜像可以包含任意数量的磁盘。

ZFS可以同时从所有镜像磁盘读取数据,因此读取数据很快。但是,当您写入数据时,ZFS必须同时将该数据写入所有磁盘。在最慢的磁盘完成之前,写入不会完成。写表现不佳。

RAID-Z

RAID-Z在所有磁盘上传播数据和奇偶校验信息,就像传统的RAID一样。如果RAID-Z中的磁盘死亡或开始提供损坏的数据,RAID-Z将使用奇偶校验信息重新计算丢失的数据。RAID-Z VDEV必须至少包含三个磁盘,并且可以承受任何单个磁盘的丢失。RAID-Z有时也被称为RAID-Z1。

你不能在RAID-Z中添加或删除磁盘。如果你创建了一个五磁盘RAID-Z,它将永远保持五磁盘RAID-Z。不要以为可以在RAID-Z中添加额外的磁盘以获得更多存储空间。你不能。

如果您使用的磁盘超过2TB,那么在修复第一个驱动器时,第二个驱动器发生故障的可能性非常大。对于大型磁盘,您可能应该考虑RAID-Z2。

RAID-Z2

RAID-Z2在VDEV中的每个磁盘上对奇偶校验和数据进行条带化,与RAID-Z1非常相似,但奇偶校验信息量增加了一倍。这意味着RAID-Z2最多可以承受两个磁盘的损失。您无法在RAID-Z2中添加或删除磁盘。它比RAID-Z稍慢。

RAID-Z2必须有四个或更多磁盘。

RAID-Z3

三重奇偶校验(triple parity)适用于最重要的数据或那些拥有大量磁盘而没有时间四处闲逛的系统管理员。RAID-Z3中最多可以丢失三个磁盘,而不会丢失数据。与任何其他RAID-Z一样,您无法在RAID-Z3中添加或删除磁盘。

RAID-Z3必须有五个或更多磁盘。

Log 和 Cache VDEVs

池可以通过专用VDEV提高性能。只有在性能问题需要时才调整或实施这些措施;不要主动添加它们。大多数人不需要它们,所以我不会详细介绍,但你应该知道它们的存在,以防你倒霉。

Separate Intent Log —— 独立意图日志(SLOG或ZIL)是ZFS的文件系统日志。挂起的写入被转储到SLOG,然后在主池中更正确地排列。每个池都为SLOG专用一块磁盘空间,但您也可以为SLOG使用单独的设备。你需要更快的写作速度吗?安装一个非常快的驱动器,并将其专用于慢速运行。池将把所有初始写入转储到快速磁盘设备,然后在时间允许的情况下将这些写入迁移到较慢的介质。专用的快速SLOG也将平滑突发I/O。

Level 2 Adaptive Replacement Cache —— 2级自适应替换缓存(L2ARC)类似于SLOG,但用于读取。ZFS将最近访问和最频繁访问的数据保存在内存中。通过添加一个真正快速的设备作为L2ARC,您可以扩展ZFS可以从缓存提供的数据量,而不是从慢速磁盘调用。L2ARC比内存慢,但比慢速磁盘快。

RAID-Z 和池

您可以将VDEV添加到池中。您无法向RAID-Z VDEV添加磁盘。在创建池之前,请考虑您的存储需求和硬件。

假设你有一台可以容纳20个硬盘的服务器,但你只有12个硬盘。您从这12个驱动器中创建了一个RAID-Z2 VDEV,以为以后需要时会向池中添加更多驱动器。您甚至还没有完成服务器的安装,而且已经失败了。

您可以将多个相同的VDEV添加到池中。如果您创建了一个包含12个磁盘的VDEV的池,而主机只能容纳另外8个磁盘,则无法创建第二个相同的VDEV。12个磁盘的RAID-Z2与8个磁盘的RAID-Z2并不完全相同。您可以强制ZFS接受不同的VDEV,但性能会受到影响。将VDEV添加到池中是不可逆的

提前计划。看看你的物理装备。决定如何扩展存储空间。这个20驱动器的服务器可以使用两个10磁盘RAID-Z2 VDEV,或者一个12磁盘池和一个单独的8磁盘池。不要破坏(sabotage)自己。

一旦你知道你想使用什么样的VDEV,你就可以创建一个池。

管理池

既然您已经了解了不同的VDEV类型,并且已经沉迷于规划您的存储,那么让我们创建一些不同类型的zpools。首先设置磁盘块大小。

ZFS和磁盘块大小

第10章介绍了现代磁盘如何具有两种不同的扇区大小,512字节和4KB。虽然文件系统可以安全地假设磁盘有4KB的扇区,但如果您的文件系统假设磁盘有512字节的扇区,而磁盘确实有4KB扇区,您的性能将大幅下降。当然,ZFS假设磁盘有512字节的扇区。如果你的磁盘真的有512字节的扇区,那就很好了。不过,如果您不确定物理扇区的大小,请谨慎行事,并告诉ZFS使用4KB扇区。使用 ashift 属性控制ZFS的磁盘扇区假设。9的ashift告诉ZFS使用512字节扇区,而12的ashiff表示4KB扇区。使用 sysctl vfs.zfs.min_auto_ashift 控制 ashift。

通过在 /etc/sysctl.conf 中设置它来使其永久化。

在创建池之前,您 必须 设置一个快捷键。在创建池后设置它没有效果。

如果您不确定磁盘的扇区大小,请使用12。这就是FreeBSD安装程序所做的。您将损失少量性能,但在4KB磁盘上使用9会耗尽系统性能。

现在创建您的池。

创建和查看池

使用 zpool create 命令创建一个池:

命令执行后如果没有提示,就标识成功了。

下面,我创建了一个名为 db 的池,使用镜像VDEV和两个GPT-labeled分区:

我们分配的结构反映在池状态中:

池数据库包含一个名为mirror-0➊的VDEV。它包括两个带有GPT标签的分区, /dev/GPT/zfs3➋和 /dev/GPT/zfs➌。所有这些分区都是在线的。

如果不包含VDEV名称,zpool(8) 将创建一个没有冗余的条带化池。在这里,我创建了一个名为 scratch 的条带池:

池状态显示每个VDEV,以底层磁盘命名。

创建任何类型的RAID-Z看起来都很像创建镜像。只需使用正确的VDEV类型。

池状态与镜像非常相似,但VDEV中有更多磁盘。

Multi-VDEV 池

当你创建一个池时,关键字 mirrorraidzraidz2raidz3 都会告诉 zpool(8)创建一个新的VDEV。在其中一个关键字后列出的任何磁盘都将用于创建新的VDEV。要创建一个包含多个VDEV的池,您可以这样做:

下面,我创建了一个包含两个RAID-Z VDEV的池,每个都有三个磁盘:

这个新池上的zpool状态看起来会有点不同:

此池包含一个名为 raidz1-0 ➊的VDEV,其中有三个磁盘。还有一个名为了 raidz1-1 ➋的第二个VDEV,里面有三个硬盘。很明显,这些是相同的池。数据在两个VDEV之间进行条带化。

销毁池

要销毁池,请使用 zpool destroy 和池名称。

请注意,zpool在销毁池之前不会询问您是否真的确定。确定你想销毁池是你的问题,而不是 zpool(8)的问题。

Errors 和 -f

如果你输入了一个没有意义的命令,zpool(8)会抱怨。

当你阅读错误消息时,你看到的第一件事是“使用 -f 覆盖此错误”。许多系统管理员将其解读为“ -f 使此问题消失”。然而,ZFS真正想说的是“你的命令行是一个可怕的错误。添加 -f 会做一些无法修复的事情,对系统稳定性有害,只要这个系统还存在,你就会后悔。”

大多数 zfs(8)zpool(8) 错误消息都是有意义的,但你必须仔细阅读。如果您不理解该消息,请参考第1章中的故障排除说明。通常,重新检查你键入的内容会暴露出问题。

在这个例子中,我要求 zpool(8)创建一个池,其中RAID-Z VDEV包含三个磁盘,第二个RAID-Z VDCV仅包含两个磁盘。我搞砸了这个命令行。添加 -f 并继续将我的数据库安装到新的格式错误的数据库池中,只会确保我必须重新创建此池并在以后重新安装数据库。如果你发现自己处于这种情况,请调查 zfs sendzfs recv

Copy-On-Write

在普通文件系统和ZFS中,文件都以块的形式存在于磁盘上。当您在传统文件系统中编辑文件时,文件系统会拾取块,对其进行修改,并将其放回磁盘上的同一位置。写入过程中出现的系统问题可能会导致写入时间缩短(shorn write):文件50%为旧版本,50%为新版本,可能100%不可用。

ZFS从不覆盖文件中的现有块。当文件发生更改时,ZFS会识别必须更改的块,并将其写入新的磁盘空间块。旧版本保持不变。这被称为写时复制(copy-on-write —— COW)。使用写时复制,短写可能会丢失对文件的最新更改,但文件的先前版本将保持不变。

永远不会损坏文件是写时复制的一大好处,但COW开辟了其他可能性。元数据块也是写时复制的,一直到形成ZFS池数据根的 uberbocks 。ZFS通过跟踪包含文件旧版本的块来创建快照。虽然这听起来很简单,但细节会让你误入歧途。

快照

快照是数据集在特定时刻存在的副本。快照是只读的,永远不会更改。您可以访问快照的内容,以访问旧版本的文件,甚至是已删除的文件。虽然快照是只读的,但您可以将数据集回滚到快照。在升级系统之前拍摄快照,如果升级出现严重错误,您可以回退到快照。ZFS使用快照来提供许多功能,例如引导环境(请参阅第276页的“引导环境”)。最重要的是,根据您的数据,快照只能占用很小的空间。

每个数据集都有一堆元数据,都是从顶级块构建成树的。创建快照时,ZFS会复制该顶级块。其中一个元数据块与数据集一起使用,另一个与快照一起使用。数据集和快照共享数据集中的数据块。

删除、修改或覆盖实时数据集上的文件意味着为新数据分配新块,并断开包含旧数据的块。然而,快照需要一些旧数据块。在丢弃旧块之前,ZFS会检查快照是否仍需要它。如果快照需要块,但数据集不再需要,ZFS将保留该块。

因此,快照只是快照拍摄时使用的数据集块的列表。创建快照告诉ZFS保留这些块,即使数据集不再需要这些块。

创建快照

使用 zfs snapshot 命令创建快照。按完整路径指定数据集,然后添加@和快照名称。我习惯用创建快照的日期和时间来命名我的快照,原因将在本章结束时变得清晰。

我即将对用户主目录进行维护,删除旧内容以释放空间。我很确定有人会抱怨我删除了他们的文件,所以我想在清理之前创建一个快照。

我没有得到任何反馈。发生什么事了吗?使用 zfs list-t snapshot 参数查看所有快照:

快照已存在。USED 列显示它使用零磁盘空间➊:它与它来自的数据集完全相同。由于快照是只读的,AVAIL 显示的可用空间➋与此无关。REFER 列显示此快照占用4.68GB的磁盘空间➌。如果您检查,您将看到这是 zroot/usr/home 的大小。最后,MOUNTPOINT 列显示此快照未被挂载➍。

这是一个活动系统,其他人已登录。我等一下,再次检查我的快照。

快照现在使用96KB➊。用户更改了数据集上的某些内容,快照将获得维持差异所需的空间。

现在我继续横冲直撞,把我认为是垃圾的文件扔掉。

此快照现在使用1.62GB的空间。这些是我删除的文件,但在快照中仍然可用。我会保留这张快照一段时间,让用户有机会抱怨。

访问快照

每个ZFS数据集的根目录中都有一个隐藏的 .zfs 目录。它不会出现在 ls(1)中;你必须知道它的存在。该目录有一个快照目录,其中包含一个以每个快照命名的目录。快照的内容位于该目录中。

对于我们的快照 zroot/usr/home@2018-07-21-13:09:00 ,我们会去 /usr/home/.zfs/snapshot/2018-07-21-13:09:09:00 。虽然 .zfs 目录没有出现在 ls(1)中,但一旦你进入其中,ls(1) 就会正常工作。该目录包含我创建快照时存在的每个文件,即使我在创建快照后删除或更改了该文件。

从快照中恢复文件只需要将文件从快照复制到读写位置。

销毁快照

快照是一个数据集,就像文件系统风格的数据集一样。用 zfs destroy 删除它。

快照使用的空间现在可用于更多垃圾文件。

压缩

快照并不是ZFS节省空间的唯一方式。ZFS使用动态压缩,透明地检查每个文件的内容,并在可能的情况下压缩其大小。使用ZFS,您的程序不需要压缩其日志文件:文件系统将实时为您完成此操作。虽然FreeBSD默认在安装时启用压缩,但如果你了解它的工作原理,你会更有效地使用它。

压缩会改变系统性能,但可能不会以你想象的方式。在数据进出磁盘时,您需要CPU时间来压缩和解压缩数据。然而,大多数磁盘请求都比平时小。你本质上是用处理器时间换取磁盘I/O。我管理的每台服务器,无论是裸机还是虚拟服务器,都有比磁盘I/O多得多的处理器容量,所以这是我乐意做的交易。最终的结果是,使用ZFS压缩通常会提高性能。

压缩在不同的数据集上的工作方式不同。二进制文件已经被非常紧密地压缩了;压缩 /usr/bin 不会节省太多空间。不过,压缩 /var/log 通常会导致文件大小减少六到七倍。检查属性 compresstratio ,看看压缩如何有效地缩小数据。我的主机写入日志的频率远远高于写入二进制文件的频率。对于最常见的任务,我会欣然接受绩效提高六倍。

ZFS支持多种压缩算法,但默认为 lz4。lz4算法的特殊之处在于能够快速识别不可压缩的文件。当你将二进制文件写入磁盘时,lz4会看着它说:“不,我帮不了你”,然后立即停止尝试。这消除了毫无意义的CPU负载。然而,它有效地压缩了可以压缩的文件。

池完整性和修复

ZFS池中的每条数据都有一个相关的加密哈希存储在其元数据中,以验证完整性。每次访问一段数据时,ZFS都会重新计算该数据中每个块的哈希值。当ZFS在具有冗余的池中发现损坏的数据时,它会透明地纠正这些数据并继续进行。如果ZFS在没有冗余的池中发现损坏的数据,它会发出警告并拒绝提供数据。如果您的池发现了任何数据错误,它们将显示在 zpool status 中。

完整性验证

除了即时验证之外,ZFS还可以显式地遍历整个文件系统树,并验证池中的每个数据块。这被称为 scrub 。与UFS的 fsck(8)不同,scrub发生在池在线和使用时。如果您之前运行过scrub,这也会显示在池状态中。

要擦洗游泳池,请运行 zpool scrub 并给出池名称:

您可以使用 zpool status 查看擦洗的进度。

擦洗池会降低其性能。如果您的系统已经达到极限,请仅在非工作时间清洗池。您可以使用 -s 选项取消scrub。

一旦负载下降,再运行一次擦洗。

维修池

磁盘故障。这就是他们的目的。冗余的要点是,您可以用工作磁盘替换故障或损坏的磁盘,并恢复冗余。

镜像和RAID-Z虚拟设备是专门为重建磁盘故障时丢失的数据而设计的。在这方面,它们与RAID非常相似。如果ZFS镜像中的一个磁盘损坏,您可以更换损坏的磁盘,ZFS会将幸存的镜像复制到新磁盘上。如果RAID-Z VDEV中的磁盘发生故障,您可以更换损坏的驱动器,ZFS将根据奇偶校验数据重建该磁盘上的数据。

在ZFS中,这种重建称为 resilvering 。与其他ZFS完整性操作一样,resilvering仅在实时文件系统上进行。resilvering并不像从奇偶校验重建RAID磁盘,因为ZFS利用其对文件系统的了解来优化替换设备的重新填充。更换故障设备时,自动开始resilvering。ZFSresilvers的优先级较低,因此不会干扰正常操作。

Pool 状态

zpool status 命令在 STATE 字段中显示底层存储硬件的运行状况。我们已经看到了几个健康池的例子,所以让我们来看看不健康的池。

池状态为 DEGRADED ➊。如果你进一步查看输出,你会看到更多 DEGRADED 条目和 UNAVAIL➍。这到底是什么意思?

池中的错误向上渗透。池状态是池整体健康状况的摘要。整个池显示为 DEGRADED,因为池的虚拟设备 mirror-0➋已降级。此错误来自底层磁盘处于UNAVAIL状态。我们获取此磁盘的ZFS GUID➌和用于创建池的标签➎。

当底层设备发生错误时,ZFS池会显示错误。当池的状态不是 ONLINE 时,请仔细查看VDEV和磁盘列表,直到找到真正的问题。

池、VDEV和磁盘可以有六种状态:

我们丢失的磁盘处于UNAVAIL状态。无论出于什么原因,ZFS都无法访问 /dev/gpt/zfs3 ,但磁盘镜像仍在提供数据,因为它有一个工作磁盘。在这里,你可以四处寻找磁盘的位置。如何管理ZFS取决于你发现了什么。

重新连接和拆卸驱动器

不可用的驱动器可能不会死机。它们可能会断开连接。如果您晃动驱动器托盘,突然绿灯亮起,则磁盘正常,但连接有故障。您应该解决这个硬件问题,是的,但与此同时,您可以重新激活驱动器。您还可以重新激活故意删除的驱动器。使用 zpool online 命令,并将池名称和丢失磁盘的GUID作为参数。如果我的示例池中的磁盘只是断开连接,我可以这样重新激活它:

ZFS使驱动器复位并恢复正常功能。

如果你想删除一个驱动器,你可以告诉ZFS让它离线,让zpool离线。将池和磁盘名称作为参数。

将磁盘脱机、物理移动、重新联机,并允许池重新调整大小,这将使您能够在不停机的情况下将大型存储阵列从一个SAS机架迁移到另一个机架。

更换驱动器

如果驱动器不仅松动,而且完全损坏,您需要更换新的驱动器。ZFS允许您以多种方式更换驱动器,但最常见的是使用 zpool replace 。使用池名称、失败的提供程序和新提供程序作为参数。在这里,我将数据库池的 /dev/gpt/zfs3 磁盘替换为 /dev/gpt/zfs6

池将自行恢复并恢复正常运行。

在大型存储阵列中,您还可以使用连续的 zpool replace 操作来清空磁盘架。仅当您的组织的操作要求不允许您脱机和联机磁盘时,才执行此操作。

启动环境

ZFS帮助我们应对系统管理员做的最危险的事情之一。不,不是我们的饮食习惯。不,不是缺乏锻炼。我说的是系统升级。当升级进展顺利时,每个人都很高兴。当升级效果不佳时,它可能会毁了你的一天、周末或工作。当关键任务软件在共享库的新版本上卡住时,没有人喜欢从备份中恢复。

通过 boot environments 的魔力,ZFS利用快照让您只需重新启动即可从系统升级中退出。引导环境是根数据集的克隆。它包括内核、基础系统用户区、附加包和核心系统数据库。在运行升级之前,请创建引导环境。如果升级顺利,你就没事了。不过,如果升级不顺利,您可以重新启动到引导环境。这将在您调查升级失败的原因以及如何解决这些问题时恢复服务。

当主机需要单独的启动池时,启动环境不起作用。安装程序为您处理启动池。当结合UEFI和GELI时,或者在MBR分区磁盘上使用ZFS时,它们会出现。

使用引导环境需要引导环境管理器。我推荐 beadm(8),可作为软件包提供。

您现在可以使用引导环境了。

查看引导环境

每个引导环境都是 zroot/ROOT 下的数据集。您刚刚安装了beadm的系统应该只有一个引导环境。使用 beadm list 查看所有内容。

此主机有一个名为 default➊的启动环境,在数据集 zroot/ROOT/default 之后。

Active列➋显示此启动环境是否正在使用中。N 表示环境正在使用中。R 表示重新启动后此环境将处于活动状态。当默认环境运行时,它们会一起出现。

Mountpoint列➌显示了此引导环境的装载点的位置。除非正在使用,否则大多数引导环境不会被挂载,但您可以使用 beadm(8)来挂载未使用的引导环境。

Space列➍显示此引导环境使用的磁盘空间量。它基于快照构建,因此数据集中的数据可能比这个数量更多。

Created列➎显示创建此引导环境的日期。在这种情况下,它是机器安装的日期。

在更改系统之前,请创建一个新的启动环境。

创建和访问引导环境

每个启动环境都需要一个名称。我根据当前操作系统版本和补丁级别或日期推荐名称。像“beforeupgrade”和“dangitall”这样的名字,虽然在当下有意义,但以后只会让你感到困惑。

使用 beadm create 创建新的引导环境。在这里,我检查了当前的FreeBSD版本,并使用它来创建引导环境名称:

我现在有两个完全相同的启动环境:

您可能会注意到,新的启动环境已经占用了236KB。这是一个实时系统。在创建引导环境和列出这些环境之间,文件系统或其元数据发生了变化。

Active列显示我们当前正在使用默认启动环境,我们将在下次启动时使用该环境。如果我更改已安装的软件包或升级基本系统,这些更改将影响默认环境。

每个引导环境都可以在 zroot/ROOT 下作为快照使用。如果您想以读写方式访问引导环境,请使用 beadm mount/tmp 下临时挂载引导环境。使用 beadm umount卸载这些环境。

激活启动环境

假设你升级了你的软件包,系统崩溃了。通过激活引导环境并重新启动,退回到早期的操作系统安装。使用beadm activate 激活开机环境。

默认启动环境的Active标志设置为 N,这意味着它现在正在运行。11.0-p11环境具有 R 标志,因此重新启动后它将处于活动状态。

重新启动系统,突然间你又回到了之前的操作系统安装,没有那些破坏系统稳定的更改。这比从备份中恢复要简单得多。

删除启动环境

经过几次升级后,你会发现你永远不会回到一些现有的启动环境。一旦我将此主机升级到12.2-RELEASE-p29,我可能永远不会再重新启动到11.0-p11。删除过时的启动环境,并使用 beadm destroy 释放其磁盘空间。

出现提示时回答 y,beadm将删除引导环境。

启动时的启动环境

所以,你真的彻底改变了你的操作系统。忘记进入多用户模式吧,你甚至不能在不产生大量奇怪错误消息的情况下进入单用户模式。您可以在加载程序提示符处选择引导环境。这需要控制台访问,但任何其他自救方法也是如此。

引导加载程序菜单包括一个选择引导环境的选项。选择那个选项。您将获得一个新菜单,按名称列出主机上的每个启动环境。选择新的启动环境,然后按ENTER键。系统将引导到该环境中,让您有机会弄清楚为什么一切都出错了。

引导环境和应用程序

升级失败是不够的。它可能会带走您的应用程序数据。

大多数应用程序将数据存储在根数据集中的某个位置。MySQL使用 /var/db/mysql,而Apache使用 /usr/local/www。这意味着,回退到较早的启动环境可以随环境还原应用程序数据。根据您的应用程序,您可能希望或不希望进行这种恢复。

如果应用程序使用不应包含在引导环境中的数据,则需要为该数据创建一个新的数据集。我在本章前面第262页的“未装载的父数据集”中提供了一个示例。考虑您的应用程序的需求,并酌情分离您的数据。

虽然ZFS有更多的功能,但这涵盖了每个系统管理员必须知道的主题。你们中的许多人会发现克隆、委派或复制很有用。你可能会发现Allan Jude和你的书《FreeBSD精通:ZFS》(倾斜风车出版社,2015年)和《FreeBSD掌握:高级ZFS》。您还可以在互联网上找到许多资源,记录所有这些主题。

现在让我们考虑一下FreeBSD管理员认为有用的其他文件系统。