跳转至内容

ZFS

来自 ArchWiki

ZFS 是一个高级文件系统,最初由 Sun Microsystems 于 2005 年开发和发布。

ZFS 被描述为 “文件系统的最终解决方案”,它稳定、快速、安全且面向未来。ZFS 的功能包括:池化存储(集成卷管理 – zpool)、写时复制快照、数据完整性验证和自动修复(擦洗)、RAID-Z、最大 16 EB 的文件大小,以及最大 256 百万 PB ZB 的存储,文件系统(数据集)或文件的数量没有限制[1]

ZFS 在通用公共许可证 (CDDL) 下获得许可。由于 CDDL 与 GPL 不兼容,因此 ZFS 无法包含在 Linux 内核中。然而,这一要求并不妨碍第三方开发和分发原生的 Linux 内核模块,正如 OpenZFS(以前称为 ZFS on Linux)的情况一样。

由于 ZFS 未包含在 Linux 内核中,

  • OpenZFS 项目必须跟上 Linux 内核版本。在发布稳定的 OpenZFS 版本后,Arch ZFS 维护者会发布它们。
  • 这种情况有时会因不满足的依赖关系而导致正常的滚动更新过程被锁定,因为更新提议的新内核版本不受 OpenZFS 支持。

安装

ZFS 的内核模块可以通过两种方式安装——要么安装一个为特定内核版本提供模块的软件包,要么使用一个 DKMS 软件包,该软件包会为已安装的内核构建模块。请参阅以下各节。

ZFS 用户空间实用程序由 zfs-utilsAUR 软件包提供,该软件包是所有 zfs 内核模块软件包的依赖项。

内核特定软件包

警告 除非您使用 dkms 版本的这些软件包,否则 ZFS 和 SPL 内核模块将与特定内核版本绑定。在更新的软件包上传到 AUR 或 archzfs 仓库之前,您将无法应用任何内核更新。
提示 如果您当前的内核比 archzfs 仓库中的版本新,您可以 降级您的 linux 版本到 archzfs 仓库中的版本,或者切换到 LTS 内核,它可能提供更好的与外部内核模块的兼容性。

archzfs 仓库或备选的 Arch 用户仓库 安装

在命令行中运行 zpool status 来测试安装。如果出现“insmod”错误,请尝试 depmod -a

DKMS

用户可以使用 动态内核模块支持 (DKMS) 来自动重新构建 ZFS 模块。

警告 有时,dkms 软件包可能无法与 Arch 中最新的内核软件包编译。使用 linux-lts 内核可以提供与外部内核模块更好的兼容性,否则 zfs-dkms-staging-gitAUR 会在稳定 zfs 分支的基础上,为 Arch 中最新的内核软件包回溯兼容性补丁和修复。

archzfs 仓库或备选的 Arch 用户仓库 安装

  • zfs-dkmsAUR 用于带有动态内核模块支持的稳定版本(可能无法与最新的稳定内核一起工作,因此建议改用 linux-lts 内核)。
  • zfs-dkms-staging-gitAUR 用于获取 zfs 稳定分支中的最新修复,以及为 Arch 中最新的内核软件包回溯支持。
  • zfs-dkms-gitAUR 用于带有动态内核模块支持版本的开发版本。
提示pacman.conf 中添加一个 IgnorePkg 条目,以防止在进行常规更新时升级这些软件包。

要编译上述 dkms 软件包提供的 ZFS 模块,还需要安装您已安装内核的相应头文件包(例如,用于 linuxlinux-headers,用于 linux-ltslinux-lts-headers 等)。当 dkms 软件包或内核更新时,由于 DKMS pacman hook 的作用,内核模块将自动重新编译。

必需依赖

zfs-utils 软件包现在需要 libunwind 作为依赖。尽管它尚未被任何软件包列为必需依赖,但尝试创建 zpool 或 zfs 挂载点将导致失败。

ZFS 上的根目录

请参阅 在 ZFS 上安装 Arch Linux#安装

尝试 ZFS

鼓励希望在*虚拟块设备*(ZFS 术语中称为 VDEVs)上进行 ZFS 实验的用户,这些设备可以是简单的文件,如 ~/zfs0.img ~/zfs1.img ~/zfs2.img 等,并且不会丢失真实数据,请参阅 ZFS 实验 文章。其中涵盖了诸如构建 RAIDZ 阵列、故意损坏数据并恢复、数据集快照等常见任务。

配置

ZFS 被其创建者视为“零管理”文件系统;因此,配置 ZFS 非常直接。配置主要通过两个命令完成:zfszpool

自动启动

为了让 ZFS 履行其“零管理”的称号,您可能希望在启动时自动导入池。

为此,请启用[2]

  • zfs.target 作为其他单元依赖的总体参考点,
  • zfs-import.target,因为它提供了正确的排序[3]
  • zfs-import-cache.service 用于导入池。

zfs-import-cache.service 通过读取文件 /etc/zfs/zpool.cache 来导入 zfs 池。对于您希望 zfs-import-cache.service 自动导入的每个已导入池,请执行

# zpool set cachefile=/etc/zfs/zpool.cache pool

要实际挂载 ZFS 文件系统(而不必在 /etc/fstab 中列出 ZFS 文件系统),您有 2 个选择

使用 zfs-mount.service

为了在启动时自动挂载 ZFS 文件系统,您需要启用 zfs-mount.service

使用 zfs-mount-generator

您也可以使用 zfs-mount-generator 在启动时为您的 ZFS 文件系统创建 systemd 挂载单元。systemd 将根据挂载单元自动挂载文件系统,而无需使用 zfs-mount.service。为此,您需要

  1. 创建 /etc/zfs/zfs-list.cache 目录。
  2. 启用 ZFS 事件守护进程 (ZED) 脚本(称为 ZEDLET),该脚本负责创建可挂载 ZFS 文件系统的列表。(如果您使用的是 OpenZFS >= 2.0.0,则会自动创建此链接。)
    # ln -s /usr/lib/zfs/zed.d/history_event-zfs-list-cacher.sh /etc/zfs/zed.d
  3. 启用 zfs.target启用/启动 ZFS 事件守护进程 (zfs-zed.service)。此服务负责运行上一步中的脚本。
  4. 您需要在 /etc/zfs/zfs-list.cache 中为您的池创建一个名为其名称的空文件。ZEDLET 仅在池对应的文件已存在时更新文件系统列表。
    # touch /etc/zfs/zfs-list.cache/<pool-name>
  5. 检查 /etc/zfs/zfs-list.cache/<pool-name> 的内容。如果它是空的,请确保 zfs-zed.service 正在运行,并通过运行以下命令更改任何 ZFS 文件系统的 canmount 属性:
    # zfs set canmount=off zroot/fs1
    此更改会导致 ZFS 引发一个事件,该事件由 ZED 捕获,ZED 随后运行 ZEDLET 来更新 /etc/zfs/zfs-list.cache 中的文件。如果 /etc/zfs/zfs-list.cache 中的文件已更新,您可以运行以下命令将文件系统的 canmount 属性改回:
    # zfs set canmount=on zroot/fs1

您需要在 /etc/zfs/zfs-list.cache 中为系统中的每个 ZFS 池添加一个文件。请确保所需的单元和目标已启用。

存储池

创建 ZFS 文件系统之前不需要对驱动器进行分区。建议将 ZFS 指向整个磁盘(例如,/dev/sdx 而不是 /dev/sdx1),这将自动创建一个 GPT(GUID 分区表)并在磁盘末尾添加一个 8 MB 的保留分区用于旧式引导加载程序。但是,如果您愿意,可以指定一个分区或现有文件系统中的一个文件,以创建具有不同冗余属性的多个卷。

注意 如果某些或所有设备曾用于软件 RAID 集,则务必清除所有旧的 RAID 配置信息
警告 对于扇区大小为 4 KiB 的高级格式磁盘,推荐使用 ashift=12 以获得最佳性能。高级格式磁盘模拟 512 字节的扇区大小以兼容旧式系统,这会导致 ZFS 有时使用不是理想的 ashift 选项编号。创建池后,更改 ashift 选项的唯一方法是重新创建池。使用 ashift=12 也会减少可用容量。请参阅 OpenZFS FAQ:性能注意事项高级格式磁盘ZFS 和高级格式磁盘

识别磁盘

OpenZFS 建议在使用少于 10 个设备的 ZFS 存储池时使用设备 ID[4]。使用 持久块设备命名#by-id 和 by-path 来识别将用于 ZFS 池的驱动器列表。

磁盘 ID 应类似于以下内容

$ ls -lh /dev/disk/by-id/
lrwxrwxrwx 1 root root  9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0JKRR -> ../../sdc
lrwxrwxrwx 1 root root  9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0JTM1 -> ../../sde
lrwxrwxrwx 1 root root  9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0KBP8 -> ../../sdd
lrwxrwxrwx 1 root root  9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0KDGY -> ../../sdb
警告 如果您使用设备名称(例如 /dev/sda/dev/sdb 等)创建 zpool,ZFS 可能无法在启动时间歇性地检测到 zpool。

使用 GPT 标签

本文或本节需要在语言、wiki 语法或风格方面进行改进。请参阅 Help:Style 获取参考。

原因: 缺少对持久块设备命名的引用,在此处解释它们之间的区别(甚至是什么)是无用的。(在 Talk:ZFS 中讨论)

磁盘标签和 UUID 也可以通过使用GPT分区用于 ZFS 挂载。ZFS 驱动器有标签,但 Linux 在启动时无法读取它们。与MBR分区不同,GPT 分区直接支持 UUID 和标签,独立于分区内的格式。相比于将整个磁盘用于 ZFS,进行分区提供了两个额外的优点。操作系统不会从 ZFS 写入分区扇区的任何不可预测数据生成虚假的分割区号,并且如果您愿意,可以轻松地过度配置 SSD 驱动器,并轻微过度配置旋转驱动器,以确保具有略微不同扇区数量的不同型号可以 zpool 替换到您的镜像中。这是使用现有的工具和技术几乎零成本地组织和控制 ZFS 的大量工作。

