ZFS zfs hold: Запобіжна шпилька, що блокує випадкове видалення

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

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

zfs hold — це маленький механізм, який перетворює снапшоти з «ой» на «не сьогодні». Він не шифрує нічого, не переміщує дані й не врятує від усіх можливих відмов. Але він виконує одну задачу надзвичайно добре: робить снапшот незнищуваним, поки утримання свідомо не зняте. У продакшні це різниця між відновлюваним інцидентом і кар’єрним оновленням.

Що zfs hold насправді робить (і чого не робить)

Снапшот ZFS є незмінним, але не незнищуваним. Його можна знищити, і якщо він був єдиним об’єктом, що посиляв посилання на старі блоки, ці блоки стануть вільними для повторного використання. У навантаженому пулі «повторне використання» може означати «назавжди втрачені» досить швидко.

zfs hold прикріплює одне або кілька «утримань» (уявіть: ярлики) до снапшота. Поки існує хоча б одне утримання, снапшот не можна знищити. Ні вам, ні скрипту, ні колезі з хорошими намірами з маскою й дедлайном. Команда знищення завершиться помилкою з посиланням на утримання.

Чого це не робить:

  • Не забороняє снапшоту споживати простір. Насправді воно може утримувати простір довше (це і є суть).
  • Не заважає комусь з достатніми правами й рішучістю знищити весь датасет або пул іншими способами.
  • Не замінює реальну політику зберігання, резервні копії, перевірку реплікації або контроль доступу.

Ось ментальна модель, яку я використовую:

  • Снапшот = заморожений вигляд блоків датасету в певний момент часу.
  • Утримання = наліпка на цьому снапшоті з написом «не видаляти, поки не виконано ці умови».
  • Зняття = зняття наліпки; як тільки всі наліпки знято, видалення знову стає можливим.

Перший жарт (коротко, як ваш бюджет відмов): Утримання — це та єдина «липка» річ у сховищі, якою ви насправді хочете, щоб вона була липкою.

Як працюють утримання під капотом

Утримання реалізовані як метадані на снапшоті. Кожне утримання має ім’я (часто називають тегом) і прив’язане до цього снапшота. Може існувати кілька утримань одночасно — це звично в організаціях, де бекапи, реплікація й юридичне зберігання кожен хочуть мати голос.

Семантика тегів: чому назви важливі

Тег — це не просто декорація. Він стає вашим операційним маркером для аудиту, усунення несправностей і безпечної автоматизації. Якщо ви назвете утримання «keep», ви пошкодуєте, коли перед вами опиниться 40 ТБ закріплених снапшотів і ніхто не згадає, за що було «keep».

Добрий тег включає в себе:

  • Власник/система: replication, backup, legal, migration
  • Область або ціль: to-dr, to-s3-gw, pre-upgrade
  • Опційно ID зміни або тикету: chg12345 (якщо у вас є такі)

Утримання проти властивостей проти «просто не робіть цього»

Так, ви можете обмежити, хто може виконувати zfs destroy. І це варто робити. Але межі привілеїв стираються під тиском: екстрений доступ, break-glass акаунти, автоматизація під root, інженери на чергуванні з підвищеними правами. Утримання додає ще один свідомий крок: «Спочатку треба зняти утримання». Саме цей додатковий крок рятує від багатьох катастроф.

Утримання і реплікація

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

Що відбувається, коли ви намагаєтеся знищити закріплений снапшот

ZFS не сперечається — вона відмовляє. Знищення не виконується й повертає помилку з посиланням на утримання. Це добре. Але це також часте джерело плутанини, коли хтось очікує, що його чистильний скрипт відразу звільнить простір.

Другий жарт: zfs hold — це як повісити табличку «Не вимикати» на кабелі живлення сервера — дратує, поки не стане в пригоді.

Цікаві факти та контекст

