第 6.2 节 ZFS

使用建议

优化见 ZFSTuningGuide

目前几乎开箱即用,无需多余配置。

ZFS 快照与还原

ZFS 快照类似于虚拟机快照。

  • 创建快照

默认创建分区(Auto ZFS)如下:

root@ykla:/home/ykla #  zfs list
NAME                 USED  AVAIL     REFER  MOUNTPOINT
zroot               1.72G   440G       96K  /zroot
zroot/ROOT          1004M   440G       96K  none
zroot/ROOT/default  1004M   440G     1004M  /
zroot/tmp            104K   440G      104K  /tmp
zroot/usr            760M   440G       96K  /usr
zroot/usr/home       128K   440G      128K  /usr/home
zroot/usr/ports       96K   440G       96K  /usr/ports
zroot/usr/src        759M   440G      759M  /usr/src
zroot/var            628K   440G       96K  /var
zroot/var/audit       96K   440G       96K  /var/audit
zroot/var/crash       96K   440G       96K  /var/crash
zroot/var/log        148K   440G      148K  /var/log
zroot/var/mail        96K   440G       96K  /var/mail
zroot/var/tmp         96K   440G       96K  /var/tmp

快照 zroot(经测试,在上述默认分区下代表快照整个 ZFS 文件系统,-r 即递归创建快照,test 是随便起的名字):

root@ykla:/home/ykla # zfs snapshot -r zroot@test
root@ykla:/home/ykla # zfs list -t snap
NAME                      USED  AVAIL     REFER  MOUNTPOINT
zroot@test                  0B      -       96K  -
zroot/ROOT@test             0B      -       96K  -
zroot/ROOT/default@test     0B      -     7.18G  -
zroot/tmp@test              0B      -      176K  -
zroot/usr@test              0B      -       96K  -
zroot/usr/home@test         0B      -     31.1M  -
zroot/usr/ports@test        0B      -     1.98G  -
zroot/var@test              0B      -       96K  -
zroot/var/log@test          0B      -      444K  -
root@ykla:/home/ykla # ls /usr/ports/
CHANGES          archivers/       emulators/       misc/            textproc/
CONTRIBUTING.md  astro/           finance/         multimedia/      ukrainian/
COPYRIGHT        audio/           french/          net-im/          vietnamese/
GIDs             base/            ftp/             net-mgmt/        www/
INDEX-13         benchmarks/      games/           net-p2p/         x11-clocks/
Keywords/        biology/         german/          net/             x11-drivers/
MOVED            cad/             graphics/        news/            x11-fm/
Makefile         chinese/         hebrew/          polish/          x11-fonts/
Mk/              comms/           hungarian/       ports-mgmt/      x11-servers/
README           converters/      irc/             portuguese/      x11-themes/
Templates/       databases/       japanese/        print/           x11-toolkits/
Tools/           deskutils/       java/            russian/         x11-wm/
UIDs             devel/           korean/          science/         x11/
UPDATING         distfiles/       lang/            security/
accessibility/   dns/             mail/            shells/
arabic/          editors/         math/            sysutils/
root@ykla:/home/ykla # rm /usr/ports/
  • 还原快照

还原时不能递归还原快照,必须挨个还原(如果你有更好的方案请告诉我们,网络上有一些脚本可用):

与虚拟机快照有所不同,在缺省情况下,zfs rollback 命令无法回滚到除最新快照以外的快照(参考手册),除非使用 r,但这会删除该快照创建后的所有快照。

root@ykla:/home/ykla # zfs rollback -r zroot@test
root@ykla:/home/ykla # zfs rollback -r zroot/ROOT@test
root@ykla:/home/ykla # zfs rollback -r zroot/ROOT/default@test
root@ykla:/home/ykla # zfs rollback -r zroot/tmp@test
root@ykla:/home/ykla # zfs rollback -r zroot/usr@test
root@ykla:/home/ykla # zfs rollback -r zroot/usr/home@test
root@ykla:/home/ykla # zfs rollback -r zroot/usr/ports@test
root@ykla:/home/ykla # zfs rollback -r zroot/var@test
root@ykla:/home/ykla # zfs rollback -r zroot/var/log@test
  • 销毁快照