使用 gdisk 对驱动器的全部或部分进行分区,作为单个分区。gdisk 不会自动命名分区,因此如果需要分区标签,请使用 gdisk 命令“c”来标记分区。您可能更喜欢标签而不是 UUID 的一些原因包括:标签易于控制,标签可以命名以使您的阵列中每个磁盘的目的显而易见,并且标签更短且易于输入。当服务器出现故障且情况紧急时,这些都是优势。GPT 分区标签有足够的空间,并且可以存储大多数国际字符wikipedia:GUID_Partition_Table#Partition_entries,允许以有组织的方式标记大型数据池。

使用 GPT 分区的驱动器具有如下所示的标签和 UUID。

$ ls -l /dev/disk/by-partlabel
lrwxrwxrwx 1 root root 10 Apr 30 01:44 zfsdata1 -> ../../sdd1
lrwxrwxrwx 1 root root 10 Apr 30 01:44 zfsdata2 -> ../../sdc1
lrwxrwxrwx 1 root root 10 Apr 30 01:59 zfsl2arc -> ../../sda1
$ ls -l /dev/disk/by-partuuid
lrwxrwxrwx 1 root root 10 Apr 30 01:44 148c462c-7819-431a-9aba-5bf42bb5a34e -> ../../sdd1
lrwxrwxrwx 1 root root 10 Apr 30 01:59 4f95da30-b2fb-412b-9090-fc349993df56 -> ../../sda1
lrwxrwxrwx 1 root root 10 Apr 30 01:44 e5ccef58-5adf-4094-81a7-3bac846a885f -> ../../sdc1
提示 为最小化输入和复制粘贴错误,请使用目标 PARTUUID 设置一个本地变量:$ UUID=$(lsblk --noheadings --output PARTUUID /dev/sdXY)

创建 ZFS 池

要创建 ZFS 池

# zpool create -f -m <mount> <pool> [raidz(2|3)|mirror] <ids>
提示 您可能想先阅读#高级格式磁盘,因为它可能建议在创建池时设置 ashift
  • create:用于创建池的子命令。
  • -f:强制创建池。这用于克服“EFI 标签错误”。请参阅#不包含 EFI 标签
  • -m:池的挂载点。如果未指定此选项,则池将挂载到 /<pool>
  • pool:这是池的名称。
  • raidz(2|3)|mirror:这是将从设备列表中创建的虚拟设备类型。Raidz 是单一磁盘校验(类似于 raid5),raidz2 是 2 磁盘校验(类似于 raid6),raidz3 是 3 磁盘校验。也有 mirror,它类似于 raid1 或 raid10,但不限于仅 2 个设备。如果未指定,每个设备将被添加为一个 vdev,类似于 raid0。创建后,可以将设备添加到每个单驱动器 vdev 以将其变成镜像,这对于迁移数据很有用。
  • ids:要包含在池中的驱动器或分区的ID

创建带有单个 raidz vdev 的池

# zpool create -f -m /mnt/data bigdata \
               raidz \
                  ata-ST3000DM001-9YN166_S1F0KDGY \
                  ata-ST3000DM001-9YN166_S1F0JKRR \
                  ata-ST3000DM001-9YN166_S1F0KBP8 \
                  ata-ST3000DM001-9YN166_S1F0JTM1

创建带有两个镜像 vdev 的池

# zpool create -f -m /mnt/data bigdata \
               mirror \
                  ata-ST3000DM001-9YN166_S1F0KDGY \
                  ata-ST3000DM001-9YN166_S1F0JKRR \
               mirror \
                  ata-ST3000DM001-9YN166_S1F0KBP8 \
                  ata-ST3000DM001-9YN166_S1F0JTM1

高级格式磁盘

创建池时,应始终使用 ashift=12,除非是具有 8k 扇区的 SSD,此时 ashift=13 是正确的。使用 4k 扇区的 512 字节磁盘的 vdev 不会遇到性能问题,但使用 512 字节扇区的 4k 磁盘会。由于创建池后无法更改 ashift,即使是仅包含 512 字节磁盘的池也应使用 4k,因为这些磁盘可能需要替换为 4k 磁盘,或者池可以通过添加由 4k 磁盘组成的 vdev 来扩展。由于无法可靠地检测 4k 磁盘,因此在创建池时应始终指定 -o ashift=12。有关更多详细信息,请参阅OpenZFS FAQ

提示 使用 lsblk(8)(来自 util-linux)列出设备扇区大小:lsblk -o +PHY-SEC

使用 ashift=12 和单个 raidz vdev 创建池

# zpool create -f -o ashift=12 -m /mnt/data bigdata \
               raidz \
                  ata-ST3000DM001-9YN166_S1F0KDGY \
                  ata-ST3000DM001-9YN166_S1F0JKRR \
                  ata-ST3000DM001-9YN166_S1F0KBP8 \
                  ata-ST3000DM001-9YN166_S1F0JTM1

GRUB 兼容的池创建

默认情况下,zpool create 会启用池上的所有功能。如果 /boot 位于 ZFS 上并使用 GRUB,您必须仅启用 GRUB 支持的功能,否则 GRUB 将无法读取池。ZFS 包含兼容性文件(请参阅 /usr/share/zfs/compatibility.d)以协助创建具有特定功能集(grub2 是其中一个选项)的池。

您可以创建一个仅启用了兼容功能的池

# zpool create -o compatibility=grub2 $POOL_NAME $VDEVS

验证池状态

如果命令成功,将没有输出。使用 mount 命令将显示池已挂载。使用 zpool status 将显示池已创建

# zpool status -v
  pool: bigdata
 state: ONLINE
 scan: none requested
config:

        NAME                                       STATE     READ WRITE CKSUM
        bigdata                                    ONLINE       0     0     0
          -0                                       ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0KDGY-part1  ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0JKRR-part1  ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0KBP8-part1  ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0JTM1-part1  ONLINE       0     0     0

errors: No known data errors

此时最好重新启动机器,以确保 ZFS 池在启动时被挂载。最好在传输数据之前处理所有错误。

按 ID 导入池

最终,池可能无法自动挂载,您需要导入才能恢复池。请注意避免最明显的解决方案。

警告 不要运行 zpool import pool!这将使用 /dev/sd? 导入您的池,这将在下次重新排列驱动器时导致问题。这可能简单地是插入 USB 驱动器时重启。

修改以下命令之一来导入您的池,以便池导入保留其创建时的持久性

# zpool import -d /dev/disk/by-id bigdata
# zpool import -d /dev/disk/by-partlabel bigdata
# zpool import -d /dev/disk/by-partuuid bigdata
注意 导入包含加密数据集密钥的池时,请使用 -l 标志,否则在加载密钥之前,加密数据集将不可用。请参阅#按 ID 导入池
# zpool import -l -d /dev/disk/by-id bigdata

最后检查池的状态

# zpool status -v bigdata

销毁存储池

ZFS 可以轻松地销毁已挂载的存储池,删除关于 ZFS 设备的所有元数据。

警告 此命令将销毁池和/或数据集中的所有数据

销毁池

# zpool destroy <pool>

销毁数据集

# zfs destroy <pool>/<dataset>

现在检查状态

# zpool status
no pools available

导出存储池

如果存储池要在另一台系统上使用,则需要先将其导出。如果池是从 archiso 导入的,则也需要导出它,因为 archiso 中的主机 ID 与启动的系统不同。zpool 命令将拒绝导入任何尚未导出的存储池。可以使用 -f 参数强制导入,但这被认为是不妥的。

任何尝试导入未导出存储池的尝试都将导致一个错误,提示该存储池已被另一系统使用。此错误可能在启动时出现,突然将系统抛入 busybox 控制台,并需要 archiso 进行紧急修复,方法是导出池,或将 zfs_force=1 添加到内核启动参数(这并非理想)。请参阅#启动时 ZFS 池未挂载,提示:“池可能被其他系统使用”

导出池

# zpool export <pool>

扩展现有 ZFS 池

可以向现有 zpool 添加设备(分区或磁盘)

# zpool add <pool> <device-id>

导入由多个设备组成的池

# zpool import -d <device-id-1> -d <device-id-2> <pool>

或者简单地:

# zpool import -d /dev/disk-by-id/ <pool>

附加设备(创建)镜像

可以附加一个设备到现有设备旁边,使其成为其镜像(类似于 RAID 1)

# zpool attach <pool> <device-id|mirror> <new-device-id>

您可以将新设备附加到已存在的镜像 vdev(例如,从 2 设备升级到 3 设备镜像),或将其附加到单设备以创建新的镜像 vdev

重命名 ZFS 池

重命名已创建的 zpool 需要 2 个步骤

# zpool export oldname
# zpool import oldname newname

设置不同的挂载点

给定 zpool 的挂载点可以随时使用一个命令移动

# zfs set mountpoint=/foo/bar poolname

升级 ZFS 池

当使用较新的 zfs 模块时,zpools 可能会显示升级指示

$ zpool status -v
pool: bigdata
state: ONLINE
status: Some supported 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(5) for details.
  • 较低版本的 zfs 模块将无法导入较高版本的 zpool。
  • 处理重要数据时,您可能希望在运行 zpool upgrade 之前创建一个备份

升级 zpool bigdata 的版本

# zpool upgrade bigdata

升级所有 zpools 的版本

# zpool upgrade -a

创建数据集