Кілька коротких, конкретних пунктів контексту, які допомагають зрозуміти, чому утримання існують і чому їх використовують саме так:

  1. Снапшоти ZFS — не копії. Це набір покажчиків на блоки; «старі дані» залишаються посиланнями, поки щось на них посилається.
  2. Утримання застосовуються до конкретного снапшота й базуються на тегах. Кілька команд можуть незалежно закріпити той самий снапшот без координації — поки не прийде час видаляти.
  3. Тиск по ємності робить людей небезпечними. У реальних операціях найбільші катастрофи трапляються під час аварій із місцем, а не під час спокійного планування.
  4. Реплікація залежить від лінійності. Інкрементальні відправлення потребують спільної базової точки; видалите неправильну базу — і ваша наступна реплікація перетвориться на повну пересилку.
  5. Інструменти авто-снапшотів можуть створити «шторм снапшотів». Якщо політика зберігання неправильно налаштована, у вас можуть з’явитися тисячі снапшотів — тоді хтось тягне за шаблон знищення. Утримання — це ремінь безпеки.
  6. Утримання і закладки — різні речі. Bookmark може зберігати точку інкрементальної відправки без збереження всіх блоків снапшота; утримання зберігає сам снапшот у живих.
  7. Утримання дешеві, поки не стануть дорогими. Метадані крихітні; вартість — в унікальних блоках, які ви не можете звільнити, поки снапшот закріплено.
  8. «Не можна знищити» — це функція, а не баг. Більшість систем трактує видалення як остаточне; ZFS дає вимушений механізм «ви дійсно впевнені», навколо якого можна будувати автоматизацію.

Практичні завдання: команди, які ви реально виконуватимете

Мета тут не показати вправну синтаксис. Мета — виробити операційну м’язову пам’ять: як застосовувати утримання, знайти їх, інтерпретувати помилки й інтегрувати утримання у політику зберігання та реплікації, не закріпивши пул назавжди.

Завдання 1: Створити снапшот з операційно змістовною назвою

cr0x@server:~$ sudo zfs snapshot tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,refer,mountpoint -r tank/app | tail -n 3
NAME                                   USED  REFER  MOUNTPOINT
tank/app@auto-2025-12-25_0000           12M   48G    -
tank/app@auto-2025-12-25_0030           8M    48G    -
tank/app@pre-upgrade-2025-12-25_0100    0B    48G    -

Інтерпретація: Снапшот створюється миттєво. USED може показувати 0B під час створення, бо блоки ще не розійшлися.

Завдання 2: Поставити утримання на цей снапшот

cr0x@server:~$ sudo zfs hold change:pre-upgrade tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME                                   TAG                 TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100    change:pre-upgrade  Fri Dec 25 01:00 2025

Інтерпретація: Снапшот тепер має тег. Його не можна знищити, поки цей тег не буде знятий (і будь-які інші теги також).

Завдання 3: Довести, що утримання блокує знищення

cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cannot destroy snapshot tank/app@pre-upgrade-2025-12-25_0100: snapshot has holds

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

Завдання 4: Додати друге утримання (кілька зацікавлених сторін)

cr0x@server:~$ sudo zfs hold replication:to-dr tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME                                   TAG                 TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100    change:pre-upgrade  Fri Dec 25 01:00 2025
tank/app@pre-upgrade-2025-12-25_0100    replication:to-dr   Fri Dec 25 01:02 2025

Інтерпретація: Знищення блокується, поки обидва теги не зняті. Ось як команди уникатимуть наступання на ноги одна одної.

Завдання 5: Зняти одне утримання та підтвердити, що захист залишається

cr0x@server:~$ sudo zfs release change:pre-upgrade tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME                                   TAG                TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100    replication:to-dr  Fri Dec 25 01:02 2025

cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cannot destroy snapshot tank/app@pre-upgrade-2025-12-25_0100: snapshot has holds

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

Завдання 6: Зняти залишкове утримання та знищити снапшот

cr0x@server:~$ sudo zfs release replication:to-dr tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs list -t snapshot -r tank/app | grep pre-upgrade || echo "snapshot removed"
snapshot removed

Інтерпретація: Як тільки останнє утримання знято, видалення поводиться звично.

Завдання 7: Обережно застосувати утримання рекурсивно до дерева датасетів

