19.4. zfs 管理

zfs 工具负责建立、摧毁与管理在一个存储池中所有的 ZFS 数据集。存储池使用 zpool 来管理。

19.4.1. 创建和删除数据集

不同于传统的磁盘与磁盘区管理程序(Volume manager),在 ZFS 中的空间并会预先分配。传统的文件系统在分割与分配空间完后,若没有增加新的磁盘便无法再增加额外的文件系统。在 ZFS,可以随时建立新的文件系统,每个数据集(Dataset)都有自己的属性,包含压缩(Compression)、去重复(Deduplication)、快取(Caching)与配额(Quota)功能以及其他有用的属性如只读(Readonly)、区分大小写(Case sensitivity)、网路档案分享(Network file sharing)以及挂载点(Mount point)。数据集可以存在于其他数据集中,且子数据集会继承其父数据集的属性。每个数据集都可以作为一个单位来管理、委托(Delegate)、备份(Replicate)、快照(Snapshot)、监禁(Jail) 与摧毁(Destroy),替每种不同类型或集合的档案建立各别的数据集还有许多的好处。唯一的缺点是在当有非常大数量的数据集时,部份指令例如 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

摧毁数据集会比删除所有在数据集上所残留的档案来的快,由于摧毁数据集并不会扫描所有档案并更新所有相关的 Metadata。

删除先前建立的数据集:

# 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

在最近版本的 ZFSzfs destroy 是非同步的,且释放出的空间会许要花费数分钟才会出现在存储池上,可使用 zpool get freeing poolname 来查看 freeing 属性,这个属性会指出数据集在背景已经释放多少资料区块了。若有子数据集,如快照(Snapshot)或其他数据集存在的话,则会无法摧毁父数据集。要摧毁一个数据集及其所有子数据集,可使用 -r 来做递回摧毁数据集及其所有子数据集,可用 -n -v 来列出会被这个操作所摧毁的数据集及快照,而不会真的摧毁,因摧毁快照所释放出的空间也会同时显示。

19.4.2. 创建与删除卷

磁盘区(Volume)是特殊类型的数据集,不会被挂载成一个文件系统,而是会被当做储存区块设备出现在 /dev/zvol/poolname /dataset 下。这让磁盘区可供其他文件系统使用、拿来备份虚拟机器的磁盘或是使用 iSCSIHAST 通讯协定汇出。

磁盘区可以被格式化成任何文件系统,或不使用文件系统来储存原始资料。对一般使用者,磁盘区就像是一般的磁盘,可以放置一般的文件系统在这些 zvols 上,并提供一般磁盘或文件系统一般所没有的功能。例如,使用压缩属性在一个 250 MB 的磁盘区可建立一个压缩的 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)

摧毁一个磁盘区与摧毁一个一般的文件系统数据集差不多。操作上几乎是即时的,但在背景会需要花费数分钟来让摧毁一个磁盘区与摧毁一个一般的文件系统数据集差不多。操作上几乎是即时的,但在背景会需要花费数分钟来让释放空间再次可用。释放空间再次可用。

19.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  -

19.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
#

19.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

It is also possible to set additional options for sharing datasets through NFS, such as -alldirs, -maproot and -network. To set additional options to a dataset shared through NFS, enter:

#  zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home

19.4.5. 管理快照(Snapshot)

快照(Snapshot)是 ZFS 最强大的功能之一,快照提供了数据集只读、单一时间点(Point-in-Time)的复制功能,使用了写入时复制(Copy-On-Write, COW)的技术,可以透过保存在磁盘上的旧版资料快速的建立快照。若没有快照存在,在资料被覆盖或删除时,便回收空间供未来使用。由于只记录前一个版本与目前数据集的差异,因此快照可节省磁盘空间。快照只允许在整个数据集上使用,无法在各别档案或目录。当建立了一个数据集的快照时,便备份了所有内含的资料,这包含了文件系统属性、档案、目录、权限等等。第一次建立快照时只会使用到更改参照到资料区块的空间,不会用到其他额外的空间.使用 -r 可以对使用同名的数据集及其所有子数据集的建立一个递回快照,提供一致且即时(Moment-in-time)的完整文件系统快照功能,这对于那些彼此有相关或相依档案存放在不同数据集的应用程序非常重要。不使用快照所备份的资料其实是分散不同时间点的。