用户可以选择在 zpool 下创建一个数据集,而不是在 zpool 下手动创建目录。数据集提供了更高级别的控制(例如配额),此外还有快照。要能够创建和挂载数据集,同名的目录必须不存在于 zpool 中。要创建数据集,请使用

# zfs create <nameofzpool>/<nameofdataset>

然后可以对数据集应用 ZFS 特定属性。例如,您可以为数据集内的特定目录分配配额限制

# zfs set quota=20G <nameofzpool>/<nameofdataset>/<directory>

要查看 ZFS 中所有可用命令,请参阅 zfs(8)zpool(8)

原生加密

ZFS 提供以下支持的加密选项:aes-128-ccmaes-192-ccmaes-256-ccmaes-128-gcmaes-192-gcmaes-256-gcm。当加密设置为 on 时,将使用 aes-256-gcm。有关原生加密的描述(包括限制),请参阅 zfs-change-key(8)

支持以下密钥格式:passphraserawhex

当使用 passphrase-o pbkdf2iters <n> 时,您还可以指定/增加 PBKDF2 的默认迭代次数,尽管这可能会增加解密时间。

提示
  • 要导入带密钥的池,需要指定 -l 标志,没有该标志,加密数据集将保持不可用状态,直到加载密钥。请参阅#按 ID 导入池
  • 原生 ZFS 加密已在稳定版 0.8.0 或更高版本中提供。以前它仅在通过 zfs-linux-gitAURzfs-dkms-gitAUR 或其他开发版本提供的开发版本中可用。仅为了原生加密而使用开发版本的用户,现在可以根据需要切换到稳定版本。
  • 默认加密套件在 0.8.4 版本中从 aes-256-ccm 更改为 aes-256-gcm

要创建包含原生加密和密码的数据集,请使用

# zfs create -o encryption=on -o keyformat=passphrase <nameofzpool>/<nameofdataset>

要使用密钥而不是密码

# dd if=/dev/random of=/path/to/key bs=32 count=1 iflag=fullblock
# zfs create -o encryption=on -o keyformat=raw -o keylocation=file:///path/to/key <nameofzpool>/<nameofdataset>

使密钥成为人类可读形式(keyformat=hex)的简便方法

# od -Anone -x -N 32 -w64 /dev/random | tr -d [:blank:] > /path/to/hex.key

验证密钥位置

# zfs get keylocation <nameofzpool>/<nameofdataset>

更改密钥位置

# zfs set keylocation=file:///path/to/key <nameofzpool>/<nameofdataset>

您还可以使用以下命令之一手动加载密钥

# zfs load-key <nameofzpool>/<nameofdataset> # load key for a specific dataset
# zfs load-key -a # load all keys
# zfs load-key -r zpool/dataset # load all keys in a dataset

挂载创建的加密数据集

# zfs mount <nameofzpool>/<nameofdataset>

启动时解锁/挂载:systemd

可以通过 systemd 单元在启动时自动解锁池数据集。例如,创建以下服务来解锁任何特定数据集

/etc/systemd/system/zfs-load-key@.service
[Unit]
Description=Load %I encryption keys
Before=systemd-user-sessions.service zfs-mount.service
After=zfs-import.target
Requires=zfs-import.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash -c 'until (systemd-ask-password "Encrypted ZFS password for %I" --no-tty | zfs load-key %I); do echo "Try again!"; done'

[Install]
WantedBy=zfs-mount.service

启用/启动每个加密数据集的服务(例如,zfs-load-key@pool0-dataset0.service)。注意使用 -,这是 systemd 单元定义中转义的 /。有关更多信息,请参阅 systemd-escape(1)

注意 Before=systemd-user-sessions.service 确保在将本地 IO 设备交给桌面环境之前调用 systemd-ask-password。

另一种方法是加载所有可能的密钥

/etc/systemd/system/zfs-load-key.service
[Unit]
Description=Load encryption keys
DefaultDependencies=no
After=zfs-import.target
Before=zfs-mount.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/zfs load-key -a
StandardInput=tty-force

[Install]
WantedBy=zfs-mount.service

启用/启动 zfs-load-key.service

登录时解锁:PAM

如果您不加密根卷,而只加密主目录卷或用户特定卷,另一个想法是等到登录时再解密[死链接 2024-11-06—HTTP 404]。此方法的优点是系统可以不间断启动,并且当用户登录时,可以使用同一密码进行身份验证和解密主目录卷,从而只需输入一次密码。

解锁主目录数据集有两种方法:#PAM 模块#自定义脚本。两种方法都假定您的数据集结构相似于

$ zfs list -o name,mountpoint,canmount,encryption
NAME                   MOUNTPOINT        CANMOUNT  ENCRYPTION
rpool                  /                 on        off
rpool/home             /home             off       off
rpool/home/user        /home/user        on        aes-256-gcm
rpool/home/user/child  /home/user/child  on        aes-256-gcm

rpool/homeuserchild 替换为您设置中的相应值。

PAM 模块

OpenZFS 自2.0.0起包含 PAM 模块。它支持自2.3.1子数据集

首先,使用以下命令

# zfs set org.openzfs.systemd:ignore=on rpool/home

这会阻止 systemd 自动挂载 rpool/home。此属性将由所有子数据集继承。

然后创建以下文件

/etc/pam.d/zfs-key
auth       optional                    pam_zfs_key.so homes=rpool/home runstatedir=/run/pam_zfs_key mount_recursively
session    [success=1 default=ignore]  pam_succeed_if.so service = systemd-user quiet
session    optional                    pam_zfs_key.so homes=rpool/home runstatedir=/run/pam_zfs_key mount_recursively
password   optional                    pam_zfs_key.so homes=rpool/home runstatedir=/run/pam_zfs_key mount_recursively

如果用户的任何数据集中没有子数据集,则可以省略 mount_recursively

最后,在 /etc/pam.d/system-auth/etc/pam.d/su-l 的开头添加以下行

auth       include      zfs-key
session    include      zfs-key
password   include      zfs-key
自定义脚本

首先将挂载点设置为 legacy,以避免被 zfs mount -a 挂载

# zfs set mountpoint=legacy rpool/home

确保它在 /etc/fstab 中,以便 mount /home 可以正常工作

/etc/fstab
rpool/home         /home           zfs             rw,xattr,posixacl,noauto        0 0

或者,如果您同时使用这两种方法,您可以继续使用 ZFS 挂载

# zfs set canmount=noauto rpool/home
# zfs set org.openzfs.systemd:ignore=on rpool/home

第一种方法将阻止 ZFS 自动挂载它,第二种方法将阻止 systemd 自动挂载它,但您仍然可以手动(或通过以下脚本)挂载它。如果您有子数据集,org.openzfs.systemd:ignore=on 将被继承,但您需要为每个数据集设置 canmount=noauto,因为它不可继承,否则它们将尝试在没有挂载点的情况下挂载。

在单用户系统上,如果只有一个 /home 卷具有与用户密码相同的加密密码,则可以在登录时按如下方式解密:首先创建 /usr/local/bin/mount-zfs-homedir

/usr/local/bin/mount-zfs-homedir
#!/bin/bash
set -eu

# $PAM_USER will be the username of the user, you can use it for per-user home volumes.
HOME_VOLUME="rpool/home" 

if [ "$(zfs get keystatus "${HOME_VOLUME}" -Ho value)" != "available" ]; then
  PASSWORD=$(cat -)
  zfs load-key "${HOME_VOLUME}" <<< "$PASSWORD" || continue
fi

# This will also mount any child datasets, unless they use a different key.
echo "$(zfs list -rHo name,keystatus,mounted "${HOME_VOLUME}")" | while IFS=$'\t' read -r NAME KEYSTATUS MOUNTED; do
  if [ "${MOUNTED}" != "yes" ] && [ "${KEYSTATUS}" == "available" ]; then
    zfs mount "${NAME}" || true
  fi
done

不要忘记使其可执行;然后通过在 /etc/pam.d/system-auth 中添加以下行来让 PAM 运行它

/etc/pam.d/system-auth
auth       optional                    pam_exec.so          expose_authtok /usr/local/bin/mount-zfs-homedir

现在,当您在任何地方登录(在控制台、通过 ssh 等)时,它将透明地解密并挂载 /home 卷。

SSH

需要注意的是,由于您的 ~/.ssh 目录未挂载,如果您通过 ssh 登录,第一次必须使用密码身份验证,而不是依赖 ~/.ssh/authorized_keys

如果您不想启用(不安全的)密码身份验证,您可以将 ~/.ssh/authorized_keys 移动到新位置。创建 /etc/ssh/user_config/ 并在其中为每个用户创建一个文件夹,该文件夹归用户所有并具有 700 权限。然后将每个用户的 authorized_keys 移动到其相应的文件夹,并编辑系统 sshd 配置

/etc/ssh/sshd_config
AuthorizedKeysFile /etc/ssh/user_config/%u/authorized_keys

然后重启 sshd.service。您还可以选择为每个用户从 ~/.ssh/authorized_keys 到新位置创建一个链接,以便用户仍然可以像以前一样编辑它。

这可以使您登录,但您的主分区不会被挂载,您需要手动挂载。有多种选项可以解决此问题

SSH 密钥和密码(当需要时)

可以配置 PAM,使其仅在通过 SSH 解密您的主分区时才提示输入密码。您需要启用 publickeykeyboard-interactive 身份验证方法

/etc/ssh/sshd_config
PubkeyAuthentication yes
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