cr0x@server:~$ sudo zfs snapshot -r tank/projects@quarterly-freeze-2025Q4
cr0x@server:~$ sudo zfs hold -r legal:q4-retention tank/projects@quarterly-freeze-2025Q4
cr0x@server:~$ sudo zfs holds -r tank/projects@quarterly-freeze-2025Q4 | head
NAME                                               TAG                 TIMESTAMP
tank/projects@quarterly-freeze-2025Q4              legal:q4-retention  Fri Dec 25 02:00 2025
tank/projects/alpha@quarterly-freeze-2025Q4        legal:q4-retention  Fri Dec 25 02:00 2025
tank/projects/beta@quarterly-freeze-2025Q4         legal:q4-retention  Fri Dec 25 02:00 2025
tank/projects/beta/builds@quarterly-freeze-2025Q4  legal:q4-retention  Fri Dec 25 02:00 2025

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

Завдання 8: Знайти, які утримання блокують видалення (команда «чому не видаляється?»)

cr0x@server:~$ sudo zfs destroy tank/projects/alpha@quarterly-freeze-2025Q4
cannot destroy snapshot tank/projects/alpha@quarterly-freeze-2025Q4: snapshot has holds

cr0x@server:~$ sudo zfs holds tank/projects/alpha@quarterly-freeze-2025Q4
NAME                                         TAG                 TIMESTAMP
tank/projects/alpha@quarterly-freeze-2025Q4  legal:q4-retention  Fri Dec 25 02:00 2025

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

Завдання 9: Провести аудит утримань по дереву датасетів

cr0x@server:~$ sudo zfs holds -r tank/projects | awk 'NR==1 || $2 ~ /legal:|replication:|backup:/ {print}'
NAME                                               TAG                 TIMESTAMP
tank/projects@quarterly-freeze-2025Q4              legal:q4-retention  Fri Dec 25 02:00 2025
tank/projects/alpha@quarterly-freeze-2025Q4        legal:q4-retention  Fri Dec 25 02:00 2025
tank/projects/beta@quarterly-freeze-2025Q4         legal:q4-retention  Fri Dec 25 02:00 2025

Інтерпретація: Ви створюєте інвентар «закріплених» снапшотів. У великих середовищах це різниця між керованою політикою зберігання та моторошним горищем.

Завдання 10: Оцінити вплив на простір: знайти важкі дельти снапшотів

cr0x@server:~$ sudo zfs list -t snapshot -o name,used,creation -s used -r tank/projects | tail -n 10
tank/projects/beta@auto-2025-12-20_0000     18G  Sat Dec 20 00:00 2025
tank/projects/beta@auto-2025-12-21_0000     22G  Sun Dec 21 00:00 2025
tank/projects/beta@quarterly-freeze-2025Q4  35G  Fri Dec 25 02:00 2025

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

Завдання 11: Використовувати утримання для захисту «останньої доброї» бази реплікації

cr0x@server:~$ sudo zfs snapshot tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs hold replication:last-good tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs holds tank/app@replica-base-2025-12-25_0300
NAME                                   TAG                   TIMESTAMP
tank/app@replica-base-2025-12-25_0300   replication:last-good Fri Dec 25 03:00 2025

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

Завдання 12: Чисто обертати закріплений «останній добрий» снапшот після успіху

cr0x@server:~$ sudo zfs holds -r tank/app | grep replication:last-good
tank/app@replica-base-2025-12-25_0300   replication:last-good Fri Dec 25 03:00 2025

cr0x@server:~$ sudo zfs snapshot tank/app@replica-base-2025-12-25_0400
cr0x@server:~$ sudo zfs hold replication:last-good tank/app@replica-base-2025-12-25_0400

cr0x@server:~$ sudo zfs release replication:last-good tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs destroy tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs holds tank/app@replica-base-2025-12-25_0400
NAME                                   TAG                   TIMESTAMP
tank/app@replica-base-2025-12-25_0400   replication:last-good Fri Dec 25 04:00 2025

Інтерпретація: Зверніть увагу на порядок: створити нову базу → закріпити її → зняти утримання зі старої бази → видалити стару базу. Якщо ви поміняєте порядок у разі втрати пакетів, вам доведеться розплачуватися повною пересилкою.

Завдання 13: Знищувати снапшоти та пропускати закріплені (безпечний патерн очищення)

