22.4.zfs 管理
22.4.1. 创建和销毁数据集
与传统的磁盘和卷管理器不同,ZFS 中的空间 不是 预先分配的。在传统文件系统中,分区和分配空间后,无法在没有新增磁盘的情况下添加新的文件系统。而在 ZFS 中,可以随时创建新的文件系统。每个 数据集 都有包括压缩、去重、缓存和配额等特性,还包含只读、大小写敏感、网络文件共享和挂载点等其他有用的属性。可以将数据集嵌套在彼此内部,子数据集将继承其父数据集的属性。可以 委托、复制、快照、jail 来管理和销毁每个数据集。为每种不同类型或文件集创建一个单独的数据集具有优势。拥有大量数据集的缺点是,像 zfs list
这样的命令会变慢,而且挂载数百个甚至数千个数据集会减慢 FreeBSD 启动过程。
创建新的数据集并启用 LZ4 压缩:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 781M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 616K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.20M 93.2G 608K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/tmp 152K 93.2G 152K /var/tmp
# zfs create -o compress=lz4 mypool/usr/mydataset
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 781M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 704K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/mydataset 87.5K 93.2G 87.5K /usr/mydataset
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.20M 93.2G 610K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/tmp 152K 93.2G 152K /var/tmp
销毁数据集比删除数据集中的文件要快得多,因为它不涉及扫描文件和更新相应的元数据。
销毁创建的数据集:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 880M 93.1G 144K none
mypool/ROOT 777M 93.1G 144K none
mypool/ROOT/default 777M 93.1G 777M /
mypool/tmp 176K 93.1G 176K /tmp
mypool/usr 101M 93.1G 144K /usr
mypool/usr/home 184K 93.1G 184K /usr/home
mypool/usr/mydataset 100M 93.1G 100M /usr/mydataset
mypool/usr/ports 144K 93.1G 144K /usr/ports
mypool/usr/src 144K 93.1G 144K /usr/src
mypool/var 1.20M 93.1G 610K /var
mypool/var/crash 148K 93.1G 148K /var/crash
mypool/var/log 178K 93.1G 178K /var/log
mypool/var/mail 144K 93.1G 144K /var/mail
mypool/var/tmp 152K 93.1G 152K /var/tmp
# zfs destroy mypool/usr/mydataset
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 781M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 616K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.21M 93.2G 612K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/tmp 152K 93.2G 152K /var/tmp
在现代版本的 ZFS 中,zfs destroy
是异步的,释放空间可能需要几分钟才会在池中显示出来。使用 zpool get freeing poolname
来查看 freeing
属性,显示哪些数据集正在后台释放其块。如果有子数据集,例如 快照 或其他数据集,销毁父数据集是不可行的。要销毁数据集及其子数据集,可以使用 -r
递归销毁数据集及其子数据集。使用 -n -v
来列出将在此操作中销毁的数据集和快照,但不会实际销毁任何东西。销毁快照时回收的空间也会显示出来。
22.4.2. 创建和销毁卷
卷是一种特殊的数据集类型。它不是作为文件系统挂载,而是作为块设备暴露在 /dev/zvol/poolname/dataset 下。这使得卷可以用于其他文件系统、作为虚拟机的磁盘,或通过 iSCSI 或 HAST 等协议将其提供给其他网络主机。
你可以格式化卷,使用任何文件系统,或者不使用文件系统来存储原始数据。对于用户来说,卷看起来像是普通磁盘。将普通文件系统放到这些 zvol 上提供了普通磁盘或文件系统没有的功能。例如,在一个 250MB 的卷上使用压缩属性,可以创建一个压缩的 FAT 文件系统。
# zfs create -V 250m -o compression=on tank/fat32
# zfs list tank
NAME USED AVAIL REFER MOUNTPOINT
tank 258M 670M 31K /tank
# newfs_msdos -F32 /dev/zvol/tank/fat32
# mount -t msdosfs /dev/zvol/tank/fat32 /mnt
# df -h /mnt | grep fat32
Filesystem Size Used Avail Capacity Mounted on
/dev/zvol/tank/fat32 249M 24k 249M 0% /mnt
# mount | grep fat32
/dev/zvol/tank/fat32 on /mnt (msdosfs, local)
销毁卷的过程与销毁常规文件系统数据集类似。该操作几乎是即时完成的,但可能需要几分钟才能在后台回收空闲空间。
22.4.3. 重命名数据集
要更改数据集的名称,使用 zfs rename
命令。要更改数据集的父级,也使用这个命令。将数据集重命名为不同的父级,将改变继承自父级的属性值。重命名一个数据集会卸载它并将其挂载到新位置(该位置继承自新父级)。要防止这种行为,使用 -u
。
将数据集重命名并移动到不同的父级:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 780M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 704K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/mydataset 87.5K 93.2G 87.5K /usr/mydataset
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.21M 93.2G 614K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/tmp 152K 93.2G 152K /var/tmp
# zfs rename mypool/usr/mydataset mypool/var/newname
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 780M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 616K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.29M 93.2G 614K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/newname 87.5K 93.2G 87.5K /var/newname
mypool/var/tmp 152K 93.2G 152K /var/tmp
重命名快照使用相同的命令。由于快照的性质,不能更改它们的父级。要重命名递归快照,指定 -r
;这将重命名所有具有相同名称的子数据集中的快照。
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/newname@first_snapshot 0 - 87.5K -
# zfs rename mypool/var/newname@first_snapshot new_snapshot_name
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/newname@new_snapshot_name 0 - 87.5K -
22.4.4. 设置数据集属性
每个 ZFS 数据集都有一些控制其行为的属性。大多数属性默认从父数据集继承,但可以在本地进行覆盖。使用 zfs set
命令设置数据集的属性,语法为 <property=value dataset>
。大多数属性有一组有限的有效值,zfs get
将显示每个可能的属性及其有效值。使用 zfs inherit
可以将大多数属性恢复为继承的值。也可以定义用户自定义属性,它们成为数据集配置的一部分,并提供关于数据集或其内容的更多信息。为了区分这些自定义属性与 ZFS 提供的属性,可以使用冒号(:
)创建自定义命名空间。
# zfs set custom:costcenter=1234 tank
# zfs get custom:costcenter tank
NAME PROPERTY VALUE SOURCE
tank custom:costcenter 1234 local
要删除自定义属性,使用 zfs inherit
并加上 -r
选项。如果自定义属性在任何父级数据集中未定义,此选项将删除它(但池的历史记录仍然会记录该更改)。
# zfs inherit -r custom:costcenter tank
# zfs get custom:costcenter tank
NAME PROPERTY VALUE SOURCE
tank custom:costcenter - -
# zfs get all tank | grep custom:costcenter
#
22.4.4.1. 获取和设置共享属性
两个常用且有用的数据集属性是 NFS 和 SMB 共享选项。设置这些属性可以定义 ZFS 如何以及是否通过网络共享数据集。目前,FreeBSD 仅支持设置 NFS 共享。要获取共享的当前状态,可以输入:
# zfs get sharenfs mypool/usr/home
NAME PROPERTY VALUE SOURCE
mypool/usr/home sharenfs on local
# zfs get sharesmb mypool/usr/home
NAME PROPERTY VALUE SOURCE
mypool/usr/home sharesmb off local
要启用数据集的共享,可以输入:
# zfs set sharenfs=on mypool/usr/home
可以为通过 NFS 共享的数据集设置其他选项,如 -alldirs
、-maproot
和 -network
。要设置共享选项,可以输入:
# zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home
22.4.5. 管理快照
快照 是 ZFS 最强大的功能之一。快照提供了数据集的只读、时间点拷贝。通过写时复制(COW)技术,ZFS 通过保留旧版本的数据在磁盘上创建快照,快速创建快照。如果没有快照,ZFS 会在数据被重写或删除时回收空间,为将来使用腾出空间。快照通过记录当前数据集与之前版本之间的差异来节省磁盘空间。快照适用于整个数据集,而不是单独的文件或目录。来自数据集的快照会复制其中包含的所有内容,包括文件系统属性、文件、目录、权限等。快照在创建时不会使用额外的空间,但随着它们引用的块发生变化,会消耗空间。使用 -r
创建的递归快照会在数据集及其子项中创建具有相同名称的快照,从而提供文件系统的一个一致的时间点快照。当一个应用程序在相关的数据集中或相互依赖的文件上工作时,这一点尤为重要。如果没有快照,备份将是不同时间点的文件副本。
ZFS 中的快照提供了许多其他文件系统所没有的功能。快照使用的一个典型示例是在执行像软件安装或系统升级等风险较高的操作时,作为快速备份当前文件系统状态的一种方式。如果操作失败,回滚到快照可以将系统恢复到创建快照时的状态。如果升级成功,则删除快照以释放空间。没有快照的情况下,失败的升级通常需要恢复备份,这既麻烦又费时,并且可能需要系统不可用的时间。回滚快照很快,即使系统在正常操作中运行,也几乎没有或没有停机时间。在多 TB 存储系统中,考虑到从备份中复制数据所需的时间,节省的时间是巨大的。快照不是池完整备份的替代品,但提供了一种快速简便的方式,在特定时间存储数据集的副本。
22.4.5.1. 创建快照
要创建快照,可以使用 zfs snapshot <数据集>@<快照名>
命令。添加 -r
选项可以递归创建快照,在所有子数据集上使用相同的名称。
创建整个池的递归快照:
# zfs list -t all
NAME USED AVAIL REFER MOUNTPOINT
mypool 780M 93.2G 144K none
mypool/ROOT 777M 93.2G 144K none
mypool/ROOT/default 777M 93.2G 777M /
mypool/tmp 176K 93.2G 176K /tmp
mypool/usr 616K 93.2G 144K /usr
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/ports 144K 93.2G 144K /usr/ports
mypool/usr/src 144K 93.2G 144K /usr/src
mypool/var 1.29M 93.2G 616K /var
mypool/var/crash 148K 93.2G 148K /var/crash
mypool/var/log 178K 93.2G 178K /var/log
mypool/var/mail 144K 93.2G 144K /var/mail
mypool/var/newname 87.5K 93.2G 87.5K /var/newname
mypool/var/newname@new_snapshot_name 0 - 87.5K -
mypool/var/tmp 152K 93.2G 152K /var/tmp
# zfs snapshot -r mypool@my_recursive_snapshot
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool@my_recursive_snapshot 0 - 144K -
mypool/ROOT@my_recursive_snapshot 0 - 144K -
mypool/ROOT/default@my_recursive_snapshot 0 - 777M -
mypool/tmp@my_recursive_snapshot 0 - 176K -
mypool/usr@my_recursive_snapshot 0 - 144K -
mypool/usr/home@my_recursive_snapshot 0 - 184K -
mypool/usr/ports@my_recursive_snapshot 0 - 144K -
mypool/usr/src@my_recursive_snapshot 0 - 144K -
mypool/var@my_recursive_snapshot 0 - 616K -
mypool/var/crash@my_recursive_snapshot 0 - 148K -
mypool/var/log@my_recursive_snapshot 0 - 178K -
mypool/var/mail@my_recursive_snapshot 0 - 144K -
mypool/var/newname@new_snapshot_name 0 - 87.5K -
mypool/var/newname@my_recursive_snapshot 0 - 87.5K -
mypool/var/tmp@my_recursive_snapshot 0 - 152K -
快照不会在正常的 zfs list
操作中显示。要列出快照,必须在 zfs list
命令后加上 -t snapshot
选项。-t all
会显示文件系统和快照。
快照不会直接挂载,因此在 MOUNTPOINT
列中不会显示路径。由于快照在创建后是只读的,因此 ZFS 不会在 AVAIL
列中显示可用空间。可以通过以下命令比较快照与原数据集:
# zfs list -rt all mypool/usr/home
NAME USED AVAIL REFER MOUNTPOINT
mypool/usr/home 184K 93.2G 184K /usr/home
mypool/usr/home@my_recursive_snapshot 0 - 184K -
同时显示数据集和快照可以揭示快照是如何以 写时复制 COW 方式工作的。它们只保存所做的更改(delta),而不是重新保存整个文件系统的内容。这意味着,在进行更改时,快照占用的空间非常小。通过复制文件到数据集,再创建第二个快照,可以更加清楚地观察空间的使用:
# cp /etc/passwd /var/tmp
# zfs snapshot mypool/var/tmp@after_cp
# zfs list -rt all mypool/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp 206K 93.2G 118K /var/tmp
mypool/var/tmp@my_recursive_snapshot 88K - 152K -
mypool/var/tmp@after_cp 0 - 118K -
第二个快照包含了复制操作后数据集的更改,从而带来了巨大的空间节省。注意,mypool/var/tmp@my_recursive_snapshot
快照的大小也在 USED
列中发生了变化,显示了它与之后创建的快照之间的差异。
22.4.5.2. 比较快照
ZFS 提供了一个内建命令,用于比较两个快照之间内容的差异。这对于长期保存大量快照时非常有用,用户可以查看文件系统如何随时间变化。例如,zfs diff
让用户找到最近的快照,查看是否仍包含被误删的文件。对前面章节中创建的两个快照进行比较,得到以下输出:
# zfs list -rt all mypool/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp 206K 93.2G 118K /var/tmp
mypool/var/tmp@my_recursive_snapshot 88K - 152K -
mypool/var/tmp@after_cp 0 - 118K -
# zfs diff mypool/var/tmp@my_recursive_snapshot
M /var/tmp/
+ /var/tmp/passwd
该命令列出了指定快照(此处为 mypool/var/tmp@my_recursive_snapshot
)与当前文件系统之间的变化。第一列显示更改类型:
-
删除路径或文件
M
修改路径或文件
R
重命名路径或文件
将输出与表格对比,可以清楚地看到 ZFS 在创建 mypool/var/tmp@my_recursive_snapshot
快照后添加了 passwd 文件,这也导致了挂载在 /var/tmp
的父目录发生了修改。
比较两个快照在使用 ZFS 复制功能将数据集传输到不同主机进行备份时非常有帮助。
通过提供完整的数据集名称和两个快照名称来比较两个快照:
# cp /var/tmp/passwd /var/tmp/passwd.copy
# zfs snapshot mypool/var/tmp@diff_snapshot
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@diff_snapshot
M /var/tmp/
+ /var/tmp/passwd
+ /var/tmp/passwd.copy
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@after_cp
M /var/tmp/
+ /var/tmp/passwd
备份管理员可以比较从发送主机接收到的两个快照,并确定数据集中的实际变化。有关更多信息,请参阅 Replication 部分。
22.4.5.3. 快照回滚
当至少有一个快照可用时,随时可以回滚到该快照。最常见的情况是当前数据集状态不再有效,或者更倾向于使用旧版本。例如,进行本地开发测试时出错,或者系统更新失败导致系统功能受损,或需要恢复被删除的文件或目录,这些情况都很常见。要回滚到快照,可以使用 zfs rollback <snapshotname>
命令。如果有大量更改,操作可能会花费很长时间。在此期间,数据集始终保持一致的状态,就像一个符合 ACID 原则的数据库执行回滚一样。这个过程在数据集在线并且无需停机的情况下进行。回滚后,数据集的状态将恢复到原始快照创建时的状态。回滚到快照会丢弃该数据集中所有不属于该快照的其他数据。如果在回滚到先前快照之前拍摄当前状态的快照,那么在之后需要某些数据时,可以方便地进行回滚。这样,用户可以在快照之间来回切换,而不会丢失仍然有价值的数据。
在第一个示例中,由于不小心执行了 rm
命令删除了超过预期的数据,因此回滚到一个快照:
# zfs list -rt all mypool/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp 262K 93.2G 120K /var/tmp
mypool/var/tmp@my_recursive_snapshot 88K - 152K -
mypool/var/tmp@after_cp 53.5K - 118K -
mypool/var/tmp@diff_snapshot 0 - 120K -
# ls /var/tmp
passwd passwd.copy vi.recover
# rm /var/tmp/passwd*
# ls /var/tmp
vi.recover
此时,用户发现删除了多余的文件,并希望恢复它们。ZFS 提供了一种简单的方式来恢复文件,只需定期执行重要数据的快照。在回滚到上一个快照后,可以恢复丢失的文件,并从该点重新开始:
# zfs rollback mypool/var/tmp@diff_snapshot
# ls /var/tmp
passwd passwd.copy vi.recover
回滚操作将数据集恢复到最后一个快照的状态。回滚到一个较早的快照并且之后有其他快照也是可能的。在尝试这样做时,ZFS 会发出以下警告:
# zfs list -rt snapshot mypool/var/tmp
AME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot 88K - 152K -
mypool/var/tmp@after_cp 53.5K - 118K -
mypool/var/tmp@diff_snapshot 0 - 120K -
# zfs rollback mypool/var/tmp@my_recursive_snapshot
cannot rollback to 'mypool/var/tmp@my_recursive_snapshot': more recent snapshots exist
use '-r' to force deletion of the following snapshots:
mypool/var/tmp@after_cp
mypool/var/tmp@diff_snapshot
此警告表示,在用户想要回滚到的快照和当前数据集状态之间存在其他快照。要完成回滚,需要删除这些快照。由于快照是只读的,ZFS 无法跟踪数据集在不同状态之间的所有更改,除非用户指定 -r
选项确认这是所需的操作。如果这是预期的操作,并理解删除所有中间快照的后果,可以执行以下命令:
# zfs rollback -r mypool/var/tmp@my_recursive_snapshot
# zfs list -rt snapshot mypool/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot 8K - 152K -
# ls /var/tmp
vi.recover
从 zfs list -t snapshot
的输出可以确认,在执行 zfs rollback -r
后,中间快照已被删除。
22.4.5.4. 从快照中恢复单个文件
快照存储在父数据集下的一个隐藏目录中:.zfs/snapshots/snapshotname。默认情况下,即使执行标准的 ls -a
命令,这些目录也不会显示。尽管目录不显示,仍然可以像访问普通目录一样访问它。名为 snapdir
的属性控制这些隐藏目录是否显示在目录列表中。将该属性设置为 visible
可以让它们出现在 ls
和其他涉及目录内容的命令的输出中。
# zfs get snapdir mypool/var/tmp
NAME PROPERTY VALUE SOURCE
mypool/var/tmp snapdir hidden default
# ls -a /var/tmp
. .. passwd vi.recover
# zfs set snapdir=visible mypool/var/tmp
# ls -a /var/tmp
. .. .zfs passwd vi.recover
通过从快照中将文件复制回父数据集,可以将单个文件恢复到先前的状态。.zfs/snapshot 下的目录结构包含一个与之前创建的快照同名的目录,便于识别。以下示例展示了如何从隐藏的 .zfs 目录中恢复文件,通过将文件从包含最新版本的快照复制回来:
# rm /var/tmp/passwd
# ls -a /var/tmp
. .. .zfs vi.recover
# ls /var/tmp/.zfs/snapshot
after_cp my_recursive_snapshot
# ls /var/tmp/.zfs/snapshot/after_cp
passwd vi.recover
# cp /var/tmp/.zfs/snapshot/after_cp/passwd /var/tmp
即使将 snapdir
属性设置为 hidden,执行 ls .zfs/snapshot
命令仍然会列出该目录的内容。管理员可以决定是否显示这些目录。该设置是针对每个数据集的。复制文件或目录从这个隐藏的 .zfs/snapshot 目录非常简单。反过来尝试复制文件到快照目录时会出现以下错误:
# cp /etc/rc.conf /var/tmp/.zfs/snapshot/after_cp/
cp: /var/tmp/.zfs/snapshot/after_cp/rc.conf: Read-only file system
此错误提醒用户,快照是只读的,创建后不能更改。无法将文件复制到快照目录中或从中删除文件,因为这样会改变它们所表示的数据集的状态。
快照根据自快照创建以来父文件系统的变化情况来消耗空间。快照的 written
属性跟踪该快照使用的空间。
要销毁快照并回收空间,可以使用 zfs destroy <数据集>@<快照>
命令。加上 -r
选项可以递归地删除父数据集下同名的所有快照。使用 -n -v
选项,命令会显示要删除的快照列表及其回收空间的估算值,而不会执行实际的销毁操作。
22.4.6. 管理克隆
克隆是某快照的副本,能像常规的数据集一样处理它。不同于快照,克隆是可写的和可挂载的,并且有自己的属性。在使用 zfs clone
创建克隆之后,无法销毁原始快照。要反转克隆和快照之间的父子关系,可以使用 zfs promote
。提升克隆将使快照成为克隆的子项,而不是原始父数据集的子项。这将改变 ZFS 对空间的计算方式,但不会实际改变占用的空间。克隆可以挂载到 ZFS 文件系统层次结构中的任何位置,而不仅仅是原始快照所在的位置。
下面是展示克隆功能的示例数据集:
# zfs list -rt all camino/home/joe
NAME USED AVAIL REFER MOUNTPOINT
camino/home/joe 108K 1.3G 87K /usr/home/joe
camino/home/joe@plans 21K - 85.5K -
camino/home/joe@backup 0K - 87K -
克隆的典型用途是在保持快照的同时,尝试对特定数据集进行实验,以便在出现问题时可以恢复。由于快照无法更改,可以创建一个可读写的快照克隆。经过实验并取得理想结果后,可以提升克隆为数据集,并删除原始文件系统。删除原始数据集不是严格必要的,因为克隆和数据集可以共存而不会发生问题。
# zfs clone camino/home/joe@backup camino/home/joenew
# ls /usr/home/joe*
/usr/home/joe:
backup.txz plans.txt
/usr/home/joenew:
backup.txz plans.txt
# df -h /usr/home
Filesystem Size Used Avail Capacity Mounted on
usr/home/joe 1.3G 31k 1.3G 0% /usr/home/joe
usr/home/joenew 1.3G 31k 1.3G 0% /usr/home/joenew
创建克隆时,它是快照创建时数据集的完全副本。现在可以独立于其原始数据集更改克隆。两者之间的连接是快照,ZFS 会在 origin
属性中记录这个连接。使用 zfs promote
提升克隆将使克隆成为独立的数据集。这将删除 origin
属性的值,并断开克隆与快照之间的连接。以下示例展示了这一过程:
# zfs get origin camino/home/joenew
NAME PROPERTY VALUE SOURCE
camino/home/joenew origin camino/home/joe@backup -
# zfs promote camino/home/joenew
# zfs get origin camino/home/joenew
NAME PROPERTY VALUE SOURCE
camino/home/joenew origin - -
在进行了一些更改后,比如将 loader.conf 复制到提升后的克隆中,旧目录在这种情况下变得过时。此时可以用提升后的克隆来替换它。要实现这一点,首先 zfs destroy
删除旧的数据集,然后 zfs rename
将克隆重命名为旧的数据集名称(或者完全不同的名称)。
# cp /boot/defaults/loader.conf /usr/home/joenew
# zfs destroy -f camino/home/joe
# zfs rename camino/home/joenew camino/home/joe
# ls /usr/home/joe
backup.txz loader.conf plans.txt
# df -h /usr/home
Filesystem Size Used Avail Capacity Mounted on
usr/home/joe 1.3G 128k 1.3G 0% /usr/home/joe
现在,克隆的快照变成了一个普通的数据集。它包含来自原始快照的所有数据以及新增的文件,如 loader.conf。克隆为 ZFS 用户提供了在不同场景中非常有用的功能。例如,可以为 jail 提供包含不同应用集的快照。用户可以克隆这些快照并根据需要添加自己的应用程序。待对更改感到满意,就可以将克隆提升为完整的数据集,并将其提供给最终用户,用户可以像使用真正的数据集一样使用它。这在提供这些 jail 时节省了时间和管理开销。
22.4.7. 复制
将数据仅存储在单个池中的单一位置,会使其暴露于诸如盗窃、自然灾害或人为灾害等风险中。因此,定期备份整个池至关重要。ZFS 提供了内置的序列化功能,可以将数据的流表示发送到标准输出。利用此功能,可以将数据存储到连接到本地系统的另一个池中,也可以通过网络将其发送到另一个系统。快照是这种复制的基础(请参见 ZFS 快照 部分)。用于复制数据的命令是 zfs send
和 zfs receive
。
以下示例展示了如何使用这两个池进行 ZFS 复制:
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 77K 896M - - 0% 0% 1.00x ONLINE -
mypool 984M 43.7M 940M - - 0% 4% 1.00x ONLINE -
名为 mypool 的池是主池,用于定期读写数据。使用第二个备用池 backup,以防主池不可用。请注意,ZFS 不会自动执行故障转移,管理员需要在需要时手动进行。使用快照提供一致的文件系统版本进行复制。在创建了 mypool 的快照后,可以通过复制快照将其复制到 backup 池。这不包括自上次快照以来的更改。
# zfs snapshot mypool@backup1
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool@backup1 0 - 43.6M -
现在已经存在了一个快照,使用 zfs send
创建一个表示快照内容的流。将此流存储为文件,或在另一个池上接收它。必须将流重定向到文件或管道,否则会出现错误:
# zfs send mypool@backup1
Error: Stream can not be written to a terminal.
You must redirect standard output.
要备份数据集,使用 zfs send
时将流重定向到挂载的备份池上的文件。确保池有足够的可用空间来容纳发送的快照的大小,这意味着快照中包含的数据,而不是与上一个快照的更改。
# zfs send mypool@backup1 > /backup/backup1
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 63.7M 896M - - 0% 6% 1.00x ONLINE -
mypool 984M 43.7M 940M - - 0% 4% 1.00x ONLINE -
zfs send
将名为 backup1 的快照中的所有数据传输到了名为 backup 的池。要自动创建并发送这些快照,可以使用 cron(8) 任务。
与存储为归档文件不同,ZFS 可以将它们接收为活动文件系统,从而允许直接访问备份数据。要访问这些流中包含的实际数据,可以使用 zfs receive
将流转换回文件和目录。以下示例将 zfs send
和 zfs receive
结合起来,使用管道将数据从一个池复制到另一个池。在传输完成后,直接在接收池上使用数据。只能将数据集复制到一个空的数据集。
# zfs snapshot mypool@replica1
# zfs send -v mypool@replica1 | zfs receive backup/mypool
send from @ to mypool@replica1 estimated size is 50.1M
total estimated size is 50.1M
TIME SENT SNAPSHOT
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 63.7M 896M - - 0% 6% 1.00x ONLINE -
mypool 984M 43.7M 940M - - 0% 4% 1.00x ONLINE -
22.4.7.1. 增量备份
zfs send
还可以确定两个快照之间的差异,并仅发送这两者之间的差异。这节省了磁盘空间和传输时间。例如:
# zfs snapshot mypool@replica2
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
mypool@replica1 5.72M - 43.6M -
mypool@replica2 0 - 44.1M -
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 61.7M 898M - - 0% 6% 1.00x ONLINE -
mypool 960M 50.2M 910M - - 0% 5% 1.00x ONLINE -
创建第二个名为 replica2 的快照。这个第二个快照包含自上一个快照 replica1 以来对文件系统所做的更改。使用 zfs send -i
并指定这两个快照对,会生成一个增量复制流,其中仅包含更改的数据。如果初始快照已经存在于接收端,则此操作成功。
# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive /backup/mypool
send from @replica1 to mypool@replica2 estimated size is 5.02M
total estimated size is 5.02M
TIME SENT SNAPSHOT
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 80.8M 879M - - 0% 8% 1.00x ONLINE -
mypool 960M 50.2M 910M - - 0% 5% 1.00x ONLINE -
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
backup 55.4M 240G 152K /backup
backup/mypool 55.3M 240G 55.2M /backup/mypool
mypool 55.6M 11.6G 55.0M /mypool
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
backup/mypool@replica1 104K - 50.2M -
backup/mypool@replica2 0 - 55.2M -
mypool@replica1 29.9K - 50.0M -
mypool@replica2 0 - 55.0M -
增量流仅复制了更改的数据,而不是整个 replica1。仅发送差异使传输时间大大缩短,并通过不每次复制整个池来节省磁盘空间。在通过慢速网络或按传输字节收费的网络进行复制时,这非常有用。
现在,一个新的文件系统 backup/mypool 可用了,其中包含 mypool 池的数据和文件。指定 -p
选项将复制数据集的属性,包括压缩设置、配额和挂载点。指定 -R
选项将复制数据集的所有子数据集及其属性。可以自动化发送和接收过程,以便在第二个池上创建定期备份。
22.4.7.2. 通过 SSH 发送加密备份
通过网络发送流数据是保持远程备份的一个好方法,但它有一个缺点:通过网络链路发送的数据没有加密,这使得任何人都可以拦截并将流数据转回原始数据,而不需要发送用户的授权。这在通过互联网向远程主机发送数据时是不可取的。为确保数据的安全传输,可以使用 SSH 对发送的数据进行加密。由于 ZFS 要求将流从标准输出重定向,因此可以通过 SSH 将其轻松地传输。为了确保文件系统的内容在传输过程中和在远程系统上都保持加密,考虑使用 PEFS。
首先更改一些设置并采取安全预防措施。以下描述了执行 zfs send
操作所需的步骤;有关 SSH 的更多信息,请参见 OpenSSH。
更改配置如下:
在发送和接收主机之间使用 SSH 密钥进行无密码 SSH 访问
ZFS 需要
root
用户的权限来发送和接收流。这要求以root
身份登录到接收系统。出于安全原因,默认情况下禁止
root
登录。使用 ZFS 授权 系统来允许每个系统上的非
root
用户执行相应的发送和接收操作。在发送系统上:
# zfs allow -u someuser send,snapshot mypool
要挂载池,非特权用户必须拥有该目录,而且常规用户需要有挂载文件系统的权限。
在接收系统上:
# sysctl vfs.usermount=1
vfs.usermount: 0 -> 1
# echo vfs.usermount=1 >> /etc/sysctl.conf
# zfs create recvpool/backup
# zfs allow -u someuser create,mount,receive recvpool/backup
# chown someuser /recvpool/backup
现在,非特权用户可以接收和挂载数据集,并将 home 数据集复制到远程系统:
% zfs snapshot -r mypool/home@monday
% zfs send -R mypool/home@monday | ssh someuser@backuphost zfs recv -dvu recvpool/backup
首先创建名为 monday 的递归快照,针对文件系统数据集 home 在池 mypool 上。然后 zfs send -R
会将数据集、所有子数据集、快照、克隆以及设置包含在流中。将输出通过 SSH 管道传输到远程主机 backuphost 上等待的 zfs receive
。使用 IP 地址或完全限定的域名是一种好的做法。接收机器将数据写入 recvpool 池上的 backup 数据集。添加 -d
到 zfs recv
会覆盖接收方池的名称为快照的名称。-u
会导致文件系统在接收方不挂载。使用 -v
可以显示更多关于传输的详细信息,包括传输的时间和传输的数据量。
22.4.8. 数据集、用户和组配额
使用 数据集配额 来限制特定数据集消耗的空间。 引用配额 的工作方式与数据集配额相似,但它计算的是数据集本身使用的空间,排除快照和子数据集。同样,可以使用 用户 和 组 配额来防止用户或组使用完池或数据集中的所有空间。
以下示例假设用户已经存在于系统中。在将用户添加到系统之前,确保先创建他们的家目录数据集并将 mountpoint
设置为 /home/bob
。然后,创建用户并使家目录指向数据集的 mountpoint
位置。这将正确设置拥有者和组权限,而不会遮蔽可能存在的任何先前的家目录路径。
要对 storage/home/bob 强制实施 10 GB 的数据集配额:
# zfs set quota=10G storage/home/bob
要对 storage/home/bob 强制实施 10 GB 的引用配额:
# zfs set refquota=10G storage/home/bob
要删除 storage/home/bob 的 10 GB 配额:
# zfs set quota=none storage/home/bob
一般格式为 userquota@user=size
,且用户的名称可以是以下任一格式:
POSIX 兼容的名称,如 joe。
POSIX 数字 ID,如 789。
SID 名称,如 joe.bloggs@example.com。
SID 数字 ID,如 S-1-123-456-789。
例如,要为名为 joe 的用户强制实施 50 GB 的用户配额:
# zfs set userquota@joe=50G
要删除任何配额:
# zfs set userquota@joe=none
注意
用户配额属性不会通过
zfs get all
显示。非root
用户无法看到其他用户的配额,除非授予userquota
特权。具有此特权的用户可以查看并设置所有人的配额。
设置组配额的一般格式为:groupquota@组=大小
。
要将组 firstgroup 的配额设置为 50 GB,请使用:
# zfs set groupquota@firstgroup=50G
要删除组 firstgroup 的配额,或者确保没有设置配额,请使用:
# zfs set groupquota@firstgroup=none
与用户配额属性类似,非 root
用户可以查看其所属组的配额。具有 groupquota
特权的用户或 root
可以查看并设置所有组的配额。
要显示每个用户在文件系统或快照中使用的空间以及任何配额,请使用 zfs userspace
。有关组信息,请使用 zfs groupspace
。有关支持的选项或如何单独显示特定选项的更多信息,请参见 zfs(1)。
特权用户和 root
可以列出 storage/home/bob 的配额,方法是:
# zfs get quota storage/home/bob
22.4.9. 保留空间
保留空间 保证数据集始终有可用空间。保留的空间将不会分配给任何其他数据集。这个有用的功能确保了重要的数据集或日志文件有足够的空间。
reservation
属性的一般格式为 reservation=大小
,因此,要对 storage/home/bob 设置 10 GB 的保留空间,请使用:
# zfs set reservation=10G storage/home/bob
要清除所有保留空间:
# zfs set reservation=none storage/home/bob
对于设置 引用保留空间 的 refreservation
属性,适用相同的原则,格式为 refreservation=size
。
此命令显示了 storage/home/bob 上存在的任何保留空间或引用保留空间:
# zfs get reservation storage/home/bob
# zfs get refreservation storage/home/bob
22.4.10. 压缩
ZFS 提供透明压缩。在块级别压缩写入的数据可以节省空间,并且还提高了磁盘吞吐量。如果数据压缩率为 25%,压缩后的数据写入磁盘的速度与未压缩版本相同,结果是有效写入速度提高了 125%。压缩也可以是 去重 的一个很好的替代方案,因为它不需要额外的内存。
ZFS 提供了不同的压缩算法,每种算法都有不同的权衡。ZFS v5000 引入了 LZ4 压缩,它能在不牺牲其他算法的性能的情况下压缩整个池。LZ4 的最大优势是 提前中止 特性。如果 LZ4 在数据的头部部分没有达到至少 12.5% 的压缩率,ZFS 会将该块数据未压缩地写入,以避免浪费 CPU 周期在已压缩或无法压缩的数据上进行压缩。有关 ZFS 中不同压缩算法的详细信息,请参见术语部分中的 压缩 条目。
管理员可以通过数据集属性查看压缩效果。
# zfs get used,compressratio,compression,logicalused mypool/compressed_dataset
NAME PROPERTY VALUE SOURCE
mypool/compressed_dataset used 449G -
mypool/compressed_dataset compressratio 1.11x -
mypool/compressed_dataset compression lz4 local
mypool/compressed_dataset logicalused 496G -
该数据集使用了 449 GB 空间(used
属性)。如果没有压缩,它将占用 496 GB 空间(logicalused
属性)。这得到了 1.11:1 的压缩比。
压缩与 用户配额 结合时可能会产生意想不到的副作用。用户配额限制的是用户在数据集上消耗的实际空间 压缩后 的空间。如果用户有 10 GB 的配额,并写入 10 GB 可压缩的数据,他们仍然可以存储更多数据。如果他们稍后更新某个文件,比如一个数据库,使用更多或更少可压缩的数据,他们可用的空间会发生变化。这可能导致一个奇怪的情况,即用户并没有增加实际的数据量(logicalused
属性),但由于压缩的变化,他们已经达到了配额限制。
压缩与备份的交互也可能会有类似的意外效果。配额通常用于限制数据存储,以确保有足够的备份空间可用。由于配额不考虑压缩,ZFS 可能会写入比未压缩备份更大的数据。
22.4.11. Zstandard 压缩
OpenZFS 2.0 增加了一种新的压缩算法。Zstandard(Zstd)提供比默认的 LZ4 更高的压缩比,同时比替代算法 gzip 提供更高的速度。OpenZFS 2.0 从 FreeBSD 12.1-RELEASE 开始通过 sysutils/openzfs 提供,并从 FreeBSD 13.0-RELEASE 起 OpenZFS 2.0 成为了默认选项。
Zstd 提供了多种压缩级别,允许对性能和压缩比进行细粒度的控制。Zstd 的一个主要优势是解压缩速度与压缩级别无关。对于那些只写一次但频繁读取的数据,Zstd 允许使用最高的压缩级别,而不会带来读取性能的惩罚。
即使在频繁更新数据的情况下,启用压缩通常也能提供更高的性能。最大优势之一来自于压缩 ARC 特性。ZFS 的自适应替换缓存(ARC)将数据的压缩版本缓存到内存中,每次解压缩时使用。这使得相同的内存容量可以存储更多的数据和元数据,从而提高缓存命中率。
ZFS 提供了 19 个 Zstd 压缩级别,每个级别都提供逐渐增加的空间节省,但会导致更慢的压缩。默认级别为 zstd-3
,它提供比 LZ4 更高的压缩比,同时不会慢得太多。级别 10 以上需要大量内存来压缩每个块,因此系统内存小于 16 GB 的系统不应使用它们。ZFS 还使用了一些 Zstd_fast_
级别,它们速度更快,但支持的压缩比更低。ZFS 支持 zstd-fast-1
到 zstd-fast-10
,zstd-fast-20
到 zstd-fast-100
(以 10 为增量),以及 zstd-fast-500
和 zstd-fast-1000
,这些提供的压缩比最小,但性能极高。
如果 ZFS 无法为一个块分配所需的内存来进行 Zstd 压缩,它将回退到将该块存储为未压缩。除非在内存受限的系统上使用 Zstd 的最高级别,否则这种情况不太可能发生。ZFS 会计算从加载 ZFS 模块以来,发生这种情况的次数,结果可以通过 kstat.zfs.misc.zstd.compress_alloc_fail
查看。
22.4.12. 去重
启用去重后,去重 使用每个块的校验和来检测重复的块。当一个新块是现有块的重复时,ZFS 会写入一个新的引用到现有数据,而不是写入整个重复块。如果数据包含大量重复的文件或重复的信息,则可能节省大量空间。警告:去重需要大量内存,启用压缩则可以在没有额外成本的情况下提供大部分的空间节省。
要启用去重,请在目标池上设置 dedup
属性:
# zfs set dedup=on pool
去重仅影响写入池的新增数据。仅仅启用此选项并不会去重已经写入池中的数据。一个新启用去重属性的池将像下面这个例子:
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
pool 2.84G 2.19M 2.83G - - 0% 0% 1.00x ONLINE -
DEDUP
列显示了池的实际去重率。值为 1.00x
表示数据尚未去重。下一个例子将一些系统二进制文件复制三次到不同的目录中,以观察去重效果:
# for d in dir1 dir2 dir3; do
> mkdir $d && cp -R /usr/bin $d &
> done
要观察冗余数据的去重效果,可以使用:
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
pool 2.84G 20.9M 2.82G - - 0% 0% 3.00x ONLINE -
DEDUP
列显示了 3.00x
的去重系数。检测并去重数据副本只使用了三分之一的空间。节省空间的潜力巨大,但需要足够的内存来跟踪去重的块。
当池中的数据没有冗余时,去重未必总是有益的。ZFS 可以通过模拟去重来显示潜在的空间节省:
# zdb -S pool
Simulated DDT histogram:
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 2.58M 289G 264G 264G 2.58M 289G 264G 264G
2 206K 12.6G 10.4G 10.4G 430K 26.4G 21.6G 21.6G
4 37.6K 692M 276M 276M 170K 3.04G 1.26G 1.26G
8 2.18K 45.2M 19.4M 19.4M 20.0K 425M 176M 176M
16 174 2.83M 1.20M 1.20M 3.33K 48.4M 20.4M 20.4M
32 40 2.17M 222K 222K 1.70K 97.2M 9.91M 9.91M
64 9 56K 10.5K 10.5K 865 4.96M 948K 948K
128 2 9.50K 2K 2K 419 2.11M 438K 438K
256 5 61.5K 12K 12K 1.90K 23.0M 4.47M 4.47M
1K 2 1K 1K 1K 2.98K 1.49M 1.49M 1.49M
Total 2.82M 303G 275G 275G 3.20M 319G 287G 287G
dedup = 1.05, compress = 1.11, copies = 1.00, dedup * compress / copies = 1.16
在 zdb -S
完成分析后,它会显示启用去重可能带来的空间减少比例。在这个例子中,1.16
是一个较低的空间节省比例,主要由压缩提供。如果在此池上启用去重,将不会节省任何空间,并且启用去重所需的内存量不值得。通过使用公式 ratio = dedup * compress / copies,系统管理员可以计划存储分配,决定工作负载是否包含足够的重复块来证明内存需求。如果数据具有良好的可压缩性,空间节省可能会很大。最佳实践是首先启用压缩,因为压缩也提供了显著的性能提升。在去重可以提供显著节省且有足够内存支持 DDT 的情况下启用去重。
22.4.13. ZFS 和 Jail
使用 zfs jail
和相应的 jailed
属性将 ZFS 数据集分配给 Jail。zfs jail jailid
将数据集附加到指定的 jail,zfs unjail
将其分离。要在 jail 内部控制数据集,请设置 jailed
属性。ZFS 禁止在主机上挂载被 jail 限制的数据集,因为它可能包含会危及主机安全的挂载点。
最后更新于
这有帮助吗?