Comment on page
22.4.zfs 管理
与传统的磁盘和卷管理器不同,ZFS 中的空间不是预先分配的。在传统的文件系统中,在分区和分配空间之后,如果不增加一个新的磁盘,就没有办法增加一个新的文件系统。使用 ZFS,创建新的文件系统在任何时候都是可能的。每个数据集 的属性包括压缩、重复数据删除、缓存和配额等功能,以及其他有用的属性,如只读、大小写敏感性、网络文件共享和挂载点。数据集之间的嵌套是可能的,子数据集将继承其祖先的属性。委托、复制、快照、jail 允许将每个数据集作为一个单元进行管理和销毁。为每个不同类型或一组文件创建一个单独的数据集有其优势。拥有大量的数据集的缺点是一些命令,如
zfs list
会比较慢,而且挂载数百甚至数千的数据集会减慢 FreeBSD 的引导过程。# 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
来列出由该操作销毁的数据集和快照,而不是实际销毁任何东西。通过销毁快照回收的空间也会被显示出来。卷是一种特殊的数据集类型。与其作为一个文件系统挂载,不如把它作为一个块设备暴露在 /dev/zvol/poolname/dataset 下。这将允许把卷用于其他文件系统,备份虚拟机的磁盘,或者使用 iSCSI 或 HAST 等协议使其对其他网络主机可用。
用任何文件系统格式化卷,或者不使用文件系统来存储原始数据。对用户来说,卷看起来就是一个普通的磁盘。把普通文件 系统放在这些 zvols 上,可以提供普通磁盘或文件系统所不具备的功能。例如,在一个 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)
销毁卷与销毁普通的文件系统数据集是一样的。该操作几乎是瞬间完成的,但可能需要几分钟的时间来回收后台的空闲空间。
要改变数据集的名称,使用
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 -
每个 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
#
两个最常用的数据集属性是共享 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
。要设置数据集 NFS 共享参数,输入:# zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home
快照功能是 ZFS 最强大的功能之一。快照提供了数据集只读和时间指针引用的复制功能。使用 写入时复制(Copy-On-Write,COW)功能,ZFS 可以在保留磁盘上老版本数据的同时快速创建快照。如果没有快照存在,当数据被重写覆盖或删除时 ZFS 就会回收磁盘空间供将来使用。快照功能通过仅记录数据集当前和过去版本的变化来节约磁盘空间。要打开基于整个数据集上的快照功能,而不是在文件或目录层面。数据集的快照会复制数据集中的所有内容,包括文件系统属性,文件,目录,权限等等。首次创建时快照不占用额外空间,但当它引用的数据块发生改变时就会开始消耗磁盘空间。用
-r
来创建的数据集及其子集的同名快照提供了文件系统在同一时间片段上的即时快照。这种功能在应用程序的文件位于相关数据集上或这些文件互相依赖时非常有用。如果没有了快照功能,备份时需要保存文件在不同时间点的许多份副本。ZFS 里的快照功能还提供了其它文件系统快照所不具备的许多不同特性。举例来说快照的一个典型用处是当执行软件安装或系统升级等有风险的操作时用来快速备份文件系统的当前状态。如果操作失败,就可以退回创建快照时的系统状态。如果升级顺利,则可以删除快照来释放磁盘空间。没有快照功能的话,升级失败往往需要进行备份恢复,而那是及其繁琐和耗费时间的,而且可能还会带来系统无法使用的宕机状态。快照恢复非常迅速,甚至可以在系统正常运行时进行,带来极少或不带来任何宕机时间。考虑到需要从备份恢复的数据复制时间的话快照为 TB 级存储系统带来的时间节约是及其巨大的。快照不是存储池完整备份的替代方案,但提供了一种在特定时间保存数据集的快速而又简便的方法。
用
zfs snapshot
dataset@snapshotname 来创建快照,添加 -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
命令不会显示快照。要显示快照列表。添加 -t snapshot
参数到 zfs list
。-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
列发生了改变,显示出之前快照和当前快照所占的不同磁盘空间。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
这条命令列出了指定快照(如此例 mymypool/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
可以在任何时候恢复已有快照。在很多情况下当前数据集状态已经不需要或更愿意使用老版本时就经常需要这样做。或者在本地开发测试出问题时,有缺陷的系统更新妨害了系统的正常功能,或者需要恢复删除的文件或目录时也往往需要这样做。要恢复到某个快照,使用
zfs roolback
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 不能保留数据集不同状态之间的所有变化,因为快照是只读的。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
命令的执行结果。快照保存在父数据集的一个隐藏目录下:.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
属性被设置为隐藏,运行 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
dataset@snapshot 命令。添加 -r
参数可以递归删除父数据集下有同样名称的所有快照。添加 -n -v
到命令行可以在不执行实际销毁操作下显示将被删除的快照和预计回收的磁盘空间大小。camino 是快照的一份副本,处理起来更近似于一个普通的数据集。与快照不同,camino 可写也可挂载,有它自己的属性。在使用
zfs clone
之后,可以销毁原始快照。要对调 camino 和快照的 child/parent 关系可以使用 zfs promote
命令。child/parent
关系对调之后快照就会变 成 camino 的 child,而不是最开始的父数据集。这回改变 ZFS 所占磁盘空间的方式,但不实际改变消耗的磁盘空间。把 camino 挂载到 ZFS 文件系统的任何层级中的位置都是可以的,并不只是在原快照位置之下的层级。下面的示例数据集可以显示 camino 的一些特性:
# 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 -
camino 的典型作用是在一些特定的数据集上做实验,当出现问题时还可以使用快照进行恢复。因为快照内容是不能改变的,所以需要创建一个可读也可写的快照的 camino。在 camino 中取得想要的结果后,就可以提升 camino 让它成为一个数据集并删除老的文件系统。删除父数据集严格来说并不必要,因为 camino 和数据集可以无障碍地同时存在。
# 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
创建 camino 等于是产生了一个快照创建时的数据集的实际副本。这样就可以在不影响原数据集的情况下改变 camino 里的内容。他们之间通过快照进行关联。ZFS 在名为
origin
的属性中保存了这种关连。用 zfs promote
提升这个 camino 可以让 camino 成为一个独立的数据集。这会删除 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 到 promote 之后的 camino 对它进行改变后,例如,老的目录就失效了。然而,提升之后的 camino 可以取代它。要这样做,可以用
zfs destroy
来先销毁原来的数据集然后再用 zfs rename
命令来把 camino 命名为原来的数据集名称(也可以命名为完全不同的名称)。# 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
快照的 camino 现在已经是一个普通的数据集。它含有源快照的所有数据和新增加的文件,如 loader.conf。camino 在不同场景下为 ZFS 用户提供了有用的功能。例如。把包含不同软件集合的快照 作为 jail 来使用。如果需要的话用户可以 camino 这些快照并加入他们自己的应用程序。如果对变更感到满意,就提升 camino 成为完成的数据集,对最终用户来说用起来就和真正的数据集别无两样。提供这些 jail 可以节约时间和管理开销。
把单独的存储池的数据存放在一个地方会有被盗,自然或人为灾害的风险。定期为整个存储池创建备份至关重要。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 可以把接收到的数据作为实时的文件系统来对待而不是存储为备份文件,允许直接访问这些数据。要获取这些确切的流数据,可以使用
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 -
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 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 -
增量流仅复制改变的数据而不是整个 replica1。发送差异化部分只要花少的多的时间并能够节约磁盘空间,不需要每次复制整个存储池。这个功能非常有用,尤其是在通过较慢的网络来复制或按传送的字节来收费时。
一个新的文件系统 backup/mupool,在来自存储池 mypool 的文件和数据到位之后就可以随时待命了。通过
-p
来指定复制数据集的属性,包含压缩设置,容量配额,挂载点等等。通过 -R
来指定复制数据集的所有子数据集及其属性。自动化式发送和接收可以在第二个存储池上建立定期备份。通过网络发送流数据是进行远程备份的好方法,但也带来了一些缺陷。通过网络链接发送的数据是不加密的,任何人可以在数据发送方不知道的情况下截取流数据并把它们传输到其它地方。通过互联网向远程主机进行流数据传输时就非常危险,这样就需要使用 SSH 在网络传输时进行安全加密。ZFS 本来就需要从标准输出重定向流数据,使用 SSH 就很方便。如果在传输时还要对文件系统的内容 在远端系统进行加密,就可以考虑使用 PEFS。
按下列步骤更改设置:
- 使用 SSH 密钥来启用发送端和接收端主机的 SSH 功能。
- ZFS 需要
root
用户权限来发送和接收流数据。用root
账号登录接收端系统。 - 默认情况下禁止使用
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
为存储池 mypool 上的文件系统数据集 home 创建名为 monday 的递归快照,然后用
zfs send -R
命令来把数据集和其所有子集,快照,camino,设置等等包含进需要发送的数据流。用 SSH 方式传递到远端使用 zfs receive
命令的主机 backuphost。这里也可以使用主机的 IP 地址或合格的域名。接收端主机把数据写入存储池 recvpool 中的 backup 数据集。添加 -d
到 zfs recv
来用快照名替换接收端的存储池名称。-u
参数则不会让接收端文件系进行 mount。使用 -v
来显示传输情况的更多详细信息,包括已用时间和已传输的数据总量。下例例子假设系统中已经存在这些用户。在向系统添加用户时,先确保创建用户的 home 数据集并将挂载点设为
/home/bob
。然后创建用户并让 home 目录指向数据集的挂载点
位置。这样就可以正确地设置用户和组相关权限而不覆盖现有可能存在的 home 目录路径。强制设置数据集 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 配额:
# zfs set quota=none storage/home/bob
常规格式是
userquota@user=size
,用户名必须符合以下标准之一:- POSIX 兼容命名如 joe
- POSIX 数字 ID 如 789
- SID 命名如 [email protected]
- 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@group=size
如要设置 firstgroup 的组配额为 50 GB,可以使用:
# zfs set groupquota@firstgroup=50G
使用用户配额属性,非
root
权限用户可以看到他们所属的组的配额。有 groupquota
授权的用户或 root
用户可以看到和设置所有组别的配额。如要显示每个用户在文件系统或快照中的配额,使用
zfs userspace
。用 zfs groupspace
可以看到组相关的配额信息。若要了解更多其它参数或如何单独查看特定参数,可以参考zfs(1)。授权用户和
root
用户可以使用以下命令查看 storage/home/bob 的配额:# zfs get quota storage/home/bob
reservation
属性的通常格式为 reservation=size
,如要为 storage/home/bob 设置 10 GB 的保留空间,使用命令:# zfs set reservation=10G storage/home/bob
取消所有保留空间:
# zfs set reservation=none storage/home/bob
下面命令会显示和 storage/home/bob 相关的任何保留空间或引用保留空间:
# zfs get reservation storage/home/bob
# zfs get refreservation storage/home/bob
ZFS 提供了透明化的压缩功能。在数据块层面的写入上进行数据压缩可以节约磁盘空间,也可以提高存盘的吞吐效率。如果数据压缩 25%,压缩后的数据会和未压缩时相同的速率被写入磁盘,有效写入速度就达到了 125%。压缩也可以成为去重功能之外的另一个好选择,因为它并不需要额外的内存。
ZFS 提供了不同的压缩算法,每种都有各自一些劣势。ZFS v5000 的 LZ4 压缩方式可以将整个存储池进行压缩,不会像其它算法那样带来大的性能损失。LZ4 的最大优势是 early abort 功能,如果 LZ4 不能在数据 header 部分获得至少 12.5% 以上的压缩率,它就不会对数据块进行压缩以避免浪费 CPU 资源来压缩那些之前要么已被压缩或未被压缩的数据。要了解 ZFS 中可用的不同压缩算法,可以参考术语部分的压缩条目。