在 ZFS 上安裝 Arch Linux

出自 Arch Linux 中文维基

這篇文章詳細描述了將 Arch Linux 安裝在 ZFS 文件系統上所需的步驟。

警告: 盲目地照搬這篇教程上的操作是行不通的。您有必要花時間了解操作系統的啟動以及創建存儲池和數據集的過程。下面是一些能幫助到您的鏈接:

安裝[編輯 | 編輯原始碼]

要將 Arch Linux 安裝在 ZFS 上,您需要從含有 ZFS 內核模塊的 archiso 安裝介質啟動。

開始之前[編輯 | 編輯原始碼]

考慮到 ZFS 內核模塊不是 Linux 的原生文件系統且 Arch Linux 是一個滾動更新發行版,總會有一段(相對較短的)時間內,外部倉庫內的對應一定內核版本的內核模塊軟件包版本會稍落後於 Arch 倉庫中的版本,而有時 ZFS 模塊(dkms 變種包)甚至在最新版本的內核上完全無法編譯。如果您需要或想要一直使用最新版本的內核的話,將 Arch 安裝在 ZFS 上可能不是一個好主意。無論如何,大多數情況下,將 linux 包及其相關變種 linux-* (如果使用dkms的話還包括 linux-*-headers 包)放在 pacman.conf 的 IgnorePkg 一欄,只對其進行手動更新,並在出現明顯問題時立即回滾是一個明智的選擇。 或者也可以使用 LTS (長期支持)版本的內核,它應至少可以搭配 dkms 正常工作。

在 archiso 安裝介質上獲取 ZFS 模塊[編輯 | 編輯原始碼]

有一個腳本可以簡單地在 archiso 安裝介質上安裝並啟用 ZFS 模塊。它應該適用於任何版本的 archiso 安裝介質。

eoli3n/archiso-zfs

在自定義的 archiso 安裝介質中啟用 ZFS 模塊[編輯 | 編輯原始碼]

要構建一個自定義的 archiso 安裝介質,請參考 ZFS 一文。

對目標磁盤驅動器進行分區[編輯 | 編輯原始碼]

要決定 ZFS 應使用哪一種分區圖類型,請見 磁盤分區 。 ZFS 支持 GUID 分區圖(GPT)和主引導記錄(MBR)。

ZFS 管理自己的分區,所以只需創建一個最簡單的分區類型即可。創建 ZFS 文件系統的分區類型應為類型 bf00 ,或「 Solaris 根目錄分區」。

大於 2TB 的磁盤驅動器需使用 GUID 分區圖。在 BIOS/GPT 類型配置上安裝 GRUB 引導器還需一個較小(約1~2MiB)的 BIOS 啟動分區來存放啟動代碼的映像。

根據您設備的固件類型以及您所選擇的引導方式,系統啟動可能需要一個 EFI 系統分區(ESP)。在使用 BIOS 的設備(或一台以 Legacy 模式啟動的設備)上不需要 EFI 系統分區。 參考 Arch 引導過程#啟動引導器 以獲取更多信息。

分區類型[編輯 | 編輯原始碼]

這是一個可以用於在 BIOS/MBR 的 ZFS 設備上使用 GRUB 啟動引導器部署安裝的基本分區類型示例:

Part     Size   Type
----     ----   -------------------------
   1     XXXG   Solaris Root (bf00)

在 BIOS(或一台以 Legacy 模式啟動的)設備上使用 GUID 分區圖與 GRUB 啟動引導器的示例:

Part     Size   Type
----     ----   -------------------------
   1       2M   BIOS boot partition (ef02)
   2     XXXG   Solaris Root (bf00)

這是另一個示例,這次使用的是 GUID 分區圖與 UEFI 啟動引導器(如 rEFInd ):

Part     Size   Type
----     ----   -------------------------
   1     100M   EFI boot partition (ef00)
   2     XXXG   Solaris Root (bf00)

ZFS 不支持使用交換文件。如果您需要一個交換分區,參考 ZFS#交換卷 來創建一個用於交換空間的 ZVOL。

