构建以 ZFS 为根文件系统的 Ubuntu 18.04
概览
可用的新版本
有关新的安装教程,请参阅 Ubuntu 20.04 Root on ZFS。本指南已不再频繁更新,仅保留,以供已经按照此指南安装的用户参考。
注意事项
本教程使用整个物理磁盘。
不要使用这些说明来安装双系统。
备份你的数据,任何现有数据将会丢失。
系统要求
Ubuntu 18.04.3 (“Bionic”) 桌面 CD(不要使用任何服务器镜像)
在提供 4 KiB 逻辑扇区的驱动器(“4Kn”驱动器)上安装仅支持 UEFI 引导。这并非 ZFS 独有的问题。GRUB 在 4Kn 驱动器上使用传统 BIOS 引导无法工作,也不会支持。
在内存少于 2 GiB 的计算机上运行 ZFS,会存在严重的性能瓶颈。建议 4 GiB 内存以在基础工作负载下获得正常性能。如果你想使用去重(deduplication),你将需要 大量内存。启用去重是一项不可轻易撤销的永久性更改。
加密
本指南支持两种加密方案:未加密和 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,其他情况请参考故障排查。对于镜像(mirror)或 RAID-Z 拓扑,请使用
DISK1、DISK2等变量。选择 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.c 中的 spa_feature_names。此步骤为 /boot 创建单独的 boot 存储池,仅启用 GRUB 支持的特性,从而能让 root 存储池使用任意特性。注意 GRUB 会以只读方式打开池,因此所有只读兼容特性均被“支持”。
提示:
如果创建镜像(mirror)或 RAID-Z(raidz)拓扑,可使用如下命令创建 boot 存储池:
或将
mirror替换为raidz、raidz2或raidz3,并列出其他磁盘的分区。可自定义存储池的名称,但如果修改,必须在整个配置中保持一致。本教程中使用
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 需要 ACL。设置
normalization=formD可消除 UTF-8 文件名的一些极端问题,同时隐含utf8only=on,意味着只允许 UTF-8 文件名。如果希望支持非 UTF-8 文件名,请不要使用该选项。关于强制 UTF-8 文件名的问题讨论。设置
relatime=on是经典 POSIXatime行为(性能影响大)和atime=off(最佳性能,但禁用访问时间更新)之间的折中方案。从 Linux 2.6.30 起,relatime已成为其他文件系统的默认值。RedHat 文档说明。设置
xattr=sa显著提升扩展属性性能。ZFS 内部使用扩展属性实现 POSIX ACL,也可被用户空间应用使用。部分桌面 GUI 应用使用扩展属性。Samba 可利用扩展属性存储 Windows ACL 和 DOS 属性,Active Directory 域控制器必需该功能。Linux 特有。若考虑迁移到其他 OpenZFS 实现(非 ZFS-on-Linux),扩展属性可能不可读(数据仍可访问)。即使不希望全盘使用xattr=sa,用于/var/log通常是可行的。确保包含磁盘路径的
-part4部分,否则指定的是整个磁盘,ZFS 会重新分区,可能会丢失 bootloader 分区。对于 LUKS,加密密钥长度选 512 位。但 XTS 模式需要两把密钥,LUKS 会将密钥对半分,因此
-s 512实际为 AES-256。密码通常是最薄弱环节,请慎重选择。cryptsetup FAQ 第 5 节 提供参考指导。
说明:
如果要创建镜像(mirror)或 RAIDZ 拓扑,请使用类似命令创建 root 存储池:
或将 mirror 替换为 raidz、raidz2 或 raidz3,并列出其他磁盘的分区。对于 LUKS,需要使用 /dev/mapper/luks1、/dev/mapper/luks2 等设备,这些需通过 cryptsetup 创建。
ZFS pool 的名称可以任意选择。如果更改了名称,后续使用中必须保持一致。在支持自动安装到 ZFS 的系统上,root 存储池默认命名为
rpool。
步骤 3:系统安装
3.1 创建作为容器的文件系统数据集:
在 Solaris 系统上,root 文件系统会被克隆,且在通过 pkg image-update 或 beadm 进行重大系统更改时,后缀会递增。在 Ubuntu 20.04 中,类似功能由 zsys 工具实现,虽然它的 dataset 布局更复杂。即便没有该工具,仍可使用 rpool/ROOT 和 bpool/BOOT 容器手动创建克隆。
3.2 为 root 和 boot 文件系统创建数据集:
通常情况下,ZFS 并不需要手动使用命令 mount 或 zfs 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 不支持 ZFS 的问题。
提示:
如果创建镜像或 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 不支持 ZFS。
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 之前,挂载文件系统与启动某些守护进程之间可能存在竞争条件。实际上,问题(例如 #5754)通常出现在 /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/7734
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:准备安装环境进行操作。
对于 LUKS,首先解锁磁盘:
正确挂载所有文件系统:
如有需要,可以 chroot 进入已安装环境:
进行必要的系统修复操作。
操作完成后,清理环境:
MPT2SAS
本教程的大多数问题报告都与 mpt2sas 硬件相关,这类硬件会执行较慢的异步驱动器初始化,例如某些 IBM M1015 卡或已经刷回参考 LSI 固件的 OEM 品牌卡。
基本问题是,这些控制器上的磁盘在常规系统启动前不会被 Linux 内核识别,而 ZoL 不会热插拔池成员。详见 https://github.com/zfsonlinux/zfs/issues/330。
大多数 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 服务:
最后更新于