cr0x@server:~$ for s in $(sudo zfs list -H -t snapshot -o name -r tank/app | head -n 5); do
>   sudo zfs destroy "$s" 2>&1 | sed "s/^/[$s] /"
> done
[tank/app@auto-2025-12-25_0000] cannot destroy snapshot tank/app@auto-2025-12-25_0000: snapshot has holds
[tank/app@auto-2025-12-25_0030] destroyed
[tank/app@auto-2025-12-25_0100] destroyed
[tank/app@auto-2025-12-25_0130] destroyed
[tank/app@auto-2025-12-25_0200] destroyed

Інтерпретація: Скрипти очищення мають трактувати «has holds» як очікуваний стан. Запишіть це, повідомте, рухайтеся далі. Не провалюйте всю задачу і, тим паче, не намагайтеся автоматично «виправити».

Завдання 14: Знайти снапшоти з утриманнями й відсортувати за часом створення

cr0x@server:~$ sudo zfs holds -r tank | awk 'NR==1{next} {print $1}' | sort -u | while read snap; do
>   sudo zfs get -H -o value creation "$snap" | awk -v s="$snap" '{print $0 " " s}'
> done | sort | head
Fri Dec 20 00:00 2025 tank/projects/beta@auto-2025-12-20_0000
Fri Dec 25 02:00 2025 tank/projects@quarterly-freeze-2025Q4
Fri Dec 25 02:00 2025 tank/projects/alpha@quarterly-freeze-2025Q4

Інтерпретація: Це допомагає відповісти на питання відповідності: «Що ми закріпили, і як давно це закріплено?» Інструмент не робить одного акуратного звіту, тому ви його будуєте самі.

Три міні-історії з корпоративного життя

1) Інцидент через хибне припущення

Команда мала чітке правило: «Ми зберігаємо 48 годин снапшотів, усе старіше видаляється». Це виконувалося cron-роботою, написаною роки тому. Нікому це не подобалося, але пул не перетворювався на музей.

Потім вони перемістили завантажену роботу — артефакти CI і кеші билдов — на той же пул, де лежали критичні бази даних. Пул почав заповнюватися швидше, ніж очікувалося. Під тиском хтось запустив роботу очищення вручну, двічі, а потім вирішив «прискорити» шляхом розширення шаблону видалення. Були використані wildcard’и. Нічого не вибухнуло відразу, і в цьому саме пастка — ви відчуваєте хибну впевненість.

Пізніше того ж дня реплікація до DR сайту провалилася. Не «тимчасово» — а «інкрементальна база не знайдена». Припущення було, що снапшот старіший за 48 годин — значить безпечний для видалення. Але розклад реплікацій зісувався під час вікна обслуговування і відставав на дні. Інкрементальний ланцюг залежав від снапшота, який скрипт очищення вважав «протухлим».

Вони опинилися перед необхідністю повної пересилки по каналу, розрахованому на інкременти, а не на повні датасети. Бізнес-наслідки були не лише в відставанні DR. Пересилка конкурувала з продуктивним трафіком і підштовхнула затримки до видимих для користувачів значень.

Післямортем-правка не була «скажи людям не використовувати wildcard-и» (удачі). Було введено операційно-енфорсоване рішення: робота реплікації ставила утримання на останній снапшот, підтверджений на цілі. Робота з утриманням видалялася іншим процесом. Неправильне припущення замінили механізмом, котрий робить безпечну поведінку за замовчуванням.

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

Інша компанія мала розумну ідею: «Давайте утримувати всі снапшоти протягом семи днів, потім випускати їх у одному пакеті. Так ми думатимемо про зберігання раз на тиждень». Здавалося ефективним: менше рухомих частин, менше шансів помилитися.

Це працювало — поки навантаження не змінилося. Нова аналітична конвеєрна лінія почала щодня переписувати великі датасети. Снапшоти почали накопичувати великі дельти. Утримання закріплювали ці дельти на цілий тиждень, і вільний простір пула почав мотатися. Щотижня вони знімали утримання й робили масове знищення, отримуючи короткий приплив вільного простору… за яким слідувала фрагментація і перфомансні провали, коли пул намагався одночасно перезаписати гарячі блоки й звільнити старі.

