github编辑

构建以 ZFS 为根文件系统的 Ubuntu 18.04

概览

可用的新版本

注意事项

  • 本教程使用整个物理磁盘。

  • 不要使用这些说明来安装双系统。

  • 备份你的数据,任何现有数据将会丢失。

系统要求

在内存少于 2 GiB 的计算机上运行 ZFS,会存在严重的性能瓶颈。建议 4 GiB 内存以在基础工作负载下获得正常性能。如果你想使用去重(deduplication),你将需要 大量内存arrow-up-right。启用去重是一项不可轻易撤销的永久性更改。

加密

本指南支持两种加密方案:未加密和 LUKS(全盘加密)。无论哪种选项,ZFS 的所有功能均可用。Ubuntu 18.04 不支持 ZFS 原生加密。

未加密方案当然不会加密任何内容,因此性能最佳。

LUKS 会加密几乎所有数据。唯一未加密的数据是引导程序、内核和 initrd。系统必须在控制台输入密码才能启动。性能良好,但 LUKS 位于 ZFS 底层,如果使用多块磁盘(镜像或 raidz 拓扑),数据需要在每个磁盘上加密一次。

第 1 步:准备安装环境

1.1 启动 Ubuntu Live CD。选择“Try Ubuntu”。根据需要将系统连接到互联网(例如,连接 WiFi 网络)。打开终端(按 Ctrl-Alt-T)。

1.2 设置更新软件源:

1.3 可选:在 Live CD 环境中安装并启动 OpenSSH 服务器:

如果你有第二台设备,通过 SSH 访问目标系统会很方便:

提示:

可以使用 ip addr show scope global | grep inet 查看你的 IP 地址,然后从主机通过 ssh ubuntu@IP 连接。

1.4 切换到 root 用户:

1.5 在 Live CD 环境中安装 ZFS:

第 2 步:磁盘格式化

2.1 设置磁盘变量:

使用 ZFS 时务必使用长别名 /dev/disk/by-id/*。直接使用设备节点 /dev/sd* 可能导致偶发的导入失败,尤其是在系统中存在多个存储池时。

提示:

  • ls -la /dev/disk/by-id 可以列出所有别名。

  • 如果在虚拟机中操作,虚拟磁盘在 /dev/disk/by-id 中缺失,可在 KVM virtio 下使用 /dev/vda,其他情况请参考故障排查arrow-up-right

  • 对于镜像(mirror)或 RAID-Z 拓扑,请使用 DISK1DISK2 等变量。

  • 选择 boot 存储池大小时,请考虑实际使用。内核和 initrd 可能占用约 100M。如果安装多个内核并使用快照,尤其是在需要重新生成 initramfs(约 85M/个)时,boot 存储池空间可能不足。请根据需求合理规划。

2.2 如果重用磁盘,需要清理:

若磁盘曾用作 MD 阵列,清除 superblock:

清空分区表:

2.3 分区:

若需 Legacy(BIOS)启动:

若使用 UEFI 启动(当前或后续使用):

创建 boot pool:

选择以下其中之一:

2.3a 不加密:

2.3b LUKS 加密:

若创建镜像或 RAID-Z 拓扑,请对所有池内磁盘重复分区操作。

2.4 创建 boot 存储池:

通常无需自定义 boot 存储池的选项。

GRUB 并不支持所有 zpool 特性。请参见 grub-core/fs/zfs/zfs.carrow-up-right 中的 spa_feature_names。此步骤为 /boot 创建单独的 boot 存储池,仅启用 GRUB 支持的特性,从而能让 root 存储池使用任意特性。注意 GRUB 会以只读方式打开池,因此所有只读兼容特性均被“支持”。

提示:

  • 如果创建镜像(mirror)或 RAID-Z(raidz)拓扑,可使用如下命令创建 boot 存储池:

    或将 mirror 替换为 raidzraidz2raidz3,并列出其他磁盘的分区。

  • 可自定义存储池的名称,但如果修改,必须在整个配置中保持一致。本教程中使用 bpool 作为约定名称。

特性说明:

  • userobj_accounting 特性在理论上是只读兼容的,但实际使用 GRUB 时可能出现“invalid dnode type”错误。该特性对 /boot 无关紧要。

2.5 创建 root 存储池:

选择以下其中之一:

2.5a 不加密:

2.5b LUKS 加密:

说明:

  • 这里推荐使用 ashift=12,因为许多现代硬盘的物理扇区为 4 KiB(或更大),即使它们报告为 512 B 逻辑扇区。同时,未来更换硬盘时可能有 4 KiB 物理扇区(此时 ashift=12 可取)或 4 KiB 逻辑扇区(此时必须 ashift=12)。

  • 设置 -O acltype=posixacl 可全局启用 POSIX ACL。如果不希望全局启用,可删除此选项,但之后需在创建 /var/log 的 ZFS 时加上 -o acltype=posixacl(注意小写“o”),因为 journald 需要 ACLarrow-up-right

  • 设置 normalization=formD 可消除 UTF-8 文件名的一些极端问题,同时隐含 utf8only=on,意味着只允许 UTF-8 文件名。如果希望支持非 UTF-8 文件名,请不要使用该选项。关于强制 UTF-8 文件名的问题讨论arrow-up-right

  • recordsize 未设置,保持默认 128 KiB。若希望调整,可参考 这些arrow-up-right博客文章 1arrow-up-right博客文章 2arrow-up-right博客文章 3arrow-up-right

  • 设置 relatime=on 是经典 POSIX atime 行为(性能影响大)和 atime=off(最佳性能,但禁用访问时间更新)之间的折中方案。从 Linux 2.6.30 起,relatime 已成为其他文件系统的默认值。RedHat 文档说明arrow-up-right

  • 设置 xattr=sa 显著提升扩展属性性能arrow-up-right。ZFS 内部使用扩展属性实现 POSIX ACL,也可被用户空间应用使用。部分桌面 GUI 应用使用扩展属性arrow-up-right。Samba 可利用扩展属性存储 Windows ACL 和 DOS 属性,Active Directory 域控制器必需该功能。Linux 特有arrow-up-right。若考虑迁移到其他 OpenZFS 实现(非 ZFS-on-Linux),扩展属性可能不可读(数据仍可访问)。即使不希望全盘使用 xattr=sa,用于 /var/log 通常是可行的。

  • 确保包含磁盘路径的 -part4 部分,否则指定的是整个磁盘,ZFS 会重新分区,可能会丢失 bootloader 分区。

  • 对于 LUKS,加密密钥长度选 512 位。但 XTS 模式需要两把密钥,LUKS 会将密钥对半分,因此 -s 512 实际为 AES-256。

  • 密码通常是最薄弱环节,请慎重选择。cryptsetup FAQ 第 5 节arrow-up-right 提供参考指导。

说明:

  • 如果要创建镜像(mirror)或 RAIDZ 拓扑,请使用类似命令创建 root 存储池:

或将 mirror 替换为 raidzraidz2raidz3,并列出其他磁盘的分区。对于 LUKS,需要使用 /dev/mapper/luks1/dev/mapper/luks2 等设备,这些需通过 cryptsetup 创建。

  • ZFS pool 的名称可以任意选择。如果更改了名称,后续使用中必须保持一致。在支持自动安装到 ZFS 的系统上,root 存储池默认命名为 rpool

步骤 3:系统安装

3.1 创建作为容器的文件系统数据集:

在 Solaris 系统上,root 文件系统会被克隆,且在通过 pkg image-updatebeadm 进行重大系统更改时,后缀会递增。在 Ubuntu 20.04 中,类似功能由 zsys 工具实现,虽然它的 dataset 布局更复杂。即便没有该工具,仍可使用 rpool/ROOT 和 bpool/BOOT 容器手动创建克隆。

3.2 为 root 和 boot 文件系统创建数据集:

通常情况下,ZFS 并不需要手动使用命令 mountzfs mount 挂载文件系统。由于 canmount=noauto,这里必须手动挂载一次。

3.3 创建其他数据集:

下面的数据集为可选,根据个人喜好或软件选择决定是否创建。

如果希望排除在快照之外:

如果系统使用 /opt

如果系统使用 /srv

如果系统使用 /usr/local

如果系统将安装游戏:

如果系统将在 /var/mail 存储本地邮件:

如果系统使用 Snap 包:

如果系统使用 /var/www

如果系统使用 GNOME:

如果系统使用 Docker(管理自身数据集与快照):

如果系统使用 NFS(锁定):

如果希望为 /tmp 创建独立数据集(后续建议使用 tmpfs):

该数据集布局的主要目的是将操作系统与用户数据分离,使得 root 文件系统可回滚而不影响用户数据。com.sun:auto-snapshot 设置可让部分 ZFS 快照工具排除临时数据。

如果不进行额外操作,/tmp 会作为 root 文件系统的一部分存储。也可以像上面示例那样创建独立数据集,将 /tmp 数据排除在 root 快照之外,同时可对 rpool/tmp 设置空间配额限制。否则,可在后续使用 tmpfs(RAM 文件系统)。

3.4 安装最小系统:

debootstrap 命令会留下一个未配置的新系统。另一种方法是将已配置好的系统完整复制到新的 ZFS root 中。

步骤 4:系统配置

4.1 配置主机名:

主机名 替换为所需的主机名:

提示:

如果觉得 vi 难以使用,可以使用 nano

4.2 配置网络接口:

查找接口名称:

将下方的 NAME 替换为你的接口名称:

如果系统不是 DHCP 客户端,可对该文件进行自定义。

4.3 配置软件源:

4.4 将 LiveCD 环境的虚拟文件系统绑定到新系统,进入 chroot:

注意:

使用的是 --rbind,而非 --bind

4.5 配置基础系统环境:

即便你选择的系统语言不是英语,也要确保 en_US.UTF-8 可用:

如果偏好 nano 而非 vi,可安装:

4.6 在 chroot 环境中安装 ZFS:

提示:

对于 HWE 内核,安装 linux-image-generic-hwe-18.04 替代 linux-image-generic

4.7 仅针对 LUKS 安装,设置 /etc/crypttab

使用 initramfs 是为了解决 cryptsetup 不支持 ZFSarrow-up-right 的问题。

提示:

如果创建镜像或 raidz 拓扑,需要为每个磁盘重复 /etc/crypttab 条目,如 luks2 等。

4.8 安装 GRUB

选择以下方案之一:

4.8a 为 legacy(BIOS)引导安装 GRUB:

使用空格键选择池中所有磁盘(而非分区)。

4.8b 为 UEFI 引导安装 GRUB:

注意:

  • -s 1 仅对呈现 4 KiB 逻辑扇区(“4Kn”驱动器)的磁盘必要,以满足 FAT32 最小簇大小(给定分区大小 512 MiB)。对 512 B 扇区的磁盘也适用。

  • 对于镜像或 raidz 拓扑,此步骤仅在第一个磁盘上安装 GRUB,其他磁盘稍后处理。

4.9(可选)移除 os-prober:

可避免 update-grub 报错。os-prober 仅在双系统配置中必要。

4.10 设置 root 密码:

4.11 启用 bpool 导入:

确保始终导入 bpool,无论是否存在 /etc/zfs/zpool.cache、是否在 cachefile 中,是否启用 zfs-import-scan.service

4.12 可选(但推荐):将 /tmp 挂载为 tmpfs

如果之前已为 /tmp 创建 dataset,可跳过此步骤。否则,可通过启用 tmp.mount 单元,将 /tmp 放在 tmpfs(内存文件系统)中:

4.13 设置系统组:

步骤 5:安装 GRUB

5.1 验证 ZFS 引导文件系统是否被识别:

5.2 刷新 initrd 文件:

注意:

在使用 LUKS 时,可能会显示“WARNING could not determine root device from /etc/fstab”。这是因为 cryptsetup 不支持 ZFSarrow-up-right

5.3 解决 GRUB 缺失 zpool-features 的问题:

5.4 可选(强烈建议):便于调试 GRUB:

系统重启两次并确认一切正常后,可根据需要撤销这些更改。

5.5 更新启动配置:

注意:

如果出现 os-prober 错误,可忽略。

5.6 安装引导加载程序:

5.6a 对于 legacy(BIOS)引导,安装 GRUB 到 MBR:

注意

这是将 GRUB 安装到整个磁盘,而非单个分区。

如果创建镜像或 raidz 拓扑,请对池中的每个磁盘重复 grub-install 命令。

5.6b 对于 UEFI 引导,安装 GRUB:

无需指定磁盘。若创建镜像或 raidz 拓扑,其他磁盘稍后处理。

5.7 修正文件系统挂载顺序:

在 ZFS 支持 systemd mount generator 之前arrow-up-right,挂载文件系统与启动某些守护进程之间可能存在竞争条件。实际上,问题(例如 #5754arrow-up-right)通常出现在 /var 下的某些文件系统,特别是 /var/log/var/tmp。将它们设置为 legacy 挂载并在 /etc/fstab 中列出,使 systemd 知道这些是独立挂载点。这样,rsyslog.service 会通过 local-fs.target 依赖 var-log.mount,使用 systemd PrivateTmp 功能的服务会自动依赖 After=var-tmp.mount

在 initramfs 中尚不支持挂载 /boot 前,我们也需要挂载它,因为它被标记为 canmount=noauto。UEFI 下,还需确保 /boot/efi 在其父文件系统挂载前挂载。

rpool 由 initramfs 保证导入,因此无需为这些文件系统添加 x-systemd.requires=zfs-import.target

UEFI 引导时,先卸载 /boot/efi

其他步骤对 BIOS 和 UEFI 都适用:

如果创建了 /var/tmp 数据集:

如果创建了 /tmp 数据集:

步骤 6:首次启动

6.1 对初始安装进行快照:

以后在每次升级前,通常也会想先创建快照,并在适当的时候删除旧快照(包括此快照)以节省空间。

6.2 退出 chroot 环境,返回 LiveCD 环境:

6.3 在 LiveCD 环境下运行以下命令卸载所有文件系统:

6.4 重启:

等待新安装的系统正常启动,并以 root 登录。

6.5 创建用户账户:

用户名 替换为所需用户名:

6.6 镜像 GRUB

如果安装了多个磁盘,需要在其他磁盘上安装 GRUB:

6.6a 对于 legacy(BIOS)引导:

6.6b 对于 UEFI 引导:

先卸载 /boot/efi

对于第二块及后续磁盘(依次将 ubuntu-2 增加为 -3 等):

步骤 7:(可选)配置 Swap

注意

在内存压力极高的系统上,无论 swap 空间剩余多少,使用 zvol 作为 swap 设备都可能导致系统锁死。此问题正在调查中:https://github.com/zfsonlinux/zfs/issues/7734arrow-up-right

7.1 创建用于 swap 的卷数据集(zvol):

可以根据需要调整大小(4G 部分)。

压缩算法设置为 zle,因为它是开销最小的算法。由于本指南推荐使用 ashift=12(磁盘上 4 KiB 块),常见的 4 KiB 页面大小意味着没有压缩算法能减少 I/O。例外情况是全零页面会被 ZFS 丢弃,但必须启用某种形式的压缩才能实现此行为。

7.2 配置 swap 设备:

注意

在配置文件中总是使用长别名 /dev/zvol。切勿使用短设备名 /dev/zdX

RESUME=none 用于禁用休眠恢复。因为 zvol 在恢复脚本运行时尚未导入,所以无法正常工作。如果不禁用,启动过程会等待 swap zvol 出现约 30 秒的停滞。

7.3 启用 swap 设备:

步骤 8:安装完整软件

8.1 升级最小系统:

8.2 安装常规软件:

选择以下方案之一:

8.2a 仅安装命令行环境:

8.2b 安装完整 GUI 环境:

提示

如果要安装完整 GUI 环境,你可能需要使用 NetworkManager 管理网络:

8.3 可选:禁用日志压缩:

由于 /var/log 已由 ZFS 压缩,logrotate 的压缩会消耗 CPU 和磁盘 I/O,而通常收益甚微。此外,如果你对 /var/log 做快照,logrotate 的压缩实际上会浪费空间,因为未压缩的数据会保留在快照中。你可以手动编辑 /etc/logrotate.d 下的文件注释掉 compress,或者使用如下循环(推荐复制粘贴执行):

8.4 重启系统:

步骤 9:最后的清理

9.1 等待系统正常启动。使用你创建的账户登录,确保系统(包括网络)正常工作。

9.2 可选:删除初始安装的快照:

9.3 可选:禁用 root 密码:

9.4 可选:重新启用图形启动过程:

如果你希望使用图形启动过程,可以现在重新启用。如果使用 LUKS,会使密码提示更美观。

注意

如出现 os-prober 错误,可忽略。

9.5 可选:仅针对 LUKS 安装,备份 LUKS 头信息:

将该备份存放在安全位置(如云存储)。虽然受你的 LUKS 密码保护,但你可能希望额外加密。

提示

如果创建了镜像或 raidz 拓扑,请对每个 LUKS 卷重复此操作(如 luks2 等)。

故障排除

使用 Live CD 进行救援

按照步骤 1:准备安装环境arrow-up-right进行操作。

对于 LUKS,首先解锁磁盘:

正确挂载所有文件系统:

如有需要,可以 chroot 进入已安装环境:

进行必要的系统修复操作。

操作完成后,清理环境:

MPT2SAS

本教程的大多数问题报告都与 mpt2sas 硬件相关,这类硬件会执行较慢的异步驱动器初始化,例如某些 IBM M1015 卡或已经刷回参考 LSI 固件的 OEM 品牌卡。

基本问题是,这些控制器上的磁盘在常规系统启动前不会被 Linux 内核识别,而 ZoL 不会热插拔池成员。详见 https://github.com/zfsonlinux/zfs/issues/330arrow-up-right

大多数 LSI 卡与 ZoL 完全兼容。如果你的 LSI 卡存在此问题,可尝试在 /etc/default/zfs 中设置:

系统将在导入池之前等待 X 秒,以确保所有磁盘都出现。

Areca

需要 arcsas 驱动程序的系统应将其添加到 /etc/initramfs-tools/modules 文件中,并运行:

如果在内核日志中出现类似 RIP: 0010:[<ffffffff8101b316>] [<ffffffff8101b316>] native_read_tsc+0x6/0x20 的信息,请升级或降级 Areca 驱动。ZoL 在出现此错误信息的系统上并不稳定。

VMware

  • 在 vmx 文件或 vSphere 配置中设置:

这样可以确保在虚拟机中创建 /dev/disk 别名。

QEMU/KVM/XEN

为每个虚拟磁盘设置唯一的序列号,可通过 libvirt 或 qemu 实现,例如:

若希望在虚拟机中使用 UEFI(而不仅仅是 BIOS 启动),请在宿主机上执行:

取消对以下若干行的注释:

然后重启 libvirt 服务:

最后更新于