销毁快照(销毁的时候可以使用 r 递归销毁):

root@ykla:/home/ykla # zfs destroy -r zroot@test
root@ykla:/home/ykla # zfs list -t snap
no datasets available
root@ykla:/home/ykla #

snapshot在命令中可以缩写为snap

启动环境

什么是启动环境?用 snapshot 和 rollback 结合,相当于在一条时间上线进行跳转。启动环境相当于一条时间线,复制一个启动环境相当于再造一条时间线(复制之后两个启动环境互相独立),两个启动环境间的切换是两条时间线的穿越(或者说平行空间的穿越)。默认安装中 zroot/ROOT/default 是默认的启动环境。

# zfs snap zroot/ROOT/default@new                         # 建一个 zfs 快照
# zfs clone zroot/ROOT/default@new zroot/ROOT/new         # 用刚建的快照复制一个镜像

复制的镜像可以作为一个启动环境,可以用工具 bectl 查看可用的启动环境

# bectl list
BE                                Active Mountpoint Space Created
0915                              -      -          4.00M 2023-09-19 19:44
13.2-RELEASE-p2_2023-09-13_141111 -      -          29.0M 2023-09-13 14:11
new                               -      -          432K  2023-09-20 15:17
default                           NR     /          40.8G 2023-04-10 10:06

其中 Active 这一列中 N 表示当前使用环境,R 表示下次启动时使用的环境。bectl 工具可以改变下次使用的启动环境(在启动 FreeBSD 时,启动菜单里选 8,也可以改变启动环境)

bectl activate new
Successfully activated boot environment new

再次用 bectl list 查看,观察 Active 列的变化

bectl list
BE                                Active Mountpoint Space Created
0915                              -      -          4.00M 2023-09-19 19:44
13.2-RELEASE-p2_2023-09-13_141111 -      -          29.0M 2023-09-13 14:11
new                               R      /          2.84M 2023-09-20 15:17
default                           N      -          40.8G 2023-04-10 10:06

重启 FreeBSD (启动菜单里选择 new 启动环境,或如上用 bectl activate new 切换到 new 启动环境),用 df 观察,挂载的根目录的文件系统已经是 zroot/ROOT/new

# df
Filesystem          1K-blocks     Used     Avail Capacity  Mounted on
zroot/ROOT/new      110611616 42612156  67999460    39%    /
devfs                       1        1         0   100%    /dev
/dev/gpt/efiboot0      266176     1872    264304     1%    /boot/efi
fdescfs                     1        1         0   100%    /dev/fd

切换回 zroot/ROOT/default 启动环境,在启动菜单里选择 default 启动环境,或如上用 bectl activate default 切换到 default 启动环境

用法扩展:可以把一个启动环境升级为 FreeBSD 14,实现 13、14 多版本共存。

警告

“用法扩展”实现的代价是 zfs 不能升级:一升级就挂了,因为旧版 ZFS 程序无法向后兼容。实践的意义不大,可以仅做备份还原使用。

参考文献

FreeBSD zfs 的 zpool 升级

13.2 到 14.0 ,zpool 版本有所升级。

此处假定已经用 freebsd-update 从 13.2 升级到了 14.0。

开始前的提醒:准备好 livecd 以应对意外,livecd 要 14.0 及以上的,13.2 不支持(无法访问新版 zfs 文件系统)14.0 的 zfs。

查看 zpool 状态:

root@u13t14 # zpool status

  pool: zroot
 state: ONLINE
status: Some supported and requested features are not enabled on the pool.
The pool can still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
the pool may no longer be accessible by software that does not support
the features. See zpool-features(7) for details.
config:

NAME        STATE     READ WRITE CKSUM
zroot       ONLINE       0     0     0
  ada0p3    ONLINE       0     0     0

errors: No known data errors

未升级前不能使用所有 zfs 新功能,下面进行升级:

root@u13t14 # zpool upgrade zroot