ZFS 中的快照提供了多种功能,即使是在其他缺乏快照功能的文件系统上。一个使用快照的典型例子是在安装软体或执行系统升级这种有风险的动作时,能有一个快速的方式可以备份文件系统目前的状态,若动作失败,可以使用快照还原(Roll back)到与快照建立时相同的系统状态,若升级成功,便可删除快照来释放空间。若没有快照功能,升级失败通常会需要使用备份来恢复(Restore)系统,而这个动作非常繁琐、耗时且可能会需要停机一段时间系统无法使用。使用快照可以快速的还原,即使系统正在执行一般的运作,只而要短暂或什至不需停机。能够节省大量在有数 TB 的储存系统上从备份复制所需资料的时间.快照并非要用来取代存储池的完整备份,但可以用在快速且简单的保存某个特定时间点的数据集。

19.4.5.1. 创建快照

快照可以使用 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 操作结果,要列出快照需在 zfs list 后加上 -t snapshot,使用 -t all 可以同时列出文件系统的内容及快照。

快照并不会直接挂载,因此 MOUNTPOINT 栏中没有显示路径。在 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 技术来运作。快照只会保存有更动(差异)的资料,并非整个文件系统的内容,这个意思是说,快照只会在有做更动时使用一小部份的空间,复制一个档案到该数据集,可以让空间使用量变的更明显,然后再做第二个快照:

# 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_snapshotUSED 栏位中的大小也更改了,这说明了这个更动在前次快照与之后快照间的关系。

19.4.5.2. 对比快照

ZFS 提供了内建指令可以用来比对两个快照(Snapshot)之间的差异,在使用者想要查看一段时间之间文件系统所的变更时非常有用。例如 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路径或文件被重命名。

对照这个表格来看输出的结果,可以明显的看到 passwd 是在快照 mypool/var/tmp@my_recursive_snapshot 建立之后才加入的,结果也同样看的到挂载到 /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

19.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 提供了简单的方可以取回档案,便是使用还原(Rollback),但这只在有定期对重要的资料使用快照时可用。要拿回档案并从最后一次快照重新开始,可执行以下指令:

# zfs rollback mypool/var/tmp@diff_snapshot
# ls /var/tmp
passwd          passwd.copy     vi.recover

The rollback operation restored the dataset to the state of the last snapshot. It is also possible to roll back to a snapshot that was taken much earlier and has other snapshots that were created after it. When trying to do this, ZFS will issue this warning:

# 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 不会删除受影响的快照。若确定要这么做,那么必须要知道会遗失所有在这之间的快照,然后可执行以下指令:

# 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 会移除的快照。

19.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

执行 ls .zfs/snapshot 时,虽然 snapdir 可能已经设为隐藏,但仍可能可以显示该目录中的内容,这取决于管理者是否要显示这些目录,可以只显示特定的数据集,而其他的则不显示。从这个隐藏的 。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属性跟踪快照正在使用的空间大小。

快照所消耗的空间是依据自快照之后父文件系统做了多少变更来决定,快照的 written 属性可以用来追踪有多少空间被快照所使用。

19.4.6. 管理副本

复本(Clone)是快照的复制,但更像是一般的数据集,与快照不同的是,复本是非只读的(可写),且可挂载,可以有自己的属性。使用 zfs clone 建立复本之后,便无法再摧毁用来建立复本的快照。复本与快照的父/子关系可以使用 zfs promote 来对换。提升复本之后 ,快照便会成为复本的子数据集,而不是原来的父数据集,这个动作会改变空间计算的方式,但并不会实际改变空间的使用量。复本可以被挂载到 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 的时间与管理成本。

19.4.7. 备份

将资料保存在单一地点的单一存储池上会让资料暴露在盗窃、自然或人为的风险之下,定期备份整个存储池非常重要,ZFS 提供了内建的序列化(Serialization)功能可以将资料以串流传送到标准输出。使用这项技术,不仅可以将资料储存到另一个已连结到本地系统的存储池,也可以透过网路将资料传送到另一个系统,这种备份方式以快照为基础(请参考章节ZFS 快照(Snapshot))。用来备份资料的指令为 zfs sendzfs 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 用来待命(Standby),万一主要存储池无法使用时可替换。注意,ZFS 并不会自动做容错移转(Fail-over),必须要由系统管理者在需要的时候手动完成。快照会用来提供一个与档系统一致的版本来做备份,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.

