ZFS canmount: налаштування, що запобігає сюрпризам при завантаженні

Було корисно?

Збої зі сховищем під час завантаження рідко оголошують себе феєрверком. Вони з’являються тихіше: система «завантажується», але не знаходить /var, вхід працює, але сервіси ні, хост монтує датасет вчорашньої копії, бо він виявився більш охочим, ніж сьогоднішній. Якщо ви довго працюєте з ZFS, рано чи пізно ви зустрінете властивість, яка вирішує, чи ваші датасети ввічливо чекатимуть своєї черги — або рвонуть у таблицю монтування в найневідповідніший момент.

Ця властивість — canmount. Вона невелика, її легко ігнорувати, але вона врятувала більше продуктивних систем, ніж будь-яка з яскравіших можливостей ZFS. Якщо у вас коли-небудь датасет «таємниче» змонтувався поверх існуючої директорії або середовище завантаження піднялося з неправильним коренем, зазвичай тут закінчується (або починається) історія. Залежно від того, скільки кави залишилось.

Що насправді контролює canmount

canmount відповідає на одне питання: «Чи може цей датасет бути змонтований?» Вона не вибирає точку монтування; це робить mountpoint. Вона не вирішує, чи система спробує змонтувати його під час завантаження; це суміш canmount, mountpoint і логіки служби монтування ZFS у вашій ОС. Але коли щось іде не так, наявність або відсутність canmount часто визначає різницю між чистим завантаженням і пошуком по /mnt.

Ось чому ця властивість має значення в практиці:

  • Датасети ZFS за замовчуванням доступні для монтування. Це корисно — поки не стає пострілом у ногу.
  • ZFS охоче монтує датасети в порядку залежностей, але все ще може зробити те, чого ви не очікували — особливо якщо ви клонували, відкотили, перейменували або імпортували пул у новому контексті.
  • Макети «boot environment» (поширені в illumos, FreeBSD та деяких Linux) покладаються на canmount=noauto у певних місцях, щоб запобігти самосмонтованню неправильного кореня.

Одна операційна істина: ZFS не знає вашого вікна змін. Якщо ви залишите датасет придатним для монтування з mountpoint, який вказує на критичний шлях, ZFS вважатиме це цілком нормальною вимогою. Це як дати собаці стейк і очікувати, що вона почекає до обіду.

Цікавi факти та історичний контекст

Декілька коротких контекстних моментів, які роблять canmount не випадковим перемикачем, а скоріше рубцем від попередніх помилок:

  1. ZFS проектувався з думкою, що «файлові системи дешеві». Тому датасети множаться — під сервіс, під орендаря, під застосунок — і поведінка монтування потребує запобіжних заходів.
  2. Ранні розгортання ZFS жорстоко вивчили, що автоматичне монтування чудове, поки ви не додаєте клонування, снапшоти і альтернативні корені (altroot) під час відновлення.
  3. Концепція «boot environments» (кілька коренів в одному пулі) популяризувала практику встановлювати корені як canmount=noauto, щоб система могла обрати, який стане /.
  4. Наслідування mountpoint — основна зручність ZFS, але воно стає небезпечним, коли дочірній датасет успадковує mountpoint, який ви не планували бачити активним на цьому хості.
  5. Логіка монтування ZFS історично відрізнялася між платформами (SMF у Solaris/illumos, rc у FreeBSD, systemd у Linux), але canmount залишається портативним перемикачем «не монтуй, якщо я не сказав».
  6. Оскільки ZFS зберігає властивості на диску, імпортований на іншу машину пул приносить свої наміри монтування. Це подарунок для консистентності і прокляття для сюрпризів.
  7. Клони і промотовані датасети можуть зберегти властивості mountpoint, які мали сенс у їхньому початковому середовищі, не в тому, куди ви їх перемістили.
  8. «Затінені точки монтування» (dataset змонтовано поверх директорії, яка вже містить файли) постійно були джерелом тихих відмов, бо система здається здоровою, але читає невірні файли.

Практична модель: право на монтування vs місце монтування

Якщо ви не візьмете з цієї статті нічого іншого — запам’ятайте це: монтування ZFS — це рішення у двох частинах.

1) Право: «Чи можу я змонтувати цей датасет?»