This system supports ZFS pool feature flags.

Enabled the following features on 'zroot':
  edonr
  zilsaxattr
  head_errlog
  blake3
  block_cloning
  vdev_zaps_v2

Pool 'zroot' has the bootfs property set, you might need to update
the boot code. See gptzfsboot(8) and loader.efi(8) for details.

重写引导(仅非 EFI 引导需要)

警告

bootfs 属性是在 zfs 上引导 FreeBSD 的重要标志,不理睬这个提示可能没事,但出了问题就不能引导系统,建议按提示重写 boot code (为什么这么建议?因为我炸了)。如果你没有 freebsd-boot 分区就 不需要 以下操作。

查看分区信息:

root@u13t14 # gpart show

=>      40  33554352  ada0  GPT  (16G)
        40      1024     1  freebsd-boot  (512K)
      1064       984        - free -  (492K)
      2048   4194304     2  freebsd-swap  (2.0G)
   4196352  29356032     3  freebsd-zfs  (14G)
  33552384      2008        - free -  (1.0M)

找到 freebsd-boot 类型分区,这里序号为 1,对应下面命令中 -i 选项,接着重写 bootcode

root@u13t14 # gpart bootcode -p /boot/gptzfsboot -i 1 ada0
partcode written to ada0p1

可再次查看 zpool 状态:

root@u13t14 # zpool status

  pool: zroot
 state: ONLINE
config:

NAME        STATE     READ WRITE CKSUM
zroot       ONLINE       0     0     0
  ada0p3    ONLINE       0     0     0

errors: No known data errors

用户级 zfs 管理

zfs 允许非特权用户管理。

自 FreeBSD 14.1 以降(请参阅发行说明,是由这个变更做出的),bsdinstall(8) 使用的工具 adduser(8):当用户主目录的父目录位于 zfs 数据集上时(即若 /home 是个 zfs 数据集,/home/xxx 亦如此),会为用户的主目录创建一个 zfs 数据集。adduser 的参数 -Z 可禁用这一行为。adduser 对于 zfs 的非特权用户主目录加密功能亦已可用。

以下操作基于 FreeBSD 14.1-RELEASE

基础的用户级 zfs 管理

先来了解一下非特权用户的 zfs 数据集。

我们首先在安装系统时,创建了两个普通用户“aria2”和“safreya”。

safreya ~ % zfs list
NAME                                           USED  AVAIL  REFER  MOUNTPOINT
zroot                                         53.7G   396G    96K  /zroot
zroot/ROOT                                    12.8G   396G    96K  none
zroot/ROOT/14.1-RELEASE-p3_2024-09-17_194642     8K   396G  11.6G  /
zroot/ROOT/default                            12.8G   396G  11.9G  /
zroot/aria2                                    187M   396G   187M  /usr/local/data/aria2
zroot/home                                    7.74G   396G    96K  /home
zroot/home/aria2                               128K   396G   128K  /home/aria2   #请注意此行
zroot/home/safreya                            7.74G   396G  7.70G  /home/safreya #请注意此行
zroot/jails                                   3.12G   396G  3.12G  /usr/jails
zroot/sec                                     28.5G   396G  28.5G  /usr/local/data/sec
zroot/tmp                                      102M   396G   102M  /tmp
zroot/usr                                     1.34G   396G    96K  /usr
zroot/usr/ports                               1.34G   396G  1.34G  /usr/ports
zroot/usr/src                                   96K   396G    96K  /usr/src
zroot/var                                     1.58M   396G    96K  /var
zroot/var/audit                                 96K   396G    96K  /var/audit
zroot/var/crash                                 96K   396G    96K  /var/crash
zroot/var/log                                 1.02M   396G  1.02M  /var/log
zroot/var/mail                                 168K   396G   168K  /var/mail
zroot/var/tmp                                  120K   396G   120K  /var/tmp
safreya ~ %

其中:

zroot/home/aria2                               128K   396G   128K  /home/aria2
zroot/home/safreya                            7.74G   396G  7.70G  /home/safreya