To back up a dataset with zfs send, redirect to a file located on the mounted backup pool. Ensure that the pool has enough free space to accommodate the size of the snapshot being sent, which means all of the data contained in the snapshot, not just the changes from the previous snapshot.

# 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  -

The zfs send transferred all the data in the snapshot called backup1 to the pool named backup. Creating and sending these snapshots can be done automatically with a cron(8) job.

若不想将备份以封存档案储存,ZFS 可用实际的文件系统来接收资料,让备份的资料可以直接被存取。要取得实际包含在串流中的资料可以用 zfs receive 将串流转换回档案与目录。以下例子会以管线符号连接 zfs sendzfs 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  -

19.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。由于不会备份完整的存储池,只传送差异的部份,所以可以减少传输的时间并节省磁盘空间,特别是在网路缓慢或需要考量每位元传输成本时非常有用。

从存储池 mypool 复制所有档案与资料的新文件系统 backup/mypool 便可以使用。若指定 -P,会一并复制数据集的属性,这包含压缩(Compression)设定,配额(Quota)及挂载点(Mount point)若指定 -R,会复制所有指定数据集的子数据集,及这些子数据集的所有属性。可将传送与接收自动化来定期使用第二个存储池做备份。

19.4.7.2. 透过 SSH 传送加密的备份

透过网路来传送串流对要远端备份是相当不错的方式,但是也有一些缺点,透过网路连结传送的资料没有加密,这会让任何人都可以在未告知传送使用者的情况下拦截并转换串流回资料,这是我们所不想见到的情况,特别是在使用网际网路传送串流到远端的主机时。 SSH 可用来安全的加密要透过网路连线传送的资料,在 ZFS 只需要从标准输出重新导向便可简单的转接到SSH 。要在传送或在远端系统中维持文件系统内容在加密的状态也可考虑使用 PEFS

有一些设定以及安全性注意事项必须先完成,只有对 zfs send 操作必要的步骤才会在此说明,要取得更多有关SSH 的资讯请参考第 13.8 节 “OpenSSH”

必要的环境设定:

  • 使用 SSH 密钥设定传送端与接收端间无密码的 SSH 存取

  • 正常会需要 root 的权限来传送与接收串流,这需要可以 root 登入到接收端系统。但是,预设因安全性考虑会关闭以 root 登入。 ZFS 委托(ZFS Delegation)系统可以用来允许一个非root 使用者在每个系统上执行各自的发送与接收操作。

  • 在传送端系统上:

    # zfs allow -u someuser send,snapshot mypool
  • 要挂载存储池,无权限的使用者必须拥有该目录且必须允许一般的使用者挂载文件系统。在接收端系统上:

    # sysctl vfs.usermount=1
    vfs.usermount: 0 -> 1
    # sysrc -f /etc/sysctl.conf vfs.usermount=1
    # 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 来传送包含该数据集及其所有子数据集、快照、复制与设定的串流。输出会被导向到 SSH 连线的远端主机 backuphost 上等候输入的 zfs receive,在此建议使用完整网域名称或IP 位置。接收端的机器会写入资料到 recvpool 存储池上的 backup 数据集,在 zfs recv 加上-d 可覆写在接收端使用相同名称的快照,加上 -u 可让文件系统在接收端不会被挂载,当使用 -v,会显示更多有关传输的详细资讯,包含已花费的时间及已传输的资料量。

19.4.8. 数据集、用户和组配额

数据集配额(Dataset quota)可用来限制特定数据集可以使用的的空间量。参考配额(Reference Quota)的功能也非常相似,差在参考配额只会计算数据集自己使用的空间,不含快照与子数据集。类似的,使用者(User)与群组(Group)配额可以用来避免使用者或群组用掉存储池或数据集的所有空间。

以下示例假定系统中已存在用户。在将用户添加到系统之前,请确保首先创建其主数据集并设置 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@group=size

设定群组 firstgroup 的配额为 50 GB 可使用:

# zfs set groupquota@firstgroup=50G

要移除群组 firstgroup 的配额,或确保该群组未设定配额可使用:

# zfs set groupquota@firstgroup=none

如同使用者配额属性,非 root 使用者只可以查看自己所属群组的配额。而 root 或拥有 groupquota 权限的使用者,可以检视并设定所有群组的任何配额。