提示:#安裝並配置啟動引導加載器 中列出了支持 ZFS 的啟動引導器。
警告: GRUB 啟動引導器的一些 BUG (bug #42861, zfsonlinux/grub/issues/5) 使得要將其安裝在 ZFS 分區上較為複雜,解決方法見 #安裝並配置啟動引導加載器

示例 parted 命令[編輯 | 編輯原始碼]

這是適用於上述第二種使用 GUID 分區圖和 BIOS/Legacy 啟動情況的一些示例命令,其中為 GRUB 創建了一個(稍大於)1MB 的 BIOS 啟動分區:

# parted /dev/sdx
(parted)mklabel gpt
(parted)mkpart non-fs 0% 2
(parted)mkpart primary 2 100%
(parted)set 1 bios_grub on
(parted)set 2 boot on
(parted)quit

您也可以把上述命令變為如下的單行命令:

# parted --script /dev/sdx mklabel gpt mkpart non-fs 0% 2 mkpart primary 2 100% set 1 bios_grub on set 2 boot on

如果您需要創建一個 EFI 系統分區,那就應將其標記為啟動分區而非根分區。

格式化目標磁盤驅動器[編輯 | 編輯原始碼]

如果您已為啟動引導分區以及非 ZFS 文件系統創建了合適分區,那麼將其格式化。不要對剛創建的 Solaris 根分區以及您的 BIOS 啟動分區做任何操作。Solaris 根分區將由 ZFS 管理,而 BIOS 啟動分區則會由您的啟動引導器管理。

設置 ZFS 文件系統[編輯 | 編輯原始碼]

警告: 不要在您的數據集名稱中使用 '-' (見此 "特性")

首先,確認 ZFS 內核模塊已經被加載,

# modprobe zfs

創建根存儲池[編輯 | 編輯原始碼]

創建存儲池並設置好數據集的默認選項。存儲池上新創建的任何數據集都會保留這個存儲池創建時使用 -O 設定的選項。默認選項在 在 ZFS 上安裝 Debian Buster. 第二步: 磁盤格式化 中有詳細說明。

注意: 物理扇區大小為512字節的磁盤驅動器應使用 -o ashift=9 參數,而物理扇區大小為4096字節的磁盤驅動器應使用 -o ashift=12 參數。要獲得每個 SCSI/SATA 磁盤驅動器的物理扇區大小,可以運行 lsblk -S -o NAME,PHY-SEC。如果想查看所有設備的物理扇區大小,可從命令中刪去 -S。若使用 NVMe 驅動器,使用 nvme id-ns /dev/nvmeXnY -H | grep "LBA Format" 來獲取正在使用的邏輯塊地址。大部分 NVMe 驅動器使用512字節的邏輯塊大小,見 OpenZFS: NVMe low level formatting 以將其大小改為4096字節。
警告: 應始終注意現代的大部分設備的物理扇區大小為4096字節,但有些設備會將自己的物理扇區大小報告為512字節,尤其是固態驅動器(SSD)。即使在報告扇區大小為512字節、實際物理扇區大小為4096字節的設備上選擇 ashift=9一定會導致嚴重的性能下降。在物理扇區大小為512字節的設備上選擇 ashift=12 不會導致性能下降,但可能使磁盤可用容量減少。如果您不確定的話,對於現代設備,應使用 ashift=12,或者您可以搜索您設備對應的正確值。對於有關的討論,參見 OpenZFS issue #967 ;對設置較高的 ashift 值可能出現的問題,見 OpenZFS issue #2497
# zpool create -f -o ashift=12         \
             -O acltype=posixacl       \
             -O relatime=on            \
             -O xattr=sa               \
             -O dnodesize=legacy       \
             -O normalization=formD    \
             -O mountpoint=none        \
             -O canmount=off           \
             -O devices=off            \
             -R /mnt                   \
             zroot /dev/disk/by-id/id-to-partition-partx

壓縮與原生加密[編輯 | 編輯原始碼]

以下命令創建的存儲池會在所有數據集上默認啟用壓縮與原生加密:

# zpool create -f -o ashift=12         \
             -O acltype=posixacl       \
             -O relatime=on            \
             -O xattr=sa               \
             -O dnodesize=legacy       \
             -O normalization=formD    \
             -O mountpoint=none        \
             -O canmount=off           \
             -O devices=off            \
             -R /mnt                   \
             -O compression=lz4        \
             -O encryption=aes-256-gcm \
             -O keyformat=passphrase   \
             -O keylocation=prompt     \
             zroot /dev/disk/by-id/id-to-partition-partx
警告:
  • 使用 ZFS 時應始終使用設備的 by-id 名稱,否則導入存儲池時會發生錯誤。
  • 除使用 by-id 名稱外,也可以考慮使用 by-partuuid 或 by-uuid名稱,因為即使一個內置磁驅動器被移入USB移動硬盤盒,這些名稱的值也不會發生改變,反之亦然。(這僅當 ZFS 在磁盤上的某個分區中才有效,若 ZFS 占據整個磁盤則無效)
  • GRUB 啟動引導器用戶應注意的是 zpool-create 命令一般會默認啟用所有功能,而其中某些是 GRUB 啟動引導器不支持的功能。見: ZFS#GRUB-compatible pool creation 來創建與 GRUB 兼容的存儲池。

創建您的數據集[編輯 | 編輯原始碼]

ZFS 使用數據集的概念來管理您的存儲,而非使用傳統的磁盤分區。與磁盤分區不同,數據集沒有固定的大小, 每個數據集也可以有各自不同的屬性,例如壓縮。普通的 ZFS 數據集由 ZFS 自動掛載,而傳統的數據集則需由 fstab 或 使用 mount 命令掛載。

ZFS 最實用的功能之一便是啟動環境。啟動環境使您可以創建系統的可引導快照,您也可以通過簡單地重啟到某啟動環境來將整個系統回滾到那個快照。這使得系統更新變得更加安全,對軟件開發與測試來講也十分有用。要使用如 beadm, zectlAUR (用於 systemd-boot), or zedenvAUR (用於 GRUB) 等的啟動環境管理器來管理啟動環境,您的數據集必須有正確的配置。其關鍵是將您存放數據的目錄 (如 /home) 與系統數據分別放在相互獨立的不同數據集中,且不要在存儲池根目錄中存放數據,因為放在存儲池根目錄的數據以後將不能被移動。

您總是至少應該為您的根目錄創建一個數據集,且多數情況下您也會想要把 /home 存放在一個單獨的數據集中。您也可以自行選擇是否要無視啟動環境而始終保留完整的日誌文件。如果您使用的某些軟件會在 /home 之外存放數據 (如數據庫服務器),您應整理數據集的結構,使得這些軟件的數據目錄與根目錄數據集分離開來。

以下的示例命令會創建一個只分根目錄數據集與 /home 數據集的最基本可用於啟動環境使用的配置。數據集使用其所在的存儲池在創建時設定的默認選項。

# zfs create -o mountpoint=none zroot/data
# zfs create -o mountpoint=none zroot/ROOT
# zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/default
# zfs create -o mountpoint=/home zroot/data/home

創建根目錄數據集時您也可以不指定掛載點,畢竟無論如何 GRUB 啟動引導器會將其掛載至 / 。這也使得您可以通過克隆舊版數據集並將其放入 GRUB 啟動菜單來直接從舊版的根目錄啟動。這種情況下,您可以使用以下命令來創建您的根目錄數據集:

# zfs create -o mountpoint=/roots/default zroot/ROOT/default

您可以將 /root 存儲在您的 zroot/data/home 數據集中。

# zfs create -o mountpoint=/root zroot/data/home/root

對於存放某些特殊目錄的數據集,您需要對其啟用某些特定選項:

特殊目錄所需的選項
目錄 數據集選項 詳細信息
/ canmount=noauto
/var/log/journal acltype=posixacl systemd#systemd-tmpfiles-setup.service在系統啟動時失敗

系統數據集[編輯 | 編輯原始碼]

為系統目錄創建數據集時,使用canmount=off選項。

示例請參見 Debian-Buster-Root-on-ZFS#step-3-system-installation

注意: 若要將如 zroot/var/log 的數據集掛載至 /var/log,應考慮使用 zfs-mount-generator 而非 zfs-mount.service。這會修復文件系統掛載順序,在 這裡 有詳細介紹。
# zfs create -o mountpoint=/var -o canmount=off     zroot/var
# zfs create                                        zroot/var/log
# zfs create -o mountpoint=/var/lib -o canmount=off zroot/var/lib
# zfs create                                        zroot/var/lib/libvirt
# zfs create                                        zroot/var/lib/docker

導出並導入您的存儲池[編輯 | 編輯原始碼]

要驗證您的設置,將您所有的 ZFS 存儲池先導出後再重新導入。

警告: 不要跳過這一步,否則您再導入存儲池時必須使用 -f 參數。這會使導入的存儲池卸載。
注意: 如果您使用交換分區的話,這一步可能會失敗。您需要先使用 swapoff 命令將其關閉。
# zpool export zroot
# zpool import -d /dev/disk/by-id -R /mnt zroot -N
注意: -d 並不是設備的實際 ID,而是包含着軟鏈接的 /dev/by-id 目錄。

如果這個命令執行失敗並且您被要求使用數字 ID 來導入某個存儲池,運行 zpool import 來找到您存儲池的 ID,然後使用類似下方的命令導入存儲池:

# zpool import 9876543212345678910 (您的设备的 ID) -R /mnt zroot

如果您啟用了原生加密選項,先加載 ZFS 密鑰。

# zfs load-key zroot

由於根目錄數據集使用 canmount=noauto 參數,您需要先將其手動掛載,然後再掛載其他數據集。

# zfs mount zroot/ROOT/default
# zfs mount -a

現在 ZFS 文件系統已準備完畢以待使用。

配置根目錄文件系統[編輯 | 編輯原始碼]

如果您使用了傳統類型的數據集,則您需要將其寫入 /etc/fstab

為根目錄所在的子文件系統設置 bootfs(啟動文件系統),以便啟動引導加載器找到操作系統。

# zpool set bootfs=zroot/ROOT/default zroot

如果您還沒有 /etc/zfs/zpool.cache,請手動創建:

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

切記要將 zpool.cache 文件放入您的新系統中。稍後 ZFS 守護進程啟動時需要這個文件。

# mkdir -p /mnt/etc/zfs
# cp /etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache

安裝並配置 Arch Linux[編輯 | 編輯原始碼]

按照 安裝指南 安裝系統。若有涉及 ZFSonLinux 所需的特殊操作,將會在此列出。

  • 首先使用 mount 命令掛載所有的傳統類型數據集以及非 ZFS 的引導或系統分區。
  • 安裝基本系統。
  • 安裝指南#生成 fstab 文件 中所描述的方式對 ZFS 來說絕非必要。通常 ZFS 會自行掛載自己的分區,所以除非用戶使用了傳統型數據集,fstab 文件中不需要任何有關 ZFS 的部分。 要為文件系統生成 fstab,運行:
# genfstab -U -p /mnt >> /mnt/etc/fstab
# arch-chroot /mnt
  • 編輯 /etc/fstab
注意:
  • 如果您選擇為系統目錄創建傳統數據集,將其在 fstab 中保留。
  • 將除交換空間與 EFI 系統分區以外的所有非傳統型數據集注釋掉。可以使用較簡單的 /dev/zvol/zroot/swap 來取代交換空間的 UUID。
  • 在更新 ramdisk 並啟用 ZFS 支持前,您需要在 /etc/pacman.conf 中加入 Arch ZFS 倉庫,將其 簽名 並在 arch-chroot 環境下 安裝 zfs-linuxAUR (如果您使用 LTS 長期支持版內核則安裝 zfs-linux-ltsAUR )。
  • 創建初始 ramdisk 前,先編輯 /etc/mkinitcpio.conf,並將 zfs 加至 MODULES:
MODULES=(zfs)

接下來在 HOOKS 中將 zfs 加至 filesystems 前。同時也應把 keyboard hook 移動至 zfs 前,這樣如果出現問題,您仍可在終端中輸入。若您不使用 Ext3 或 Ext4,您也可以移除 fsck。您的 HOOKS 一行看起來應類似如下示例:

HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block zfs filesystems)
  • 若在 initrd 中使用 systemd hook,您應安裝 mkinitcpio-sd-zfsAUR 並將 sd-zfs hook 而非 zfs hook 放至 systemd hook 後面。應注意的是此 hook 和默認的 zfs hook 相比使用了不同的內核參數,可在其 項目主頁 找到更多信息。
