ZFS系列(六)存储池属性、最佳实践和注意事项

在前一篇文章讲完数据清理和数据重建之后,
本文是ZFS存储池管理系列的最后一篇文章。在此之后,我们继续讨论一些关于ZFS的理论主题,这些主题将为ZFS数据集奠定基础。本文我们将讨论:

  • ZFS存储池的属性
  • ZFS存储池的最佳实践
  • 构建存储池之前必须了解的注意事项

动机

对于ext4和GNU/Linux中的许多文件系统,我们有办法来调优文件系统中的各种参数。比如设置标签、默认挂载选项和其他可调参数。对于ZFS,它也没有什么不同,事实上,它要详细得多。这些属性允许我们修改所有类型的变量,包括存储池的变量和存储池中包含的数据集的变量。因此,我们可以根据自己的喜好或需要“调优”文件系统。然而,并不是每个属性都是可调的。有些属性是只读的。但是,我们将定义每个属性是什么以及它们如何影响存储池。注意,当前我们只查看zpool属性,当我们访问数据集子主题时,我们将访问ZFS数据集属性。

Zpool 属性

  • allocated :所有ZFS数据集已提交到存储池中的数据量。该设置是只读的。
  • altroot: 标识一个备用的根目录。如果已设置,则该目录将可以被附加到池中的任何挂载点上。当检查未知存储池时(如果挂载点不受信任),或者在替代引导环境中(其中典型路径无效),可以使用此属性。设置altroot默认使用”cachefile=none”,当然可以使用显式设置覆盖该项配置。
  • ashift: 只能在创建池时设置。存储池扇区大小为2的指数。I/O操作将按指定的大小边界对齐。默认值是”9”,即2^9 = 512,这是操作系统工具用于读写数据的标准扇区大小。对于具有4个KiB边界的高级格式硬盘(指扇区大小为4KB的4K盘),该值应设置为“asshift =12”,即2^12 = 4096。
  • autoexpand: 在更换池中的第一个驱动器之前必须设置。控制存储池自动扩容的设置。默认设置是“关闭”。当池中的所有驱动器都被更大的磁盘替换后,存储池将自动增长到新的大小。该设置是一个布尔值,值为“on”或“off”。
  • autoreplace: 控制存储池中“spare”VDEV的自动设备替换。默认设置为“off”。因此,此时必须使用“zpool replace”命令手动启动设备替换。这个设置是一个布尔值,值为“on”或“off”。
  • bootfs: 在存储池中定义可引导ZFS数据集的只读设置。这通常由安装程序设置。
  • cachefile: 控制存储池缓存配置的位置。当在系统上导入zpool时,ZFS可以使用磁盘上的元数据检测到硬盘的分布情况。但是,在一些集群环境中,不会自动导入存储池,这时可能需要将缓存文件存储在不同的位置。可以将缓存配置设置为任何字符串,但对于大多数ZFS的安装来说,默认位置为“/etc/zfs/zpool.cache”应该足够了。
  • capacity: 只读值,标识已使用池空间的百分比。
  • comment: 由不超过32个可打印ASCII字符组成的文本字符串,即使存储池出现故障,它也可用。管理员可以使用此设置提供有关池的附加信息。
  • dedupditto: 设置块重复数据删除阈值,如果重复数据删除块的引用计数超过该阈值,则自动存储该块的重复副本。默认值为0。可以是任何正数。
  • dedupratio: 为存储池指定的重复数据删除率(即重删率,只读)
  • delegation: 控制是否可以授予非特权用户访问指定的数据集。该设置是一个布尔值,默认值为“on”,可以是“on”或“off”。
  • expandsize: 存储池或设备中可用于增加存储池总容量的未初始化空间的数量。该空间是动态扩展LUN时产生的。
  • failmode: 在发生灾难性故障时控制系统行为。这种情况通常是由于与底层存储设备的连接丢失或池内所有设备故障造成的。该事件的行为如下所示:
    (1)wait: 阻塞所有I/O访问,直到设备连接恢复和错误清除。这是默认行为。
    (2)continue: 返回任何新的写I/O请求的EIO,但允许读任何剩余的健康设备。任何尚未提交到磁盘的写请求都将被阻塞。
    (3)panic: 将消息打印到控制台并crash dump。
  • free: 只读值,用于标识池中未分配的块的数量。
  • guid: 标识存储池的唯一标识符的只读属性。类似于ext4文件系统的UUID字符串。
  • health: 只读属性,标识池的当前运行状况为“在线”、“降级”、“故障”、“离线”、“已删除”或“不可用”(ONLINE, DEGRADED, FAULTED, OFFLINE, REMOVED, or UNAVAIL)。
  • listsnapshots: 控制在使用“zfs list”命令时是否显示与此存储池关联的快照信息。如果禁用该属性,则可以使用“zfs list -t snapshot”命令显示快照信息。默认值为“off”。布尔值,可以是“off”或“on”。
  • readonly: 布尔值,可以是“off”或“on”。默认值为“off”。控制将存储池设置为只读模式,以防止写或数据损坏。
  • size: 只读属性,用于标识存储池的总大小。
  • version: 可写设置,用于标识存储池的当前版本。可以是从1到“zpool upgrade -v”命令输出的任意值。当需要向后兼容特定版本时,可以使用此属性。