即,在创建用户时,已默认为用户 safreyaaria2 分别创建了各自独立的数据集 zroot/home/aria2zroot/home/safreya

接下来,分别查看一下两个数据集上的用户权限。

safreya ~ % zfs allow zroot/home/aria2
---- Permissions on zroot/home/aria2 ---------------------------------
Local+Descendent permissions:
        user aria2 create,destroy,mount,snapshot
safreya ~ % zfs allow zroot/home/safreya
---- Permissions on zroot/home/safreya -------------------------------
Local+Descendent permissions:
        user safreya create,destroy,mount,snapshot

可以看到,在创建用户集时,默认为用户创设了 createdestroymountsnapshot 四项权限。

所以,对于这两个数据集,普通用户亦可使用快照功能:

safreya ~ % zfs snap zroot/home/safreya@snap1
safreya ~ % zfs list -t snap
NAME                                       USED  AVAIL  REFER  MOUNTPOINT
zroot/home/safreya@snap1                     0B      -  7.70G  -

再来看 create, destroy, mount 权限:

safreya ~ % zfs create zroot/home/safreya/dataset_1
cannot mount 'zroot/home/safreya/dataset_1': Insufficient privileges
filesystem successfully created, but not mounted
safreya ~ % su -m root -c 'sysctl vfs.usermount=1'
Password:
vfs.usermount: 0 -> 1
safreya ~ % zfs create zroot/home/safreya/dataset_2
safreya ~ % zfs list
NAME                                           USED  AVAIL  REFER  MOUNTPOINT
      ...此处省略一部分...
      
zroot/home                                    7.79G   396G    96K  /home
zroot/home/aria2                               128K   396G   128K  /home/aria2
zroot/home/safreya                            7.79G   396G  7.68G  /home/safreya
zroot/home/safreya/dataset_1                    96K   396G    96K  /home/safreya/dataset_1
zroot/home/safreya/dataset_2                    96K   396G    96K  /home/safreya/dataset_2
      ...此处省略一部分...
safreya ~ % zfs destroy zroot/home/safreya/dataset_1
safreya ~ % zfs destroy zroot/home/safreya/dataset_2      

可以看到,创建(create)权限、销毁(destroy)可以正常使用,但是挂载(mount)权限需要开启内核参数 vfs.usermount,以允许用户级挂载。

到这里,用户级的 zfs 管理需求已经基本满足。但是你够仔细的话会发现 rollback 权限并不可用,可以用 root 用户授权普通用户 rollback 权限。即:

safreya ~ % zfs rollback zroot/home/safreya@snap1
cannot rollback 'zroot/home/safreya': permission denied
safreya ~ % su -m root -c 'zfs allow safreya rollback zroot/home/safreya'
Password:
safreya ~ % zfs rollback zroot/home/safreya@snap1

用户级 zfs 加密功能

FreeBSD 14.1 中,在用户级使用 zfs 加密功能,需要为用户授于特定权限。

safreya ~ % su -m root -c 'zfs allow safreya change-key,load-key,keyformat,keylocation,encryption zroot/home/safreya'
Password:
safreya ~ % zfs allow zroot/home/safreya
---- Permissions on zroot/home/safreya -------------------------------
Local+Descendent permissions:
        user safreya change-key,create,destroy,encryption,keyformat,keylocation,load-key,mount,snapshot
safreya ~ %

change-keyload-keykeyformatkeylocationencryption 这五个权限属性都用于 zfs 加密功能。

现在创建一个加密数据集 zroot/home/safreya/secret

safreya ~ % zfs create -o encryption=on -o keyformat=passphrase zroot/home/safreya/secret
Enter new passphrase:     #此处输入密码,密码不会回显,就是什么也没有
Re-enter new passphrase:  #此处重复输入上述密码,密码不会回显,就是什么也没有

查看加密情况:

safreya ~ % zfs get mounted zroot/home/safreya/secret
NAME                       PROPERTY  VALUE    SOURCE
zroot/home/safreya/secret  mounted   yes      -

查看 mounted 属性,加密数据集创建即挂载,现在创建一个文件,然后卸载加密数据集:

safreya ~ % cd secret
safreya secret % echo "a secret makes a man mad" >abc.txt
safreya secret % cd ..
safreya ~ % zfs unmount zroot/home/safreya/secret
safreya ~ % zfs unload-key zroot/home/safreya/secret
safreya ~ % zfs get mounted zroot/home/safreya/secret
NAME                       PROPERTY  VALUE    SOURCE
zroot/home/safreya/secret  mounted   no       -
safreya ~ % ls secret #并无输出
safreya ~ %

卸载加密数据集必须记得也要同时卸载密钥。挂载加密数据集亦要先加载密钥:

safreya ~ % zfs load-key zroot/home/safreya/secret
Enter passphrase for 'zroot/home/safreya/secret':
safreya ~ % zfs mount zroot/home/safreya/secret
safreya ~ % ls secret
Permissions Size User    Date Modified Name
.rw-r--r--    25 safreya 19 Sep 20:26  abc.txt

注意

无论数据集是否挂载,子命令 destroy 都可以成功销毁数据集,因为 destroy 权限是默认就授予的,所以如果非用户本人操作系统的话,可能出现“我得不到的,就毁灭”的情况。

而且要记住的是,“授权”是授于普通用户“代理权限”,操作有授权的数据集时就相当于 root,过程中无需密码。因此在授于权限时还要综合考量,合理的限制授权范围和权限属性,如禁用 destroy 权限属性等。

safreya ~ % su -m root -c 'zfs unallow safreya destroy zroot/home/safreya'
Password:
safreya ~ % zfs allow  zroot/home/safreya
---- Permissions on zroot/home/safreya -------------------------------
Local+Descendent permissions:
        user safreya change-key,create,encryption,keyformat,keylocation,load-key,mount,rollback,snapshot

这里 zroot/home/safreya/secret 继承了数据集 zroot/home/safreya 的权限属性,授权/反授权都针对 zroot/home/safreya,对 zroot/home/safreya/secret 的操作不起作用(操作后属性不变,原因未知)。

adduser 与加密用户主目录

adduser 命令中可以直接使用加密的用户主目录数据集,但默认给出的权限不足,一经卸载就无法由普通用户直接挂载

root safreya # adduser
Username: test
Full name:
Uid (Leave empty for default):
Login group [test]:
Login group is test. Invite test into other groups? []:
Login class [default]:
Shell (sh csh tcsh zsh rzsh git-shell bash rbash nologin) [sh]:
Home directory [/home/test]:
Home directory permissions (Leave empty for default):
Enable ZFS encryption? (yes/no) [no]: yes #该功能为新增功能
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]:
Enter password:
Enter password again:
Lock out the account after creation? [no]:
Username    : test
Password    : *****
Full Name   :
Uid         : 1003
ZFS dataset : zroot/home/test
Encrypted   : yes
Class       :
Groups      : test
Home        : /home/test
Home Mode   :
Shell       : /bin/sh
Locked      : no
OK? (yes/no) [yes]:
Enter encryption keyphrase for ZFS dataset (zroot/home/test):
Enter new passphrase:
Re-enter new passphrase:
adduser: INFO: Successfully created ZFS dataset (zroot/home/test).
adduser: INFO: Successfully added (test) to the user database.
Add another user? (yes/no) [no]: no
Goodbye!

查看权限:

root safreya # zfs allow zroot/home/test
---- Permissions on zroot/home/test ----------------------------------
Local+Descendent permissions:
        user test create,destroy,mount,snapshot

卸载:

root safreya # zfs unmount zroot/home/test
root safreya # zfs unload-key  zroot/home/test

切换到普通用户 test 挂载尝试:

# su test
$ zfs load-key zroot/home/test
Enter passphrase for 'zroot/home/test':
Key load error: Permission denied.

Permission denied 即权限不足,拒绝访问。

参考文献

备注

  • ZFS 并不使用 /etc/fstab,但是 EFI、Swap 仍然使用。

最后更新于

FreeBSD 中文社区 2024