Оптимізація зробила зберігання «простим», але сконцентрувала записове підсилення й роботу видалення в передбачуваний щотижневий шторм. А коли шторм припав на піковий бізнес-період, затримки стали регулярними. Це не була загадка; це була запланована біль.

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

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

3) Нудна, але правильна практика, що врятувала день

Ця історія улюблена, бо тут немає героїки — тільки дисципліна.

Додаток, пов’язаний із фінансами, мав процедури квартального закриття, які завжди були стресовими. Команда зберігання мала крок у рукбуці: перед початком закриття зробити рекурсивний снапшот конкретного піддерева датасетів і застосувати юридичний тег утримання. Всі закочували очі, бо це займало лиш п’ять хвилин і ніколи не здавалося корисним.

Під час одного закриття інструмент розгортання неправильно прочитав конфігурацію і виконав крок очищення проти неправильного маунтпоінта. Це не було зловмисністю; це той тип багу автоматизації, що проявляється, коли змінна пуста й її не взято в лапки. Файли зникли. Команда застосунку намагалася відкотитися на рівні додатку, але видалення вже відбулося на диску.

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

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

Практика не була гламурною. Це був механічний крок, який не дозволив панікеру зробити постійну помилку.

План швидкої діагностики

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

1) Перша перевірка: що саме блокує destroy?

Візьміть конкретний снапшот, який відмовляється померти, і подивіться утримання.

cr0x@server:~$ sudo zfs destroy tank/app@auto-2025-12-24_0000
cannot destroy snapshot tank/app@auto-2025-12-24_0000: snapshot has holds

cr0x@server:~$ sudo zfs holds tank/app@auto-2025-12-24_0000
NAME                               TAG                  TIMESTAMP
tank/app@auto-2025-12-24_0000       replication:last-good Thu Dec 24 00:05 2025

Рішення: Якщо тег очікуваний (реплікація/комплаєнс), не видаляйте його під тиском без перевірки залежності на цілі.

2) Друга перевірка: утримання поширені чи локальні?

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

cr0x@server:~$ sudo zfs holds -r tank/app | wc -l
128

Інтерпретація: Велике число? Ймовірно, у вас проблема з політикою або автоматизацією, а не одиничний випадок.

3) Третя перевірка: куди насправді йде простір?

Подивіться на використання простору датасетів і снапшотів, зосередившись на тому, що закріплено.

cr0x@server:~$ sudo zfs list -o name,used,avail,refer,compressratio,mounted tank
NAME   USED  AVAIL  REFER  RATIO  MOUNTED
tank   68T   1.2T   256K   1.42x  yes

cr0x@server:~$ sudo zfs list -t snapshot -o name,used -s used -r tank/app | tail -n 5
tank/app@auto-2025-12-23_0000   210G
tank/app@auto-2025-12-24_0000   260G
tank/app@auto-2025-12-25_0000   300G
tank/app@quarterly-freeze       420G
tank/app@legal-freeze           1.1T

Інтерпретація: Великі снапшоти з USED — головні підозрювані. Якщо вони під утриманням, ви не можете швидко повернути цей простір без зміни бізнес-рішення.

4) Четверта перевірка: чи є вузьке місце на рівні пулу?

Проблеми з простором і спроби видалення можуть співпадати з поганою латентністю. Швидко перевірте стан пулу і I/O.

cr0x@server:~$ sudo zpool status -x
all pools are healthy

cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0 (server)  12/25/2025  _x86_64_  (32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.40    0.00    4.10    8.20    0.00   75.30

Device            r/s     w/s   rkB/s   wkB/s  await  %util
nvme0n1         220.0   180.0  18000   26000   6.20  92.0
nvme1n1         210.0   170.0  17500   24000   6.50  89.0

Інтерпретація: Високий %util і зростаючий await свідчать про I/O-звуження. Видалення снапшотів може бути не причиною, але те ж саме навантаження запису, що створило великі дельти, часто є причиною.

5) П’ята перевірка: переконайтеся, що ви не заблоковані тривалим send/receive або scrub

cr0x@server:~$ sudo zpool status tank | sed -n '1,25p'
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 02:10:21 with 0 errors on Fri Dec 25 01:30:11 2025
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            nvme0n1 ONLINE       0     0     0
            nvme1n1 ONLINE       0     0     0

