На моей домашней машине вот уже 7 лет работает пара дисков, объединенная в soft raid1. И вот на днях один диск в зеркале наконец начал сыпаться. Появился повод переустановить систему с нуля и начать использовать шифрование, которое 7 лет назад не было задействовано. В процессе гугления о состоянии дел в конфигурации LUKS поверх mdadm я вышел на статью сравнивающей производительность zfs vs mdadm/ext4. А потом нашел другую статью с тестированием производительности зашифрованных дисков использующих LUKS и zfs. Согласно обеим статьям zfs демонстрирует весьма неплохую производительность и я решил попробовать ее в деле.
На хабре некоторое время назад уже были статьи на ту же тему:
2019 Ubuntu 18.04 Root on ZFS
2018 Установка Debian с корнем на шифрованном ZFS зеркале
2018 /boot на ZFS зеркале
Я решил написать свою статью, поскольку использую для загрузки UEFI (в прошлых статьях использовалась legacy загрузка), ну и плюс с момента последней статьи прошло 3 года и мне показалось, что проверенная в деле современная инструкция может быть полезна для сообщества.
При установке я в основном ориентировался на вот эти статьи:
Ubuntu 20.04 Root on ZFS
Installing UEFI ZFS Root on Ubuntu 20.04 with Native Encryption
Я буду описывать установку на виртуальную машину virtualbox. Установка на настоящее железо ничем не отличается.
Итак, я создал виртуальную машину с парой дисков по 20гб и 8гб памяти. Загрузился с ubuntu-20.04.1-desktop-amd64.iso, кликнул на try ubuntu и запустил терминал. В терминале я сразу перешел под рута, так как все используемые команды требуют рутовых привилегий. Первым делом я определил несколько переменных:
export DISK1=/dev/disk/by-id/ata-VBOX_HARDDISK_VBad5107ca-df268eef
export DISK2=/dev/disk/by-id/ata-VBOX_HARDDISK_VBaf134a71-943e2d11
export HOSTNAME=ubuntu-zfs-vm
export USERNAME=toor
Теперь можно разметить диски:
sgdisk --zap-all $DISK1
sgdisk --zap-all $DISK2
sgdisk -n1:1M:+256M -t1:EF00 -c1:EFI $DISK1
sgdisk -n1:1M:+256M -t1:EF00 -c1:EFI $DISK2
sgdisk -n2:0:+1024M -t2:be00 -c2:Boot $DISK1
sgdisk -n2:0:+1024M -t2:be00 -c2:Boot $DISK2
sgdisk -n3:0:0 -t3:bf00 -c3:Ubuntu $DISK1
sgdisk -n3:0:0 -t3:bf00 -c3:Ubuntu $DISK2
Для загрузки я буду использовать UEFI, так что надо создать диск с типом EF00, отформатированный под vfat:
mkfs.msdos -F 32 -n EFI ${DISK1}-part1
mkfs.msdos -F 32 -n EFI ${DISK2}-part1
Настала пора создавать zfs. Я буду использовать grub, который поддерживает загрузку с zfs, хотя и не со всеми опциями. Так что создание загрузочного раздела требует явным образом указать только то, что понимает grub:
zpool create -f -o cachefile=/etc/zfs/zpool.cache -o ashift=12 \
-o autotrim=on -d -o feature@async_destroy=enabled \
-o feature@bookmarks=enabled -o feature@embedded_data=enabled \
-o feature@empty_bpobj=enabled -o feature@enabled_txg=enabled \
-o feature@extensible_dataset=enabled \
-o feature@filesystem_limits=enabled -o feature@hole_birth=enabled \
-o feature@large_blocks=enabled -o feature@lz4_compress=enabled \
-o feature@spacemap_histogram=enabled -O acltype=posixacl -O canmount=off \
-O compression=lz4 -O devices=off -O normalization=formD -O relatime=on \
-O xattr=sa -O mountpoint=/boot -R /mnt \
bpool mirror ${DISK1}-part2 ${DISK2}-part2
Загрузочный раздел создан, можно создавать корневой раздел (поскольку мы используем шифрование нам надо будет ввести пароль):
zpool create -f -o ashift=12 -o autotrim=on -O encryption=aes-256-gcm \
-O keylocation=prompt -O keyformat=passphrase -O acltype=posixacl \
-O canmount=off -O compression=lz4 -O dnodesize=auto \
-O normalization=formD -O relatime=on -O xattr=sa -O mountpoint=/ \
-R /mnt rpool mirror ${DISK1}-part3 ${DISK2}-part3
Можно создавать датасеты. Я решил ограничиться минимумом:
zfs create -o canmount=off -o mountpoint=none rpool/ROOT
zfs create -o canmount=off -o mountpoint=none bpool/BOOT
UUID=$(dd if=/dev/urandom bs=1 count=100 2>/dev/null \
|tr -dc 'a-z0-9' | cut -c-6)
zfs create -o mountpoint=/ -o com.ubuntu.zsys:bootfs=yes \
-o com.ubuntu.zsys:last-used=$(date +%s) \
rpool/ROOT/ubuntu_$UUID
zfs create -o mountpoint=/boot bpool/BOOT/ubuntu_$UUID
zfs create -o canmount=off -o mountpoint=/ rpool/USERDATA
zfs create -o com.ubuntu.zsys:bootfs-datasets=rpool/ROOT/ubuntu_$UUID \
-o canmount=on -o mountpoint=/home/$USERNAME \
rpool/USERDATA/${USERNAME}_$UUID
Для установки системы давайте использовать debootstrap:
apt-get install -y debootstrap
debootstrap focal /mnt
Копируем на новую файловую систему недостающие компоненты:
echo $HOSTNAME >/mnt/etc/hostname
sed '/cdrom/d' /etc/apt/sources.list > /mnt/etc/apt/sources.list
sed "s/ubuntu/$HOSTNAME/" /etc/hosts > /mnt/etc/hosts
cp /etc/netplan/*.yaml /mnt/etc/netplan/
И монтируем псевдофс, нужные для продолжения установки:
mount --make-private --rbind /dev /mnt/dev
mount --make-private --rbind /proc /mnt/proc
mount --make-private --rbind /sys /mnt/sys
Заходим в chroot среду:
chroot /mnt /usr/bin/env DISK1=$DISK1 DISK2=$DISK2 USERNAME=$USERNAME \
/bin/bash –login
Обновляем индексы бинарных пакетов, устанавливаем локаль:
apt-get update
locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales
Устанавливаем нужный нам часовой пояс:
dpkg-reconfigure tzdata
Монтируем EFI партицию. Обычно ее монтируют под /boot/efi, но в нашем случае партиций 2, плюс есть проблема очередности монтирования дисков. Я решил монтировать диск в другой иерархии и использовать симлинку:
mkdir /run/efi1
mount $DISK1-part1 /run/efi1
ln -s /run/efi1 /boot/efi
echo /dev/disk/by-uuid/$(blkid -s UUID -o value \
${DISK1}-part1) /run/efi1 vfat defaults 0 0 >> /etc/fstab
echo /dev/disk/by-uuid/$(blkid -s UUID -o value \
${DISK2}-part1) /run/efi2 vfat defaults 0 0 >> /etc/fstab
Устанавливаем прочие необходимые пакеты:
apt-get install -y grub-efi-amd64 grub-efi-amd64-signed linux-image-generic \
shim-signed zfs-initramfs zsys ubuntu-minimal network-manager
Из-за регрессии надо добавить параметр ядра init_on_alloc=0:
sed -ie 's/\(GRUB_CMDLINE_LINUX_DEFAULT="[^"]*\)/\1 init_on_alloc=0/' \
/etc/default/grub
Я предпочитаю иметь небольшой своп:
zfs create -V 4G -b $(getconf PAGESIZE) -o compression=off \
-o logbias=throughput -o sync=always -o primarycache=metadata \
-o secondarycache=none rpool/swap
mkswap -f /dev/zvol/rpool/swap
echo "/dev/zvol/rpool/swap none swap defaults 0 0" >> /etc/fstab
echo RESUME=none > /etc/initramfs-tools/conf.d/resume
Добавляем пользователя:
adduser $USERNAME
find /etc/skel/ -type f|xargs cp -t /home/$USERNAME
chown -R $USERNAME:$USERNAME /home/$USERNAME
usermod -a -G adm,cdrom,dip,plugdev,sudo $USERNAME
Обновляем inird и grub и выходим из chroot среды:
update-initramfs -c -k all
update-grub
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=ubuntu --recheck --no-floppy
exit
Размонтируем то, что было смонтировано в chroot среде и перезагружаемся:
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
zpool export -a
reboot
Поскольку все происходит в virtualbox отмечу, что с включенным UEFI виртуалка отказывалась грузиться с оптического привода. Так что в этом месте я убираю диск из виртуального привода и включаю UEFI загрузку.
Если не случилось ничего непредвиденного вы увидите меню grub. Но не спешите жать enter! Вместо этого загрузитесь в recovery mode, потому что при импорте корневого пула zfs произойдет ошибка, вызванная тем, что последний раз пул использовался на машине с другим именем. Исправляется это просто:
zpool import -f rpool
exit
После этого у вас спросят пароль для доступа к диску и загрузка продолжится вплоть до момента, когда вам будет предложено воспользоваться аварийной консолью (потому что мы загрузились в recovery mode) или нажать ctrl-d для загрузки в обычном режиме. Нажимайте ctrl-d. Через несколько секунд вы сможете войти в систему используя созданного ранее пользователя. На этом, впрочем, наши злоключения не заканчиваются. Посмотрите на директорию /boot и вы увидите, что она пустая. Загрузочный пул тоже не был импортирован. Исправим это:
zpool import bpool
И последний штрих — отметим обе партиции EFI, как требующие обновления при изменениях grub:
dpkg-reconfigure grub-efi-amd64
Вот теперь установка системы полностью завершена и вы можете перезагрузиться и воспользоваться пунктом меню grub по умолчанию. Из-за того, что по умолчанию в параметрах ядра присутствует quiet вы увидите черный экран. Пароль на доступ к диску придется вводить вслепую через несколько секунд после начала загрузки. Вы можете убрать quiet из параметров или поставить пакет plymouth.
Все команды выше можно скачать единым скриптом, которому для работы необходимо определить переменные DISK1, DISK2, HOSTNAME и USERNAME.
Комментарии (10)
edo1h
09.02.2022 19:43Так что создание загрузочного раздела требует явным образом указать только то, что понимает grub
по мне проще сделать этот раздел ext4
или вообще обойтись без него (и без grub), копируя ядро и initramfs на vfat-раздел
https://habr.com/ru/post/314412/Пароль на доступ к диску придется вводить вслепую через несколько секунд после начала загрузки. Вы можете убрать quiet из параметров или просто поставить десктопную среду
я не понял, как эта десктопная среда будет грузиться до ввода пароля
Я предпочитаю иметь небольшой своп:
а какой смысл в свопе, существенно меньшем размера памяти?
kt97679 Автор
09.02.2022 20:30я не понял, как эта десктопная среда будет грузиться до ввода пароля
В статье я использовал мета пакет ubuntu-minimal. Если вы поставите ubuntu-dektop-minimal, то получите при загрузке splash screen с логотипом ubuntu и вместе с ним поля ввода пароля доступа к диску.а какой смысл в свопе, существенно меньшем размера памяти?
Прошу прощения, надо было явно уточнить, что виртуальная машина, на которой я экспериментировал при написании статьи, была в чистом виде полигоном и больше ни для каких целей не использовалась. На физической машине у меня 64гб.edo1h
09.02.2022 23:31В статье я использовал мета пакет ubuntu-minimal. Если вы поставите ubuntu-dektop-minimal, то получите при загрузке splash screen с логотипом ubuntu и вместе с ним поля ввода пароля доступа к диску.
понятия не прибавилось.
если у вас корень файловой системы зашифрован, то пароль должен запрашиваться из initramfs до монтирования корня.
хотите сказать, что ubuntu пихает в initramfs графические утилиты для ввода пароля?kt97679 Автор
10.02.2022 02:05+1Пароль действительно запрашивается из initramfs. А за красивости вокруг этого процесса отвечает пакет plymouth. У меня в минимальной установке его не было, но вы правы в том, что поставить его можно и без десктопного окружения. Сейчас поправлю статью. Спасибо за замечание!
Jsty
09.02.2022 23:20В чем плюсы zfs для домашнего использования?
Gaernebjorn
10.02.2022 04:12+1Защитой от вымогателей-шифровальщиков с помощью "бесплатных" снапшотов, например
Jsty
10.02.2022 23:03Для себя я это решил тем, что куча приложений запускаются под своими пользователями без доступа куда-то еще. Пусть свой хомяк шифруют, если вдруг уязвимость будет в хроме, которая позволит с правами процесса хрома запустить бинарник при скачивании.
Можно накидывать причин так не делать или запускать каждое приложение в отдельной виртуалке. А виртуалку каждый раз из снэпшота поднимать.
13werwolf13
позволь предложить поправочку:
efi boot partition может прекрастно жить на mdadm зеркале
пишу это потому что несколько раз столкнулся с ситуацией когда на двух efi разделах данные разъехались, на одном был свежак а на втором ещё от предыдущего релиза системы до апгрейда что вызвало боль когда первый диск немножко посыпался
kt97679 Автор
К сожалению этот подход работает ровно до того момента, пока uefi не сохранит что-то в процессе загрузки в обход mdadm. Больше подробностей тут:
13werwolf13
Насколько я понимаю это не происходит само по себе а должно быть явно вызвано. Но может я и не прав.