要显示在文件系统或快照上每位使用者所使用的空间量及配额可使用 zfs userspace,要取得群组的资讯则可使用 zfs groupspace,要取得有关支持的选项资讯或如何只显示特定选项的资讯请参考zfs(1)

有足够权限的使用者及 root 可以列出 storage/home/bob 的配额,使用指令:

# zfs get quota storage/home/bob

19.4.9. 保留空间

保留空间(Reservation)可以确保数据集最少可用的空间量,其他任何数据集无法使用保留的空间,这个功能在要确保有足够的可用空间来存放重要的数据集或日志档时特别有用。

reservation 属性的一般格式为 reservation=size,所以要在 storage/home/bob 设定保留 10 GB 的空间可以用:

# zfs set reservation=10G storage/home/bob

要清除任何保留空间:

# zfs set reservation=none storage/home/bob

同样的原则可以应用在refreservation 属性来设定参考保留空间(Reference Reservation),参考保留空间的一般格式为refreservation=size

这个指令会显示任何已设定于 storage/home/bob 的 reservation 或 refreservation:

# zfs get reservation storage/home/bob
# zfs get refreservation storage/home/bob

19.4.10. 压缩(Compression)

ZFS 提供直接的压缩功能,在资料区块层级压缩资料不仅可以节省空间,也可以增加磁盘的效能。若资料压缩了 25%,但压缩的资料会使用了与未压缩版本相同的速率写入到磁盘,所以实际的写入速度会是原来的 125%。压缩功能也可来替代去重复(Deduplication)功能,因为压缩并不需要使用额外的内存。

ZFS 提了多种不同的压缩演算法,每一种都有不同的优缺点,随着ZFS v5000 引进了LZ4 压缩技术,可对整个存储池开启压缩,而不像其他演算法需要消耗大量的效能来达成,最大的优点是LZ4 拥有提早放弃 的功能,若LZ4 无法在资料一开始的部份达成至少 12.5% 的压缩率,便会以不压缩的方式来写入资料区块来避免CPU 在那些已经压缩过或无法压缩的资料上浪费运算能力.要取得更多有关 ZFS 中可用的压缩演算法详细资讯,可参考术语章节中的压缩(Compression)项目。

管理者可以使用数据集的属性来监视压缩的效果。

# 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。

Compression can have an unexpected side effect when combined with User Quotas. User quotas restrict how much space a user can consume on a dataset, but the measurements are based on how much space is used after compression. So if a user has a quota of 10 GB, and writes 10 GB of compressible data, they will still be able to store additional data. If they later update a file, say a database, with more or less compressible data, the amount of space available to them will change. This can result in the odd situation where a user did not increase the actual amount of data (the logicalused property), but the change in compression caused them to reach their quota limit.

压缩功能在与备份功能一起使用时也可能会有类似的问题,通常会使用配额功能来限制能够储存的资料量来确保有足够的备份空间可用。但是由于配额功能并不会考量压缩状况,可能会有比未压缩版本备份更多的资料量会被写入到数据集。

19.4.11. 去重复(Deduplication)

当开启,去重复(Deduplication)功能会使用每个资料区块的校验码(Checksum)来侦测重复的资料区块,当新的资料区块与现有的资料区块重复,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 代表资料尚未被去重复.在下一个例子会在前面所建立的去重复存储池中复制三份 Port 树到不同的目录中。

# for d in dir1 dir2 dir3; do
> mkdir $d && cp -R /usr/ports $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 的去重复率,这代表已侦测到多份复制的Port 树资料并做了去重复的动作,且只会使用第三份资料所占的空间.去重复能节省空间的潜力可以非常巨大,但会需要消耗大量的内存来持续追踪去重复的资料区块。

去重复并非总是有效益的,特别是当存储池中的资料本身并没有重复时。 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 时才开启。

19.4.12. ZFS 与Jail

zfs jail 以及相关的jailed 属性可以用来将一个ZFS 数据集委托给一个 Jail 管理。zfs jail jailid 可以将一个数据集连结到一个指定的 Jail,而 zfs unjail 则可解除连结。数据集要可以在 Jail 中控制需设定 jailed 属性,一旦数据集被隔离便无法再挂载到主机,因为有挂载点可能会破坏主机的安全性。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.