在 ZFS 上安裝 Arch Linux
這篇文章詳細描述了將 Arch Linux 安裝在 ZFS 文件系統上所需的步驟。
由於 ZFS 不是 Linux 的原生文件系統(也即:不包含在主線內核之內)且 Arch Linux 是一個滾動更新發行版,總有些時候外部倉庫內的對應一定內核版本的內核模塊軟體包版本會稍落後於 Arch 倉庫中的版本,這有時會導致 ZFS 模塊(或其 dkms 變種包)在最新版本的內核上無法編譯。如果您想要一直使用最新版本的內核的話,將 Arch 安裝在 ZFS 上可能並不是很理想。
可能的解決方案見 ZFS#安裝。
安裝
要在 ZFS 上安裝 Arch Linux,您需要使用帶有 ZFS 內核模塊的安裝介質。您可以使用現有的 Arch Linux; 或者,如果您沒有現有的 Arch Linux 系統,您也可以使用啟用了「共享文件夾」的虛擬機軟體,如 VirtualBox 或 VMWare。
在自定義 archiso 安裝介質中嵌入 ZFS 模塊
要構建自定義的ISO, 安裝 archiso包。依照 archiso#準備自定義配置文件 來準備一個自定義的配置文件(下文中以 archlive 為例)。
首先,編輯 軟體包列表 packages.x86_64 並在其中加入 linux-lts包 內核與下列 ZFS 軟體包:
packages.x86_64
... linux-lts linux-lts-headers libunwind zfs-utils zfs-dkms
請確保從列表中移除 linux 與 broadcom-wl (後者會拉取 linux包 包) 。
您還需要編輯 pacman.conf 並在其中加入:
... [archzfs] SigLevel = TrustAll Optional Server = https://github.com/archzfs/archzfs/releases/download/experimental
您還需要編輯以下文件以將 vmlinuz-linux 與 initramfs-linux.img 的條目改為 vmlinuz-linux-lts 和 initramfs-linux-lts.img:
archlive/airootfs/etc/mkinitcpio.d/linux.preset archlive/efiboot/loader/entries/01-archiso-linux.conf archlive/efiboot/loader/entries/02-archiso-speech-linux.conf archlive/syslinux/archiso_pxe-linux.cfg archlive/syslinux/archiso_sys-linux.cfg archlive/grub/loopback.cfg archlive/grub/grub.cfg
接下來我們要創建 isobuild 與 work 兩個目錄來開始構建 ISO 鏡像:
# mkdir isobuild # mkarchiso -v -r -w /tmp/archiso-tmp -o isobuild ~/archlive
現在,您的 archlive/isobuild 目錄下應該已經出現了archlinux-YYYY.MM.DD.x86_64.iso。
請將該文件燒錄至您選擇使用的安裝介質。
- 如果在系統更新後,
zpool status -v的輸出顯示您的存儲池需要升級以支持新的特性,您可能需要重新製作上述安裝介質。
- 在實際安裝時,您應該使用 linux-lts包 與 linux-lts-headers包 來保證最大兼容性。同時,您應從非官方的 archzfs 倉庫而非 AUR 獲取 zfs-utils 與 zfs-dkms 的更新。如果您選擇使用 AUR 中的版本,則可能出現當前的主線或 zen 內核不被 ZFS 支持的情況,從而導致您的系統無法啟動。因此,在實際操作時,強烈建議您使用 linux-lts 內核。
請確保先在虛擬機中測試您的 ISO。只需運行:
# modprobe zfs # zpool status
如果命令執行失敗,則您的 ZFS 模塊並未成功構建,您需要重新嘗試。
對目標磁碟驅動器進行分區
ZFS 支持 GUID 分區圖與主啟動記錄分區表。要決定使用哪種分區圖,請參考分區#選擇 GPT 還是 MBR。
ZFS 會管理自己的分區,所以只需創建一個最簡單的分區類型即可。創建 ZFS 文件系統的分區類型應為類型 bf00 ,或「 Solaris 根目錄分區」。
分區類型
雖然您在一些使用主啟動記錄的舊式電腦上也能創建出可以啟動的 ZFS 根分區,但由於主啟動記錄與 GUID 分區圖的差異,並不建議以這種方式使用 ZFS。為了保證兼容性並避免啟動加載器出現問題,建議使用一個單獨的 /boot 分區。
在 BIOS(或一台以 Legacy 模式啟動的)設備上使用 GUID 分區圖與 GRUB 啟動引導器的示例:
Part Size Type ---- ---- ------------------------- 1 2M BIOS boot partition (ef02) 2 1G Linux Partition (8300) 3 XXXG Solaris Root (bf00)
在使用Grub 與 rEFInd 時可使用以下這種分區形式。由於其較好的兼容性,這也是作者推薦的分區形式。
Part Size Type ---- ---- ------------------------- 1 1G EFI System Partition (ef00) 2 XXXG Solaris Root (bf00)
您可以選擇創建獨立的 linux 交換分區或將 zvol 用作交換空間,後者詳見 ZFS#交換卷。 如果您想要創建一個傳統的交換分區,詳見 Partitioning#布局示例.
格式化目標磁碟驅動器
如果您已為啟動引導分區以及非 ZFS 文件系統創建了合適的分區,那麼現在請將這些分區格式化。不要對剛創建的 Solaris 根分區以及您的 BIOS 啟動分區做任何操作。Solaris 根分區將由 ZFS 管理,而 BIOS 啟動分區則會由您的啟動引導器管理。
設置 ZFS 文件系統
首先,確認 ZFS 內核模塊已經被加載,
# modprobe zfs
創建根存儲池
創建存儲池並設置好數據集的默認選項。存儲池上新創建的任何數據集都會保留這個存儲池創建時使用 -O 設定的選項。默認選項在在 ZFS 上安裝 Debian Buster # 第二步: 磁碟格式化中有詳細說明。
-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位元組。ashift=9 也一定會導致嚴重的性能下降。在物理扇區大小為512位元組的設備上選擇 ashift=12 不會導致性能下降,但可能使磁碟實際可用的容量減少。如果您不確定的話,對於現代設備,應使用 ashift=12,或者您可以搜索您設備對應的正確值。對於有關的討論,參見 OpenZFS issue #967 ;對設置較高的 ashift 值可能出現的問題,見 OpenZFS issue #2497 。
ls -lh /dev/disk/by-id/
並記錄您將要使用的正確 by-id 名稱,如:
lrwxrwxrwx 1 root root 9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0JKRR -> ../../sdc
# zpool create -f -o ashift=12 \
-O acltype=posixacl \
-O relatime=on \
-O xattr=sa \
-O dnodesize=auto \
-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=auto \
-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
-O 之後的參數將控制 ZFS 的行為。
您可以在 zfsprops(7) 手冊頁面中找到關於這些參數的詳細解釋。
- 使用 ZFS 時應始終使用設備的 by-id 名稱,否則導入存儲池時會發生錯誤。
- 除使用 by-id 名稱外,也可以考慮使用 by-partuuid 或 by-uuid名稱,因為即使一個內置磁碟驅動器被移入USB移動硬碟盒,這些名稱的值也不會發生改變,反之亦然。(這僅當 ZFS 在磁碟上的某個分區中才有效,若 ZFS 占據整個磁碟則無效)
創建您的數據集
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=off選項。
示例請參見 在 ZFS 上安裝 Debian Buster # 第三步:安裝系統。
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/log/journal -o acltype=posixacl zroot/var/log/journal # 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 參數。這會使導入的存儲池卸載。# 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
- 依照安裝指南#chroot 到新安裝的系統中的方法將根目錄切換至新安裝的系統內。
# arch-chroot /mnt
- 編輯
/etc/fstab:
- 如果您選擇為系統目錄創建傳統類型數據集,則請在
fstab中保留這些數據集。 - 將除交換空間與 EFI 系統分區以外的所有非傳統類型數據集注釋掉。可以使用較簡單的
/dev/zvol/zroot/swap來取代交換空間的 UUID。
- 在更新 ramdisk 並啟用 ZFS 支持前,您需要在
/etc/pacman.conf中加入 archzfs 倉庫,將其簽名並在 arch-chroot 環境下安裝 zfs-dkmsAUR 和 zfs-utilsAUR。
- 創建初始 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)
HOOKS 一欄中的 systemd 替換為 base udev 以確認自己正在使用基於 busybox 的 initramfs 鏡像。 安裝並配置啟動引導加載器
- 將 ZFS 添加至您的內核參數
現在,您可以設置您的引導加載程序。要使得 ZFS 可以被引導,您還需要添加一個內核參數:
root=ZFS=zroot/ROOT/default rw
配置 systemd ZFS 掛載
要確保您的系統能夠正常地重新啟動,您需要啟用 zfs.target 以便自動掛載存儲池與生成 hostid。
- 這一節內容的前提是假設您仍在
arch-chroot環境中。 - 雖然上游正在考慮移除 zpool.cache [詳見: https://github.com/openzfs/zfs/issues/1035],但由於 zfs-utils 仍需要使用 zpool.cache 來確保系統可以啟動,目前這仍然是推薦的默認操作。
分別為每個您想要自動掛載的存儲池執行:
# zpool set cachefile=/etc/zfs/zpool.cache pool(存储池名)
啟用 zfs.target
要在系統啟動時自動掛載 ZFS 存儲池,您需要啟用 zfs-import-cache.service、zfs-mount.service 與 zfs-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 # zfs umount zroot/ROOT/default # zpool export zroot
現在,重新啟動系統。
從 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
- 移除
/etc/hostid。 - 重新生成 initramfs。
zpool 因為忙碌而拒絕導出
Arch-chroot 會在系統裡掛在特定的內核空間文件系統。如果沒有卸載這些文件系統,存儲池可能會拒絕導出。此時,請重新掛載 ZFS 分區,並運行 findmnt -R /mnt
然後運行對於每個仍然掛載的文件系統,運行 umount -f /path/to/partition。
之後您應當能夠正常導出您的存儲池。