## Example of excluding a certain user who does not have an encrypted home directory.
#Match User nohome
#  KbdInteractiveAuthentication no
#  AuthenticationMethods publickey
警告 注意 AuthenticationMethods publickey,keyboard-interactive 中的逗号,这意味着您需要执行两种身份验证方法才能使用 SSH 登录。非常相似的 AuthenticationMethods publickey keyboard-interactive 意味着您可以执行任意一种来登录,这会让某人绕过您的公钥身份验证。
注意 您可能会问为什么使用 keyboard-interactive 而不是 passwordpassword 是在客户端完成的,所以即使跳过了身份验证,用户仍然会收到提示,密码只是被丢弃了。使用 keyboard-interactive 时,当我们跳过它时,用户根本不会收到提示。

这将意味着在验证密钥后会要求输入密码,但使用 PAM 我们可以阻止在不需要时要求输入密码。我们创建一个脚本,当密钥不可用时脚本会失败

/usr/local/bin/require-encrypted-homedir
#!/bin/bash
set -eu

HOME_VOLUME="rpool/home" # You can use $PAM_USER to use the username in the volume for a per-user solution.

if [ "$(zfs get keystatus "${HOME_VOLUME}" -Ho value)" != "available" ]; then
  exit 27 # PAM_TRY_AGAIN
elif [[ "${SSH_AUTH_INFO_0:-""}" =~ ^"publickey " ]]; then
  exit 0
else
  # If this happens, it implies a configuration error: either you are allowing auth without a public 
  # key, or have enabled this in a non-SSH PAM service. Both are dangerous and this should block it, 
  # but if you see it, fix your configuration.
  exit 3 # PAM_SERVICE_ERR
fi

并使其可执行

现在我们想配置 PAM 来调用它,如果脚本成功,则跳过要求输入密码,因为我们已经有了密钥。将此行添加到您想要跳过的现有 auth 行(除非您有其他设置,否则全部跳过)的开头,用于 SSH 服务

/etc/pam.d/sshd
auth sufficient pam_exec.so /usr/local/bin/require-encrypted-homedir
警告 这是针对 /etc/pam.d/sshd不是 /etc/pam.d/system-auth(如上)。您不希望没有公钥的本地用户能够跳过密码。脚本中有针对此的保护措施,但最好还是谨慎。
注意 使用私钥时,PAM 会跳过 auth 步骤,因为私钥身份验证完全由 sshd 处理。这意味着我们在这里添加的脚本永远不会为私钥运行,它们也无法被跳过,但是,为了进行纵深防御,我们仍然会进行检查,以确保密钥已被验证。

有了这个,您将在密钥未加载时才被提示输入密码。

SSH 密钥和密码

一个更简单的选择是启用这两种方法,这意味着您的密钥仍然会被检查,但之后您也必须输入密码,这将解密您的主分区。

/etc/ssh/sshd_config
PubkeyAuthentication yes
PasswordAuthentication yes
AuthenticationMethods publickey,password
警告 注意 AuthenticationMethods publickey,password 中的逗号,这意味着您需要执行两种身份验证方法才能使用 SSH 登录。非常相似的 AuthenticationMethods publickey password 意味着您可以执行任意一种来登录,这会让某人绕过您的公钥身份验证。

这可以工作(并且不会让任何人仅凭密码进行身份验证),但缺点是每次都需要输入密码。

您还可以指定类似的内容

AuthenticationMethods publickey password,publickey

这允许客户端使用公钥或公钥加密码进行身份验证。客户端将根据 PreferredAuthentications 选项执行哪种操作。-o PreferredAuthentications=password,publickey 将要求输入密码,而 -o PreferredAuthentications=publickey 则不会。这比自动回退更手动,但移动的部分更少,并且可以避免每次都询问您是否偏好默认的 publickey(您可以在客户端上使用主机特定选项来简化设置这些选项)。

交换卷

警告

ZFS 不允许使用交换文件,但用户可以使用 ZFS 卷 (ZVOL) 作为交换。重要的是将 ZVOL 的块大小设置为与系统页面大小匹配,这可以通过 getconf PAGESIZE 命令获得(x86_64 上的默认值为 4KiB)。另一个有助于在低内存情况下保持系统良好运行的选项是不缓存 ZVOL 数据。

注意 ZFS 默认推荐的 zvol 交换卷大小为 16GiB。根据此处所示的示例,如果您系统的存储容量不足,使用 8GiB 是安全的,但 zfs-utils 会在生成的警告中抱怨大小。

创建一个 8 GiB 的 zfs 卷

# zfs create -V 8G -b $(getconf PAGESIZE) -o compression=zle \
              -o logbias=throughput -o sync=always\
              -o primarycache=metadata -o secondarycache=none \
              -o com.sun:auto-snapshot=false <pool>/swap

将其准备为交换分区

# mkswap -f /dev/zvol/<pool>/swap
# swapon /dev/zvol/<pool>/swap

要使其永久生效,请编辑 /etc/fstab。ZVOL 支持 discard,这可能会帮助 ZFS 的块分配器,并在交换空间未满时/如果交换空间未满,减少所有其他数据集的碎片。

/etc/fstab 添加一行

/dev/zvol/<pool>/swap none swap discard 0 0

访问控制列表

要在数据集上使用ACL

# zfs set acltype=posixacl <nameofzpool>/<nameofdataset>
# zfs set xattr=sa <nameofzpool>/<nameofdataset>

出于性能原因,建议设置 xattr[5]

最好在 zpool 上启用 ACL,因为数据集将继承 ACL 参数。aclinherit=passthrough 可能有用,因为默认模式是 restricted[6];但是,值得注意的是 aclinherit 不影响 POSIX ACL[7]

# zfs set aclinherit=passthrough <nameofzpool>
# zfs set acltype=posixacl <nameofzpool>
# zfs set xattr=sa <nameofzpool>

数据库

ZFS 与大多数其他文件系统不同,它具有可变记录大小,或通常称为块大小。默认情况下,ZFS 上的记录大小为 128KiB,这意味着它将根据写入文件的大小动态分配从 512B 到 128KiB 的任何大小的块。这通常有助于碎片和文件访问,但缺点是 ZFS 在每次只写入几个字节时都必须分配新的 128KiB 块。

本文或本章节的准确性存在争议。

原因: 至少 MariaDB 默认使用 16KiB 页面!在设置此值之前,请检查您的特定 DBMS。(在 Talk:ZFS 中讨论)

大多数 RDBMS 默认以 8KiB 大小的块进行工作。尽管块大小可以为 MySQL/MariaDBPostgreSQL 和 Oracle 数据库进行调整,但这三者默认都使用 8KiB 的块大小。为了性能考虑和尽量减少快照差异(对于备份目的,这很有帮助),通常希望将 ZFS 调整为适应数据库,使用类似以下的命令

# zfs set recordsize=8K <pool>/postgres

这些 RDBMS 还倾向于实现自己的缓存算法,通常与 ZFS 自己的 ARC 类似。为了节省内存,最好直接禁用 ZFS 对数据库文件数据的缓存,让数据库自行处理。

注意 L2ARC 需要 primarycache 才能工作,因为它由从 primarycache 中驱逐的数据填充。如果您打算使用 L2ARC,请不要设置以下选项,否则 L2ARC 上不会缓存实际数据。
# zfs set primarycache=metadata <pool>/postgres

ZFS 使用 ZIL 进行崩溃恢复,但数据库通常会在其事务提交时自行将数据文件同步到文件系统。最终结果是 ZFS 将数据两次提交到数据磁盘,并且会严重影响性能。您可以告诉 ZFS 优先不使用 ZIL,在这种情况下,数据只提交到文件系统一次。但是,在非固态存储(例如 HDD)上这样做可能会因碎片而降低读取性能(OpenZFS Wiki)——对于机械硬盘,请考虑使用专用的 SSD 作为 ZIL,而不是设置以下选项。此外,为非数据库文件系统或已配置日志设备的池设置此选项也可能负面影响性能,因此请小心。

# zfs set logbias=throughput <pool>/postgres

这些也可以在文件系统创建时完成,例如

# zfs create -o recordsize=8K \
             -o primarycache=metadata \
             -o mountpoint=/var/lib/postgres \
             -o logbias=throughput \
              <pool>/postgres

请注意:这些类型的调优参数非常适合 RDBMS 等专用应用程序。在通用文件系统(如您的 /home 目录)上设置这些参数很容易损害 ZFS 的性能。

/tmp

如果您想使用 ZFS 来存储您的 /tmp 目录,这可能有助于存储任意大的文件集,或者简单地保持您的 RAM 内存中没有空闲数据,您可以通过禁用文件系统同步来提高某些应用程序写入 /tmp 的性能。这会导致 ZFS 忽略应用程序的同步请求(例如,使用 fsyncO_SYNC)并立即返回。虽然这会对应用程序端的数据一致性产生严重后果(切勿禁用数据库同步!),但 /tmp 中的文件不太可能重要且受影响。请注意,这不会影响 ZFS 本身的一致性,只会影响应用程序期望在磁盘上的数据在崩溃后可能尚未实际写入的可能性。

# zfs set sync=disabled <pool>/tmp

此外,出于安全考虑,您可能需要禁用 /tmp 文件系统上的setuiddevices,这可以防止某些类型的权限提升攻击或设备节点的使用。

# zfs set setuid=off <pool>/tmp
# zfs set devices=off <pool>/tmp

将所有这些结合起来创建一个创建命令,如下所示

# zfs create -o setuid=off -o devices=off -o sync=disabled -o mountpoint=/tmp <pool>/tmp

另外请注意,如果您想在 ZFS 上使用 /tmp,您需要屏蔽(禁用)systemd 的自动 tmpfs 备份的 /tmp(tmp.mount),否则 ZFS 将无法在启动时或导入时挂载您的数据集。

使用 ZFS Send 和 ZFS Recv 传输快照

通过配对 zfs sendzfs recv,可以将 ZFS 快照传输到任意目标。这是通过标准输出来完成的,允许数据发送到任何文件、设备、网络,或通过在管道中加入其他程序进行中间流处理。