注意:
  • sd-zfs 暫不支持原生加密,見 dasJ/sd-zfs/issues/4
  • 如果您的 /usr 存放在單獨的數據集上,並按照下面的指示進行了操作,您需要確認您的 zfs hook 後啟用了 usr hook ,否則您的系統將無法啟動。
  • 當您生成 initramfs 時, zpool.cache 文件會被複製到 initrd 中。如果您之前還未生成或需要重新生成 zpool.cache,記得在完成後重新生成 initramfs。
  • 您也能使用 legacy 掛載點來讓 fstab 掛載它。

安裝並配置啟動引導加載器[編輯 | 編輯原始碼]

原則上講,只要內核與 initrd 在一個非 ZFS 分區上,那麼啟動引導器的配置就與平時沒有什麼差別。只要內核有 ZFS 模塊、生成 initrd 時正確地使用了 zfs hook 且內核的命令行裡添加了 zfs 參數,系統就可以啟動。比如,大部分情況下, zfs=zroot/ROOT/default rw 就足夠了。如果您的根目錄文件系統參數是按照本篇內容設定好的,您甚至也可以只用 zfs=zroot rw 。您用不到 root 參數,因為 ZFS 會掛載根目錄。至於如何設定內核參數,詳見您所使用的啟動引導加載器的幫助文檔。