Інтерпретація: Якщо scrub/resilver активний, пул буде зайнятий. Це безпосередньо не блокує знищення, але змінює оцінку ризику, коли у вас ще й бракує місця.

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

Помилка 1: Сприймати «snapshot has holds» як помилку, яку треба автоматично усунути

Симптом: Скрипт очищення щовечора падає, повторює агресивно, надсилає дзвінок або (гірше) автоматично знімає утримання, щоб «полагодити».

Виправлення: Робіть «has holds» першим класом стану: логувати й звітувати окремо про закріплені снапшоти. Зняття утримання має вимагати явної перевірки власності (успішна реплікація, закриття вікна зміни, юридичне схвалення).

Помилка 2: Використання невизначених тегів утримання без вказівки власника

Симптом: Ви знаходите теги на кшталт keep, hold або important. Ніхто не може їх безпечно зняти; вони тиняються роками.

Виправлення: Впровадьте конвенцію імен: system:purpose (опційно з ідентифікатором зміни). Приклад: replication:last-good, change:pre-upgrade, legal:q4-retention.

Помилка 3: Утримувати снапшоти «на випадок», не вимірявши вплив на простір

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

Виправлення: Визначте снапшоти з високим USED, що закріплені. Вирішіть, чи вони справді потрібні. Якщо відповідність вимагає зберігання, плануйте ємність відповідно; не думайте, що легко «очистите» це шляхом видалення.

Помилка 4: Рекурсивні утримання застосовані не за тією областю

Симптом: Раптом тисячі снапшотів по багатьом дочірнім датасетам закріплені; споживання простору зростає; політика зберігання перестає працювати.

Виправлення: Перед використанням -r перелічте дерево датасетів і підтвердіть корінь. В автоматизації — білдліст префіксів датасетів. Розгляньте можливість утримувати лише конкретні імена снапшотів замість застосування до всіх дочірніх автоматично.

Помилка 5: Плутати утримання з правами доступу

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

Виправлення: Використовуйте делеговані права ZFS і операційні контролі. Утримання захищають снапшоти від знищення, але не ваше сховище від root-а.

Помилка 6: Скрипти реплікації, що ставлять утримання й ніколи їх не знімають

Симптом: replication:last-good існує на десятках або сотнях снапшотів; ви планували мати лише один.

Виправлення: При успішній реплікації робіть ротацію утримання: застосуйте до нової бази, зніміть со старої, потім видаліть стару, якщо політика дозволяє. Додайте моніторинг, що сигналізуватиме, якщо більше N снапшотів мають тег replication:last-good.

Помилка 7: Припускати, що утримання — єдина причина, чому снапшот не можна знищити

Симптом: Знищення не вдається і люди фокусуються лише на утриманнях, але проблема в іншому (опечатка, залежні клоні, проблеми з правами).

Виправлення: Читайте повне повідомлення про помилку і перевіряйте на наявність клонів і прав. Утримання часті, але не єдині.

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

Чеклист A: Вводимо утримання безпечно в існуюче середовище

  1. Визначте теги власників. Вирішіть, які системи можуть ставити утримання (реплікація, бекап, управління змінами, юридичний відділ).
  2. Виберіть конвенцію імен. Краще owner:purpose і опційно додати короткий ідентифікатор у саму назву снапшота.
  3. Вирішіть правила охоплення. Коли дозволені рекурсивні утримання? Для яких коренів датасетів? Документуйте дозволені префікси.
  4. Оновіть задачі очищення. Трактуйте «has holds» як умову пропуску. Формуйте окремий звіт про закріплені снапшоти.
  5. Додайте видимість. Створіть планову задачу інвентаризації, що рахує утримання за тегом і показує найстаріший закріплений снапшот для кожного тегу.
  6. Проведіть тренування. Відпрацюйте процедуру «надзвичайної нестачі простору» без реальної кризи, щоб не вчитися під стресом.

Чеклист B: Передзмінний «снапшот безпеки» з утриманням

  1. Зробіть снапшот(и) датасету(ів), за можливості рекурсивно, якщо зміна охоплює кілька дочірніх датасетів.
  2. Застосуйте тег утримання, специфічний для зміни.
  3. Підтвердіть наявність утримань.
  4. Проведіть зміну.
  5. Після завершення вікна перевірки зніміть тег утримання.
  6. Дайте звичайній політиці зберігання видалити снапшот пізніше (або явно знищіть, якщо політика вимагає).