Це canmount. Якщо canmount=off, відповідь «ні, ніколи». Якщо canmount=noauto, відповідь «не автоматично». Якщо canmount=on, відповідь «так».

2) Місце: «Куди він змонтується?»

Це mountpoint. Це може бути реальний шлях, наприклад /var, може бути успадкований, може бути legacy (це означає «монтування обробляє /etc/fstab або еквівалент»), або може бути none.

У реальному житті катастрофи трапляються, коли право і місце випадково збігаються з чимось важливим. Наприклад:

  • Клон кореневого датасету придатний для монтування (canmount=on) і має mountpoint /. Це не файловий ресурс — це спроба перевороту.
  • Датасет, призначений для відновлення, успадковує mountpoint=/var і придатний для монтування. Раптом ваш «відновлювальний» датасет стає вашим продуктивним /var.

Жарт №1: ZFS не «краде» ваші точки монтування. Воно просто бере їх у довгу прогулянку і повертається, одягнувши ваші речі.

Три значення: on, off, noauto

canmount зазвичай приймає такі значення:

canmount=on

Датасет може бути змонтований і зазвичай буде змонтований автоматично, якщо має валідний mountpoint і служба монтування ZFS у вашій ОС запущена. Це те, що потрібно для звичайних датасетів, наприклад pool/home або pool/var.

canmount=off

Датасет не може бути змонтований взагалі. Ви все одно зможете робити снапшоти, реплікацію і доступ через інші механізми (наприклад, монтування снапшотів в іншому місці), але ZFS не змонтує його як файлову систему. Це ідеально для «контейнерних» датасетів, які існують лише щоб містити дочірні або нести успадковані властивості.

Типовий шаблон: верхній датасет, який існує лише для утримання дітей і встановлення спільних властивостей:

  • pool/ROOT з canmount=off, діти — це boot environments
  • pool/containers з canmount=off, діти — датасети для кожного контейнера

canmount=noauto

Датасет може бути змонтований, але не буде змонтований автоматично. Це налаштування для boot-environment «не монтуй себе сам». Це не «off», це «не підходь, поки я не покличу».

Особливо корисно для датасетів, які повинні монтуватись лише в конкретному контексті:

  • Boot environment, який завантажувач обирає як root
  • Датасет, який монтують тимчасово для судової експертизи
  • Датасет для chroot-обслуговування, де автоматичне монтування може конфліктувати з живим коренем

Жарт №2: canmount=noauto — це «я не асоціальний, я просто не йду на вашу зустріч» налаштування.

Звідки беруться сюрпризи під час завантаження

Більшість сюрпризів ZFS під час завантаження — це не «ZFS зламався». Це «ZFS зробив те, що йому наказали, а ми забули, що наказали минулого кварталу». Повторювані причини:

Сюрприз №1: Датасет змонтований поверх директорії, яка вже містить критичні файли

Це затемнення точок монтування. Підлягаюча директорія все ще існує, але ви не бачите її, бо зверху змонтована файлова система. Якщо в тій директорії були конфігураційні файли (наприклад під /etc у chroot-сценарії або /var/lib для бази даних), ви можете завантажитись і працювати — але з іншим набором файлів, ніж очікували.

Сюрприз №2: Імпортовані пули приносять свої mountpoint

Властивості ZFS зберігаються з пулом. Якщо ви імпортуєте пул з іншого хоста, він може прийти з mountpoint як /data, /var або, що ще страшніше, /. На системі з автоматичним монтуванням це може перетворити «я просто підключив диск» у «чому річ — SSH зупинився?»

Сюрприз №3: Boot environments множаться і один змонтується, коли не повинен

Чиста схема boot environment очікує, що лише вибраний BE змонтується як root. Якщо старі BE придатні для монтування і мають значущі mountpoint, вони також можуть змонтуватись, іноді в тій же дереві директорій, викликаючи конфлікти і дивні часткові відображення файлової системи.

Сюрприз №4: Ви «оптимізували» монтування і випадково змінили порядок

Порядок монтування важливий, коли сервіси очікують, що директорії будуть існувати перед їхнім стартом. Пізнє монтування датасету може призвести до того, що сервіс створить директорії на базовій файловій системі, а потім ZFS змонтує зверху — приховавши ці новостворені директорії. Сервіс продовжує роботу, але тепер пише в інше місце, ніж воно створювалося під час старту. Так виникає «мій лог зник», який насправді означає «мій лог під mountpoint, а не в ньому».