但是,如果您需要從 ZFS 上加載內核映像與/或 initrd,您就需要使用一個可以讀取 ZFS 的啟動引導器,並將其正確配置。

考慮到以上情況,配置您啟動引導加載器的過程應該還算直接。下面有一些例子,不過也不要被其所局限。

使用 EFISTUB[編輯 | 編輯原始碼]

如果您使用 EFISTUB 啟動引導器,您可以手動向您的 UEFI 啟動菜單中加入一個條目:

# efibootmgr --create --disk your_esp_disk --part your_esp_partition_number --label "Arch Linux (ZFS)" --loader /vmlinuz-linux --unicode 'zfs=zroot/ROOT/default rw initrd=\initramfs-linux.img'

使用統一內核映像 (Unified Kernel Image, UKI)[編輯 | 編輯原始碼]

使用 統一內核映像英語Unified kernel image (UKI) 就會比較直接。您只需要確保您在 /etc/cmdline.d 中的某個地方加入了 zfs 參數即可。除了配置 mkinitcpio 模塊和 hook 的過程需要依照本文來做,其他部分與使用一個普通統一內核映像沒有區別。例如:

/etc/cmdline.d/root.conf
zfs=zroot/ROOT/default rw

設置 zfs=zroot 大概率就夠用了(未經過測試),畢竟 ZFS 應該可以自動掛載根目錄。只要記得每次作出更改時重新運行一遍 mkinitcpio 即可。

