Привет! Меня зовут Денис, я 3D-художник и Linux-энтузиаст. Более 8 лет Arch Linux является моей основной и единственной операционной системой. Мне не удалось найти ни одного приличного гайда по установке Arch на ZFS. Где-то была устаревшая информация, где-то использовался плохо подходящий для ZFS GRUB, где-то ядро ставилось на FAT. Хороший повод написать новый гайд.

Почему ZFS? Однажды я решил собрать stripe (RAID0) на ноутбуке с двумя дисками. Хотелось сохранить привычную структуру директорий и не думать о том, на каком диске что лежит.
На тот момент я уже использовал ZFS на файловом сервере, а на ноутбуках - Btrfs + LUKS2. Загрузить Arch Linux с RAID на Btrfs у меня не получилось. С ZFS, напротив, не возникло никаких трудностей.

Почему страйп а не зеркало? Ноутбук ношу с собой, ноутбук можно потерять, забыть в кафе, ноутбуки воруют. Все важное в любом случае бекапится на сервер, поэтому избыточность на ноутбуке мне не нужна. На сервере ZFS, поэтому Btrfs на ноутбуке мне не подходит.

Задача:

  • ZFS stripe из двух дисков

  • Полное шифрование

  • Ядро на ZFS

Установочный образ archiso не имеет поддержки ZFS. Нам придётся добавить zfs модуль в archiso самостоятельно.

Ставим пакет archiso и копируем дефолтный профиль для дальнейшего редактирования.

pacman -Syu archiso
cp -r /usr/share/archiso/configs/releng/ ~/archlive

Из файла ~/archlive/packages.x86_64 удаляем linux и broadcom-wl и добавляем пакеты:

linux-lts
linux-lts-headers
libunwind
zfs-utils
zfs-dkms

В ~/archlive/pacman.conf добавляем репозиторий archzfs:

[archzfs]
SigLevel = Never
Server = https://github.com/archzfs/archzfs/releases/download/experimental

Ставим LTS ядро для лучшей совместимости с ZFS. Заменяем 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/syslinux/archiso_sys-linux.cfg
archlive/grub/loopback.cfg
archlive/grub/grub.cfg

Собираем образ. Затем пишем на флешку и грузимся.

mkdir isobuild
mkarchiso -v -r -w /tmp/archiso-tmp -o isobuild ~/archlive

На дисках создаём таблицу разделов GPT и следующую разметку:

nvme0n1p1 - 256 Mb - EFI System
nvme0n1p2 - всё свободное место - Solaris Root
nvme1n1p1 - весь диск - Solaris Root

Тут подойдет любая программа для разметки диска, например cfdisk.
На EFI-разделе будет находиться только загрузчик ZFSBootMenu, поэтому большой раздел нам не нужен. Ядро и система будут храниться непосредственно на ZFS.

Создаем ZFS пул:

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
-o autotrim=on \
-R /mnt \
-O compression=lz4 \
-O encryption=aes-256-gcm \
-O keyformat=passphrase \
-O keylocation=prompt \
zroot /dev/nvme0n1p2 /dev/nvme1n1p1

Несколько важных опций:

  • compression=lz4 - практически бесплатное сжатие

  • encryption=aes-256-gcm - встроенное шифрование ZFS

  • keyformat=passphrase - парольная фраза

  • keylocation=prompt - пароль вводится при загрузке

Создаем датасеты:

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  

После создания датасетов необходимо заново импортировать пул:

zpool export zroot  
zpool import -d /dev/nvme0n1p2 -R /mnt zroot -N  
zfs load-key zroot  
zfs mount zroot/ROOT/default  
zfs mount -a

Указываем загрузочный датасет:

zpool set bootfs=zroot/ROOT/default zroot

Монтируем EFI раздел для загрузчика:

mkdir -p /mnt/efi  
mkfs.vfat -F32 -n EFI /dev/nvme0n1p1  
mount /dev/nvme0n1p1 /mnt/efi

Ставим базовую систему:

pacstrap /mnt base linux-lts linux-lts-headers neovim base-devel iwd sudo

Генерируем fstab:

genfstab -U -p /mnt > /mnt/etc/fstab