Три історії з корпоративного світу

Міні-історія 1: Інцидент через неправильне припущення

У середньому підприємстві зі стандартною ZFS-on-Linux конфігурацією команда сховищ підтримувала «утилітарний пул» для бекапів і одноразових відновлень. Зазвичай пул імпортували лише на виділеному хості для відновлення. Одного п’ятничного вечора хтось підключив полиці з бекапами до продуктивного сервера додатків для швидшого відновлення — тимчасово, сказали вони, з тим специфічним упевненістю, яка з’являється безпосередньо перед тим, як тикет стає інцидентом.

Припущення було простим: «Імпорт пулу нічого не змінить, поки ми нічого не змонтуємо». Але датасети пулу мали mountpoint як /var/lib/postgresql і /srv з попереднього життя. Ці датасети були ще canmount=on, бо чому б ні? Продуктивний хост імпортував пул, systemd-зайнявся монтуванням, і раптом на сервері додатку з’явились нові /srv і /var/lib/postgresql — прямо поверх старих.

Додаток не впав одразу. Це було найжорсткіше. Він продовжував працювати, читаючи частину файлів з нових змонтованих датасетів, залишаючи відкриті файлові дескриптори до старих. Потім база перезапустилась під час ротації логів, і вона піднялась, дивлячись на «не ту» директорію даних. Ззовні це виглядало як спонтанна втрата даних. Всередині — це були два датасети, що боролись за один і той же неймспейс.

Виправлення було нудним: експортувати пул, встановити топ-рівневі датасети як canmount=off або mountpoint=none, і повторно імпортувати з altroot під час відновлення. Урок запам’ятався: імпорт — це не пасивна дія при увімкненому автозмонтуванні; це подія розгортання.

Міні-історія 2: Оптимізація, що обернулась проти

Велика компанія стандартизувалася на датасетах для кожного сервісу: pool/var, pool/var/log, pool/var/lib тощо. Це робило квоти і снапшоти акуратними. Але це також зробило завантаження залежним від довгого ланцюга монтувань. Під час оптимізації продуктивності хтось спробував «спростити», згорнувши mountpoint і покладаючись на наслідування, щоб зменшити розкиданість властивостей.

Зміна виглядала чисто на рев’ю: встановити mountpoint=/var на батьківському датасеті, дозволити кільком дітям успадковувати. Але один дочірній датасет раніше був навмисно встановлений як canmount=noauto, бо використовувався для поетапної міграції: він містив підготовлені дані і монтувався лише під час cutover. В новій схемі наслідування цей дитина почала успадковувати mountpoint, зберігаючи своє значення canmount — і хтось пізніше переключив його на canmount=on, щоб «щось потестувати».

Через тижні під час перезавантаження підготовчий датасет змонтувався автоматично і затінів директорію під /var/lib, яку сервіс використовував для стану. Сервіс стартував чисто, створив новий стан на базовій файловій системі до завершення монтування (час має значення), потім ZFS змонтувався зверху. Стан «зник». Сервіс перейшов у шлях ініціалізації й почав повторно заповнювати дані з upstream, що спричинило сплески навантаження і каскад тротлінгу. Це не була одна відмова; це був самонавмисний тест розподіленої системи.

Постмортем був прямолінійний: оптимізація майже не заощадила операційних зусиль, але зняла критичний запобіжник. Ремедіація — маркувати «контейнерні» датасети як canmount=off політикою і трактувати зміни canmount як високоризикові, що вимагають тесту перезавантаження в stage. Помилка не була в складності ZFS — вона була в забутті, що завантаження — найчутливіший до часу робочий процес.

Міні-історія 3: Нудна, але правильна практика, що врятувала день

Ще одна організація запускала ZFS пули на флоті збірних серверів. Ці хости постійно перевстановлювались, але пули іноді переміщувалися між машинами при перепризначенні обладнання. Команда SRE мала практику, яку ніхто не святкував: кожен імпорт пулу для не-кореневих пулів використовував altroot, а кожний топ-рівневий датасет для організації піддатасетів мав canmount=off і mountpoint=none.