使用 GRUB[編輯 | 編輯原始碼]

如果您使用 GRUB 啟動引導器,您可以將您的 /boot 目錄保存在 ZFS 存儲池上。請詳細閱讀 Debian-Buster-Root-on-ZFS#step-3-system-installation

您可以在 GRUB#BIOS 系統GRUB#UEFI 系統 找到安裝 GRUB 的方法。GRUB 的 手冊 也提供了手動配置該軟件的詳細方法。您也可以閱讀 GRUBGRUB/方法與技巧 作為補充。

BUG: 無法檢測根存儲池[編輯 | 編輯原始碼]

一個已知的 BUG 會導致 grub-mkconfig 無法檢測根存儲池並將其正確寫入 /boot/grub/grub.cfg 中。這個 BUG 仍未修復,但有兩種解決方案:

  • 解決方案 A :編輯 /etc/grub.d/10_linux 中檢測 rpool 的代碼。將
rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
替換為
rpool=`zdb -l ${GRUB_DEVICE} | grep " name:" | cut -d\' -f2`
這樣 grub-mkconfig 應該就能夠檢測到正確的存儲池名並將正確路徑寫入 /boot/grub/grub.cfg
  • 解決方案 B :如果上述方法仍不起作用,您可以將正確路徑硬編碼至 /etc/grub.d/10_linux 內。將
linux   ${rel_dirname}/${basename} root=${linux_root_device_thisversion} rw ${args}
替換為
linux   ${rel_dirname}/${basename} root=ZFS=zroot/ROOT/default rw ${args}

錯誤:無法獲取 …… 的規範路徑(error: failed to get canonical path of)[編輯 | 編輯原始碼]

grub-mkconfig 無法正確地為 ZFS 上的系統生成相應條目。

# grub-mkconfig -o /boot/grub/grub.cfg
/usr/bin/grub-probe: error: failed to get canonical path of `/dev/bus-Your_Disk_ID-part#'
grub-install: error: failed to get canonical path of `/dev/bus-Your_Disk_ID-part#'

