У кожного адміністратора Linux є шрам від «ще одного рутинного оновлення». Ядро піднялося, initramfs перебудовано, заплановано перезавантаження.
І потім машина «повертається»… лише у ваших уявленнях. Насправді вона застрягає в оболонці initramfs, або зациклюється під час завантаження, або працює, але без імен мережевих інтерфейсів, модулів GPU чи драйверів сховища. Тим часом ви пояснюєте комусь нетехнічному, чому «ми розслідуємо» означає «я вдивляюся в чорний екран о 2 годині ночі».
Завантажувальні середовища ZFS — це доросла відповідь на цей біль. Вони не пафосні. Вони не «cloud-native».
Вони надзвичайно практичні: створити нову завантажувальну копію файлової системи кореня, оновити ту, і якщо щось піде не так,
вибрати попереднє середовище під час завантаження й продовжити роботу. Користувачі Linux ігнорують це, бо основні дистрибутиви не наполегливо це пропихають,
а фраза «boot environment» звучить так, ніби нею оперують на Solaris-конференціях, куди ви не ходите.
Що таке завантажувальне середовище (і чим воно не є)
Завантажувальне середовище ZFS (BE) — це окрема завантажувальна версія вашої кореневої файлової системи ОС, зазвичай реалізована як ZFS clone
(або набір datasets, клонованих разом) плюс запис завантажувача, що вказує на неї. Ви можете зберігати кілька середовищ: «current»,
«before-upgrade», «post-upgrade», «testing», «oh-no» і так далі. Кожне середовище дешеве, бо ділиться блоками з джерелом
за принципом copy-on-write. Ви платите лише за зміни.
Чим воно не є: повноцінним образом диска, який ви копіюєте через dd; знімком VM; резервною копією; заміною для конфігураційного менеджменту.
Завантажувальні середовища — це страховка для системних змін: оновлень, перемикань ядра, встановлення драйверів, переходів libc,
великих рефакторів конфігурацій. Вони дозволяють швидко зафейлити і ще швидше відкотитися.
Ключова властивість така: відкат — це вибір при завантаженні. Не процедура відновлення. Не завантаження з ISO, chroot і молитви.
Якщо ви досі можете дістатися до меню завантаження, зазвичай можна повернутися до робочої системи.
Чому користувачі Linux досі ризикують при оновленнях
Культура Linux історично ставила перевстановлення як обряд ініціації, а відновлення після збою вважалося навичкою. На серверах ми пом’якшуємо ризики
канарками, blue/green, фіксацією пакетів і знімками в гіпервізорах. На робочих станціях ми схрещуємо пальці і тримаємо «завантажувальний USB десь поруч».
Жоден із цих підходів не такий прямий, як: «вибрати кореневу файлову систему вчора і завантажитись у неї».
Є практичні причини, через які люди пропускають BE на Linux:
- Підтримка дистрибутивів нерівномірна. Деякі налаштування роблять це плавним (наприклад, ZFS-on-root в Ubuntu з ZSys у минулому; зараз є кілька інструментів спільноти),
інші — DIY зі скриптами. - Завантажувачі вибагливі. GRUB + ZFS працює, але треба розуміти, як він знаходить datasets. systemd-boot чистіший, але очікує EFI
розділ із видимими ядрами; це змінює історію BE. - Люди плутають snapshots із boot environments. Snapshot — чудово; snapshot, у який можна завантажитись, — ще краще.
- Root на ZFS досі вважається «просунутим» у світі Linux. Багато організацій довіряють ZFS для даних, але не для /.
Жодне з цього не є фатальною причиною. Це просто причини, чому варто свідомо спроєктувати своє оточення, а не сподіватися, що дефолти врятують вас у день перезавантаження.
Факти та історія, що пояснюють дизайн
Трохи контексту робить вибір дизайну менш магічним і більше нудною інженерією — а це мій улюблений тип.
- Завантажувальні середовища були популяризовані на Solaris/Illumos. Операційний робочий процес — клонувати root, оновити клон, перезавантажити — був нормою там задовго до того, як Linux це запозичив.
- ZFS створювався з думкою про адміністративні робочі процеси. Snapshots, clones, send/receive і властивості — це первинні концепти, а не доповнення.
- GRUB отримав розуміння ZFS пізніше і нерівномірно. Багато Linux-команд уникали root-on-ZFS просто тому, що інструменти раннього завантаження відставали.
- Copy-on-write означає «дешеві копії», а не «безкоштовні копії». BE діляться блоками, поки ви їх не зміните; великі оновлення все ще можуть зайняти значний простір.
- Властивості datasets — це площина керування. mountpoints, canmount, bootfs та властивості шифрування вирішують, чи є BE завантажувальним.
- OpenZFS став крос-платформеною ініціативою. Та сама концептуальна модель працює на illumos, FreeBSD, Linux — інструменти завантаження різняться, але семантика ZFS послідовна.
- initramfs став привратником. На багатьох Linux-системах саме initramfs (не тільки ядро) має імпортувати пул і змонтувати правильний dataset.
- UEFI змінив історію розміщення ядра. Деякі робочі процеси тримають ядра всередині кореневого dataset; інші — на окремому EFI-парті, яке всі BE розділяють.
Практична ментальна модель: datasets, snapshots, clones і завантажувачі
Root-on-ZFS зазвичай виглядає як дерево datasets
Розумна схема відокремлює те, що змінюється часто, від того, що ви могли б хотіти ділити між завантажувальними середовищами. Ви хочете, щоб корінь ОС
був клонованим, тоді як деякі datasets мають залишатися постійними між BE (домашні директорії, контейнери, образи VM, дані баз даних).
Поширений шаблон:
rpool/ROOT/<BE-name>змонтовано в/rpool/USERDATAзмонтовано в/home(спільне для BE)rpool/varіноді розділяють на datasets, з уважним плануванням щодо логів, кешів та стану
Snapshots vs clones
Snapshot — це лише для читання точка-в-часі. Clone — це записуваний dataset, створений із snapshot. Більшість реалізацій BE використовують clones,
бо для завантаження та роботи потрібен записуваний корінь.
Життєвий цикл BE по суті такий:
- Створити snapshot поточного кореневого dataset(ів).
- Клонувати snapshot у новий dataset з новою назвою.
- Налаштувати цей dataset так, щоб він монтувався як
/(властивості). - Переконатися, що завантажувач/initramfs можуть його знайти.
- Оновити всередині нового середовища.
- Перезавантажитись і вибрати його.
- Тримати старе середовище, поки не будете впевнені. Потім видалити, щоб повернути простір.
Реальність завантажувача: потрібен вказівник на «який корінь»
Є три поширені підходи:
- GRUB читає ZFS і завантажує kernel/initrd з ZFS. Тоді командний рядок ядра вказує на dataset (або логіка initramfs його вибирає).
- GRUB завантажує kernel/initrd з окремого /boot (ext4) розділу. Root залишається ZFS; вибір BE залежить від того, як записи /boot співвідносяться з datasets.
- systemd-boot завантажує EFI stub kernels з EFI System Partition. Це часто означає, що ваші образи ядра не всередині BE, тож BE потрібно ретельно керувати, щоб ядро+initramfs залишались узгодженими.
Вам не обов’язково любити якийсь із цих підходів. Ви просто маєте вибрати один і протестувати відкат під навантаженням, а не лише в умовах оптимізму.
Одна перефразована ідея від Werner Vogels, якою живуть операційні люди: все ламається, і ви плануєте це — потім автоматизуєте відновлення, щоб це не було героїчним заходом
(перефразована ідея).
Практичні завдання: команди, виводи та рішення
Це щоденні кроки, що роблять BE реальними. Кожне завдання включає: команду, що означає вивід, і рішення, яке ви приймаєте.
Імена хостів/пулів — приклади. Налаштовуйте їх, але не імпровізуйте концепти.
Завдання 1: Підтвердити, що ваша коренева файловая система на ZFS (і який dataset)
cr0x@server:~$ findmnt -no SOURCE /
rpool/ROOT/ubuntu_1a2b3c
Значення: Ваш корінь — це ZFS dataset з назвою rpool/ROOT/ubuntu_1a2b3c.
Рішення: Цей dataset (і будь-які дочірні datasets, змонтовані в ньому) — одиниця, яку потрібно знімати/клонувати для BE.
Завдання 2: Перевірити пули та базовий стан перед будь-яким втручанням
cr0x@server:~$ sudo zpool status
pool: rpool
state: ONLINE
scan: scrub repaired 0B in 00:06:21 with 0 errors on Tue Dec 24 03:12:14 2025
config:
NAME STATE READ WRITE CKSUM
rpool ONLINE 0 0 0
nvme0n1p3 ONLINE 0 0 0
errors: No known data errors
Значення: Пул здоровий і щойно просканований.
Рішення: Продовжуйте. Якщо ви бачите помилки або деградований vdev, виправте це спочатку; розгортати BE на хворому пулі — як фарбувати тонучий човен.
Завдання 3: Перевірити вільне місце, щоб «дешева копія» не стала повним диском
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint rpool
NAME USED AVAIL REFER MOUNTPOINT
rpool 64.1G 112G 96K /
Значення: У вас 112G вільно, достатньо для кількох BE.
Рішення: Якщо avail замало, спочатку видаліть старі BE або додайте ємність. Оновлення можуть швидко накачати /usr, initramfs і кеші.
Завдання 4: Перелік властивостей поточного root dataset, що впливають на завантаження
cr0x@server:~$ sudo zfs get -o name,property,value -s local,received mountpoint,canmount,atime,compression rpool/ROOT/ubuntu_1a2b3c
NAME PROPERTY VALUE
rpool/ROOT/ubuntu_1a2b3c mountpoint /
rpool/ROOT/ubuntu_1a2b3c canmount noauto
rpool/ROOT/ubuntu_1a2b3c atime off
rpool/ROOT/ubuntu_1a2b3c compression zstd
Значення: Root dataset монтується в /, але canmount=noauto вказує, що його монтує спеціальна логіка завантаження (звично для BE).
Рішення: Зберігайте ці властивості при клонуванні; невідповідність mountpoint/canmount — класична пастка «завантаження в initramfs».
Завдання 5: Створити snapshot поточного кореневого dataset
cr0x@server:~$ sudo zfs snapshot rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52
Значення: Snapshot створено миттєво; це послідовний стан у часі (настільки послідовний, наскільки дозволяє запущена система).
Рішення: Тепер у вас є безпечна базова точка. Далі: клонувати її у нове BE для змін.
Завдання 6: Перевірити, що snapshot існує і скільки «коштує»
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,refer | grep pre-upgrade
rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52 0B 32.4G
Значення: Snapshot спочатку використовує 0B, бо посилається на існуючі блоки; він зростатиме, коли живий dataset зміниться.
Рішення: Snapshot безпечний для збереження під час оновлення клону. Якщо ви бачите великий USED відразу, ймовірно, ви зафіксували dataset з великою змінністю або вже видаленими блоками, що утримуються.
Завдання 7: Клонувати snapshot у новий dataset завантажувального середовища
cr0x@server:~$ sudo zfs clone rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52 rpool/ROOT/ubuntu_1a2b3c-upg
Значення: Тепер у вас є записуваний dataset rpool/ROOT/ubuntu_1a2b3c-upg, що ділиться блоками з оригіналом.
Рішення: Встановіть mountpoint/canmount правильно і переконайтеся, що система завантаження може націлювати цей dataset.
Завдання 8: Встановити властивості на новому BE, щоб він міг змонтуватися як / при виборі
cr0x@server:~$ sudo zfs set mountpoint=/ canmount=noauto rpool/ROOT/ubuntu_1a2b3c-upg
Значення: Клон має ту саму поведінку монтування, що й ваш існуючий root dataset.
Рішення: В реальному часі тримайте змонтованим лише одне BE як /. Якщо ви випадково встановите кільком datasets canmount=on з mountpoint /, виникне конфлікт під час завантаження.
Завдання 9: Змонт образ нового BE кудись і chroot у нього для оновлення
cr0x@server:~$ sudo mkdir -p /mnt/be-upg
cr0x@server:~$ sudo mount -t zfs rpool/ROOT/ubuntu_1a2b3c-upg /mnt/be-upg
cr0x@server:~$ sudo mount --bind /dev /mnt/be-upg/dev
cr0x@server:~$ sudo mount --bind /proc /mnt/be-upg/proc
cr0x@server:~$ sudo mount --bind /sys /mnt/be-upg/sys
cr0x@server:~$ sudo chroot /mnt/be-upg /bin/bash
root@server:/# cat /etc/os-release | head -2
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
Значення: Ви тепер оперуєте всередині файлової системи нового BE.
Рішення: Виконуйте оновлення тут, а не на поточному корені, щоб відкат залишався чистим.
Завдання 10: Запустити контрольоване оновлення всередині нового BE і підтвердити створення kernel/initramfs
cr0x@server:~$ sudo chroot /mnt/be-upg /bin/bash -lc "apt update && apt -y full-upgrade"
...output...
Setting up linux-image-6.8.0-45-generic ...
update-initramfs: Generating /boot/initrd.img-6.8.0-45-generic
Processing triggers for grub-pc ...
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.0-45-generic
Found initrd image: /boot/initrd.img-6.8.0-45-generic
done
Значення: Середовище містить ядро та initramfs і оновлену конфігурацію завантажувача.
Рішення: Якщо ви не бачите генерації initramfs або тригерів оновлення завантажувача, зупиніться. Виправте це перед перезавантаженням; BE без артефактів завантаження — музейний експонат, а не шлях відновлення.
Завдання 11: Підтвердити, який dataset пул спробує завантажити за замовчуванням (bootfs)
cr0x@server:~$ sudo zpool get bootfs rpool
NAME PROPERTY VALUE SOURCE
rpool bootfs rpool/ROOT/ubuntu_1a2b3c local
Значення: bootfs пулу вказує на поточний BE.
Рішення: Якщо ваш потік завантаження поважає bootfs, встановіть його на новий BE перед перезавантаженням. Якщо ваш потік ігнорує bootfs і використовує записи меню GRUB, керуйте вибором через завантажувач.
Завдання 12: Змінити BE за замовчуванням через bootfs (коли застосовно)
cr0x@server:~$ sudo zpool set bootfs=rpool/ROOT/ubuntu_1a2b3c-upg rpool
cr0x@server:~$ sudo zpool get bootfs rpool
NAME PROPERTY VALUE SOURCE
rpool bootfs rpool/ROOT/ubuntu_1a2b3c-upg local
Значення: Пул тепер віддає перевагу завантаженню з оновленого BE.
Рішення: Перезавантажуйтесь у новий BE. Зберігайте попередній BE недоторканим доти, поки не буде кількох успішних завантажень і сервіси не будуть працювати нормально.
Завдання 13: Після перезавантаження перевірити, що ви в очікуваному BE
cr0x@server:~$ findmnt -no SOURCE /
rpool/ROOT/ubuntu_1a2b3c-upg
Значення: Ви працюєте з оновленого BE.
Рішення: Тепер ви можете валідовувати: мережу, сховище, агенти, GPU — усе, що раніше ламається на вашому парку.
Завдання 14: Порівняти використання простору BE і вирішити, що обрізати
cr0x@server:~$ sudo zfs list -o name,used,refer,origin rpool/ROOT
NAME USED REFER ORIGIN
rpool/ROOT 128K 96K -
rpool/ROOT/ubuntu_1a2b3c 6.4G 32.4G -
rpool/ROOT/ubuntu_1a2b3c-upg 2.1G 33.0G rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52
Значення: Оновлений BE наразі відрізняється приблизно на ~2.1G від оригіналу; решта блоків спільні.
Рішення: Тримайте щонайменше один відомо робочий fallback. Видаляйте старі BE, коли будете впевнені і потрібен простір.
Завдання 15: Відкат, обравши попередній BE (метод через bootfs)
cr0x@server:~$ sudo zpool set bootfs=rpool/ROOT/ubuntu_1a2b3c rpool
cr0x@server:~$ sudo reboot
Значення: Наступне завантаження віддасть перевагу попередньому кореневому dataset.
Рішення: Використовуйте це, коли новий BE зламаний, але старий все ще завантажується. Після відкату діагностуйте збійний BE з безпечного середовища.
Завдання 16: Якщо потрібно коректно знищити зламаний BE, робіть це в правильному порядку
cr0x@server:~$ sudo zfs destroy rpool/ROOT/ubuntu_1a2b3c-upg
cr0x@server:~$ sudo zfs destroy rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52
Значення: Клон потрібно знищити перед його оригінальним snapshot (інакше отримаєте помилку залежності).
Рішення: Не «чистіть» snapshot, поки clone BE від нього залежить. ZFS заборонить це, але помилка зазвичай з’являється, коли ви вже роздратовані.
Жарт №1: ZFS snapshots — як офісна кава: усім подобається, поки хтось не запитає, хто чистить старі.
План швидкої діагностики
Проблеми з завантажувальними середовищами рідко загадкові. Зазвичай це невелика невідповідність між тим, що думає завантажувач,
тим, що може імпортувати initramfs, і тим, які datasets налаштовані для монтажу. Трик — перевіряти потрібні речі в потрібному порядку.
Перше: чи можна імпортувати пул і чи він здоровий?
Якщо пул деградований, відсутні пристрої або він не імпортується в initramfs, нічого іншого ще не має значення.
cr0x@server:~$ sudo zpool import
pool: rpool
id: 16084073626775123456
state: ONLINE
action: The pool can be imported using its name or numeric identifier.
config:
rpool ONLINE
nvme0n1p3 ONLINE
Інтерпретація: Пул можна імпортувати в поточному середовищі.
Рішення: Якщо це не вдається в initramfs, але працює в запущеній ОС, ймовірно, initramfs не має модулів ZFS, відсутні ID пристроїв або є проблеми з обробкою ключів шифрування.
Друге: що система вважає «/»?
Перевірте активний root dataset і вказівник bootfs пулу.
cr0x@server:~$ findmnt -no SOURCE /
rpool/ROOT/ubuntu_1a2b3c-upg
cr0x@server:~$ sudo zpool get bootfs rpool
NAME PROPERTY VALUE SOURCE
rpool bootfs rpool/ROOT/ubuntu_1a2b3c-upg local
Інтерпретація: Bootfs і змонтований root співпадають. Добре.
Рішення: Якщо вони різні, можливо, ви завантажили інший BE, ніж думаєте (поширено, коли записи меню GRUB вказують інакше).
Третє: чи узгоджені властивості монтажу datasets?
cr0x@server:~$ sudo zfs get -r -o name,property,value mountpoint,canmount rpool/ROOT
NAME PROPERTY VALUE
rpool/ROOT mountpoint none
rpool/ROOT canmount off
rpool/ROOT/ubuntu_1a2b3c mountpoint /
rpool/ROOT/ubuntu_1a2b3c canmount noauto
rpool/ROOT/ubuntu_1a2b3c-upg mountpoint /
rpool/ROOT/ubuntu_1a2b3c-upg canmount noauto
Інтерпретація: Декілька BE ділять mountpoint /, але використовують canmount=noauto, що нормально для BE-стилю монтажу.
Рішення: Якщо ви бачите canmount=on на більш ніж одному dataset з mountpoint /, виправте це перед перезавантаженням.
Четверте: якщо завантаження провалюється, підтвердьте, що initramfs бачить ZFS і пул
З оболонки initramfs (або середовища порятунку) перевірте наявність модулів і спробуйте імпорт.
cr0x@server:~$ lsmod | grep zfs
zfs 4980736 5
zunicode 335872 1 zfs
znvpair 126976 2 zfs
zcommon 98304 1 zfs
icp 315392 1 zfs
spl 135168 5 zfs,icp
cr0x@server:~$ zpool import -N rpool
cr0x@server:~$ zfs list rpool/ROOT
NAME USED AVAIL REFER MOUNTPOINT
rpool/ROOT 128K 112G 96K none
Інтерпретація: Модулі ZFS завантажені, пул імпортований без монтування datasets.
Рішення: Якщо модулі відсутні, перебудуйте initramfs з відомого робочого BE. Якщо імпорт пулу не вдається — шукайте проблеми з іменами пристроїв, відсутні драйвери або шифрування.
П’яте: швидко ідентифікуйте вузьке місце — завантажувач, initramfs чи userspace?
Якщо ви дісталися ядра і initramfs, але не дісталися до userspace: зазвичай це «не вдалося змонтувати root» (вибір dataset, імпорт, ключі).
Якщо ви дісталися userspace, але сервіси падають: це нормальна регресія після оновлення; відкат усе ще ваш друг, але налагоджуйте як звичайну проблему сервісу.
Поширені помилки: симптом → причина → виправлення
1) Симптом: завантажується в initramfs з “cannot import rpool”
Причина: initramfs не має модулів ZFS або правильного hostid/cache для імпорту; іноді після оновлення ядра initramfs не перебудували правильно в новому BE.
Виправлення: Завантажтесь у відомий робочий BE, потім перебудуйте initramfs і оновіть завантажувач з цільового BE.
cr0x@server:~$ sudo chroot /mnt/be-upg /bin/bash -lc "update-initramfs -u -k all && update-grub"
...output...
update-initramfs: Generating /boot/initrd.img-6.8.0-45-generic
Generating grub configuration file ...
done
2) Симптом: система завантажується, але /home порожній або неправильний
Причина: Ви клонували /home у BE, коли мали на меті його розділити, або змінили mountpoints і тепер очікуваний постійний dataset не змонтовано.
Виправлення: Відокремте постійні datasets від BE datasets. Переконайтеся, що /etc/fstab або властивості ZFS монтують спільний dataset послідовно.
cr0x@server:~$ sudo zfs list -o name,mountpoint | egrep 'USERDATA|home'
rpool/USERDATA /home
3) Симптом: GRUB показує лише один запис; неможливо вибрати старіший BE
Причина: Генерація конфігурації GRUB не перераховує BE, або kernels/initrds відсутні/неузгоджені для кожного BE.
Виправлення: Прийміть послідовну стратегію розміщення ядер і інструмент/процес BE, що інтегрується з вашим завантажувачем. Як мінімум, перегенеруйте GRUB з BE, що має правильні скрипти ввімкнені.
cr0x@server:~$ sudo grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
done
4) Симптом: “dataset is busy” під час знищення старого BE
Причина: Ви намагаєтесь знищити поточний змонтований root, або якийсь процес має монтування всередині BE, змонтоване деінде.
Виправлення: Перевірте mountи й відмонтуйте BE mountpoint, який ви використовували для chroot. Знищуйте клон перед snapshot-джерелом.
cr0x@server:~$ mount | grep be-upg
rpool/ROOT/ubuntu_1a2b3c-upg on /mnt/be-upg type zfs (rw,xattr,noacl)
cr0x@server:~$ sudo umount /mnt/be-upg
5) Симптом: відкат «працює», але сервіси поводяться непослідовно
Причина: Ви ділили змінний стан (наприклад, /var/lib, образи контейнерів або бази даних) між BE, не усвідомлюючи цього. Старий userspace бачить новий стан.
Виправлення: Свідомо вирішіть, що ділиться між BE. Або тримайте стан у виділених datasets з урахуванням сумісності, або знімайте/клонуйте стан разом з BE для ризикованих оновлень.
6) Симптом: оновлений BE споживає набагато більше простору, ніж очікувалось
Причина: Ви тримали довгоживучий snapshot, який утримує багато звільнених блоків; або ви оновили великі пакети і кеші, що сильно відрізняються.
Виправлення: Обрізайте старі snapshots/BEs; перемістіть кеші в окремі datasets з агресивною політикою очищення; уникайте зберігання «pre-upgrade» snapshots місяцями.
cr0x@server:~$ sudo zfs list -t snapshot -o name,used | sort -h | tail -5
rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52 9.8G
Жарт №2: Найшвидший спосіб вивчити завантажувачі — зламати один в п’ятницю: раптом у вас буде весь вікенд, щоб вчитись.
Три корпоративні історії з практики
Міні-історія 1: Інцидент через хибне припущення
Компанія середнього розміру використовувала ZFS для «даних», але не для ОС. Потім команда зробила новий аналітичний шлюз, який працював з root-on-ZFS,
бо інженер любив snapshots і вже обпікся на поганих оновленнях. Він відправив це в продакшн з одним ключовим припущенням:
«Якщо GRUB читає ZFS, відкат тривіальний».
Перше велике оновлення ядра пішло не так. Не катастрофа — але достатньо, щоб змінити порядок ініціалізації драйверів під час завантаження,
що змістило виявлення пристроїв. Пул інколи імпортувався, інколи ні, залежно від того, як швидко піднімався NVMe. Вони спробували «відкотитися», обравши старий запис BE.
Машина все одно потрапляла в initramfs. Та сама ознака. Та сама паніка.
Хибне припущення полягало в тому, що BE — єдина рухома частина. Це було не так. Initramfs було побудовано всередині оновленого BE і зберігалося
на спільному /boot розділі, який використовували всі BE. Коли вони завантажували старий BE, вони все одно підвантажували новий initramfs.
Отже «відкат» не був відкатом; це був косплей.
Виправлення не було складним, але вимагало визнати реальну архітектуру. Вони змінили процес так, щоб у кожного BE були свої артефакти kernel+initramfs,
що їм відповідали, і додали просту після-оновлення перевірку: «чи має цей BE те ядро/initramfs, яке ми очікуємо, і чи вказує запис завантаження на них?»
Після цього відкат почав робити саме те, що на етикетці.
Міні-історія 2: Оптимізація, що обернулась проти
Інша організація мала парк робочих станцій розробників з root-on-ZFS і BE. Команда флоту захотіла пришвидшити оновлення,
тож вони агресивно налаштували ZFS. Змінили компресію, recordsize, вимкнули atime (добре) та експериментували з кешуванням і trim-налаштуваннями. Мета — прискорити операції оновлення й компіляції.
Наслідки не були миттєвими. Вони проявилися поступово: машини почали «випадково» закінчуватися з простором, здебільшого після великих оновлень.
Люди звинувачували логи, браузери та системи збірки. Сховище виглядало наче зачароване. Деякі машини мали вільного місця одного дня і були повні наступного,
без очевидних нових файлів.
Причина була в політиці, а не в багу. Вони тримали багато BE та snapshots «для безпеки», але не планували простір для дивергенції. Після оновлень
старі snapshots утримували пре-апгрейд блоки. Оптимізації погіршили змінність, бо більше контенту переписувалось під час оновлень, і вони також розміщували кеші
в datasets, що випадково стали частиною BE-клону.
Урок: «зберігати все назавжди» — не стратегія стійкості; це повільне виведення з ладу. Вони впровадили політику збереження:
тримати останні N успішних BE, один місячний «золотий» і видаляти решту. Також вони перемістили високочастотні кеші поза дерево BE.
Продуктивність і передбачуваність покращилися.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Команда з фінансів мала кілька критичних Linux-боксів на root-on-ZFS. Нічого гламурного: пара внутрішніх сервісів,
кілька баз даних і стандартні моніторингові агенти. SRE-лідер наполягав на нудній практиці:
перед будь-яким оновленням створити новий BE; після оновлення вимагати двох чистих перезавантажень і чек-лист сервісів перед видаленням старого BE.
Якось оновлення внесло тонку регресію в мережевий драйвер. Воно не блокувало завантаження. Воно не вбивало машину.
Але спричиняло переривчасту втрату пакетів під навантаженням — тип багу, що підриває довіру до моніторингу. Моніторинг стрибнув.
Сервіси «здебільшого були в порядку», поки іноді ні.
Команда не дебажила в продакшні. Вони відкотилися до попереднього BE, стабілізувалися, а потім відтворили поведінку в оновленому BE
під час робочого часу. Оскільки оновлений BE все ще існував, вони могли завантажитись у нього навмисно і тестувати, замість намагатися відновити стан, що був перезаписаний.
Вплив інциденту залишився малим, здебільшого завдяки тому, що відкат був рутинною дією, а не останнім засобом. Нудне правило — дві чисті перезавантаження і чек-лист —
дало їм готовий відомо робочий BE, коли з’явився регрес. Жодної героїки. Жодної півночної археології в /var/log.
Контрольні списки / покроковий план
План A: Впровадити boot environments на одній машині (робоча станція або сервер)
- Підтвердити root-on-ZFS та структуру datasets. Переконайтеся, що
findmnt /показує dataset підrpool/ROOT. - Визначити, що має бути спільним. Зазвичай:
/home, можливо/var/libдля вибраних сервісів, але будьте обережні. - Визначити механізм вибору завантаження. Pool
bootfs, записи меню GRUB або інструмент BE, що інтегрується з вашим дистрою. - Прогнати workflow без змін пакетів. Створіть snapshot, клон, змонтуйте його, chroot, потім вийдіть і знищіть його.
- Виконати реальне оновлення у клоні. Оновлення ядра — ключове; включіть його.
- Перезавантажитись і валідовати. Перевірте активний dataset. Проганіть сервісні перевірки.
- Практикувати відкат. Не чекайте на інцидент, щоб дізнатися, що ваш відкат теоретичний.
- Встановити політику збереження. Тримайте невелику кількість BE; видаляйте решту за графіком.
План B: Оперціоналізувати це для невеликого парку
- Стандартизувати іменування. Людям доведеться читати це о 3 ранку. Використовуйте назви на кшталт
prod-2025w52-kernel, а не «clone1». - Визначити політику «спільних datasets». Документуйте, які mountpoints знаходяться в дереві BE, а які — постійні.
- Автоматизувати створення й очищення. BE без політики збереження перетворюються на витік простору з бейджиком.
- Зробити відкат частиною інцидент-реакції. Відкатати спочатку, коли загрожує доступності; відлагоджувати пізніше в контрольованому оточенні.
- Тестувати оновлення завантажувача. Дрейф флоту часто виявляється тут: різні версії GRUB, різні EFI-розмітки, різна поведінка.
- Моніторити простір пулу і зростання snapshot-блоку. Попередження за
zfs list -t snapshotused growth і про низький вільний простір.
План C: Вправа з відновлення (коли ви вже щось поламали)
- У меню завантаження спробуйте обрати попередній BE (якщо доступний). Якщо це працює — стабілізуйте й діагностуйте звідти.
- Якщо ви потрапили в initramfs, спробуйте імпортувати пул через
zpool import -Nі переглянути datasets черезzfs list. - Ручно змонтуйте потрібний root dataset лише для читання, щоб переглянути логи й конфігурації.
- Завантажте rescue-середовище з підтримкою ZFS, якщо потрібно, потім вкажіть
bootfsна відомо робочий dataset і перебудуйте initramfs/GRUB.
Питання та відповіді (FAQ)
1) Чи завантажувальні середовища ZFS — це те саме, що ZFS snapshots?
Ні. Snapshot — це точка в часі для читання. Завантажувальне середовище — це завантажувальний корінь ОС, зазвичай clone snapshot-а плюс інтеграція з завантажувачем.
Snapshots — інгредієнти; BE — це страва.
2) Чи замінюють BE резервні копії?
Абсолютно ні. BE локальні, на тому самому пулі, підпадають під ті ж ризики обладнання. Вони для відкату оновлень, а не для відновлення після катастроф.
3) Скільки завантажувальних середовищ слід тримати?
Тримайте невелику кількість відомо робочих: зазвичай 2–5. Більше без політики збереження — витік простору, який ви виявите під час наступного оновлення.
4) Що має бути спільним між BE?
Зазвичай /home. Для серверів дані станкових сервісів повинні жити у виділених datasets поза деревом BE.
Ділити /var повністю заманливо, але часто помилково; розділяйте його свідомо.
5) Чи можна використовувати BE, якщо моє ядро і initramfs живуть на EFI-парті?
Так, але будьте обережні: якщо всі BE ділять ті самі файли kernel/initramfs, ваш відкат може не відкотити ранній етап завантаження.
Узгодьте артефакти завантаження з BE або прийміть, що відкат буде «лише userspace», що слабше.
6) Який мінімально життєздатний workflow BE на Linux?
Snapshot поточного кореня, клонувати його, chroot у клон для оновлення, переконатися, що завантажувач/initramfs співпадають, перезавантажитися в клон.
Тримайте старий кореневий dataset недоторканим, поки не довіритеся новому.
7) Що найчастіше ламає BE?
Невідповідні властивості монтажу (mountpoint/canmount), записи завантажувача, що вказують на неправильний dataset, та initramfs, що не може імпортувати пул
(модулі, hostid, ключі шифрування, виявлення пристроїв).
8) Як я дізнаюсь, що snapshots утримують занадто багато простору?
Перевірте USED для snapshots. Якщо старі snapshots виросли великі, вони прикріплюють старі блоки, які інакше були б звільнені.
cr0x@server:~$ sudo zfs list -t snapshot -o name,creation,used | sort -k3 -h | tail -10
rpool/ROOT/ubuntu_1a2b3c@pre-upgrade-2025w52 Tue Dec 24 10:01 9.8G
9) Чи root-on-ZFS занадто ризиковано для продакшна?
Це не «занадто ризиковано». Це «ви маєте трактувати ранній етап завантаження як частину системи». Якщо ви не можете тестувати оновлення завантажувача/initramfs і практикувати відкат,
то так — це ризиковано, бо ви самі цього допустили.
10) Який найшвидший метод відкату, коли «я впав»?
Якщо в меню завантаження є записи BE, оберіть останній відомо робочий. Якщо ваша конфігурація використовує bootfs, завантажте rescue-середовище і вкажіть bootfs назад
на попередній dataset, потім перезавантажтесь.
Наступні кроки, які ви реально можете зробити цього тижня
Якщо ви працюєте на Linux і оновлюєте системи, вам потрібна історія відкату, що не включає живий USB і жаління. Завантажувальні середовища ZFS — це ця історія,
за умови, що ви поважаєте ланцюжок завантаження: властивості dataset, initramfs і записи завантажувача мають узгоджено визначати, що таке «root».
Зробіть це далі:
- На одній машині ідентифікуйте свій root dataset і створіть BE клон перед наступним оновленням.
- Практикуйте відкат навмисно поки нічого не горить. Підтвердьте, що можете завантажитись в обидві сторони.
- Напишіть правило збереження і дотримуйтеся його. Простір не безкінечний, як і ваше терпіння.
- Стандартизируйте розмітку, де персистентний стан живе поза
rpool/ROOT.
Коли наступне оновлення вас вкусить — а воно вкусить — у вас буде спокійний варіант: перезавантажитись у вчорашній стан. Потім налагоджуйте як професіонал, вдень, з теплою кавою.