获取和设置属性

有几种方法可以获取池的属性——可以一次获取所有属性,也可以一次获取一个属性,或者多个属性(以逗号分隔)。例如,假设我只想获取池的健康状况。可以用如下命令:

1
2
3
# zpool get health tank
NAME PROPERTY VALUE SOURCE
tank health ONLINE -

如果我想要获得多个设置,比如系统的健康状况、有多少空闲以及分配了多少,可以用如下命令:
1
2
3
4
5
# zpool get health,free,allocated tank
NAME PROPERTY VALUE SOURCE
tank health ONLINE -
tank free 176G -
tank allocated 32.2G -

当然,如果我想获得所有可用的设置,可以用如下命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# zpool get all tank
NAME PROPERTY VALUE SOURCE
tank size 208G -
tank capacity 15% -
tank altroot - default
tank health ONLINE -
tank guid 1695112377970346970 default
tank version 28 default
tank bootfs - default
tank delegation on default
tank autoreplace off default
tank cachefile - default
tank failmode wait default
tank listsnapshots off default
tank autoexpand off default
tank dedupditto 0 default
tank dedupratio 1.00x -
tank free 176G -
tank allocated 32.2G -
tank readonly off -
tank ashift 0 default
tank comment - default
tank expandsize 0 -

设置属性也很简单。然而,有一个问题。对于需要字符串参数的属性,没有办法将其恢复为默认值,至少据我所知没有。对于其余的属性,如果您试图将属性设置为无效参数,则会在屏幕上显示一个错误,让您知道哪些是可用的,但它不会通知您哪些是默认的。不过,您可以查看“SOURCE”列。如果该列中的值是”default”,那么它就是default。如果它是“local”,那么它是用户定义的。
假设我想要改变“comment”属性,我是这样做的:
1
2
3
4
# zpool set comment="Contact admins@example.com" tank
# zpool get comment tank
NAME PROPERTY VALUE SOURCE
tank comment Contact admins@example.com local

如您所见,SOURCE对于“comment”属性是“本地的”。因此,它是用户定义的。如上所述,我不知道如何在设置完字符串属性后将其恢复为默认值。此外,可以在创建池时使用“-o”参数设置任何可修改的属性,如下所示:
1
zpool create -o ashift=12 tank raid1 sda sdb

小结