Одного дня хост впав, і його пул було підключено до іншого сервера, щоб відновити артефакти збірки. Пул містив датасети з mountpoint, що на оригінальній машині вказували під /srv/build. На хості для відновлення /srv вже існував і використовувався для чогось іншого. Без запобіжних заходів це могло стати класичним «чому мій сервіс переналаштувався» інцидентом.

Натомість імпорт виглядав так: zpool import -o altroot=/mnt/recovery poolname. Всі mountpoint були переміщені під /mnt/recovery. Датасети, які не мали монтуватись, залишились непридатними. Команда змонтувала лише те, що було потрібно, скопіювала артефакти і експортувала пул. Жоден сервіс не моргнув.

Ця практика ніколи не потрапила в слайд-дек. Але вона перетворила дію відновлення на неназваний інцидент. У виробничих операціях «нудне» — не образа; це KPI.

Практичні завдання (команди + інтерпретація)

Ось завдання, які ви можете виконати сьогодні на системі ZFS. Команди написані для типової CLI-логіки OpenZFS. Підлаштуйте назви пулів/датасетів під своє середовище.

Задача 1: Переглянути датасети з поведінкою монтування в одному вигляді

cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank
NAME                 CANMOUNT  MOUNTPOINT         MOUNTED
tank                 on        /tank             yes
tank/ROOT            off       none              no
tank/ROOT/default    noauto    /                 yes
tank/var             on        /var              yes
tank/var/log         on        /var/log          yes

Інтерпретація: Шукайте датасети з canmount=on (або випадково noauto) з mountpoint, що конфліктують з критичними шляхами. tank/ROOT зі значенням off і none — це здоровий шаблон «контейнерного датасету».

Задача 2: Знайти «придатні для монтування» датасети, які наразі не змонтовані

cr0x@server:~$ zfs list -H -o name,canmount,mountpoint,mounted -r tank | awk '$2=="on" && $4=="no" {print}'
tank/tmp	on	/tmp	no

Інтерпретація: Придатний, але незмонтований датасет може бути нормальним (можливо новий або навмисно розмонтований), але це також потенційна бомба уповільненого часу, якщо його mountpoint чутливий і служба монтування змінить поведінку.

Задача 3: Виявити датасети з mountpoint=legacy

cr0x@server:~$ zfs list -H -o name,mountpoint -r tank | awk '$2=="legacy" {print}'
tank/oldroot	legacy

Інтерпретація: legacy означає, що ваша ОС покладеться на /etc/fstab (або еквівалент) для монтування. Мішання legacy-монтувань з автозмонтуванням ZFS іноді необхідне, але воно збільшує кількість шляхів завантаження, про які потрібно думати.

Задача 4: Перевірити наслідування властивостей (інструмент «чому це сталося»)

cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank/var
NAME         PROPERTY   VALUE       SOURCE
tank/var     canmount   on          local
tank/var     mountpoint /var        local
tank/var/log canmount   on          inherited from tank/var
tank/var/log mountpoint /var/log    local

Інтерпретація: Поле source каже, чи маєте ви справу з локальним перевизначенням або з наслідуванням. Більшість «він змонтувався десь дивно» таємниць вирішуються, коли помічаєш, що дитина успадкувала властивість, яку ви вважали локальною в іншому місці.

Задача 5: Зробити контейнерний датасет немонтуваним (безпечний дефолт)

cr0x@server:~$ sudo zfs set canmount=off tank/containers
cr0x@server:~$ sudo zfs set mountpoint=none tank/containers
cr0x@server:~$ zfs get -o name,property,value tank/containers canmount,mountpoint
NAME            PROPERTY   VALUE  SOURCE
tank/containers canmount   off    local
tank/containers mountpoint none   local

Інтерпретація: Це запобігає випадковому монтуванню батьківського датасету, дозволяючи дітям, наприклад tank/containers/app1, монтуватись там, де потрібно.

Задача 6: Позначити boot environment як «монтувати лише коли вибрано»

cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/be-2025q4
cr0x@server:~$ zfs get -o name,property,value tank/ROOT/be-2025q4 canmount
NAME               PROPERTY  VALUE   SOURCE
tank/ROOT/be-2025q4 canmount  noauto  local