以下是常见场景的示例

基本 ZFS Send

首先,创建一些 ZFS 文件系统的快照

# zfs snapshot zpool0/archive/books@snap

现在将快照发送到不同 zpool 上的新位置

# zfs send -v zpool0/archive/books@snap | zfs recv zpool4/library

zpool0/archive/books@snap 的内容现在已在 zpool4/library 中生效

提示 有关标志的详细信息,请参阅 man zfs-sendman zfs-recv
到文件和从文件

首先,创建一些 ZFS 文件系统的快照

# zfs snapshot zpool0/archive/books@snap

将快照写入 gzip 文件

# zfs send zpool0/archive/books@snap > /tmp/mybooks.gz
警告 确保使用 -w 标志运行 zfs send,如果您希望在传输过程中保留加密。

现在从文件恢复快照

# gzcat /tmp/mybooks.gz | zfs recv -F zpool0/archive/books

通过 ssh 发送

首先,创建一些 ZFS 文件系统的快照

# zfs snapshot zpool1/filestore@snap

接下来,我们将“发送”流量通过运行“recv”的 ssh 会话进行管道传输

# zfs send -v zpool1/filestore@snap | ssh $HOST zfs recv coldstore/backups

-v 标志会打印有关正在生成的数据流的信息。如果您使用的是密码或密匙,系统会提示您输入。

增量备份

您可能希望在不重新传输所有数据的情况下更新先前发送的 ZFS 文件系统。或者,您可能需要在长时间传输过程中使文件系统保持在线,现在是时候发送自初始快照以来的写入操作了。

首先,创建一些 ZFS 文件系统的快照

# zfs snapshot zpool1/filestore@initial

接下来,我们将“发送”流量通过运行“recv”的 ssh 会话进行管道传输

# zfs send -v -R zpool1/filestore@initial | ssh $HOST zfs recv coldstore/backups

写入更改后,创建另一个快照

# zfs snapshot zpool1/filestore@snap2

以下命令将发送 zpool1/filestore@initial 和 zpool1/filestore@snap2 之间在本地存在的差异,并为远程文件系统 coldstore/backups 创建一个额外的快照

# zfs send -v -i -R zpool1/filestore@initial | ssh $HOST zfs recv coldstore/backups

现在 zpool1/filestore 和 coldstore/backups 都拥有 @initial 和 @snap2 快照。

在远程主机上,您现在可以将最新快照提升为活动文件系统

# zfs rollback coldstore/backups@snap2

调优

常规

ZFS 池和数据集可以通过参数进一步调整。

注意 除配额和保留外,所有可设置的属性都从父数据集继承其值。

要检索当前池参数状态

# zfs get all <pool>

要检索当前数据集参数状态

# zfs get all <pool>/<dataset>

要禁用访问时间(atime),它默认启用

# zfs set atime=off <pool>

要在特定数据集上禁用访问时间(atime)

# zfs set atime=off <pool>/<dataset>

与完全关闭 atime 相比,relatime 是一个替代方案。这带来了 ext4/XFS 的默认 atime 语义到 ZFS,其中仅在修改时间或更改时间发生更改时,或在现有访问时间未在过去 24 小时内更新时,才会更新访问时间。它是 atime=offatime=on 之间的折衷。此属性atimeon 时生效。

# zfs set atime=on <pool>
# zfs set relatime=on <pool>

压缩就是透明压缩数据。ZFS 支持几种不同的算法,目前 lz4 是默认的,对于很少写入但高度可压缩的数据,也可用 gzip;有关更多详细信息,请参阅 OpenZFS Wiki

启用压缩

# zfs set compression=on <pool>

要将池和/或数据集的属性重置为其默认状态,请使用 zfs inherit

# zfs inherit -rS atime <pool>
# zfs inherit -rS atime <pool>/<dataset>
警告 使用 -r 标志将递归重置 zpool 的所有数据集。

擦除 (Scrubbing)

每当 ZFS 在读取数据时遇到错误时,它会在可能的情况下静默修复,重新写入磁盘并记录下来,以便您可以获得池上错误的概览。ZFS 没有 fsck 或等效工具。相反,ZFS 支持称为 scrubbing 的功能。这会遍历池中的所有数据并验证所有块是否可读。

擦洗一个池

# zpool scrub <pool>

取消正在进行的擦洗

# zpool scrub -s <pool>

应该多久进行一次?

来自 Oracle 的博文 Disk Scrub - Why and When?

这个问题对支持部门来说很难回答,因为总是真实的答案是“这取决于”。所以在我提供一般性指导之前,这里有一些技巧可以帮助您创建一个更符合您使用模式的答案。
  • 您最旧的备份的有效期是多久?您应该至少像您最旧的磁带过期一样频繁地擦洗您的数据,以便您有一个已知的良好恢复点。
  • 您多久会遇到一次磁盘故障?虽然热备用磁盘的启用会调用“resilver”——仅仅是丢失磁盘的 VDEV 的目标擦洗——但您应该至少像在您的特定环境中平均遇到磁盘故障一样频繁地擦洗。
  • 您硬盘上最旧的数据多久会被读取一次?您应该偶尔进行擦洗,以防止非常旧、非常过时的数据出现位腐烂并死亡而不被您知道。
如果以上任何一个问题的答案是“我不知道”,那么一般指导是:您可能应该至少每月擦洗一次您的 zpool。这是一个适合大多数用例的计划,对于除最繁忙和最重负荷的系统之外的所有系统,它提供了足够的时间来完成擦洗,然后再开始下一次擦洗,即使在非常大的 zpools(192+ 磁盘)上,在磁盘故障之间也应该相当频繁地完成。

在 Aaron Toponce 的ZFS 管理指南中,他建议每周擦洗一次消费者磁盘。

从服务或计时器开始

注意 从 OpenZFS 2.1.3 开始,包含了每周和每月systemd 计时器/服务。要使用这些,请为所需的池启用/启动 zfs-scrub-weekly@pool-to-scrub.timerzfs-scrub-monthly@pool-to-scrub.timer

使用systemd 计时器/服务可以自动擦洗池。

每月对特定池执行擦洗

/etc/systemd/system/zfs-scrub@.timer
[Unit]
Description=Monthly zpool scrub on %i

[Timer]
OnCalendar=monthly
AccuracySec=1h
Persistent=true

[Install]
WantedBy=multi-user.target
/etc/systemd/system/zfs-scrub@.service
[Unit]
Description=zpool scrub on %i

[Service]
Nice=19
IOSchedulingClass=idle
KillSignal=SIGINT
ExecStart=/usr/bin/zpool scrub %i

[Install]
WantedBy=multi-user.target

为指定的 zpool 启用/启动 zfs-scrub@pool-to-scrub.timer 单元以进行每月擦洗。

启用 TRIM

要快速查询您的 vdevs 的 TRIM 支持,您可以使用 -t 将修剪信息包含在 zpool status 中。

$ zpool status -t tank
pool: tank
 state: ONLINE
  scan: none requested
 config:

	NAME                                     STATE     READ WRITE CKSUM
	tank                                     ONLINE       0     0     0
	  ata-ST31000524AS_5RP4SSNR-part1        ONLINE       0     0     0  (trim unsupported)
	  ata-CT480BX500SSD1_2134A59B933D-part1  ONLINE       0     0     0  (untrimmed)

errors: No known data errors

ZFS 能够按需或定期通过 autotrim 属性来修剪支持的 vdevs。

手动对 zpool 执行 TRIM 操作

 # zpool trim <zpool>

在池中的所有受支持 vdevs 上启用定期修剪

 # zpool set autotrim=on <zpool>
  • 由于自动 TRIM 和完整的 zpool trim 的操作方式不同,偶尔进行手动修剪是有意义的
  • 从 OpenZFS 2.2.0 开始,包含每周和每月systemd 计时器/服务(参见 [8])。要使用这些,请为所需的池启用/启动 zfs-trim-weekly@pool-to-trim.timerzfs-trim-monthly@pool-to-trim.timer

使用systemd 计时器/服务,每月对特定池执行一次完整的 zpool trim

/etc/systemd/system/zfs-trim@.timer
[Unit]
Description=Monthly zpool trim on %i

[Timer]
OnCalendar=monthly
AccuracySec=1h
Persistent=true

[Install]
WantedBy=multi-user.target
/etc/systemd/system/zfs-trim@.service
[Unit]
Description=zpool trim on %i
Documentation=man:zpool-trim(8)
Requires=zfs.target
After=zfs.target
ConditionACPower=true
ConditionPathIsDirectory=/sys/module/zfs

[Service]
Nice=19
IOSchedulingClass=idle
KillSignal=SIGINT
ExecStart=/bin/sh -c '\
if /usr/bin/zpool status %i | grep "trimming"; then\
exec /usr/bin/zpool wait -t trim %i;\
else exec /usr/bin/zpool trim -w %i; fi'
ExecStop=-/bin/sh -c '/usr/bin/zpool trim -s %i 2>/dev/null || true'

[Install]
WantedBy=multi-user.target

为指定的 zpool 启用/启动 zfs-trim@pool-to-trim.timer 单元以进行每月修剪。

SSD 缓存

如果您的池没有配置日志设备,ZFS 会为您的意图日志(ZIL,也称为 SLOG)在池的数据磁盘上预留空间。如果您的数据磁盘速度较慢(例如 HDD),强烈建议在固态硬盘上配置 ZIL 以提高写入性能,并考虑使用 L2ARC(自适应替换缓存)。添加它们的流程与添加新的 VDEV 非常相似。