这些zpool属性是应用于整个存储池的,这意味着ZFS数据集将从存储池继承这些属性。您在ZFS数据集上设置的一些属性(将在本系列的最后讨论)将应用于整个存储池。例如,如果您为一个ZFS数据集启用了块重复数据删除功能,那么它将重删在整个池中找到的块,而不仅仅是在您的数据集中。然而,只有该数据集中的块将被主动重删,而其他ZFS数据集可能不会。此外,设置属性是不可追溯的。在你的“autoexpand”zpool属性的情况下,自动扩展zpool大小时,所有驱动器已经被替换,如果你在启用该属性之前替换了一个驱动器,该驱动器将被认为是一个更小的驱动器,即使它的物理上不是(也就是说,你在设置“autoexpand”属性为on之前用一块更大容量的盘替换了一个小的盘,也不会自动扩容,默认也会把这个盘认为是原来的小盘,即使后面你开了“autoexpand”属性也不会把之前操作进行扩容了)。设置属性只对设置后属性后面的操作生效。

尽管有这些注意事项,但是作为GNU/Linux存储管理员,能够更改池的一些参数以满足您的需要,这使您能够更好地控制其他文件系统所没有的功能。而且,正如我们到目前为止所发现的,所有事情都可以用一个命令“zpool”的子命令来处理。我们将再写一篇文章,讨论在创建池之前你想要考虑的注意事项,然后我们将转而研究ZFS数据集,这是ZFS的整体支柱。

最佳实践

和所有的建议一样,这些指南中有一些很重要,而另一些可能没那么重要。你甚至可能无法像你想的那样严格地遵循它们。无论如何,你应该意识到它们。我将尝试为每一项提供一个理由。它们没有特定的顺序。“最佳实践”的目的是为了优化空间效率、性能并确保最大程度的数据完整性。

  • 在64位内核上运行ZFS。它具有特定于64位的代码,32位内核无法使用这些代码。
  • 只在大内存的系统上安装ZFS。1 GB是最小值,2 GB更好,启动时首选4 GB。记住,ZFS将为ARC使用1/2的可用RAM。
  • 使用ECC RAM,在可能的情况下,会对寄存器中的数据进行数据清理并保持数据一致性。ARC是RAM中真正的只读数据缓存。
  • 使用整个磁盘而不是分区。因此,ZFS可以更好地利用磁盘上的缓存。如果必须使用分区,则备份分区表,并在将数据重新安装到其他分区时要小心,这样才不会损坏池中的数据。
  • 保持存储池中的每个VDEV的大小相同。如果VDEV的大小不同,ZFS将倾向于更大的VDEV,这可能会导致性能瓶颈。
  • 尽可能使用冗余,因为ZFS可以并且希望纠正池中存在的数据错误。如果池中的其他地方没有冗余的良好副本,则无法修复这些错误。镜像和RAID-Z级别实现了这一点。
  • 优先考虑使用RAIDZ-2或者RAIDZ-3,而不是RAIDZ-1。你一定听过“祸不单行”这句话。对于磁盘故障也是如此。如果RAIDZ-1中的某个硬盘故障,并且热备盘正在进行数据重建处理(resilver),直到数据完全复制完成,则在数据重建期间(resilver data)无法承受另一个硬盘故障,否则将丢失数据。使用RAIDZ-2,您可能会遇到两个磁盘故障,而不是一个磁盘故障,这增加了在第二个甚至第三个磁盘故障之前完全恢复必要数据的可能性。
  • 对整个存储池执行定期备份(至少每周)。这不是备份,除非你有多个复本。硬盘冗余不能保证在断电、硬件故障或线缆断开的情况下,仍有数据能提供服务。
  • 使用热备盘快速恢复损坏的磁盘。将存储池的“autoreplace”属性设置为开启。
  • 当使用多种设备的混合存储池时,需要对SLOG进行镜像,并对L2ARC进行分条(条带化)。
  • 如果使用混合存储池,并对快速SSD或NVRAM驱动器进行分区,除非您明确知道自己需要多大的空间,否则1GB可能已经足够用于您的SLOG了。将剩余的SSD或NVRAM驱动器用于L2ARC,L2ARC的存储空间越大越好。
  • 为了获得最佳性能,请将存储池容量保持在80%以下。由于ZFS的写时复制特性,文件系统变得非常碎片化。至少每月通过电子邮件等方式报告容量情况。
  • 如果可能,建议每周对消费级SATA和SCSI硬盘进行一次数据清理,每月对企业级SAS和FC硬盘进行一次数据清理。数据清理的频率这取决于很多因素,有时这可能是不可能的,所以你可以根据自己业务的具体情况去制定数据清理的频次。但是,基本上,你应该尽可能频繁地进行数据清理。
  • 对于冗余阵列,每周发送存储池运行状况的电子邮件报告,对于非冗余阵列,每两周发送一次。
  • 当使用高级格式化磁盘读写4KB扇区数据时,为了获得最佳性能,在创建池时应该把“ashift”值设为12。对于512字节扇区,默认值是9。(简单的说,用4K盘时,为了获得最佳性能就应该把“ashift”值设为12,普通盘一般是512字节扇区,用这种盘,把“ashift”的值设为9就可以了)。
  • 将“autoexpand”设置为“on”,当存储池中的磁盘全部替换为更大的磁盘后,存储池将自动扩容。默认是关闭的。
  • 记得先导出存储池,当将磁盘从一个物理系统移动到另一个物理系统时。
  • 在考虑性能时,要知道对于顺序写,镜像总是优于RAID-Z级别。对于顺序读取,RAID-Z级别在较小的数据块上执行得比镜像慢,在较大的数据块上执行得更快。对于随机的读写,镜像和RAID-Z性能相差无几。在顺序和随机读写方面,条带镜像的性能都优于镜像和RAID-Z。
  • 压缩在默认情况下是禁用的,这对于今天的硬件来说没有多大意义。ZFS压缩非常便宜、非常快,几乎不增加读写延迟。事实上,在某些情况下,启用压缩的磁盘响应速度比禁用压缩的磁盘响应速度更快。压缩另一个好处是节约存储空间。