Інтерпретація: Boot environment зазвичай не повинен самостійно монтуватися як звичайна файлова система після імпорту; він має змонтуватися як root лише коли обраний процесом завантаження. noauto — це запобіжник.

Задача 7: Тимчасово змонтувати датасет з noauto для інспекції

cr0x@server:~$ sudo zfs mount tank/ROOT/be-2025q4
cr0x@server:~$ zfs list -o name,mountpoint,mounted tank/ROOT/be-2025q4
NAME               MOUNTPOINT  MOUNTED
tank/ROOT/be-2025q4 /          yes

Інтерпретація: Якщо датасет має mountpoint=/, монтування його на живій системі небезпечне, якщо ви не використовуєте альтернативний корінь (див. наступне завдання). На практиці для BE-дataset зазвичай змонтовують під альтернативним коренем, а не на /.

Задача 8: Безпечно імпортувати пул з використанням altroot (краща практика відновлення)

cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup
cr0x@server:~$ zfs mount | head
backup/ROOT/be-old  /mnt/recovery/     

Інтерпретація: altroot додає безпечний префікс до mountpoint, тому датасет з mountpoint /var стане /mnt/recovery/var. Це дозволяє зберегти роботу відновлення від втручання у продуктивні шляхи.

Задача 9: Розмонтувати датасет, який змонтувався не там, де треба

cr0x@server:~$ zfs list -o name,mountpoint,mounted | grep '/var/lib/postgresql'
backup/pgdata  /var/lib/postgresql  yes
cr0x@server:~$ sudo zfs unmount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME         MOUNTPOINT            MOUNTED
backup/pgdata /var/lib/postgresql  no

Інтерпретація: Розмонтування зупиняє негайну проблему, але не запобігає повторному монтуванню після перезавантаження. Для цього потрібно виправити canmount і/або mountpoint.

Задача 10: Запобігти повторному монтуванню, встановивши canmount=off

cr0x@server:~$ sudo zfs set canmount=off backup/pgdata
cr0x@server:~$ zfs get -o name,property,value backup/pgdata canmount
NAME         PROPERTY  VALUE  SOURCE
backup/pgdata canmount  off    local

Інтерпретація: Це робить датасет непридатним для монтування. Якщо вам все ще потрібно доступатися до вмісту, можна тимчасово змінити його назад або використати інший підхід для відновлення, але за замовчуванням він буде безпечним.

Задача 11: Виправити небезпечний mountpoint, не змінюючи право на монтування

cr0x@server:~$ sudo zfs set mountpoint=/mnt/pgdata-staging backup/pgdata
cr0x@server:~$ sudo zfs set canmount=on backup/pgdata
cr0x@server:~$ sudo zfs mount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME         MOUNTPOINT            MOUNTED
backup/pgdata /mnt/pgdata-staging  yes

Інтерпретація: Іноді ви хочете, щоб датасет був змонтований, але не поверх продуктивної директорії. Пересування mountpoint зазвичай безпечніше, ніж покладатися на «ніхто не імпортує цей пул тут».

Задача 12: Виявити ризик затінення (датасети змонтовані під критичними деревами)

cr0x@server:~$ zfs list -H -o name,mountpoint,canmount -r tank | awk '$2 ~ "^/(etc|var|usr|srv|home)($|/)" {print}'
tank/var	/var	on
tank/var/log	/var/log	on
tank/home	/home	on

Інтерпретація: Це не обов’язково «погано». Це інвентар датасетів під критичними деревами. Будь-який несподіваний запис тут вартий розслідування перед наступним перезавантаженням.

Задача 13: Перевірити, що ZFS вважає змонтованим зараз

cr0x@server:~$ zfs mount | sed -n '1,8p'
tank                           /tank
tank/var                       /var
tank/var/log                   /var/log
tank/home                      /home

Інтерпретація: Це погляд ZFS, а не обов’язково повна таблиця монтувань системи. Проте це авторитетний список змонтованих ZFS-файлових систем.

Задача 14: Зіставити з таблицею монтувань ядра

cr0x@server:~$ mount | grep ' type zfs ' | head
tank/var on /var type zfs (rw,xattr,noacl)
tank/var/log on /var/log type zfs (rw,xattr,noacl)

Інтерпретація: Якщо zfs mount і mount не погоджуються, ви перебуваєте в зоні edge-case: можливо, невдале монтування, можливо legacy-монтування, можливо проблема неймспейсу в контейнерах.

