使用普通文件系统,您可以创建分区来分隔不同类型的数据,对它们应用不同的优化,并限制分区可以消耗的空间量。每个分区从磁盘接收特定数量的空间。我们都去过那里。我们猜测下个月、明年和五年后这个系统上的每个分区需要多少磁盘空间。快进到未来,你决定给每个分区的空间量很可能是错误的。没有足够空间容纳所有数据的分区会向您添加磁盘或移动数据,使系统管理复杂化。当一个隔板有太多的空间时,你会踢自己,把它当作你宁愿放在别处的东西的倾倒场。Lucas的UFS2系统中不止一个将 /usr/ports 作为指向 /home 中某处的符号链接。Jude通常会将 /var 的一部分保存在 /usr/local/var 中。
ZFS通过池化(pooling)可用空间来解决这个问题,为您的分区提供了更常见的文件系统所无法比拟的灵活性。您创建的每个ZFS数据集只消耗其中存储文件所需的空间。每个数据集都可以访问池中的所有可用空间,消除了您对分区大小的担忧。您可以使用配额(quota)限制数据集的大小,也可以通过保留(reservation)来保证其最小空间量,如第6章所述。
常规文件系统使用单独的分区为不同类型的数据建立不同的策略和优化。/var 包含经常更改的文件,如日志和数据库。根文件系统需要一致性和安全性,而不是性能。在 /home 里,什么都可以。不过,一旦为传统文件系统建立了策略,就很难更改。UFS的 tunefs(8) 实用程序要求卸载文件系统才能进行更改。创建文件系统后,某些特征(如索引节点的数量)就无法更改。
传统文件系统的核心问题归结为缺乏灵活性。ZFS数据集几乎是无限灵活的。
第四章:ZFS数据集数据集数据集类型为什么需要数据集?查看数据集创建、移动、销毁数据集创建文件系统创建卷数据集改名移动数据集销毁数据集ZFS属性查看属性更改属性只读属性文件系统属性atimeexecreadonlysetuid用户定义属性父子关系继承和重命名移除属性挂载ZFS文件系统没有挂载点的数据集多数据集挂载在同一挂载点没有挂载点的池手动挂载和卸载文件系统ZFS 和 /etc/fstab调整 ZFS 卷空间预留Zvol 模式数据集完整性校验和拷贝元数据冗余
数据集是一个命名的数据块。这些数据可能类似于传统的文件系统,包含文件、目录和权限以及所有有趣的东西。它可能是一个原始块设备,也可能是其他数据的副本,或者任何可以塞进磁盘的东西。
ZFS使用数据集,就像传统的文件系统可能使用分区一样。需要 /usr 的策略和 /home 的单独策略吗?制作每个数据集。需要iSCSI目标的块设备吗?这是一个数据集。想要数据集的副本吗?这是另一个数据集。
数据集具有层次关系。单个存储池是每个顶级数据集的父级。每个数据集都可以有子数据集。数据集继承了其父级的许多特征,正如我们将在本章中看到的那样。
您将使用 zfs(8) 命令执行所有数据集操作。此命令有各种子命令。
ZFS目前有五种类型的数据集:文件系统、卷、快照、克隆和书签。
chflags(2) 等。你显然需要数据集。将文件放在磁盘上需要一个文件系统数据集。你可能希望为每个传统的Unix分区都建立一个数据集,比如 /usr 和 /var 。但使用ZFS,你需要很多数据集。大量的数据集。对于传统的文件系统来说,这将是残酷的疯狂,因为它对分区数量的硬编码限制以及这些分区的不灵活性。但是使用许多数据集可以增加您对数据的控制。
每个ZFS数据集都有一系列控制其操作的属性,允许管理员控制数据集的执行方式以及保护数据的谨慎程度。您可以像使用传统文件系统一样精确地调整每个数据集。数据集属性的工作方式与池属性非常相似。
系统管理员可以将单个数据集的控制权委托给另一个用户,允许该用户在没有root权限的情况下对其进行管理。如果你的组织有一大群项目团队,你可以给每个项目经理一块自己的空间,然后说:“在这里,按照你的意愿安排。”任何能减少我们工作量的事情都是一件好事。 许多ZFS功能,如复制和快照,都是在每个数据集的基础上运行的。将数据分成逻辑组可以更容易地使用这些ZFS功能来支持您的组织。
以一个拥有数十个站点的web服务器为例,每个站点由不同的团队维护。一些团队负责多个站点,而另一些团队只负责一个站点。有些人属于多个团队。如果你遵循传统的文件系统模型,你可能会创建一个 /webserver 数据集,将所有内容放入其中,并使用组权限和 sudo(8) 控制访问。你已经这样生活了几十年,而且它奏效了,那么为什么要改变呢?
但是,为每个团队创建一个数据集,并在父数据集中为每个站点提供自己的数据集,可能性就会成倍增加。
团队需要一份网站副本进行测试?克隆它。使用传统的文件系统,您必须复制整个站点目录,这会使站点所需的磁盘量加倍,并且需要更长的时间。克隆只使用站点之间差异的空间量,并立即显示。
该团队即将部署一个新版本的站点,但想要旧站点的备份?创建快照。这个新网站可能使用了一大堆和旧网站相同的文件,所以你会减少磁盘空间的使用。此外,当部署出现严重错误时,您可以通过回滚到快照来恢复旧版本。
一个特定的网站需要文件系统级的性能调整、压缩或一些本地创建的属性?为该网站设置它。
您可以为每个团队创建一个数据集,然后让团队为自己的网站创建自己的子数据集。你可以组织你的数据集以适应你的人,而不是组织你的人来适应你的技术。
当您必须更改所有站点上的文件系统设置(属性)时,请更改父数据集并让子数据集继承它。
同样的好处也适用于用户主目录。
您还可以在机器之间移动数据集。您的网站溢出了web服务器?将一半的数据集及其自定义设置以及所有克隆和快照发送到新服务器。
使用多个文件系统数据集有一个缺点。当您在文件系统中移动文件时,该文件会被重命名。在单独的文件系统之间移动文件需要将文件复制到新位置并从旧位置删除,而不仅仅是重命名。数据集间文件复制需要更多时间和更多可用空间。但是,与ZFS为您提供的多个数据集的所有好处相比,这是微不足道的。这个问题也存在于其他文件系统上,但使用大多数其他文件系统的主机只有几个分区,因此不太明显。
使用 zfs list 命令查看所有数据集以及有关它们的一些基本信息。:
xxxxxxxxxx# zfs listNAME USED AVAIL REFER MOUNTPOINTmypool 420M 17.9G 96K nonemypool/ROOT 418M 17.9G 96K nonemypool/ROOT/default 418M 17.9G 418M /...USED 和 REFER 表示数据集使用了多少磁盘空间。ZFS令人难以置信的灵活性和效率的一个缺点是,如果你不理解它,它对磁盘空间使用的解释似乎有点超现实。第6章讨论了磁盘空间和使用它的策略。AVAIL 列表示池或数据集中剩余的空间。MOUNTPOINT 表示此数据集的挂载位置。这并不意味着数据集已经被挂载到该为位置了,只是表示如果它被挂载,就会被挂载到这里。(使用 zfs mount 命令查看所有已经挂载的ZFS文件系统)如果指定数据集,zfs list 命令将列出指定数据集的信息:
xxxxxxxxxx# zfs list mypool/lambNAME USED AVAIL REFER MOUNTPOINTmypool/lamb 192K 17.9G 96K /lamb限制使用 -t 标志显示的数据集类型和类型。您可以显示文件系统(-t filesystem)、卷(-t volume)或快照(-t snapshot)。在这里,我们显示快照,并且只显示快照。
xxxxxxxxxx# zfs list -t snapshotNAME USED AVAIL REFER MOUNTPOINTzroot/var/log/db@backup 0 - 10.0G -现在您可以看到文件系统了,让我们制作一些。
使用 zfs create 命令创建任何数据集。我们将在第7章中介绍快照、克隆和书签,但现在让我们讨论文件系统和卷。
文件系统是大多数系统上最常见的数据集类型。每个人都需要一个地方来存储和组织文件。通过指定池和文件系统名称来创建文件系统数据集。
xxxxxxxxxx# zfs create mypool/lamb# mount | grep lambmypool/lamb on /lamb (zfs, local, noatime, nfsv4acls)以上命令在池 mypool 中创建一个新的数据集 lamb 。如果池有默认的挂载点,那么新数据集将默认挂载(请参阅本章后面的【挂载ZFS文件系统】)。
括号中的挂载设置通常是从父数据集继承的ZFS属性。要创建子文件系统,请提供父文件系统的完整路径。
xxxxxxxxxx# zfs create mypool/lamb/baby数据集继承了其父级的许多特征,包括其挂载点,正如我们将在本章稍后的【父/子关系】中看到的那样。
在 zfs create 命令中使用 -V 选项和卷大小创建卷。同样需要提供卷数据集的完整路径:
xxxxxxxxxx# zfs create -V 4G mypool/avolume# zfs list mypool/avolumeNAME USED AVAIL REFER MOUNTPOINTmypool/avolume 4.13G 17.9G 64K -zvols像任何其他数据集一样出现在数据集列表中。您可以通过添加 -t volume 选项来告诉 zfs list 只显示zvols。
xxxxxxxxxx# zfs list mypool/avolumeNAME USED AVAIL REFER MOUNTPOINTmypool/avolume 4.13G 17.9G 64K -zvols自动保留的空间量等于卷的大小加上ZFS元数据。这个4 GB的zvol使用了4.13 GB的空间。
作为块设备,zvols没有挂载点。它们确实在 /dev/zvol 下获得了一个设备节点,因此您可以像访问任何其他块设备一样访问它们。
xxxxxxxxxx# ls -al /dev/zvol/mypool/avolumecrw-r----- 1 root operator 0x4d Mar 27 20:22 /dev/zvol/mypool/avolume您可以在此设备节点上运行 newfs(8) ,将磁盘映像复制到它,并像使用任何其他块设备一样使用它。
奇怪的是,您可以使用 zfs rename 命令重命名数据集。将数据集的当前名称作为第一个参数,将新位置作为第二个参数。
xxxxxxxxxx# zfs rename db/production db/old# zfs rename db/testing db/production使用 -f 标志强制重命名数据集。您无法卸载其中运行进程的文件系统,但 -f 标志愉快地强制卸载。任何使用数据集的进程都会失去对它正在使用的任何内容的访问,并以任何方式做出反应。
您可以将数据集从ZFS树的一部分移动到另一部分,使数据集成为其新父级的子级。这可能会导致数据集的许多属性发生变化,因为子数据集继承了其父数据集的属性。数据集上专门设置的任何属性都不会改变。
在这里,我们将一个数据库从 zroot/var/db 数据集下移动到一个新的父级,在那里您设置了一些属性来提高容错性。
xxxxxxxxxx# zfs rename zroot/var/db/mysql zroot/important/mysql请注意,由于挂载点是继承的,这可能会改变数据集的挂载点。在重命名命令中添加 -u 标志将导致ZFS不会立即更改装载点,从而有时间将属性重置为预期值。请记住,如果重新启动计算机或手动重新装载数据集,它将使用其新的装载点。
您可以重命名快照,但不能将快照移出其父数据集。第7章详细介绍了快照。
厌倦了这个数据集?把它拖到谷仓后面,用 zfs destroy 把它从你的痛苦中解脱出来。
xxxxxxxxxx# zfs destroy db/old使用 -r 选项可以递归销毁数据集的所有子项(数据集、快照等)。
使用 -R 选项会销毁任何克隆的数据集。
使用 -v 和 -n 选项可以查看销毁数据集时会发生什么。
-v 显示关于被销毁内容的详细信息。
-n 则执行一次干(dry)运行。(不实际运行,仅显示运行后的效果)
ZFS数据集有许多设置,称为属性(properties),用于控制数据集的工作方式。虽然只有在创建数据集时才能设置其中的一些,但它们中的大多数在数据集运行时都是可调的。ZFS还提供了许多只读属性,这些属性提供了数据集消耗的空间量、压缩或重复数据删除率以及数据集的创建时间等信息。
每个数据集都继承其父数据集的属性,除非该属性是在该数据集上特别设置的。
ZFS的属性有:
xThe following properties are supported: PROPERTY EDIT INHERIT VALUES available NO NO <size> clones NO NO <dataset>[,...] compressratio NO NO <1.00x or higher if compressed> createtxg NO NO <uint64> creation NO NO <date> defer_destroy NO NO yes | no encryptionroot NO NO <filesystem | volume> filesystem_count NO NO <count> guid NO NO <uint64> keystatus NO NO none | unavailable | available logicalreferenced NO NO <size> logicalused NO NO <size> mounted NO NO yes | no objsetid NO NO <uint64> origin NO NO <snapshot> receive_resume_token NO NO <string token> redact_snaps NO NO <snapshot>[,...] refcompressratio NO NO <1.00x or higher if compressed> referenced NO NO <size> snapshot_count NO NO <count> snapshots_changed NO NO <date> type NO NO filesystem | volume | snapshot | bookmark used NO NO <size> usedbychildren NO NO <size> usedbydataset NO NO <size> usedbyrefreservation NO NO <size> usedbysnapshots NO NO <size> userrefs NO NO <count> written NO NO <size> aclinherit YES YES discard | noallow | restricted | passthrough | passthrough-x aclmode YES YES discard | groupmask | passthrough | restricted acltype YES YES off | nfsv4 | posix atime YES YES on | off canmount YES NO on | off | noauto casesensitivity NO YES sensitive | insensitive | mixed checksum YES YES on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr | blake3 compression YES YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4 | zstd | zstd-[1-19] | zstd-fast | zstd-fast-[1-10,20,30,40,50,60,70,80,90,100,500,1000] context YES NO <selinux context> copies YES YES 1 | 2 | 3 dedup YES YES on | off | verify | sha256[,verify] | sha512[,verify] | skein[,verify] | edonr,verify | blake3[,verify] defcontext YES NO <selinux defcontext> devices YES YES on | off dnodesize YES YES legacy | auto | 1k | 2k | 4k | 8k | 16k encryption NO YES on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | aes-128-gcm | aes-192-gcm | aes-256-gcm exec YES YES on | off filesystem_limit YES NO <count> | none fscontext YES NO <selinux fscontext> jailed YES YES on | off keyformat NO NO none | raw | hex | passphrase keylocation YES NO prompt | <file URI> | <https URL> | <http URL> logbias YES YES latency | throughput mlslabel YES YES <sensitivity label> mountpoint YES YES <path> | legacy | none nbmand YES YES on | off normalization NO YES none | formC | formD | formKC | formKD overlay YES YES on | off pbkdf2iters NO NO <iters> prefetch YES YES none | metadata | all primarycache YES YES all | none | metadata quota YES NO <size> | none readonly YES YES on | off recordsize YES YES 512 to 1M, power of 2 redundant_metadata YES YES all | most | some | none refquota YES NO <size> | none refreservation YES NO <size> | none relatime YES YES on | off reservation YES NO <size> | none rootcontext YES NO <selinux rootcontext> secondarycache YES YES all | none | metadata setuid YES YES on | off sharenfs YES YES on | off | NFS share options sharesmb YES YES on | off | SMB share options snapdev YES YES hidden | visible snapdir YES YES hidden | visible snapshot_limit YES NO <count> | none special_small_blocks YES YES zero or 512 to 1M, power of 2 sync YES YES standard | always | disabled utf8only NO YES on | off version YES NO 1 | 2 | 3 | 4 | 5 | current volblocksize NO YES 512 to 128k, power of 2 volmode YES YES default | full | geom | dev | none volsize YES NO <size> vscan YES YES on | off xattr YES YES on | off | dir | sa userused@... NO NO <size> groupused@... NO NO <size> projectused@... NO NO <size> userobjused@... NO NO <size> groupobjused@... NO NO <size> projectobjused@... NO NO <size> userquota@... YES NO <size> | none groupquota@... YES NO <size> | none projectquota@... YES NO <size> | none userobjquota@... YES NO <size> | none groupobjquota@... YES NO <size> | none projectobjquota@... YES NO <size> | none written@<snap> NO NO <size> written#<bookmark> NO NO <size>Sizes are specified in bytes with standard units such as K, M, G, etc.User-defined properties can be specified by using a name containing a colon (:).The {user|group|project}[obj]{used|quota}@ properties must be appended witha user|group|project specifier of one of these forms: POSIX name (eg: "matt") POSIX id (eg: "126829") SMB name@domain (eg: "matt@sun") SMB SID (eg: "S-1-234-567-89")
zfs(8) 工具可以检索数据集的特定属性或所有属性。使用 zfs get 命令、所需的属性,如果需要,还可以使用数据集名称。
xxxxxxxxxx# zfs get compression mypool/lambNAME PROPERTY VALUE SOURCEmypool/lamb compression lz4 inherited from mypool在 NAME 下,我们看到您询问的数据集, PROPERTY 显示您请求的属性。 VALUE 是属性设置的值。
SOURCE 有四种情况:
default 表示使用了默认属性;local 表示有人设置了此数据集自己的属性;temporary 表示当前数据集挂载时使用了临时属性,卸载后将恢复为正常值;inherited 表示从其父数据集继承的属性。有些属性没有来源,因为来源要么无关紧要,要么本质上显而易见。记录数据集创建日期和时间的 create 属性没有来源。该值来自系统时钟。
如果不指定数据集名称,zfs get 将显示所有数据集的此属性的值。特殊属性关键字 all 检索数据集的所有属性。
xxxxxxxxxx# zfs get all mypool/lambNAME PROPERTY VALUE SOURCEmypool/lamb type filesystem -mypool/lamb creation Fri Mar 27 20:05 2015 -mypool/lamb used 192K -...如果使用 all 而不给出数据集名称,则可以获得所有数据集的所有属性。这是很多信息。
通过用逗号分隔属性名称来显示多个属性。
xxxxxxxxxx# zfs get quota,reservation zroot/homeNAME PROPERTY VALUE SOURCEzroot/home quota none localzroot/home reservation none default您还可以使用 zfs list 和 -o 修饰符查看属性。当您想从多个数据集中查看多个属性时,这最适合。指定属性 name 显示数据集的名称。
xxxxxxxxxx# zfs list -o name,quota,reservationNAME QUOTA RESERVdb none nonezroot none nonezroot/ROOT none nonezroot/ROOT/default none none...zroot/var/log 100G 20G...您还可以添加数据集名称,以便以这种格式查看该数据集的这些属性。
xxxxxxxxxx# zfs list -o name,quota,reservation,creation zroot/oldNAME PROPERTY VALUE SOURCEzroot/old name zroot/old -zroot/old quota none defaultzroot/old reservation none defaultzroot/old creation Wed Aug 14 10:23 2024 -# zfs get name,quota,reservation,creation zroot/oldNAME PROPERTY VALUE SOURCEzroot/old name zroot/old -zroot/old quota none defaultzroot/old reservation none defaultzroot/old creation Wed Aug 14 10:23 2024 -使用 zfs set 命令更改属性。指定属性名称、新设置和数据集名称。在这里,我们将 compression (压缩)属性更改为 off 。
xxxxxxxxxx# zfs set compression=off mypool/lamb/baby# zfs get compression mypool/lamb/babyNAME PROPERTY VALUE SOURCEmypool/lamb/baby compression off local大多数属性仅适用于属性更改后写入的数据。 compression 属性告诉ZFS在将数据写入磁盘之前压缩数据。我们在第6章讨论压缩。禁用压缩不会解压缩在更改之前写入的任何数据。同样,启用压缩并不会神奇地压缩磁盘上已有的数据。为了获得启用压缩的全部好处,您必须重写每个文件。你最好创建一个新的数据集,用 zfs send 复制数据,并销毁原始数据集。
ZFS使用只读属性来提供有关数据集的基本信息。磁盘空间使用情况表示为属性。通过更改“您的磁盘已半满”的属性,您无法更改正在使用的数据量。(第6章介绍了ZFS磁盘空间的使用情况。) creation 属性记录了创建此数据集的时间。您可以通过向磁盘添加或删除数据来更改许多只读属性,但不能直接写入这些属性。
管理传统文件系统性能和行为的一个关键工具是挂载选项。您可以以只读方式挂载传统文件系统,也可以使用 noexec 标志禁用从中运行的程序。ZFS使用属性来实现相同的效果。以下是用于实现这些熟悉目标的属性。
文件的 atime 表示上次访问文件的时间。ZFS的 atime 属性控制数据集是否跟踪访问时间。默认值为 on ,每次访问文件时都会更新文件的 atime 元数据。使用 atime 意味着每次读取磁盘时都要写入磁盘。
关闭此属性可以避免在读取文件时写入磁盘,并可以显著提高性能。它可能会混淆邮件程序和其他依赖于确定文件上次读取时间的类似实用程序。
启用 atime 会增加快照大小。第一次访问文件时,其 atime 会被更新。快照保留原始访问时间,而实时文件系统包含新更新的访问时间。这是默认设置。
exec 属性决定是否有人可以在此文件系统上运行二进制文件和命令。默认设置为 on ,允许执行。某些环境不允许用户从其个人或临时目录执行程序。将 exec 属性设置为 off ,以禁用文件系统上的程序执行。
然而,exec 属性并不禁止人们运行解释脚本。如果用户可以运行 /bin/sh ,他们可以运行 /bin/sh /home/mydir/script.sh 。实际执行的是shell——它只从脚本中获取指令。
如果您不希望向此数据集写入任何内容,请将 readonly 属性设置为 on 。默认值为 off ,允许用户在管理权限内修改数据集。
许多人认为setuid程序有风险。虽然一些setuid程序必须是setuid,如 passwd(1) 和 login(1) ,但很少需要在 /home 和 /tmp 等文件系统上安装setuid程序。许多系统管理员不允许使用setuid程序,除非在特定的文件系统上。
ZFS的 setuid 属性切换setuid支持。如果设置为 on ,则文件系统支持setuid。如果设置为 off ,则忽略setuid标志。
setuid程序有风险,默认情况下,zroot/tmp、zroot/usr/ports、zroot/usr/src、zroot/var/audit、zroot/var/crash、zroot/var/log、zroot/var/tmp这些数据集的setuid是禁用的(off)。
ZFS属性很好,你不能得到足够的,对吧?好吧,开始添加你自己的。将自己的元数据与数据集一起存储的能力使您能够开发全新的自动化领域。子数据集自动继承这些财产的事实使生活变得更加容易。
为了确保您的自定义属性仍然是您的,并且不会与其他人的自定义属性冲突,请创建一个命名空间。大多数人在自定义属性前加上组织标识符和冒号。例如,FreeBSD特定的属性具有 org.freebsd:propertyname 的格式,例如 org.freebsd:swap 。如果illumos项目创建了自己的名为swap的属性,他们会称之为 org.illumos:swap 。这两个值不会冲突。
例如,假设Jude想通过数据集属性控制备份哪些数据集。他创建了名称空间com.allanjude 。在该名称空间中,他创建了属性 backup_ignore 。
xxxxxxxxxx# zfs set com.allanjude:backup_ignore=on mypool/lambJude的备份脚本检查此属性的值。如果设置为 true ,备份过程将跳过此数据集。
较真实的实例,zfstools工具使用特定的属性来实现快照:
xxxxxxxxxxMessage from zfstools-0.3.6_2:--To enable automatic snapshots, place lines such as these into /etc/crontab: PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin 15,30,45 * * * * root /usr/local/sbin/zfs-auto-snapshot frequent 4 0 * * * * root /usr/local/sbin/zfs-auto-snapshot hourly 24 7 0 * * * root /usr/local/sbin/zfs-auto-snapshot daily 7 14 0 * * 7 root /usr/local/sbin/zfs-auto-snapshot weekly 4 28 0 1 * * root /usr/local/sbin/zfs-auto-snapshot monthly 12This will keep 4 15-minutely snapshots, 24 hourly snapshots, 7 daily snapshots,4 weekly snapshots and 12 monthly snapshots. Any resulting zero-sized snapshotswill be automatically cleaned up.Enable snapshotting on a dataset or top-level pool with: zfs set com.sun:auto-snapshot=true DATASETChildren datasets can be disabled for snapshot with: zfs set com.sun:auto-snapshot=false DATASETOr for specific intervals: zfs set com.sun:auto-snapshot:frequent=false DATASETSee website and command usage output for further details.数据集继承其父数据集的属性。在数据集上设置属性时,该属性将应用于该数据集及其所有子数据集。为了方便起见,您可以通过添加 -r 标志在数据集及其所有子数据集上运行 zfs(8) 命令。在这里,我们查询数据集及其所有子数据集的 compression 属性。
xxxxxxxxxx# zfs get -r compression mypool/lambNAME PROPERTY VALUE SOURCEmypool/lamb compression lz4 inherited from mypoolmypool/lamb/baby compression off local看看 SOURCE 值。第一个数据集 mypool/lamb 从父池继承了此属性。在第二个数据集中,此属性具有不同的值。SOURCE 是 local ,这意味着该属性是专门在此数据集上设置的。
我们可以使用 zfs inherite 命令恢复原始设置。
xxxxxxxxxx# zfs inherit compression mypool/lamb/baby# zfs get -r compression mypool/lambNAME PROPERTY VALUE SOURCEmypool/lamb compression lz4 inherited from mypoolmypool/lamb/baby compression lz4 inherited from mypool子对象现在继承了父对象的 compression 属性,而父对象又继承了祖父母的压缩属性。
当您更改父级的属性时,新属性会自动向下传播到子级。
xxxxxxxxxx# zfs set compression=gzip-9 mypool/lamb# zfs get -r compression mypool/lambNAME PROPERTY VALUE SOURCEmypool/lamb compression gzip-9 localmypool/lamb/baby compression gzip-9 inherited from mypool/lamb我告诉父数据集使用 gzip-9 压缩。这渗透到了孩子身上。
当您移动或重命名数据集以使其具有新的父级时,父级的属性会自动向下传播到子级。本地设置的属性保持不变,但继承的属性会切换到新父级的属性。
在这里,我们创建一个新的父数据集并检查其压缩属性。
xxxxxxxxxx# zfs create mypool/second# zfs get compress mypool/secondNAME PROPERTY VALUE SOURCEmypool/second compression lz4 inherited from mypool我们的 baby 数据集使用 gzip-9 压缩。这是从 mypool/lamb 继承的。现在,让我们将 baby 移动到 second 数据集,看看 compression 属性会发生什么变化。
xxxxxxxxxx# zfs rename mypool/lamb/baby mypool/second/baby# zfs get -r compression mypool/secondNAME PROPERTY VALUE SOURCEmypool/second compression lz4 inherited from mypoolmypool/second/baby compression lz4 inherited from mypool子数据集现在属于另一个父数据集,并从新父数据集继承其属性。子数据集保留任何 local 属性。
然而, baby 数据集上的数据有点混乱。在启用 compression 之前写入的数据是未压缩的。当数据集使用gzip-9压缩时写入的数据用gzip-9进行压缩。现在写入的任何数据都将使用lz4进行压缩。ZFS会自动为您解决所有这些问题,但一想到它确实会让人头疼。
虽然您可以将属性设置回其默认值,但如何将源更改回 inherit 或 default ,或者如何在设置自定义属性后将其删除,这并不明显。
要删除自定义属性,请继承它。
xxxxxxxxxx# zfs inherit com.allanjude:backup_ignore mypool/lamb即使在根数据集上设置了属性,这也可以工作。
要将数据集及其所有子数据集上的属性重置为默认值,或完全删除自定义属性,请在池的根数据集上使用 zfs inherit 命令。
xxxxxxxxxx# zfs inherit -r compression mypool这有违直觉,但它消除了根数据集的自定义设置。
对于传统的文件系统,您可以在 /etc/fstab 中列出每个分区、其类型以及应挂载的位置。为了方便起见,您甚至列出了临时挂载,如软盘和CD-ROM驱动器。ZFS允许您创建如此大量的文件系统,以至于这很快变得不切实际。
每个ZFS文件系统都有一个 mountpoint 属性,定义了它应该挂载的位置。默认 mountpoint 是从池的mountpoint 构建的。如果池没有挂载点,则必须为要挂载的任何数据集分配挂载点。
xxxxxxxxxx# zfs get mountpoint zroot/usr/homeNAME PROPERTY VALUE SOURCEzroot/usr/home mountpoint /usr/home inherited from zroot/usr文件系统通常挂载在 /usr/home 。您可以在手动挂载文件系统时覆盖此设置。
【现在的系统通常是 zroot/home 挂载在 /home 】
用于默认FreeBSD安装的 zroot 池没有设置挂载点。如果你直接在zroot下创建新的数据集,它们将没有挂载点。在zroot上创建的数据集,例如/usr,从其父数据集继承一个挂载点。
【从14版开始,zroot 池默认有挂载点 /zroot 。】
除具有根文件系统的池之外的任何池通常都有一个以池命名的挂载点。如果您创建了一个名为 db 的池,它将被挂载到 /db 。除非你改变它们,否则所有子数据集都会从该池继承他们的挂载点。
当您更改文件系统的 mountpoint 属性时,该文件系统和继承挂载点的任何子文件系统都将被卸载。如果新值是 legacy ,则它们将保持卸载状态。否则,如果属性以前是 legacy 或 none ,或者如果它们是在属性更改之前挂载的,则它们会自动重新挂载到新位置。此外,任何共享文件系统都会被取消共享,并在新位置共享。
与普通文件系统一样,ZFS文件系统不一定被挂载。
canmount 属性控制文件系统的挂载行为。如果 canmount 设置为 yes ,运行 zfs mount -a 会挂载文件系统,就像 mount -a 一样。当您在 /etc/rc.conf 中启用ZFS时,FreeBSD会在启动时运行 zfs mount -a 。canmount 属性设置为 noauto 时,数据集只能显式挂载和卸载。创建或导入数据集时,数据集不会自动挂载,也不会由 zfs mount -a 命令装载,也不是由 zfs unmount -a 卸载。canmount 设置为 off 时,事情会变得有趣。您可能有两个具有相同挂载点的不可挂载数据集。一个数据集可以仅仅为了成为未来数据集的父数据集而存在,但实际上并不能存储文件,正如我们将在下一节看到的那样。子数据集不继承 canmount 属性。
更改 canmount 属性不会自动卸载或挂载文件系统。如果在已挂载的文件系统上禁用挂载,则需要手动卸载文件系统或重新启动。
ZFS数据集是分层的。您可能需要创建一个永远不会只包含任何文件的数据集,这样它就可以成为许多其他数据集的公共父级。考虑默认安装FreeBSD 10.1或更高版本。
xxxxxxxxxx# zfs mountzroot/ROOT/default /zroot/tmp /tmpzroot/usr/home /usr/homezroot/usr/ports /usr/portszroot/usr/src /usr/src...我们在 /usr 下有各种各样的数据集,但没有挂载 /usr 数据集。怎么回事?
zfs list 显示存在一个数据集,其挂载点为 /usr 。但是,让我们检查一下 zroot/usr 及其所有子目录的 mountpoint 和 canmount 属性。
xxxxxxxxxx# zfs list -o name,canmount,mountpoint -r zroot/usrNAME CANMOUNT MOUNTPOINTzroot/usr off /usrzroot/usr/home on /usr/homezroot/usr/ports on /usr/portszroot/usr/src on /usr/src如果 canmount 设置为 off,则永远不会挂载 zroot/usr 数据集。在 /usr 中写入的任何文件,如 /usr/bin 中的命令和 /usr/local 中的包,都会进入根文件系统。较低级别的挂载点(如 /usr/src)有自己的数据集,这些数据集被挂载。
该数据集仅作为子数据集的父数据集而存在。您将在 /var 分区中看到类似的情况。
将 canmount 设置为 off 允许数据集仅用作继承属性的机制。将 canmount 设置为 off 的一个原因是有两个具有相同挂载点的数据集,这样两个数据集的子数据集就会出现在同一目录中,但可能具有不同的继承特征。
FreeBSD的安装程序在默认池 zroot 上没有 mountpoint 。创建新数据集时,必须为其分配挂载点。
如果你不想为在池下创建的每个数据集分配一个挂载点,你可以将 / 的挂载点分配给 zroot 池,并将 canmount 设置为关闭。这样,当你创建一个新数据集时,它有一个要继承的 mountpoint 。这是一个使用具有相同挂载点的多个数据集的非常简单的例子。
想象一下,你想要一个包含两组子目录的 /opt 目录。其中一些目录包含程序,安装后不应写入。其他目录包含数据。您必须锁定在文件系统级别运行程序的能力。
xxxxxxxxxx# zfs create db/programs# zfs create db/data现在给这两个数据集赋予 /opt 的挂载点,并设置不能挂载:
xxxxxxxxxx# zfs set canmount=off db/programs# zfs set mountpoint=/opt db/programs安装软件到数据集,然后设置其为只读:
xxxxxxxxxx# zfs set readonly=on db/programs不能从 db/data 数据集运行软件,所以关闭 exec 和 setuid 。但可以写数据:
xxxxxxxxxx# zfs set canmount=off db/data# zfs set mountpoint=/opt db/data# zfs set setuid=off db/data# zfs set exec=off db/data现在创建一些子数据集,db/programs 数据集的子级继承该数据集的属性,而 db/data 数据集的子集则继承另一组属性:
xxxxxxxxxx# zfs create db/programs/bin# zfs create db/programs/sbin# zfs create db/data/test# zfs create db/data/production我们现在在 /opt 中挂载了四个数据集,其中两个用于二进制文件,两个用于数据。据用户所知,这些是普通目录。然而,无论文件权限如何,没有人可以写入其中两个目录。无论人们耍什么花招,系统都不会识别其他两个中的可执行文件和setuid文件。当您需要数据或程序的另一个数据集时,请使用所需的设置将其创建为数据集的子数据集。对父数据集的更改会立即传播到所有子数据集。
目前不知道如何使用此功能。
池通常挂载在以池命名的目录中,但以下操作可以产生一个例外:
xxxxxxxxxx# zfs set mountpoint=none mypool这个池将不再被挂载。除非指定挂载点,否则池上的任何数据集都不会挂载。FreeBSD安装程序就是这样为操作系统创建池的。
xxxxxxxxxx# zfs set mountpoint=/someplace mypool/lamb如有必要,将创建目录并挂载文件系统。
要手动挂载文件系统,请使用 zfs mount 和数据集名称。这最常用于 canmount 设置为 noauto 的文件系统。
xxxxxxxxxx# zfs mount mypool/usr/src使用 zfs unmount 目录卸载文件系统即其所有子系统:
xxxxxxxxxx# zfs unmount mypool/second如果要在其他位置临时挂载数据集,请使用 -o 标志指定新的挂载点。此挂载点仅持续到卸载数据集为止。
xxxxxxxxxx# zfs mount -o mountpoint=/mnt mypool/lamb只有定义了挂载点的数据集才能挂载。当数据集没有挂载点时定义临时挂载点会导致错误。
可以使用 /etc/fstab 管理某些或全部ZFS文件系统。将数据集的 mountpoint 属性设置为 legacy (这将卸载文件系统):
xxxxxxxxxx# zfs set mountpoint=legacy mypool/second现在可以使用 mount(8) 命令挂载这个数据集:
xxxxxxxxxx# mount -t zfs mypool/second /tmp/second您还可以将ZFS数据集添加到系统的 /etc/fstab 中。使用完整的数据集名称作为设备节点。将类型设置为 zfs 。您可以使用 noatime 、 noexec 、 readonly 或 ro 以及 nosuid 等标准文件系统选项。(您也可以显式给出 atime 、exec、rw 和 suid 的默认行为,但这些是ZFS的默认行为。)挂载顺序是正常的,但fsck字段被忽略。下面是一个 /etc/fstab 条目,它将数据集 scratch/junk 以 nouid 挂载到/tmp。
xxxxxxxxxxscratch/junk /tmp nosuid 2 0但是,我们建议使用ZFS属性来管理您的挂载。属性几乎可以完成 /etc/fstab 所做的一切,甚至更多。
Zvol非常简单——这里有一块空间作为块设备;使用它。您可以调整卷使用空间的方式以及它提供的设备节点类型。
zvol的 volsize 属性指定卷的逻辑大小。默认情况下,创建卷会为数据集保留与卷大小相等的空间量。(如果你向前看第6章,它会建立一个大小相等的重新预订。)更改 volsize 会更改预订。 volsize 只能设置为 volblocksize 属性的倍数,不能为零。
如果没有保留,卷可能会耗尽空间,导致未定义的行为或数据损坏,具体取决于卷的使用方式。当体积大小在使用过程中发生变化时,也会出现这些影响,特别是在缩小尺寸时。调整音量大小可能会混淆使用块设备的应用程序。
Zvol还支持稀疏卷(sparse volumes),也称为精简资源调配(thin provisioning)。稀疏卷是指保留量小于卷大小的卷。从本质上讲,使用稀疏卷可以分配比数据集可用的空间更多的空间。通过稀疏资源调配,您可以在5 TB的数据集上创建10个1 TB的稀疏卷。只要你的卷从未被大量使用,就没有人会注意到你被过度使用了。
不建议使用稀疏卷。即使卷本身看起来只是部分满,写入稀疏卷也可能会失败,并出现“空间不足”错误。
通过在 zfs create -V 命令中指定 -s 选项,在创建时指定稀疏卷。volsize 的更改不会反映在预定中(reservation)。您还可以在创建卷后减少保留。
FreeBSD通常将zvols作为 geom(4) 提供者暴露给操作系统,赋予它们最大的灵活性。您可以使用 volmode 属性更改此设置。
volmode 设置为 dev 只会将卷作为 /dev 中的字符设备公开。此类卷只能作为原始磁盘设备文件访问。它们不能被分区或挂载,也不能参与RAID或其他GEOM功能。它们更快。在某些情况下,如果您不信任使用卷的设备,dev 模式可能更安全。volmode 设置为 none 意味着卷不会暴露在ZFS之外。但是,这些卷可以快照、克隆和复制。这些卷可能适用于备份目的。default 意味着卷暴露由 sysctl vfs.zfs.vol.mode 控制。您可以在系统范围内设置默认的zvol模式。值 1 表示默认值为geom,2 表示dev,3 表示无。虽然您可以更改活动卷上的属性,但它没有任何效果。此属性仅在卷创建和池导入期间处理。您可以通过使用 zfs rename 卷来重新创建zvol设备。
ZFS的大多数保护都在VDEV层工作。毕竟,这就是块和磁盘变坏的地方。然而,一些硬件限制了池冗余。很少有笔记本电脑有足够的硬盘来使用镜像,更不用说RAID-Z了。但是,您可以在数据集层做一些事情,通过使用校验和、元数据冗余和副本来提供一些冗余。大多数用户永远不应该触摸前两个,拥有冗余虚拟设备的用户可能希望不去碰这三个。
ZFS计算并存储它写入的每个块的校验和。这确保了当一个块被读回时,ZFS可以验证它与写入时相同,并且没有以某种方式被静默损坏。checksum 属性控制数据集使用哪种校验和算法。有效设置包括on、fletcher2、fletcher4、sha256、off 和 noparity 。
默认值 on 使用OpenZFS开发人员选择的算法。2015年,该算法是 fletcher4 ,但在未来的版本中可能会发生变化。
标准算法 fletcher4 是默认的校验和算法。它足够好用,而且速度很快。如果你想永远使用 fletcher4 ,你可以将此属性设置为 fletcher4 。但是,我们建议保持默认值 on ,并在适当的时候让ZFS升级池的校验和算法。
值 off 将禁用对用户数据的完整性检查。
值 noparity 不仅禁用完整性,还禁用维护用户数据的奇偶校验。此设置由驻留在RAID-Z池上的转储设备在内部使用,不应被任何其他数据集使用。不建议禁用校验和。
ZFS的旧版本使用 fletcher2 算法。虽然它支持旧池,但肯定不鼓励。
sha256 算法比 fletcher4 慢,但不太可能导致碰撞。在大多数情况下,碰撞是无害的。在执行重复数据删除时,经常建议使用 sha256 算法。
ZFS存储两到三个重要元数据的副本,并可以对重要用户数据进行相同的处理。copies 属性告诉ZFS要保留多少个用户数据副本。ZFS试图将这些副本放在不同的磁盘上,或者如果失败,则尽可能存储在同一磁盘上相互远离的物理位置,以帮助防止硬件故障。当您增加 copies 属性时,ZFS还会将该数据集的元数据副本数量增加到最多三个。
如果您的池在两个镜像磁盘上运行,并且您将 copies 设置为 3 ,则您将有六个数据副本。其中一个应该能在你不明智地在原始提供者设备上使用 dd(1) 或从屋顶上摔下来的情况下幸存下来。
增加或减少副本只会影响设置更改后写入的数据。将副本从 1 更改为 2 不会突然创建所有数据的重复副本,正如我们在这里看到的那样。创建一个10 MB的随机数据文件:
xxxxxxxxxx# dd if=/dev/random of=/lamb/random1 bs=1m count=1010+0 records in10+0 records out10485760 bytes transferred in 0.144787 secs (72421935 bytes/sec)# zfs set copies=2 mypool/lamb现在每个块都存储两次。如果其中一个副本损坏,ZFS仍然可以读取您的文件。它知道哪些块已损坏,因为它的校验和不匹配。但是看看池上的空间使用情况(池列表中的 REFER 空间)。
xxxxxxxxxx# zfs list mypool/lambNAME USED AVAIL REFER MOUNTPOINTmypool/lamb 10.2M 13.7G 10.1M /lamb只使用了我们写的10MB。由于您在更改 copies 属性之前编写了此文件,因此没有对其进行额外的复制。
然而,当 copies 设置为 2 时,如果我们写入另一个文件或覆盖原始文件,我们将看到不同的磁盘使用情况。
xxxxxxxxxx# dd if=/dev/random of=/lamb/random2 bs=1m count=1010+0 records in10+0 records out10485760 bytes transferred in 0.141795 secs (73950181 bytes/sec)现在看看磁盘用量:
xxxxxxxxxx# zfs list mypool/lambNAME USED AVAIL REFER MOUNTPOINTmypool/lamb 30.2M 13.7G 30.1M /lamb总空间使用量为30 MB,第一个随机数据文件为10 MB,第二个10 MB文件的2个副本为20 MB。
当我们查看 ls(1) 的文件时,它们只显示实际大小:
xxxxxxxxxx# ls -l /lamb/random*-rw-r--r-- 1 root wheel 10485760 Apr 6 15:27 /lamb/random1-rw-r--r-- 1 root wheel 10485760 Apr 6 15:29 /lamb/random2如果你真的想破坏数据集的弹性,看看元数据冗余。
每个数据集都存储了其内部元数据的额外副本,因此,如果单个块损坏,丢失的用户数据量是有限的。此额外副本是VDEV级别提供的任何冗余(例如,通过镜像或RAID-Z)的补充。它也是副本属性(如下)指定的任何额外副本的补充,最多三份。
redundant_metadata 属性允许您决定数据集元数据的冗余程度。大多数用户永远不应该更改此属性。
当 redundant_metadata 设置为 all (默认值)时,ZFS会存储所有元数据的额外副本。如果磁盘上的单个块损坏,最坏的情况是,单个用户数据块可能会丢失。
当您将 redundant_metadata 设置为 most 时,ZFS仅存储大多数类型元数据的额外副本。这可以提高随机写入的性能,因为必须写入的元数据更少。当只有大多数元数据是冗余的时,如果磁盘上的单个块损坏,最多可能会丢失大约100个用户数据块。冗余存储元数据块的确切行为可能会在未来的版本中发生变化。
如果将 redundant_metadata 设置为 most ,将 copies 设置为 3 ,并且数据集位于镜像池中,则ZFS将存储大多数元数据的六个副本,以及数据和一些元数据的四个副本。
此属性是为经常更新元数据的特定用例(如数据库)而设计的。如果数据已经受到足够强的容错保护,减少每次数据库更改时必须写入的元数据副本数量可以提高性能。只有当你知道自己在做什么时,才能更改此值。
现在您已经掌握了数据集,让我们来谈谈池维护。