要解決這個問題,您必須設置 ZPOOL_VDEV_NAME_PATH=1 環境變量。例如:

# ZPOOL_VDEV_NAME_PATH=1 grub-mkconfig -o /boot/grub/grub.cfg

錯誤:未知的文件系統(error: unknown filesystem)[編輯 | 編輯原始碼]

GRUB 的命令行工具,如 grub-probegrub-install 等在無法檢測文件系統時可能會因 unknown filesystem 錯誤而執行失敗。這可能是因為使用了 GRUB 不支持的文件系統,或使用了 GRUB 不支持的 ZFS 參數(見 ZFS#GRUB-compatible pool creation 來為啟動所用的 ZFS 存儲池設定合適的參數)。

要排除這個故障,首先應找出 GRUB 無法辨認的是哪個文件系統(可以在您的懷疑對象上運行 grub-probe,例如 grub-probe /grub-probe /boot)。下面是一個示例:

# grub-probe /boot
zfs

# grub-probe /
grub-probe: error: unknown filesystem.

找到出問題的文件系統後,運行 grub-probe -vvvv / 並瀏覽輸出內容中 GRUB 嘗試辨認的文件系統。下面這個例子中,GRUB 嘗試辨認了 ZFS , 但出現了如下輸出:

grub-probe -vvvv /
(...)
grub-core/kern/fs.c:56: Detecting zfs...
grub-core/osdep/hostdisk.c:420: opening the device `/dev/sda4' in open_device()
grub-core/fs/zfs/zfs.c:1199: label ok 0
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda4'
grub-core/fs/zfs/zfs.c:1014: check 2 passed
grub-core/fs/zfs/zfs.c:1025: check 3 passed
grub-core/fs/zfs/zfs.c:1032: check 4 passed
grub-core/fs/zfs/zfs.c:1042: check 6 passed
grub-core/fs/zfs/zfs.c:1050: check 7 passed
grub-core/fs/zfs/zfs.c:1061: check 8 passed
grub-core/fs/zfs/zfs.c:1071: check 9 passed
grub-core/fs/zfs/zfs.c:1093: check 11 passed
grub-core/fs/zfs/zfs.c:1119: check 10 passed
grub-core/fs/zfs/zfs.c:1135: str=com.delphix:hole_birth
grub-core/fs/zfs/zfs.c:1135: str=com.delphix:embedded_data
grub-core/fs/zfs/zfs.c:1144: check 12 passed (feature flags)
grub-core/fs/zfs/zfs.c:1884: zio_read: E 0: size 4096/4096
(...)
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda4'
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:extensible_dataset, value = 18, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.datto:bookmark_v2, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.datto:encryption, value = c, cd = 0 # <------------------ (檢測到啟用了原生加密功能)
grub-core/kern/fs.c:78: zfs detection failed.  # <---------------------------------------------------- (檢測失敗)
grub-core/kern/fs.c:56: Detecting xfs...
grub-core/fs/xfs.c:931: Reading sb
grub-core/fs/xfs.c:270: Validating superblock
grub-core/kern/fs.c:78: xfs detection failed.
grub-core/kern/fs.c:56: Detecting ufs2...
(...)
grub-core/kern/fs.c:56: Detecting affs...
grub-core/kern/fs.c:78: affs detection failed.
grub-probe: error: unknown filesystem.

這說明在檢測到 com.datto:encryption 功能前,程序都在正常運行。由於 GRUB 不支持 ZFS 的原生加密功能(截止2021年8月),ZFS 檢測就失敗了。這時可以創建另一個兼容 GRUB 的存儲池用來引導加密的存儲池(截止2021年8月,這也是 OpenZFS 官方推薦的做法,參見 有關的 OpenZFS 項目頁面)。

在一個兼容 GRUB 的存儲池上成功運行 grub-probe 的輸出應該類似下方:

grub-probe -vvvv /boot
(...)
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda3'
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:extensible_dataset, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:embedded_data, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:hole_birth, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = org.open-zfs:large_blocks, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = org.illumos:lz4_compress, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = , value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = , value = 0, cd = 0
grub-core/fs/zfs/zfs.c:3285: alive
(...)
grub-core/fs/zfs/zfs.c:1906: endian = 1
grub-core/fs/zfs/zfs.c:597: dva=8, 20008
grub-core/fs/zfs/zfs.c:2697: alive
zfs

從 ZFS 上引導您的內核與 initrd[編輯 | 編輯原始碼]

如果您的內核與 initrd 保存在單獨的使用 ext4 或 vfat 等文件系統的非 ZFS /boot 分區上,您可以跳過這一步。

如果您的內核與 initrd 在 ZFS 數據集上保存,GRUB 使用的內核與 initrd 路徑應該是如下格式:

/dataset(数据集)/@/actual(实际)/path(路径)

下面是當 Arch 安裝在根數據集上時的例子:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /@/boot/vmlinuz-linux zfs=zroot rw
    initrd /@/boot/initramfs-linux.img
}

下面是當 Arch 安裝在某個子數據集上時的情況:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /ROOT/default/@/boot/vmlinuz-linux zfs=zroot/ROOT/default rw
    initrd /ROOT/default/@/boot/initramfs-linux.img
}

從單獨的引導分區啟動您的內核與 initrd[編輯 | 編輯原始碼]

這是當您的內核與 initrd 保存在一個單獨的非 ZFS 分區上且 Arch 安裝在某個子數據集上時的情況:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /vmlinuz-linux zfs=zroot/ROOT/default rw
    initrd /initramfs-linux.img
}

只使用 systemd-boot 進行 EFI 啟動[編輯 | 編輯原始碼]

systemd-boot 無法讀取 ZFS 存儲池,所以您必須將您的 /boot 目錄保存在一個單獨的 VFAT 或 ext4 分區上。

注意: 這種情況下要使用 zectlAUR 來管理您的啟動環境,請按照 zectl/docs/plugins/systemdboot.md 的指示操作。

按照 systemd-boot#安裝 EFI 啟動管理器 的教程將啟動引導器安裝在 esp 分區上。

創建一個啟動項:

/efi/loader/entries/archlinux.conf
title           Arch Linux
linux           vmlinuz-linux
initrd          intel-ucode.img
initrd          initramfs-linux.img
options         zfs=zroot/ROOT/default rw

使用 rEFInd 進行 UEFI 啟動[編輯 | 編輯原始碼]

為 UEFI 啟動管理器使用 EFISTUBrEFIndrefind_linux.conf 中針對 ZFS 設定的內核參數應該包含 zfs=bootfszfs=zroot 以便系統從 ZFS 啟動。無需額外設定 rootrootfstype 參數。

使用 ZFSBootMenu 進行 UEFI 啟動[編輯 | 編輯原始碼]

若使用 ZFSBootMenu,您的 /boot 目錄必須存放在您的根文件系統中。更多信息請見 Boot Environments and You: A Primer

您可以選擇 #生成 ZFSBootMenu 映像 或使用已構建好的 #ZFSBootMenu EFI 二進制文件

生成 ZFSBootMenu 映像[編輯 | 編輯原始碼]

/etc/zfsbootmenu/config.yaml
Global:
  ManageImages: true
  BootMountPoint: path_to_your_ESP ( ESP 分區掛載點) 
  DracutConfDir: /etc/zfsbootmenu/dracut.conf.d
  PreHooksDir: /etc/zfsbootmenu/generate-zbm.pre.d
  PostHooksDir: /etc/zfsbootmenu/generate-zbm.post.d
  InitCPIO: true
  InitCPIOConfig: /etc/zfsbootmenu/mkinitcpio.conf
Components:
  ImageDir: path_to_your_ESP ( ESP 分區掛載點) /EFI/zbm
  Versions: 3
  Enabled: false
  syslinux:
    Config: /boot/syslinux/syslinux.cfg
    Enabled: false
EFI:
  ImageDir: path_to_your_ESP ( ESP 分區掛載點) /EFI/zbm
  Versions: false
  Enabled: true
Kernel:
  CommandLine: ro quiet loglevel=0
  • 生成 ZFSBootMenu 映像:
# generate-zbm
  • 設置啟動時的 zfs 命令行參數:
# zfs set org.zfsbootmenu:commandline="rw" zroot/ROOT
  • 向 EFI 啟動管理器中加入 ZFSBootMenu 條目:
# efibootmgr -c -d your_esp_disk -p your_esp_partition_number -L "ZFSBootMenu" -l '\EFI\zbm\vmlinuz-linux.EFI'

ZFSBootMenu EFI 二進制文件[編輯 | 編輯原始碼]

# zfs set org.zfsbootmenu:commandline="rw" zroot/ROOT
  • 在 EFI 啟動管理器中加入 ZFSBootMenu 條目:
# efibootmgr -c -d your_esp_disk -p your_esp_partition_number -L "ZFSBootMenu" -l '\EFI\zbm\zfsbootmenu-release-vmlinuz-x86_64.EFI'

配置 systemd ZFS 掛載[編輯 | 編輯原始碼]

要確保您的系統能夠正常地重新啟動,您需要啟用 zfs.target 以便自動掛載存儲池與生成 hostid。

注意: 這一節內容的前提是假設您仍在 arch-chroot 環境中。

分別為每個您想要自動掛載的存儲池執行:

# zpool set cachefile=/etc/zfs/zpool.cache pool(存储池名)

啟用 zfs.target

要在系統啟動時自動掛載 ZFS 存儲池,您需要 啟用 zfs-import-cache.servicezfs-mount.servicezfs-import.target

當根目錄文件系統為 ZFS 時,掛載根文件系統的過程中設備的 hostid 將不可用。對此有兩個解決方案。 您可以將您的 spl hostid 寫入啟動引導器的啟動 內核參數 中。例如向內核參數中加入 spl.spl_hostid=0x00bab10c。要獲取您的 hostid 數字,運行 hostid 命令。

另一個(建議的)解決方案是在 /etc/hostid 內寫入一個 hostid ,然後重新生成 initramfs,這個過程中您的 hostid 將會被複製到 initramfs 映像中。要安全寫入 hostid 文件,您需要運行 zgenhostid 命令。

要使用 libc 生成的 hostid(建議的操作):

# zgenhostid $(hostid)

要使用自定義的 hostid,運行以下命令。注意,hostid 必須是8個字符的十六進制數字。

# zgenhostid deadbeef

要讓 zgenhostid 工具生成一個 hostid :

# zgenhostid

完成後別忘了 重新生成 initramfs

卸載文件系統並重新啟動[編輯 | 編輯原始碼]

我們離成功不遠了!如果您使用傳統類型的啟動引導分區,先運行:

# umount /mnt/boot 

如果未使用傳統的單獨引導分區,應直接運行:

# zfs umount -a
# zpool export zroot

現在,重新啟動系統。

警告: 如果您沒有正確將存儲池導出,重新啟動後 ZFS 將拒絕在 ramdisk 環境中導入存儲池,從而使您卡在 busybox 終端界面。

從 USB 存儲設備加載密鑰[編輯 | 編輯原始碼]

可以將密鑰存儲在 USB 存儲設備上並在啟動時加載:

在 USB 存儲介質的初始字節處存儲密鑰:

# dd if=your_password_file (您的密钥文件) bs=32 count=1 of=/dev/disk/by-id/usb_stick (USB 存储设备)

要創建 ZFS 分區,您可以使用上文所述的輸入密鑰的方式,或直接使用 dd 命令配合管道寫入 USB 存儲設備中存儲的密鑰:

# dd if=/dev/disk/by-id/usb_stick bs=32 count=1 | zfs create -o encryption=on -o keyformat=passphrase zroot/ROOT

下一步就要更改 zfs hook。zfs 默認會詢問密鑰。您需要將獲取密鑰的方式改為通過從您存放着密鑰的 USB 設備中 dd 來獲取。要達成這個目的,將 /usr/lib/initcpio/hooks/zfs 中的以下行:

# ! eval zfs load-key "${encryptionroot}"; do

改為:

# ! eval dd if=/dev/disk/by-id/usb_stick bs=32 count=1 | zfs load-key "${encryptionroot}"; do

您剛剛更改了您的 zfs hook,所以不要忘記 重新生成 initramfs。現在 zfs 應該能在啟動時從您的 USB 設備中加載密鑰。

故障排除[編輯 | 編輯原始碼]

系統因 "無法導入 zroot :存儲池不存在(cannot import zroot: no such pool available)" 而無法啟動[編輯 | 編輯原始碼]

您可以嘗試以下步驟,看看是否有幫助。

  • 使用 archzfs 倉庫 中提供的內核模塊,不要使用 dkms 版本。您可以在成功啟動後再改為使用 dkms 變種。
  • 移除 /etc/zfs/zpool.cache 並運行:
# zpool set cachefile=none zroot

另請參閱[編輯 | 編輯原始碼]