Задача 15: Знайти датасети, які спробують змонтуватись, але мають mountpoint=none

cr0x@server:~$ zfs list -H -o name,canmount,mountpoint -r tank | awk '$2=="on" && $3=="none" {print}'
tank/ROOT	on	none

Інтерпретація: Зазвичай це запах конфігурації. Якщо датасет може монтуватись, але йому нікуди йти, ймовірно, це контейнерний датасет, який повинен бути canmount=off.

Задача 16: Аудит недавніх змін, порівнюючи локальні і успадковані властивості

cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank | awk '$4=="local" {print}' | head
tank            mountpoint  /tank   local
tank/ROOT       canmount    off     local
tank/ROOT       mountpoint  none    local
tank/var        mountpoint  /var    local

Інтерпретація: «Local» властивості — це місця, де звичайно вводяться сюрпризи. Це дає короткий список місць, куди торкалися люди.

Швидкий план діагностики

Це робочий процес «03:12 і хост не повернувся чисто». Він передбачає, що ви підозрюєте проблеми монтування ZFS. Мета — швидко знайти вузьке місце: це проблема імпорту, право на монтування, конфлікт mountpoint чи порядок запуску сервісів?

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

cr0x@server:~$ sudo zpool status
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 00:10:12 with 0 errors on Sun Dec 22 02:00:03 2025
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          sda3      ONLINE       0     0     0

Інтерпретація: Якщо пул не імпортований або деградований/фолтен, припиніть шукати точки монтування. Спочатку виправте імпорт/пристрої. Сюрпризи монтування — це похідні від «пул придатний».

Друге: шукайте очевидні колізії mountpoint і пастки права на монтування

cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank | egrep ' /(var|etc|usr|srv|home|)($|/)'

Інтерпретація: Ви скануєте на предмет несподіваних датасетів з критичними mountpoint. Зверніть увагу на будь-що, що монтується на /, /var або /usr, якщо ви цього не проектували явно.

Третє: перевірте, що змонтовано, а чого не вистачає

cr0x@server:~$ zfs mount
cr0x@server:~$ mount | grep ' type zfs '

Інтерпретація: Якщо очікуваних датасетів немає, перевірте canmount і mountpoint. Якщо присутні несподівані датасети, перевірте імпортовані чужі пули або помилково придатні boot environments.

Четверте: проінспектуйте наслідування і джерело властивостей для дивного датасету

cr0x@server:~$ zfs get -o name,property,value,source canmount,mountpoint tank/suspect
NAME         PROPERTY   VALUE  SOURCE
tank/suspect canmount   on     inherited from tank
tank/suspect mountpoint /var   local

Інтерпретація: Локальний mountpoint з успадкованою придатністю — класичний шаблон сюрпризів: хтось змінив, куди монтувати, але не вирішив, чи має бути датасет придатним для монтування.

П’яте: якщо це імпорт для відновлення, зупиніться і використайте altroot

Якщо ви імпортували чужий пул і він почав монтуватись у продуктивні шляхи, зазвичай виправлення — експортувати і повторно імпортувати з альтернативним коренем.

cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup

Інтерпретація: Це перетворює «сюрпризне монтування» в «всі монтування відфендчені під /mnt/recovery», що є правильною позицією під час інцидент-реакції.

Поширені помилки: симптоми та виправлення

Помилка 1: Залишати контейнерні датасети як canmount=on

Симптом: Батьківський датасет, наприклад tank/ROOT, з’являється змонтованим десь або намагається змонтуватись і генерує збиваючі попередження.

Чому так трапляється: Хтось створив його і ніколи не змінив дефолти. Воно успадкувало mountpoint або mountpoint було встановлено під час експериментів.

Виправлення: Явно зробіть його немонтуваним і дайте йому mountpoint=none.

cr0x@server:~$ sudo zfs set canmount=off tank/ROOT
cr0x@server:~$ sudo zfs set mountpoint=none tank/ROOT

Помилка 2: Плутати canmount=noauto з «вимкнено»

Симптом: Датасет несподівано змонтувався після ручного zfs mount -a або автоматизації, або оператор змонтував його, не усвідомлюючи, що це BE/датасет подібний до root.