以下所有关于 device-id 的引用都来自 /dev/disk/by-id/* 中的 ID。

ZIL

添加一个镜像 ZIL

 # zpool add <pool> log mirror <device-id-1> <device-id-2>

或者添加一个单设备 ZIL(不安全)

 # zpool add <pool> log <device-id>

因为 ZIL 设备存储了尚未写入池的数据,所以使用能够断电时完成写入的设备很重要。使用冗余也很重要,因为设备故障可能导致数据丢失。此外,ZIL 仅用于同步写入,因此当您的数据驱动器与您的 ZIL 驱动器一样快时,可能不会提供任何性能改进。

L2ARC

添加 L2ARC

# zpool add <pool> cache <device-id>

L2ARC 仅是读取缓存,因此不需要冗余。自ZFS 版本 2.0.0 起,L2ARC 会在重启后持久化。[9]

L2ARC 通常仅在热数据量大于系统内存但小于 L2ARC 容量的工作负载中有用。L2ARC 由系统内存中的 ARC 索引,每个记录(默认 128KiB)占用 70 字节。因此,内存使用量的计算公式是

(L2ARC size) / (recordsize) * 70 bytes

因此,在某些工作负载中,L2ARC 会从 ARC 中占用内存,从而可能损害性能。

ZVOLs

ZFS 卷 (ZVOLs) 可能面临与 RDBMS 相同的块大小相关问题,但值得注意的是,ZVOLs 的默认记录大小已经是 8 KiB。如果可能,最好将 ZVOL 中包含的任何分区与您的记录大小对齐(fdisk 和 gdisk 的当前版本默认自动对齐到 1MiB 段,这很有效),并将文件系统块大小也设置为相同大小。除此之外,您可以根据需要调整 recordsize 以适应 ZVOL 中的数据(尽管 8 KiB 对于大多数文件系统来说都是一个好值,即使在文件系统级别使用 4 KiB 块)。

RAIDZ 和高级格式化物理磁盘

ZVOL 的每个块都有自己的奇偶校验磁盘,如果您有逻辑块大小为 4096B、8192B 或类似的物理介质,则奇偶校验需要存储在完整的物理块中,这会大大增加 ZVOL 的空间需求,需要 2 倍或更多的物理存储容量才能满足 ZVOL 的逻辑容量。将 recordsize 设置为 16k 或 32k 可以显著减小这种占用空间。

有关详细信息,请参阅 OpenZFS issue #1807

I/O 调度器

虽然 ZFS 预计会与包括 mq-deadlinenone 在内的现代调度器很好地协同工作,但通过手动设置 ZFS 磁盘上的 I/O 调度器可能会带来性能提升。ZFS 的建议是“[...] 用户应保留默认调度器“除非您遇到特定问题,或者已经清楚地测量到您的工作负载的性能有所提高”[10]

故障排除

创建 zpool 失败

如果出现以下错误,则可以修复。

 the kernel failed to rescan the partition table: 16
 cannot label 'sdc': try using parted(8) and then provide a specific slice: -1

发生这种情况的一个原因是 ZFS 期望池创建时间少于 1 秒[11][12]。在普通情况下,这是一个合理的假设,但在许多情况下可能需要更长时间。每次尝试之前,都需要再次清除每个驱动器。

# zpool labelclear /dev/sdX
# wipefs --all /dev/sdX

本文或本章节的准确性存在争议。

原因: 虽然下面描述的方法在技术上可行,但最好解决根本原因。根据上面链接的 GitHub 问题,电源管理#硬盘驱动器似乎是最合适的起点。(在 Talk:ZFS 中讨论)

可以反复尝试蛮力创建,并且在某些情况下,ZPool 创建时间将少于 1 秒。创建速度慢的一个原因是驱动器的突发读取速度慢。通过并行读取磁盘和创建 ZPool,可能可以提高突发速度。

# dd if=/dev/sda of=/dev/null

对于多个驱动器,可以通过将上述命令保存到文件中的不同行并运行来完成

# cat $FILE | parallel

然后同时运行 ZPool 创建。

ZFS 使用过多的 RAM

默认情况下,ZFS 使用主机上可用系统内存的一半来缓存文件操作(ARC)。要调整 ARC 大小,请将以下内容添加到内核参数列表中

zfs.zfs_arc_max=536870912 # (for 512MiB)

如果 zfs_arc_min(系统内存的 1/32)的默认值高于指定的 zfs_arc_max,则需要将以下内容也添加到内核参数列表中

zfs.zfs_arc_min=268435456 # (for 256MiB, needs to be lower than zfs.zfs_arc_max)

您可能还想增加 zfs_arc_sys_free(在本例中为 8GiB)

# echo $((8*1024**3)) > /sys/module/zfs/parameters/zfs_arc_sys_free

有关更详细的描述以及其他配置选项,请参阅 Gentoo:ZFS#ARC

ZFS 应该在应用程序预留更多 RAM 时释放 ARC,但有些应用程序仍然会感到困惑,并且报告的可用 RAM 总是错误的。但是,如果所有应用程序都按预期工作并且您没有问题,则无需更改 ARC 设置。

不包含 EFI 标签

尝试创建 zfs 文件系统时会发生以下错误,

/dev/disk/by-id/<id> does not contain an EFI label but it may contain partition

克服此问题的方法是使用 -f 配合 zfs create 命令。

未找到主机 ID

启动时出现错误,出现以下行在 initscript 输出之前

ZFS: No hostid found on kernel command line or /etc/hostid.

此警告发生是因为 ZFS 模块无法访问 spl 主机。有两种解决方案。一种是将 spl hostid 放在启动加载器中的内核参数中。例如,添加 spl.spl_hostid=0x00bab10c

另一种解决方案是确保 /etc/hostid 中存在 hostid,然后重新生成 initramfs 映像。这会将 hostid 复制到 initramfs 映像中。

从 SAS/SCSI 设备启动时找不到池

如果您正在使用基于 SAS/SCSI 的启动,您可能会偶尔遇到启动问题,即找不到您尝试从中启动的池。这很可能的原因是您的设备在过程中初始化得太晚。这意味着 ZFS 在尝试组装您的池时找不到任何设备。

在这种情况下,您应该强制 scsi 驱动程序在继续之前等待设备上线。您可以通过将此内容放入 /etc/modprobe.d/zfs.conf 来实现

/etc/modprobe.d/zfs.conf
options scsi_mod scan=sync

之后,重新生成 initramfs

这是因为 zfs 钩子会将 /etc/modprobe.d/zfs.conf 文件复制到 initcpio 中,然后该文件将在构建时使用。

启动时 zfs 池未挂载,并显示:“池可能正在被其他系统使用”

未导出的池

如果新安装无法启动,因为 zpool 无法导入,请 chroot 进入安装并正确导出 zpool。请参阅 #Emergency chroot repair with archzfs

进入 chroot 环境后,加载 ZFS 模块并强制导入 zpool,

# zpool import -a -f

现在导出池

# zpool export <pool>

要查看可用池,请使用,

# zpool status

导出池是必需的,因为 ZFS 使用 hostid 来跟踪 zpool 创建的系统。hostid 是部分基于网络设置生成的。在 archiso 安装过程中,网络配置可能不同,生成与新安装中的不同的 hostid。一旦 zfs 文件系统被导出然后在新安装中重新导入,hostid 就会被重置。请参阅 Re: Howto zpool import/export automatically? - msg#00227

如果 ZFS 在每次重启后都抱怨“池可能正在使用中”,请按上述方法正确导出池,然后在正常启动的系统中重新生成 initramfs

错误的主机 ID

仔细检查池是否已正确导出。导出 zpool 会清除主机 ID 标记的所有权。因此,在首次启动时,zpool 应正确挂载。如果未挂载,则存在其他问题。

再次重新启动,如果 ZFS 池拒绝挂载,则表示主机 ID 在早期启动阶段尚未正确设置,这会混淆 ZFS。一旦主机 ID 在重新启动之间保持一致,zpool 就会正确挂载。手动告知 ZFS 正确的数字。

使用 zfs_force 引导并写下主机 ID。这只是一个示例。

$ hostid
0a0af0f8

必须将此数字添加到 内核参数中,例如 spl.spl_hostid=0x0a0af0f8。另一种解决方案是将主机 ID 写入 initramfs 映像,有关详细信息,请参阅 安装指南中有关此内容的说明。

用户始终可以通过在 内核参数中添加 zfs_force=1 来忽略检查,但这不建议作为永久解决方案。

设备具有不同的扇区对齐方式

一旦驱动器发生故障,应尽快使用相同的驱动器进行更换。

# zpool replace bigdata ata-ST3000DM001-9YN166_S1F0KDGY ata-ST3000DM001-1CH166_W1F478BD -f

但在此实例中,会产生以下错误

cannot replace ata-ST3000DM001-9YN166_S1F0KDGY with ata-ST3000DM001-1CH166_W1F478BD: devices have different sector alignment

ZFS 使用 ashift 选项来调整物理块大小。替换故障磁盘时,ZFS 尝试使用 ashift=12,但故障磁盘使用的是不同的 ashift(可能是 ashift=9),这会导致相应的错误。

对于具有 4 KiB 块大小的 Advanced Format 磁盘,建议使用 ashift12 以获得最佳性能。请参阅 OpenZFS FAQ:性能注意事项ZFS 和 Advanced Format 磁盘

使用 zdb 查找 zpool 的 ashift:zdb ,然后使用 -o 参数设置替换驱动器的 ashift。

# zpool replace bigdata ata-ST3000DM001-9YN166_S1F0KDGY ata-ST3000DM001-1CH166_W1F478BD -o ashift=9 -f

检查 zpool 状态以确认。

# zpool status -v
pool: bigdata
state: DEGRADED
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
scan: resilver in progress since Mon Jun 16 11:16:28 2014
    10.3G scanned out of 5.90T at 81.7M/s, 20h59m to go
    2.57G resilvered, 0.17% done
config:

        NAME                                   STATE     READ WRITE CKSUM
        bigdata                                DEGRADED     0     0     0
        raidz1-0                               DEGRADED     0     0     0
            replacing-0                        OFFLINE      0     0     0
            ata-ST3000DM001-9YN166_S1F0KDGY    OFFLINE      0     0     0
            ata-ST3000DM001-1CH166_W1F478BD    ONLINE       0     0     0  (resilvering)
            ata-ST3000DM001-9YN166_S1F0JKRR    ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0KBP8    ONLINE       0     0     0
            ata-ST3000DM001-9YN166_S1F0JTM1    ONLINE       0     0     0

errors: No known data errors

池恢复卡住/重启/缓慢?

根据 ZFS issue #840,这是自 2012 年以来 ZFS-ZED 的一个已知问题,该问题导致恢复过程不断重启,有时会卡住,并且对某些硬件来说速度很慢。最简单的解决方法是停止 zfs-zed.service,直到恢复完成。

修复由于 initramfs zpool.cache 中无法访问的池导入失败而导致的缓慢启动。

当您更新 initramfs(例如在进行内核更新时)时,如果您已导入其他但未永久连接的池,则您的启动时间可能会受到显著影响,因为这些池将被添加到您的 initramfs zpool.cache 中,并且 ZFS 将尝试在每次启动时导入这些额外的池,而不管您是否已导出它并将其从常规 zpool.cache 中删除。

如果您注意到 ZFS 在启动时尝试导入无法访问的池,请首先运行

# zdb -C

以检查您的 zpool.cache 中是否有您不希望在启动时导入的池。如果此命令显示(一个)额外的、当前不可用的池,请运行

# zpool set cachefile=/etc/zfs/zpool.cache zroot

以清除 zpool.cache 中除名为 zroot 的池之外的所有池。有时无需刷新 zpool.cache,但您需要的是 重新生成 initramfs

ZFS 命令历史

ZFS 会原生将池结构的更改作为执行命令的日志记录在环形缓冲区中(无法关闭)。该日志可能有助于恢复降级或失败的池。

# zpool history zpool
History for 'zpool':
2023-02-19.16:28:44 zpool create zpool raidz1 /scratch/disk_1.img /scratch/disk_2.img /scratch/disk_3.img
2023-02-19.16:31:29 zfs set compression=lz4 zpool
2023-02-19.16:41:45 zpool scrub zpool
2023-02-19.17:00:57 zpool replace zpool /scratch/disk_1.img /scratch/bigger_disk_1.img
2023-02-19.17:01:34 zpool scrub zpool
2023-02-19.17:01:42 zpool replace zpool /scratch/disk_2.img /scratch/bigger_disk_2.img
2023-02-19.17:01:46 zpool replace zpool /scratch/disk_3.img /scratch/bigger_disk_3.img

技巧与提示

创建支持 ZFS 的 Archiso 映像

遵循 Archiso 步骤来创建功能齐全的 Arch Linux live CD/DVD/USB 映像。要将 ZFS 支持包含在映像中,您可以从 AUR 构建您选择的 PKGBUILD,或者包含来自 非官方用户存储库的预构建包。

使用 AUR 中自建的 ZFS 包

按照 常规流程构建您想要的 ZFS 包。如果您不确定,zfs-dkmsAURzfs-utilsAUR 很可能与其他您可能想对 Archiso 映像进行的修改兼容。继续设置 自定义本地存储库。将 生成的存储库包含在您新配置文件中的 Pacman 配置中。

将构建的包包含在要安装的包列表中。下面的示例假定您要包含 libunwindzfs-dkmsAURzfs-utilsAUR 包。

packages.x86_64
...
libunwind
zfs-dkms
zfs-utils

如果您包含任何 DKMS 包,请确保您还包含要包含在 ISO 中的任何内核的头文件(例如,对于默认的 linux 内核,这将是 linux-headers。其他内核有各自的头文件包)。

使用 archzfs 非官方用户存储库

archzfs 非官方用户存储库添加到您新 Archiso 配置文件中的 pacman.conf

archzfs 组添加到要安装的包列表(archzfs 存储库仅提供 x86_64 架构的包)以及必需的依赖项。

packages.x86_64
...
libunwind
linux-headers-lts
zfs-utils
zfs-dkms
  • 如果您正在使用 archzfs 存储库,您可以使用提供的 zfs-utilszfs-dkms 来简化安装媒体的创建。
  • 如果您以后在运行 modprobe zfs 时遇到问题,您应该在 packages.x86_64 中包含 linux-headers。

收尾工作

无论您从何处获取 ZFS 包,您都应该通过 构建 ISO 来完成。

自动快照

zrepl

zreplAUR 包提供了一个 ZFS 自动复制服务,它也可以用作快照服务,类似于 snapper

有关如何配置 zrepl 守护进程的详细信息,请参阅 zrepl 文档。配置文件应位于 /etc/zrepl/zrepl.yml。然后,运行 zrepl configcheck 以确保配置文件语法正确。最后,启用 zrepl.service

sanoid

sanoidAUR 是一个用于拍摄快照的策略驱动工具。Sanoid 还包括 syncoid,用于复制快照。它带有 systemd 服务和定时器。

Sanoid 仅在本地系统上修剪快照。要修剪远程系统上的快照,也应在远程系统上运行 sanoid 并带有 prune 选项。可以使用 --prune-snapshots 命令行选项,或者结合 autoprune = yesautosnap = no 配置选项使用 --cron 命令行选项。

Linux 版 ZFS 自动快照服务

注意 zfs-auto-snapshot-gitAUR 自 2019 年以来没有更新,并且功能非常有限。建议您切换到更新的工具,如 zreplAUR

zfs-auto-snapshot-gitAUR 包提供了一个 shell 脚本来自动化快照的管理,每个快照都按日期和标签(每小时、每天等)命名,提供对所有 ZFS 数据集的快速方便的快照。该包还安装了用于每刻钟、每小时、每天、每周和每月快照的 cron 任务。根据快照要追溯的时间,可选地调整 --keep 参数(每月脚本默认保留数据最多一年)。

要完全阻止数据集被快照,请在其上设置 com.sun:auto-snapshot=false。同样,可以根据标签进行更精细地控制,例如,如果不希望保留月度快照,则设置 com.sun:auto-snapshot:monthly=false

注意 zfs-auto-snapshot-git 在 擦洗期间不会创建快照。可以通过 编辑提供的 systemd 单元并从 ExecStart 行中删除 --skip-scrub 来覆盖此行为。后果未知,请有人编辑。

安装包后,启用并启动选定的计时器zfs-auto-snapshot-{frequent,daily,weekly,monthly}.timer)。

创建共享

ZFS 通过 NFSSMB 支持创建共享。

警告 如果 mountpoint 设置为 legacy(当然,如果设置为 none,则此方法无效)。未能找到此来源,但我因此问题卡了几天,直到我意识到问题所在。

NFS

确保已安装/配置 NFS,请注意无需编辑 /etc/exports 文件。要通过 NFS 共享,应 启动 nfs-server.servicezfs-share.service 服务。

使池在网络上可用

# zfs set sharenfs=on nameofzpool

使数据集在网络上可用

# zfs set sharenfs=on nameofzpool/nameofdataset

为特定 IP 范围启用读/写访问

# zfs set sharenfs="rw=@192.168.1.100/24,rw=@10.0.0.0/24" nameofzpool/nameofdataset

检查数据集是否已成功导出

# showmount -e `hostname`
Export list for hostname:
/path/of/dataset 192.168.1.100/24

要更详细地查看当前加载的导出状态,请使用

# exportfs -v
/path/of/dataset
    192.168.1.100/24(sync,wdelay,hide,no_subtree_check,mountpoint,sec=sys,rw,secure,no_root_squash,no_all_squash)

查看 ZFS 当前的 NFS 共享列表

# zfs get sharenfs

SMB

注意 SMB 功能非常有限。usershare 路径必须是 /var/lib/samba/usershares,并且唯一支持的 sharesmb 选项是 onoff。通过 sharesmb=guest_ok=y 启用访客访问不受支持。

通过 SMB 共享时,使用 /etc/samba/smb.conf 中的 usershares 将允许 ZFS 设置和创建共享。有关详细信息,请参阅 Samba#Enable Usershares

/etc/samba/smb.conf
[global]
    usershare path = /var/lib/samba/usershares
    usershare max shares = 100
    usershare allow guests = yes
    usershare owner only = no

以 root 身份创建用户目录并设置权限

# mkdir /var/lib/samba/usershares
# chmod +t /var/lib/samba/usershares

使池在网络上可用

# zfs set sharesmb=on nameofzpool

使数据集在网络上可用

# zfs set sharesmb=on nameofzpool/nameofdataset

检查数据集是否已成功导出

# smbclient -L localhost -U%
        Sharename       Type      Comment
        ---------       ----      -------
        IPC$            IPC       IPC Service (SMB Server Name)
        nameofzpool_nameofdataset        Disk      Comment: path/of/dataset
SMB1 disabled -- no workgroup available

查看 ZFS 当前的 SMB 共享列表

# zfs get sharesmb

ZFS 使用 dm-crypt 进行加密

OpenZFS 0.8.0 版本之前,ZFS 不直接支持加密(请参阅 #原生加密)。相反,zpool 可以在 dm-crypt 块设备上创建。由于 zpool 在纯文本抽象上创建,因此可以对数据进行加密,同时保留 ZFS 的所有优势,如重复数据删除、压缩和数据稳健性。此外,利用 dm-crypt 将加密 zpool 的元数据,这是原生加密本身无法提供的。[13]

dm-crypt,可能通过 LUKS,会在 /dev/mapper 中创建设备,并且它们的名称是固定的。因此,您只需更改 zpool create 命令以指向这些名称即可。其思想是配置系统创建 /dev/mapper 块设备并从中导入 zpool。由于 zpool 可以在多个设备上创建(raid、镜像、条带化等),因此所有设备都必须加密,否则保护可能会部分丢失。

例如,可以使用纯 dm-crypt(不带 LUKS)创建加密的 zpool,命令如下:

# cryptsetup open --type=plain --hash=sha256 --cipher=aes-xts-plain64 --offset=0 \
             --key-file=/dev/sdZ --key-size=512 /dev/sdX enc
# zpool create zroot /dev/mapper/enc

对于根文件系统池,mkinitcpio.conf 中的 HOOKS 行将启用用于密码的键盘,创建设备并加载池。它将包含类似以下内容:

HOOKS=(... keyboard encrypt zfs ...)

由于 /dev/mapper/enc 名称是固定的,因此不会发生导入错误。

创建加密的 zpool 工作正常。但是,如果您需要加密目录(例如,以保护您的用户主目录),ZFS 将丢失某些功能。

ZFS 将看到加密数据,而不是纯文本抽象,因此压缩和重复数据删除将不起作用。原因是加密数据始终具有高熵,导致压缩无效,并且即使是相同的输入,由于加盐也会产生不同的输出(从而使重复数据删除变得不可能)。为了减少不必要的开销,可以为每个加密目录创建一个子文件系统,并在其上使用 eCryptfs

例如,要拥有一个加密的主目录:(两个密码,加密和登录密码,必须相同)

# zfs create -o compression=off -o dedup=off -o mountpoint=/home/<username> <zpool>/<username>
# useradd -m <username>
# passwd <username>
# ecryptfs-migrate-home -u <username>
<log in user and complete the procedure with ecryptfs-unwrap-passphrase>

使用 archzfs 进行紧急 chroot 修复

要从 live 系统访问 ZFS 文件系统进行维护,有两种选择:

  1. 根据 #创建支持 ZFS 的 Archiso 映像中的说明,构建自定义 archiso。
  2. 引导最新的官方 archiso 并启动网络。然后,在 live 系统中像往常一样启用 archzfs 存储库,同步 pacman 包数据库,并安装 archzfs-archiso-linux 包。

要开始恢复,请加载 ZFS 内核模块

# modprobe zfs

导入池

# zpool import -a -R /mnt

挂载引导分区和 EFI 系统分区(如果存在)。

# mount /dev/sda2 /mnt/boot
# mount /dev/sda1 /mnt/efi

Chroot 到 ZFS 文件系统

# arch-chroot /mnt /bin/bash

检查内核版本

# pacman -Qi linux
# uname -r

uname 将显示 archiso 的内核版本。如果不同,请在 chroot 中使用 chroot 安装的正确内核版本运行 depmod。

# depmod -a 3.6.9-1-ARCH (version gathered from pacman -Qi linux but using the matching kernel modules directory name under the chroot's /lib/modules)

这将加载 chroot 安装中安装的内核版本的正确内核模块。

重新生成 initramfs。不应有任何错误。

绑定挂载

此处创建了从 /mnt/zfspool 到 /srv/nfs4/music 的绑定挂载。配置确保在创建绑定挂载之前 ZFS 池已准备就绪。

fstab

有关 systemd 如何将 fstab 转换为挂载单元文件的详细信息,请参阅 systemd.mount(5)systemd-fstab-generator(8)

/etc/fstab
/mnt/zfspool		/srv/nfs4/music		none	bind,defaults,nofail,x-systemd.requires=zfs-mount.service	0 0

监控/事件邮件通知

有关更多信息,请参阅 ZED:ZFS 事件守护进程

需要一个邮件转发器,例如 S-nail,才能完成此操作。请测试它以确保它能正常工作。

在配置文件中取消注释以下内容:

/etc/zfs/zed.d/zed.rc
 ZED_EMAIL_ADDR="root"
 ZED_EMAIL_PROG="mailx"
 ZED_NOTIFY_VERBOSE=0
 ZED_EMAIL_OPTS="-s '@SUBJECT@' @ADDRESS@"

ZED_EMAIL_ADDR="root" 中的“root”更新为您想要接收通知的电子邮件地址。

如果您将 mailrc 保存在您的主目录中,可以通过设置 MAILRC 来让 mail 从那里获取它。

/etc/zfs/zed.d/zed.rc
export MAILRC=/home/<user>/.mailrc

这样做是因为 ZED 会源取此文件,因此 mailx 会看到此环境变量。

如果您希望无论池的状态如何都能收到电子邮件,请将 ZED_NOTIFY_VERBOSE=1 设置为 1。您需要暂时设置此选项进行测试。

启动启用 zfs-zed.service

使用 ZED_NOTIFY_VERBOSE=1,您可以作为 root 运行擦洗来测试:zpool scrub <pool-name>

将 shell 命令包装在快照前后

由于创建快照的成本很低,我们可以将其用作对系统和软件包升级等敏感命令的安全措施。如果在之前和之后都创建一个快照,我们可以稍后 diff 这些快照以找出命令执行后文件系统发生了什么变化。此外,如果结果不理想,我们还可以回滚。

znp

例如

# zfs snapshot -r zroot@pre
# pacman -Syu
# zfs snapshot -r zroot@post
# zfs diff zroot@pre zroot@post 
# zfs rollback zroot@pre

一个实用程序,可以自动创建 shell 命令前后的快照,该实用程序是 znp

例如

# znp pacman -Syu
# znp find / -name "something*" -delete

您将获得在提供的命令前后创建的快照,并且命令的输出也会记录到文件中以供将来参考,以便我们知道哪个命令创建了在预/后快照对中看到的差异。

远程解锁 ZFS 加密根目录

PR #261 起,archzfs 支持 SSH 解锁原生加密的 ZFS 数据集。本节介绍如何使用此功能,并且很大程度上基于 dm-crypt/Specialties#Busybox based initramfs (built with mkinitcpio)

  1. 安装 mkinitcpio-netconf 以提供设置早期用户空间网络的钩子。
  2. 选择一个 SSH 服务器在早期用户空间中使用。选项包括 mkinitcpio-tinysshmkinitcpio-dropbear,两者不能同时使用。
    1. 如果使用 mkinitcpio-tinyssh,还建议安装 tinysshtinyssh-convert-gitAUR。此工具将现有的 OpenSSH 主机密钥转换为 TinySSH 密钥格式,保留密钥指纹并避免连接警告。TinySSH 和 Dropbear mkinitcpio 安装脚本将在生成新的 initcpio 映像时自动转换现有主机密钥。
  3. 决定是使用现有的 OpenSSH 密钥还是生成一个新的密钥(推荐),用于连接到并解锁加密 ZFS 机器的主机。将公钥复制到 /etc/tinyssh/root_key/etc/dropbear/root_key。生成 initcpio 映像时,此文件将被添加到 root 用户的 authorized_keys 中,并且仅在 initramfs 阶段有效。
  4. ip= 内核参数添加到您的引导加载程序配置中。ip 字符串是 高度可配置的。下面显示了一个简单的 DHCP 示例。
    ip=:::::eth0:dhcp
  5. 编辑 /etc/mkinitcpio.conf,在 zfs 钩子之前包含 netconfdropbeartinyssh 以及 zfsencryptssh 钩子。
    HOOKS=(... netconf <tinyssh>|<dropbear> zfsencryptssh zfs ...)
  6. 重新生成 initramfs.
  7. 重新启动并进行尝试!

更改 SSH 服务器端口

默认情况下,mkinitcpio-tinysshmkinitcpio-dropbear 监听端口 22。您可能希望更改它。

对于 TinySSH,将 /usr/lib/initcpio/hooks/tinyssh 复制到 /etc/initcpio/hooks/tinyssh,然后在 run_hook() 函数中找到/修改以下行:

/etc/initcpio/hooks/tinyssh
/usr/bin/tcpserver -HRDl0 0.0.0.0 <new_port> /usr/sbin/tinysshd -v /etc/tinyssh/sshkeydir &

对于 Dropbear,将 /usr/lib/initcpio/hooks/dropbear 复制到 /etc/initcpio/hooks/dropbear,然后在 run_hook() 函数中找到/修改以下行:

/etc/initcpio/hooks/tinyssh
 /usr/sbin/dropbear -E -s -j -k -p <new_port>

重新生成 initramfs.

首先,我们需要使用 puttygen.exe 将前面生成的 OpenSSH 密钥导入并转换为 PuTTY 的 .ppk 私钥格式。在本示例中,我们将其命名为 zfs_unlock.ppk

上面的 mkinitcpio-netconf 过程没有设置 shell(我们也不需要)。但是,由于没有 shell,PuTTY 在成功连接后会立即关闭。这可以在 PuTTY SSH 配置中禁用(Connection > SSH > [X] Do not start a shell or command at all),但这仍然不允许我们看到 stdout 或输入加密密码。相反,我们使用 plink.exe 并带有以下参数:

plink.exe -ssh -l root -i c:\path\to\zfs_unlock.ppk <hostname>

plink 命令可以放入批处理脚本中以方便使用。

启用 bclone 支持

要使用 cp --reflink 和其他需要 bclone 支持的命令,如果来自 2.2.2 之前的版本,则必须升级 功能标志。这将允许池支持 bclone。这是通过 zpool upgrade 完成的,如果池状态显示这是可能的。

还要求启用一个模块参数,否则用户空间应用程序将无法使用此功能。您可以通过将此内容放入 /etc/modprobe.d/zfs.conf 来实现:

/etc/modprobe.d/zfs.conf
options zfs zfs_bclone_enabled=1

检查是否正常工作,以及使用了多少空间节省,命令为:zpool get all POOLNAME | grep clon

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.