После этого удаляем из /mnt/etc/fstab всё, кроме EFI-раздела. ZFS монтируется самостоятельно.

Чрутимся:

arch-chroot /mnt

Добавляем archzfs репозиторий в /etc/pacman.conf:

[archzfs]  
Server = https://github.com/archzfs/archzfs/releases/download/experimental
Server = https://mirrors.uni-plovdiv.net/archzfs/experimental

Добавляем ключ:

pacman-key --init
pacman-key --recv-keys 3A9917BF0DED5C13F69AC68FABEC0A1208037BE9
pacman-key --lsign-key 3A9917BF0DED5C13F69AC68FABEC0A1208037BE9

Ставим ZFS:

pacman -S zfs-utils zfs-dkms

ZFS модули привязаны к определённой версии ядра. Если при обновлении ядра модули не собираются, ядро необходимо откатить и подождать, пока обновятся модули.
Альтернатива - использовать репозитории CachyOS, где есть ядра со встроенной поддержкой ZFS. Последние полгода я использую linux-cachyos-zfs.

Редактируем /etc/mkinitcpio.conf.
Добавляем модуль:
MODULES=(zfs)
Правим HOOKS:
HOOKS=(base udev ... keyboard zfs filesystems)

Пересобираем initramfs:

mkinitcpio -P

Важно:

  • zfs должен идти перед filesystems

  • keyboard - перед zfs

  • если используется systemd, его нужно заменить на base udev

Базовая настройка системы:

echo "en_US.UTF-8 UTF-8" > /etc/locale.gen  
locale-gen  
echo "LANG=en_US.UTF-8" > /etc/locale.conf  
echo "KEYMAP=us" > /etc/vconsole.conf  
echo "somename" > /etc/hostname    
zgenhostid $(hostid)

Только ZFSBootMenu умеет нормально грузить ядро с ZFS, так что выбор загрузчика очевиден.

Скачиваем загрузчик:

mkdir -p /efi/EFI/zbm
wget https://get.zfsbootmenu.org/latest.EFI -O /efi/EFI/zbm/zfsbootmenu.EFI

Создаём EFI-запись:

efibootmgr --disk /dev/nvme0n1 --part 1 --create --label "ZFSBootMenu" --loader '\EFI\zbm\zfsbootmenu.EFI' --unicode "spl_hostid=$(hostid) zbm.timeout=3 zbm.prefer=zroot zbm.import_policy=hostid" --verbose

Важно:
--disk /dev/nvme0n - диск, где находится загрузчик
--part 1 - номер раздела на этом диске

Параметры загрузки ядра:

zfs set org.zfsbootmenu:commandline="noresume init_on_alloc=0 rw spl.spl_hostid=$(hostid) zswap.enabled=0" zroot/ROOT

Параметр zswap.enabled=0 нужен если планируется использовать zram.

Проверяем:

zfs get org.zfsbootmenu:commandline zroot/ROOT

Включаем сервисы ZFS:

systemctl enable zfs.target  
systemctl enable zfs-import.target  
systemctl enable zfs-volumes.target  
systemctl enable zfs-import-scan.service

Настраиваем zfs-mount-generator:

mkdir /etc/zfs/zfs-list.cache  
ln -s /usr/lib/zfs/zed.d/history_event-zfs-list-cacher.sh /etc/zfs/zed.d  
systemctl enable zfs-zed.service  
touch /etc/zfs/zfs-list.cache/zroot

Создаём пароль root:

passwd

Создаём пользователя:

useradd -m -G wheel username  passwd username

Разрешаем sudo. Добавляем в /etc/sudoers:

%wheel ALL=(ALL) ALL

Завершаем установку:

exit  
umount /mnt/efi  
zfs umount -a  
zpool export zroot

Настройка после установки.
Чтобы не вводить пароль дважды при загрузке, можно добавить ключ в initramfs.

Создаём файл ключа:

echo '' > /etc/zfs/zroot.key  
chmod 000 /etc/zfs/zroot.key
zfs set keylocation=file:///etc/zfs/zroot.key zroot

Добавляем ключ в /etc/mkinitcpio.conf:

FILES=(/etc/zfs/zroot.key)

Пересобираем initramfs:

mkinitcpio -P

Проверяем:

zfs get keylocation,keyformat zroot