Чому так трапляється: noauto не забороняє монтування; воно забороняє автоматичне монтування. Люди й скрипти все ще можуть змонтувати його вручну.

Виправлення: Якщо ви насправді ніколи не хочете, щоб він монтувався, використайте canmount=off. Якщо хочете, щоб він монтувався лише в ізольованих контекстах, комбінуйте noauto з імпортами altroot і ретельними інструментами.

cr0x@server:~$ sudo zfs set canmount=off tank/archive/never-mount

Помилка 3: Імпорт чужого пулу без altroot

Симптом: Після імпорту пулу сервіси поводяться дивно, директорії даних «змінюються», або шляхи як /srv раптом містять інший вміст.

Чому так трапляється: Датасети пулу монтуються там, де вони були налаштовані монтуватися, і ваш хост це виконує.

Виправлення: Експортуйте негайно, потім повторно імпортуйте з altroot. За бажанням встановіть canmount=off або mountpoint=none на топ-рівнях перед наступними переміщеннями.

cr0x@server:~$ sudo zpool export foreignpool
cr0x@server:~$ sudo zpool import -o altroot=/mnt/foreign foreignpool

Помилка 4: Встановлення mountpoint=/ на декількох датасетах (хаос BE)

Симптом: Завантаження опускає в emergency shell або кореневий filesystem не той, що ви очікували. Після завантаження бачите декілька BE-подібних датасетів, які ймовірно могли б змонтуватись як root.

Чому так трапляється: Клонування або перейменування BE без оновлення властивостей mount і права на монтування.

Виправлення: Переконайтеся, що лише обраний root змонтовано як / під час завантаження. У багатьох схемах BE не обрані корені повинні бути canmount=noauto. Батьківський ROOT повинен бути off.

cr0x@server:~$ zfs list -o name,canmount,mountpoint -r tank/ROOT
cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/oldbe

Помилка 5: Покладання на «mounted=no» як доказ, що він не змонтується при наступному завантаженні

Симптом: Зараз все виглядає нормально, але після перезавантаження датасет знову монтується і повторює проблему.

Чому так трапляється: mounted — це стан, а не політика. canmount і mountpoint — це політика.

Виправлення: Встановіть політику явно.

cr0x@server:~$ sudo zfs set canmount=off pool/surprise
cr0x@server:~$ sudo zfs set mountpoint=none pool/surprise

Помилка 6: Мішання legacy-монтувань і автозмонтування ZFS без чіткого контракту

Симптом: Деякі датасети монтуються двічі, або монтування періодично не вдається під час завантаження, або ви бачите суперечливий стан між zfs mount і mount.

Чому так трапляється: Ви розділили відповідальність між ZFS і системою монтувань ОС, але ніхто не записав, який датасет управляється де.

Виправлення: Або поверніть ці датасети під управлінням ZFS, або повністю перейдіть на legacy-монтування для них і переконайтеся, що canmount узгоджується з цим вибором. Як мінімум, проаудіть датасети з mountpoint=legacy і тримайте їх навмисними.

Чеклисти / покроковий план

Чеклист A: Проектування ієрархії датасетів, що не здивує при завантаженні

  1. Створюйте контейнерні датасети для групування і наслідування властивостей (compression, atime, recordsize).
  2. Негайно встановлюйте контейнерні датасети як canmount=off і mountpoint=none.
  3. Встановлюйте явні mountpoint тільки для датасетів, які повинні монтуватись у звичайній роботі.
  4. Використовуйте canmount=noauto для boot environments або датасетів, що повинні монтуватись лише вручну.
  5. Уникайте неоднозначних mountpoint (особливо /, /var, /usr) якщо це не частина запланованої архітектури.
  6. Задокументуйте одну інваріанту: «Лише ці датасети можуть монтуватись під критичними шляхами», і забезпечте її періодичними аудитами.

Чеклист B: Перед імпортом пулу з іншої системи

  1. Визначте безпечну директорію для стагування (наприклад, /mnt/recovery).
  2. Імпортуйте з altroot.
  3. Перегляньте mountpoint і значення canmount перед тим, як явно щось монтувати.
  4. Якщо потрібно зробити пул портативним, встановіть топ-рівневі датасети як canmount=off і mountpoint=none перед експортом.