注意事项

注意事项的条条框框并不是为了阻止你使用ZFS。相反,作为规划存储服务器的存储管理员,你需要注意这些事情,以免出现丢掉数据的严重事故。如果您不理解,不重视这些警告,您可能会得到损坏的数据。上述的“最佳实践”列表可能会模糊这一界限。我已经试着列出了所有关于数据损坏的列表。阅读并理解这些说明,应该对你使用ZFS大有裨益。

  • 您的VDEV决定存储的IOPS,而该VDEV中最慢的磁盘将决定整个VDEV的IOPS。
  • ZFS将可用的原始存储容量的1/64用于元数据。因此,如果您购买了一个1TB的硬盘,那么实际的原始大小是976 GiB。在ZFS使用它之后,您将拥有961 GiB的可用空间。“zfs list”命令将显示可用存储的准确表示。规划存储容量的时候,需要记住这一点。
  • ZFS会控制整个块。它校验、恢复实时数据而不是整个磁盘(resilver data)、自我修复损坏的块,以及许多其他独特的功能。如果使用RAID卡,请确保将其配置为真正的JBOD(或“直通模式”),这样ZFS就可以控制这些磁盘。如果您的RAID卡不能这样做,就不要使用它。请使用真正的HBA卡。
  • 不要使用ZFS下的其他卷管理软件。如果ZFS能够控制整个块设备,那么它性能更好,并更能确保数据的完整性。因此,避免在ZFS下使用dm-crypt、mdadm或LVM。
  • 不要跨池共享SLOG或L2ARC设备。每个存储池应该有自己的物理设备,而不是逻辑驱动器,就像一些PCI-Express SSD卡的情况一样。在一个存储池上使用全卡,在另一个池子上使用另外一张物理卡。如果您共享一个物理设备,就创建了竞争条件,并最终可能导致数据损坏。
  • 不要在不同的服务器上共享单个存储池。ZFS不是集群文件系统。如果您希望拥有一个共享存储后端,可以在池的顶部使用GlusterFS、Ceph、Lustre或其他一些集群文件系统。
  • 混合池中的除了spare,SLOG和L2ARC之外,不要在单个池中混合VDEVs。如果一个VDEV是镜像,则所有VDEV都应该是镜像。如果一个VDEV是RAIDZ-1,则所有VDEV都应该是RAIDZ-1。当然,除非你知道自己在做什么,并愿意承担后果。ZFS试图在VDEVs之间平衡数据。拥有不同冗余的VDEV可能会导致性能问题和空间效率问题,并且在发生故障时很难恢复。
  • 不要在单个VDEV中混合不同容量或者不同转速的硬盘。但是,务必混合制造日期,以防止大规模硬盘故障(也就是集群中要用不同生产日期的硬盘,因为硬盘的使用寿命是差不多的,如果用同一批次,那么很可能出现某一段时间大规模出现硬盘故障的问题)。
  • 事实上,最好完全不要在存储池中混合不同容量或者不同转速的硬盘。
  • 不要在不同VDEVs之间混合磁盘个数。如果一个VDEV使用4个硬盘,则所有VDEV都应该使用4个硬盘。
  • 请勿将单个控制器下的所有硬盘放入同一个VDEV中。规划存储的时候,需要考虑到,当一个控制器故障时,它只影响保持数据在线所需的磁盘数量。
  • 当使用4K盘的时候,必须在创建池时将ashift值设置为12。这是事后无法改变的。创建时参考例子“zpool create -o asshift =12 tank mirror sda sdb”。
  • 默认情况下,热备盘不会被添加到VDEV中来替换故障的硬盘。您必须启用此功能。将“autoreplace”功能设置为on。以“zpool set autoreplace=on tank”为例。
  • 当池中所有较小的硬盘都被较大的硬盘替换时,存储池不会自动调整自己的大小。您必须启用该特性,并且必须在更换第一个磁盘之前启用该特性。以”zpool set autoexpand=on tank”为例。
  • ZFS不会在VDEV中重新条带化数据,也不会跨多个VDEV重新条带化数据。通常,当向RAID阵列添加新设备时,RAID控制器将通过创建新的条带宽度重建数据。这将释放池中的硬盘上的一些空间,因为它将数据复制到新磁盘。ZFS没有这样的机制。最终,随着时间的推移,磁盘会由于写而平衡,但即使是一次数据清理也不会重建条带宽度。
  • 你不能缩小zpool,只能增大它。这意味着不能从存储池中移除vdev。
  • 你只能用命令“zpool detach”来从镜像VDEV中移除硬盘。不过在RAIDZ和镜像VDEVs中你可以一块盘替换另一块盘。
  • Do not create a storage pool of files or ZVOLs from an existing zpool. Race conditions will be present, and you will end up with corrupted data. Always keep multiple pools separate.
  • Linux内核可能不会在每次启动时给一个驱动器分配相同的驱动器号。因此,您应该为您的SLOG和L2ARC使用/dev/disk/by-id/来作为约定。如果不这样做,您的zpool设备可能最终成为SLOG设备,这将反过来可能会破坏您的ZFS数据。
  • 不要“仅仅因为有能力”就创建大规模存储池。尽管ZFS可以创建78位的存储池大小,但这并不意味着您需要创建一个。
  • 不要直接将生产环境放到zpool中,而是放到ZFS数据集上。
  • 不要将生产数据提交到文件VDEVs。只使用文件VDEVs来测试脚本或学习ZFS的细节。
    如果我遗漏了什么,或者有什么需要改正的地方,请在下面的评论中添加。

参考资料

https://pthree.org/2012/12/12/zfs-administration-part-vii-zpool-properties/

https://pthree.org/2012/12/13/zfs-administration-part-viii-zpool-best-practices-and-caveats/

如果你觉得本文对你有帮助,欢迎打赏