cr0x@server:~$ sudo zfs snapshot -r tank/app@chg-pre-2025-12-25_0500
cr0x@server:~$ sudo zfs hold -r change:chg-pre tank/app@chg-pre-2025-12-25_0500
cr0x@server:~$ sudo zfs holds -r tank/app@chg-pre-2025-12-25_0500 | head -n 5
NAME                                     TAG              TIMESTAMP
tank/app@chg-pre-2025-12-25_0500         change:chg-pre   Fri Dec 25 05:00 2025
tank/app/db@chg-pre-2025-12-25_0500      change:chg-pre   Fri Dec 25 05:00 2025
tank/app/uploads@chg-pre-2025-12-25_0500 change:chg-pre   Fri Dec 25 05:00 2025

Чеклист C: Утримання для реплікації з «останньою доброю базою»

  1. Створити новий снапшот для реплікації.
  2. Відправити/прийняти (або використовувати ваш механізм реплікації).
  3. Підтвердити успіх на боці цілі.
  4. Закріпити нову базу на джерелі.
  5. Зняти утримання зі попередньої бази.
  6. Опційно видалити старі бази, якщо вони поза політикою зберігання.

FAQ

1) Чи те саме утримання й властивість датасету типу readonly=on?

Ні. readonly=on впливає на поведінку запису в датасеті. Утримання впливає лише на те, чи можна конкретний снапшот знищити.

2) Чи можна утримати датасет, а не снапшот?

Утримання застосовуються до снапшотів. Ви можете рекурсивно зробити снапшоти, а потім утримати ці снапшоти — саме те, що люди часто мають на увазі під «утримати датасет».

3) Чому ZFS дозволяє кілька утримань на одному снапшоті?

Тому що в реальних середовищах є кілька зацікавлених сторін. Реплікація може потребувати снапшот для інкрементальних ланцюгів, тоді як юридичне зберігання може вимагати його для аудиту. Кілька утримань дозволяють кожній системі незалежно заявити свою потребу.

4) Якщо снапшот закріплений, чи він з часом продовжує займати більше місця?

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

5) Як знайти, що перешкоджає видаленню снапшота?

Запустіть zfs holds <snapshot>. Якщо маєте справу з багатьма датасетами, використайте zfs holds -r для інвентаризації, а потім звузьте до конкретних снапшотів, що блокують політику.

6) Чи можна використовувати утримання як механізм відповідності (compliance)?

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

7) У чому різниця між утриманням і bookmark?

Утримання закріплює снапшот (і, отже, всі посиланні на блоки). Bookmark зберігає точку інкрементальної відправки, не утримуючи повні блоки снапшота так само. Утримання — про недоторканність; bookmark — про реплікаційну лінійність без повного збереження снапшота.

8) Мій інструмент ретеншну видаляє снапшоти, але простір не звільняється. Чи винні утримання?

Можливо, але не завжди. Утримання перешкоджають видаленню конкретних снапшотів; вони не пояснюють, чому видалення інших не вивільняє багато простору. Часті причини: залишилися інші снапшоти (закріплені чи ні), які посилаються на старі блоки; патерн зміни датасету утримує блоки; або ви дивитеся не на те дерево датасетів.

9) Чи варто ставити утримання на кожен снапшот?

Зазвичай ні. Це шлях до перетворення політики зберігання в кризу ємності. Використовуйте утримання вибірково: останні добрі бази реплікації, передзмінні точки безпеки та явні комплаєнс-снапшоти.

10) Хто має право знімати утримання?

У зрілих середовищах: лише власна автоматизація або невеликий набір привілейованих ролей з аудиторським слідом. Операційно «зняти утримання» — це еквівалент дозволу на постійне видалення, і його слід поводитись так само серйозно.

Висновок

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

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

← Попередня
Чіплетні GPU: чому ідея логічна — і надзвичайно складна
Наступна →
Proxmox HA «не може запустити ресурс»: як знайти справжній блокувальник (кворум, сховище, мережа)

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