Чеклист C: Перед перезавантаженням після змін у сховищі

  1. Запустіть zfs list -o name,canmount,mountpoint,mounted -r pool і перевірте критичні шляхи.
  2. Підтвердіть, що ніякий несподіваний датасет не має mountpoint=/.
  3. Підтвердіть, що контейнерні датасети не придатні для монтування.
  4. Якщо ви змінили наслідування, перевірте з zfs get ... source, щоб знати, що буде поширюватись.
  5. Якщо можливо, зробіть контрольоване перезавантаження в stage. Проблеми з порядком монтування рідко виявляються на працюючій системі.

Питання та відповіді

1) У чому різниця між canmount=off і mountpoint=none?

canmount=off робить датасет непридатним для монтування. mountpoint=none означає «в нього немає місця монтування». У виробництві контейнерні датасети часто отримують обидва: їх не слід монтувати і вони навіть не повинні мати правдоподібну ціль.

2) Якщо я встановлю canmount=noauto, чи може датасет все одно змонтуватись під час завантаження?

Загалом noauto перешкоджає автоматичному монтуванню ZFS. Але boot environments — особливий випадок: процес завантаження може змонтувати noauto датасет як root, якщо він явно обраний. Також адміністратор або скрипт все ще можуть його змонтувати вручну.

3) Чому датасет змонтувався після імпорту пулу? Я не запускав zfs mount.

На багатьох системах імпорт пулу запускає служби монтування ZFS, які монтують придатні датасети з валідними mountpoint. Імпорт — це не «лише підключити сховище»; це «активувати наміри файлової системи пулу». Використовуйте zpool import -o altroot=... для чужих пулів.

4) Чи можу я поставити canmount=off на датасет, що має дітей?

Так. Діти все одно можуть монтуватись нормально. Це краща практика для батьків/контейнерів, наприклад pool/ROOT або pool/containers.

5) Як визначити, чи несподіване монтування викликане наслідуванням?

Використайте zfs get ... source. Якщо canmount або mountpoint показує «inherited from …», то у вас історія про наслідування, а не випадкова поведінка.

6) Чи те саме canmount і readonly=on?

Ні. readonly=on все ще монтує датасет, але в режимі тільки для читання. canmount=off запобігає монтуванню зовсім. У сценаріях відновлення можна використовувати обидва: залишити датасети змонтованими лише для читання або зробити їх непридатними для монтування, поки ви не будете готові.

7) Який найбезпечніший спосіб оглянути BE-датасет з mountpoint=/?

Імпортуйте пул з altroot (або тимчасово змініть його mountpoint і змонтуйте під безпечним директорієм). Головне: не монтуйте «root» датасет на / працюючої системи, якщо вам не подобається жити небезпечно.

8) Чи варто встановити canmount=noauto для всіх датасетів і монтувати їх через systemd/fstab замість цього?

Можна, але ви міняєте одне джерело правди на два. Монтування під управлінням ZFS зазвичай простіше, якщо ви віддаєте їм перевагу. Залишайте noauto для спеціальних випадків: boot environments, стагуючі датасети і робочі процеси обслуговування.

9) Чому я бачу датасети з canmount=on але mounted=no?

Придатність не гарантує, що зараз змонтовано. Монтування могло не вдалось, mountpoint може не існувати, пул могли імпортувати з опцією -N (не монтувати), або служба ОС ще не запустилась. Розглядайте це як підказку, а не вердикт.

Висновок

canmount не гламурна річ. Воно не стискає дані, не пришвидшує читання і не вразить нікого на демо-презентації. Але воно запобігає типам сюрпризів під час завантаження і імпорту, які забирають цілі ранки і залишають відчуття, що файлові системи мають власну волю.

Якщо ви експлуатуєте ZFS у продакшні, зробіть це правилом: контейнерні датасети — canmount=off; спеціально призначені датасети — canmount=noauto; усе інше — спроектовано явно як on з mountpoint, який ви зможете захистити у постмортемі. Найкращі інциденти зі сховищем — це ті, які вам ніколи не доведеться називати.

← Попередня
Рішення IBM щодо ПК, яке створило індустрію клонів
Наступна →
Ubuntu 24.04: «зависання» диска під навантаженням — налаштування таймаутів, що запобігають повним зависанням (випадок №90)

Залишити коментар