Видалення знімка ZFS має бути нудною операцією: zfs destroy pool/fs@snap, і все. Але в реальних системах це часто схоже на сцену з детективного серіалу: знімок ніби «зник», а місце не повернулося, або команда destroy повертає помилку, бо щось — десь — все ще «потребує» його.
Це польовий довідник для таких моментів. Не рекламний буклет. Ми розглянемо технічні причини, чому знімки відмовляються «вмирати» (holds, клони, зайняті набори, відкладене видалення, реплікація, облік простору) і операційні виправлення, які працюють, коли ваш пейджер уже гарячий.
Що насправді таке знімок ZFS (і чому видалення не «безкоштовне»)
Знімок ZFS — це не копія ваших файлів. Це заморожений погляд на набір даних у межах транзакційної групи: набір вказівників на блоки, що каже «ці блоки визначають файлову систему в той момент». Коли ви змінюєте живі дані, ZFS записує нові блоки в інше місце (copy-on-write). Старі блоки лишаються посиланими знімком. Видаліть знімок — і ці старі блоки можуть стати непосиланими й підлягати звільненню — якщо більше ніхто на них не посилається.
Ось чому при видаленні знімка бувають два різні режими невдачі:
- Операцію destroy заблоковано (holds, клони, «dataset is busy»). Ви навіть не можете видалити метадані знімка.
- Операція destroy успішна, але місце не повернулося (бо блоки все ще посилаються іншими знімками, клонами або через непорозуміння в обліку простору).
Це також означає, що видалення може бути дорогим. ZFS може потребувати обходу метаданих, щоб визначити, які блоки можна звільнити. Великі дерева знімків із роками змін можуть зробити видалення хвилинним — або годинним — залежно від навантаження пулу, затримок сховища й прапорців функцій.
Одна операційна істина: коли люди кажуть «знімки дешеві», вони мають на увазі «створення знімків дешеве». Саме при їх видаленні приходить рахунок.
Цікаві факти й історичний контекст (ви відчуєте це в проді)
- Знімки ZFS походять від Sun Microsystems і були спроєктовані як примітив для клонування, відкату й реплікації — задовго до того, як «незмінні бекапи» стали маркетинговим слоганом.
- Copy-on-write — причина, чому знімки існують без зупинки записів: ви можете зробити знімок живого набору даних бази даних без заморожування I/O, тому що нові записи йдуть у нові блоки.
- Імена знімків є частиною простору імен набору даних:
pool/fs@snapне є окремим типом об’єкта, як LVM-знімки; це тісно пов’язано з обліком набору даних. - Клони — це записувані знімки: клон — це набір даних, який спочатку ділить усі блоки зі знімком; ця залежність робить деякі знімки «непідйомними» для видалення.
- Holds введені, щоб запобігти випадковому видаленню під час робочих процесів, таких як реплікація, перевірка бекапів і розгортання на основі знімків.
- Відкладене видалення існує тому, що видалення може бути повільним: ZFS може помітити знімок для пізнішого очищення, щоб команда повернулась швидко, а звільнення блоків виконувалось асинхронно.
- Простір — це не одне число в ZFS: «used», «refer», «logicalused», «written», «usedbysnapshots» і «usedbychildren» відповідають на різні питання. Люди під тиском часто вибирають неправильне.
- Стиснення змінює інтуїцію: знімок може «тримати» блоки, які логічно виглядають великими, але фізично маленькими — або навпаки, якщо змінюється recordsize або відбуваються перезаписи.
І невеликий жарт, бо ми цього заслуговуємо: знімки ZFS схожі на офісні крісла — легко додати, дивно важко позбутися, і ви помічаєте вартість тільки коли коридор заблоковано.
Шпаргалка швидкої діагностики (перевірити це спочатку)
Ось послідовність, якою я користуюсь, коли хтось каже: «Ми видалили знімки, але місце не повернулося» або «destroy не працює і ми не знаємо чому». Мета — швидко знайти вузьке місце, а не милуватись філософією пулу.
1) Знімок фактично зник, чи це відкладене видалення?
Спочатку перевірте, чи не маєте ви справи з відкладеним видаленням. ZFS може прийняти команду destroy, але звільнення блоків відкласти.
2) Чи є holds?
Holds — це найпоширеніша причина «не хоче вмирати» в дисциплінованих середовищах (програмне забезпечення для бекапів/реплікації їх обожнює). Вони також найпоширеніша причина «ніхто не пам’ятає, хто їх поставив».
3) Чи є залежність від клону?
Якщо знімок був використаний для створення клону, ви не можете видалити цей знімок, поки клон не буде промотований або знищений. У багатьох командах хтось «тимчасово» клонував продакшн-дані для тестування і мовчки перетворив очищення знімків на переговори про заручників.
4) Якщо знімок видалено, чому місце не повертається?
Перевірте usedbysnapshots, і чи інші знімки все ще посилаються на ті ж перезаписані блоки. Також переконайтеся, що дивитесь на правильний набір даних і не плутаєте рівень пулу з рівнем набору даних.
5) Чи пул нездоровий або під сильним навантаженням?
Деградований пул, сильна фрагментація або насичені IOPS можуть уповільнити видалення. Видалення знімків інтенсивно оперує метаданими; це конкурує з вашими основними робочими навантаженнями.
6) Ви видаляли знімки з невірного боку реплікації?
Топології реплікації можуть підтримувати знімки живими: відправник зберігає їх, щоб задовольнити інкрементальні ланцюги; приймач зберігає їх, бо ви їх зафіксували, і обидві сторони звинувачують одна одну.
Чому знімки відмовляються видалятися: реальні причини
Причина A: Holds (користувацькі, інструментальні, «це для вашого ж блага»)
Hold — це тег, прикріплений до знімка, який забороняє його видалення. Ви побачите це, коли zfs destroy зламається з повідомленням про holds, або коли інструмент видалення «пропускає захищені знімки». Holds — чудова річ, поки вони не стали сиротами через аварійний процес, напівміграцію бекап-системи або скрипт, який ставить теги, але ніколи їх не знімає.
У природі holds з’являються як:
- Ланцюжки реплікації, що утримують знімки до завершення receive.
- Завдання перевірки бекапів, що тримають знімки до завершення контрольних сум.
- «Запобіжні» holds, додані адміністраторами під час інциденту і забуті.
Причина B: Залежності від клонів (найтихіший блокувальник)
Якщо у знімка є клон, цей знімок є частиною походження клона. ZFS відмовиться його видаляти, бо це розірве посилання клона на блоки. Ви можете виявити це, перевіривши властивість clones знімка. Виправлення — знищити клон або promote клон, щоб він став новим origin і залежність перевернулась.
Це найпоширеніший випадок «ми не розуміємо, чому це не працює» в змішаних командах, бо той, хто видаляє знімки, часто не знає, що хтось створив клон місяці тому.
Причина C: Помилки «dataset is busy» (змонтовано, у використанні або особливі випадки)
Зазвичай ви можете видаляти знімки змонтованих наборів без проблем. Але «dataset is busy» може виникнути, коли ви видаляєте файлову систему або том з активними посиланнями, або коли ви намагаєтесь зруйнувати знімки під час певних операцій (наприклад, ongoing receive у деяких робочих процесах, залежно від платформи й прапорців).
Також будьте обережні з zfs destroy -r або -R: ви можете видаляти набори даних (не лише знімки) і наштовхнутись на використання точок монтування, NFS-експорти, jails/zones або runtime-контейнери, які «прикріпили» точки монтування.
Причина D: Відкладене видалення (він «видалився», але пул не вдихнув)
Відкладене видалення дозволяє ZFS швидко прибрати знімок з простору імен, відтермінувавши фактичну роботу зі звільнення блоків. Це не магія; це рішення планувальника. Якщо пул під навантаженням, очищення може відставати. Оператори бачать «знімок зник» і припускають, що місце має повернутися миттєво. Може й не повернутися.
Відкладене видалення часто викликається через zfs destroy -d, але може також проявлятися як поведінка, коли система вирішує не блокувати команду занадто довго (реалізація залежить від платформи й прапорців).
Причина E: Неправильні уявлення про облік простору (ви звільнили місце — просто не там, де дивитесь)
ZFS повідомляє про простір на кількох рівнях:
- рівень пулу вільний простір: що пул може виділити.
- рівень набору даних «used»: включає дітей і знімки залежно від властивостей і перспективи.
- usedbysnapshots: блоки, унікально утримувані знімками для цього набору даних.
- referenced: що сам набір даних посилає в цей момент (без знімків).
Поширена ситуація: ви видаляєте купу знімків, і zfs list все одно показує майже незмінний «used», бо живий набір даних (або інші знімки) все ще посилаються на більшість блоків. Це особливо типовий випадок, коли навантаження апенд-орієнтоване (логи), а не перезапис-орієнтоване.
Причина F: Реплікаційні ланцюги й інкрементальні залежності
Інкрементальний zfs send потребує спільного знімка між джерелом і призначенням. Якщо ви видалите «не той» знімок на будь-якій стороні, ви порушите ланцюг і змусите наступний раз виконати повний send. Щоб цього уникнути, багато інструментів реплікації утримують або зберігають знімки. Якщо ви вручну обійдете політику збереження, ви можете виграти кілька гігабайт і програти вихідні вихідні вихідні вихідні — або у вихідні терміни для повторного насіння.
Причина G: Ви видаляєте занадто багато занадто швидко (операційно правильно, але фізично болісно)
Знищення тисяч знімків одночасно може створити неприємний сплеск метаданих. На зайнятих пулах — особливо на HDD — це може виглядати як інцидент продуктивності. ZFS не зламується навмисно; він робить облік, який ви просили. Виправлення — трохи пригальмувати, пакетувати або відкласти.
Другий жарт (і останній): політики збереження знімків схожі на дієти — легко почати, важко підтримувати, і всі брешуть про те, скільки їх у них є.
Практичні завдання: команди, виводи й що вони означають
Нижче — конкретні завдання, які я запускав в реальних інцидентах. Команди — стандартні інструменти ZFS; виводи репрезентативні. Ваша платформа (OpenZFS on Linux, FreeBSD, illumos) може трохи відрізнятись, але робочий процес однаковий.
Завдання 1: Підтвердити, що знімок існує (і ви правильно його пишете)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation pool/app
NAME USED REFER CREATION
pool/app@auto-2025-12-24 12G 1.20T Wed Dec 24 02:00 2025
pool/app@auto-2025-12-25 14G 1.21T Thu Dec 25 02:00 2025
Пояснення: У вас два знімки. USED — це скільки простору цей знімок унікально споживає в порівнянні з головою набору даних та іншими знімками.
Завдання 2: Спробуйте звичайний destroy і прочитайте помилку буквально
cr0x@server:~$ sudo zfs destroy pool/app@auto-2025-12-24
cannot destroy snapshot pool/app@auto-2025-12-24: snapshot has holds
Пояснення: Це не загадка. Є holds. Не починайте вгадувати про «busy». Перевіряйте holds.
Завдання 3: Перелік holds на знімку
cr0x@server:~$ sudo zfs holds pool/app@auto-2025-12-24
NAME TAG TIMESTAMP
pool/app@auto-2025-12-24 repl-inflight Thu Dec 25 02:05 2025
pool/app@auto-2025-12-24 backup-verify Thu Dec 25 02:10 2025
Пояснення: Дві різні системи вважають, що їм ще потрібен цей знімок. Це добра новина: у вас є імена, за якими можна йти.
Завдання 4: Зняти hold (безпечно, з наміром)
cr0x@server:~$ sudo zfs release backup-verify pool/app@auto-2025-12-24
cr0x@server:~$ sudo zfs holds pool/app@auto-2025-12-24
NAME TAG TIMESTAMP
pool/app@auto-2025-12-24 repl-inflight Thu Dec 25 02:05 2025
Пояснення: Ви прибрали один тег. Якщо hold належить реплікації або бекап-завданню, раннє зняття може порушити гарантії. Координуйте або переконайтеся, що завдання мертве/зависло.
Завдання 5: Примусове знищення (не рекомендовано, але майте уявлення)
cr0x@server:~$ sudo zfs destroy -f pool/app@auto-2025-12-24
cannot destroy snapshot pool/app@auto-2025-12-24: snapshot has holds
Пояснення: -f не означає «ігнорувати holds». Holds існують спеціально, щоб цього не дозволяти. Ви маєте release ці holds або усунути залежні умови.
Завдання 6: Перевірити, чи знімок має клони
cr0x@server:~$ zfs get -H -o value clones pool/app@auto-2025-12-24
pool/dev/app-clone
Пояснення: Цей знімок є origin для клонованого набору даних. Ви не можете видалити знімок, поки не впораєтеся з клонованим набором.
Завдання 7: Інспектувати клон і його origin
cr0x@server:~$ zfs get -o name,property,value origin pool/dev/app-clone
NAME PROPERTY VALUE
pool/dev/app-clone origin pool/app@auto-2025-12-24
Пояснення: Чітке походження. Якщо клон потрібен, розгляньте промоцію. Якщо його можна викинути — знищіть.
Завдання 8: Промотувати клон, щоб розірвати залежність (коли вам потрібен клон, а не origin)
cr0x@server:~$ sudo zfs promote pool/dev/app-clone
cr0x@server:~$ zfs get -o name,property,value origin pool/dev/app-clone
NAME PROPERTY VALUE
pool/dev/app-clone origin -
Пояснення: Після промоції клон стає незалежним набором даних (його origin очищено). Колишній origin може тепер мати origin, що вказує назад, залежно від лінії наслідування. Перевірте список клонів знімка, який хочете видалити.
Завдання 9: Видалити знімок після того, як знято holds/клони
cr0x@server:~$ sudo zfs release repl-inflight pool/app@auto-2025-12-24
cr0x@server:~$ sudo zfs destroy pool/app@auto-2025-12-24
Пояснення: Якщо це вдасться і знімок зникне з zfs list -t snapshot, ви прибрали запис із простору імен. Звільнення місця може зайняти ще якийсь час.
Завдання 10: Перевірити, чи місце дійсно зайняте знімками
cr0x@server:~$ zfs get -o name,property,value -s local,default used,usedbysnapshots,usedbydataset,referenced pool/app
NAME PROPERTY VALUE
pool/app used 3.10T
pool/app usedbysnapshots 420G
pool/app usedbydataset 2.68T
pool/app referenced 2.68T
Пояснення: Якщо usedbysnapshots велике, видалення знімків може допомогти. Якщо воно мале, видалення знімків вас не врятує; сам набір даних є головним споживачем простору.
Завдання 11: Визначити, які знімки споживають найбільше місця
cr0x@server:~$ zfs list -t snapshot -o name,used,refer -s used pool/app | tail -n 5
pool/app@auto-2025-10-01 85G 1.05T
pool/app@auto-2025-11-01 92G 1.10T
pool/app@auto-2025-11-15 97G 1.12T
pool/app@auto-2025-12-01 110G 1.18T
pool/app@auto-2025-12-15 140G 1.20T
Пояснення: Це хороші кандидати для очистки, якщо політика дозволяє. Зазвичай «used»-стрибки корелюють із великими перезаписами, компактами, змінами образів VM або обслуговуванням баз даних.
Завдання 12: Пакетне видалення знімків за шаблоном (обережно)
cr0x@server:~$ zfs list -H -t snapshot -o name -s creation pool/app | grep '@auto-2025-10' | head
pool/app@auto-2025-10-01
pool/app@auto-2025-10-02
pool/app@auto-2025-10-03
pool/app@auto-2025-10-04
pool/app@auto-2025-10-05
cr0x@server:~$ zfs list -H -t snapshot -o name -s creation pool/app | grep '@auto-2025-10' | xargs -n 1 sudo zfs destroy
Пояснення: Це видаляє знімки по одному (менш вибуховий підхід, ніж один рекурсивний destroy через багато наборів даних). Якщо якийсь знімок не вдасться через holds/клони, ви побачите, який саме і чому. У великих середовищах додавайте паузи між видаленнями.
Завдання 13: Використати deferred destroy, коли потрібен швидкий поворот команди
cr0x@server:~$ sudo zfs destroy -d pool/app@auto-2025-12-15
cr0x@server:~$ zfs list -t snapshot pool/app | grep auto-2025-12-15
# (no output)
Пояснення: Знімок швидко видаляється з простору імен, але пул все ще може фоново звільняти блоки. Слідкуйте за I/O пулу та трендом вільного місця з часом.
Завдання 14: Перевірити стан пулу й очевидні червоні прапорці перед тим, як звинувачувати логіку видалення ZFS
cr0x@server:~$ zpool status -x
all pools are healthy
cr0x@server:~$ zpool status pool
pool: pool
state: ONLINE
scan: scrub repaired 0B in 03:21:10 with 0 errors on Sun Dec 22 03:00:11 2025
config:
NAME STATE READ WRITE CKSUM
pool ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
errors: No known data errors
Пояснення: Якщо пул деградований або триває resilver, видалення знімків може тягнутись, і варто відкласти масове прибирання до завершення відновлення.
Завдання 15: Підтвердити, що ви не потрапили в пастку очікувань реплікації
cr0x@server:~$ zfs list -t snapshot -o name,creation pool/app | tail -n 3
pool/app@repl-2025-12-24-0200 Wed Dec 24 02:00 2025
pool/app@repl-2025-12-25-0200 Thu Dec 25 02:00 2025
pool/app@repl-2025-12-25-1400 Thu Dec 25 14:00 2025
cr0x@server:~$ zfs holds pool/app@repl-2025-12-24-0200
NAME TAG TIMESTAMP
pool/app@repl-2025-12-24-0200 zfs-send-chain Thu Dec 25 14:05 2025
Пояснення: Інструмент реплікації навмисне тримає цей знімок. Якщо ви його видалите, наступний інкрементальний може не спрацювати або перейти на повний перенос.
Завдання 16: Подивитись кількість знімків і виявити «вибух знімків»
cr0x@server:~$ zfs list -H -t snapshot -o name pool/app | wc -l
1827
Пояснення: Набір даних із тисячами знімків не є автоматично неправильним, але це змінює модель вартості видалення й відкату. Плануйте прибирання так само серйозно, як плануєте перебудову індексу: призначайте вікно, обмежуйте швидкість і моніторте процес.
Три короткі історії з корпоративного життя
1) Інцидент через неправильне припущення: «Видалення знімків миттєво звільнить терабайти»
У великому корпоративному кластері VM команда зберігання отримала тривожний алерт: первинний пул опустився нижче порогу вільного простору. Інженер зробив те, що багато хто робить: видалив «старі знімки» на найбільш завантаженому наборі даних, очікуючи швидкого падіння використання. Видалення завершилося, але вільного простору майже не додалося. За наступну годину затримка запису зросла, і деякі VM почали час від часу таймаутитись.
Неправильне припущення полягало не в тому, що знімки займають простір; а в тому, що саме ті знімки були причиною. Набір даних мав високий рівень перезаписів: образи VM щодня компактувались і перезаписувались, але нові знімки також створювались щогодини. Видалення кількох старих не допомогло, бо перезаписані блоки все ще посилались новішими знімками.
Команда подвоїла зусилля: більше видалень, швидше. Ось як вони спричинили другорядний ефект: масове знищення знімків створило метадані навантаження. Це не «зламало ZFS», але конкурувало з I/O VM і погіршило ситуацію. Тепер у них було і мало вільного простору, і деградована продуктивність.
Виправлення було поєднанням смирення й арифметики. Вони виміряли usedbysnapshots, визначили найгірші діапазони знімків за USED і видаляли пакетами контрольовано. Також скоригували розклад знімків для того набору даних: менше щогодинних, більше щоденних, і коротший період збереження для образів з високим churn. Пул стабілізувався, і продуктивність відновилась — повільно, як будь-яка система, яку змусили робити багато обліку в поспіху.
2) Оптимізація, що відбилася боком: «Зробімо все клонами для швидкості dev»
Одна компанія з сильною платформною командою хотіла прискорити середовища розробників. Ідея була елегантна: брати нічний знімок production-подібних наборів даних, а потім створювати для команд клони за секунди. Це працювало. Розробники були задоволені. Платформна команда отримала похвалу за «використання можливостей сховища».
Місяці потому пул досягнув стелі. Видалення знімків почало провалюватись через залежності клонів. Гірше, ці клони вже не були «тимчасовими». Команди інсталювали пакунки, завантажували тестові дані та побудували робочі процеси навколо своїх клонів. Знищення їх перестало бути завданням прибирання; це стало політичним процесом.
Наслідок був тонким: оптимізація перемістила складність життєвих циклів в шар зберігання. Знімки стали непідйомними не тому, що ZFS впертий, а тому, що організація включила знімки в неявний контракт. Кожен клон прив’язував знімок; кожен прив’язаний знімок тримав історію; і історія тримала простір.
План відновлення не був героїчним однолайнером. Вони ввели політику: dev-клони повинні бути промотовані в межах часу (або відтворені з більш нового знімка). Також реалізували тегування і звітність: вік клону, вік origin-знімка та винятки політики збереження. Перемога була такою ж культурною, як і технічною: вартість «миттєвих клонів» стала видимою, і команда перестала сприймати прибирання знімків як післядумку.
3) Нудна, але правильна практика, що врятувала день: «Holds з тегом власника і контракт на очищення»
У іншому середовищі під час міграції працювали дві системи реплікації — стара і нова — паралельно деякий час. Це та ситуація, де знімки тихо накопичуються, бо ніхто не хоче видаляти те, що може знадобитися для інкрементальних відправок.
Практика команди була безликою: кожен тег hold включав ім’я власника системи і ідентифікатор запуску, а holds були обмежені за часом політикою. Щоденне завдання звітувало знімки з holds, старші за поріг, згруповані за тегом. Коли pipeline вмирав, він залишав доказ, а не таємницю.
Під час міграції один із реплікаційних завдань почав періодично падати. Знімки накопичувались, але вони не стали «безсмертними». Звіт показав один талон, що застарів. Інженеру на чергуванні не довелося гадати, кому він належить; тег це говорив прямо.
Вони призупинили проблемний pipeline, підтвердили безпечний базовий знімок, зняли застарілі holds і відновили ланцюг з оновленими настройками. Ніякої драми, ніяких спекулятивних видалень. Пул ніколи не опустився в небезпечну зону, і міграція пройшла без звичного питання «чому збереження не працює?». Практика була нудною. Вона працювала — а в проді це найвища похвала.
Поширені помилки (конкретні симптоми й виправлення)
Помилка 1: Плутати «знімок видалено» з «місце негайно звільнено»
Симптом: Знімок більше не з’являється в zfs list -t snapshot, але вільний простір пулу не збільшується.
Виправлення: Перевірте usedbysnapshots і чи інші знімки ще існують. Урахуйте затримку відкладеного видалення і навантаження пулу. Вимірюйте в часі, а не в секундах.
Помилка 2: Видаляти знімки, ігноруючи залежності клонів
Симптом: cannot destroy snapshot ...: snapshot has dependent clones або властивість знімка clones не пуста.
Виправлення: Визначте клони через zfs get clones. Знищіть непотрібні клони або zfs promote клон, якщо він має жити далі.
Помилка 3: Знімати holds, не розуміючи, хто їх поставив
Симптом: Реплікація або бекап-завдання починають падати після «прибирання». Інкрементальні відправки скаржаться на відсутні знімки.
Виправлення: Перед зняттям holds ідентифікуйте власника процесу/інструменту і підтвердьте безпеку. Якщо треба порушити ланцюг, плануйте повне повторне насіння й витрати пропускної здатності / часу.
Помилка 4: Використовувати рекурсивні destroy занадто широко
Симптом: Ви запускаєте zfs destroy -r pool/fs@snap і несподівано торкаєтесь великого піддерева; видалення тягнуться вічно або падають у несподіваних місцях.
Виправлення: Спочатку перелікуйте те, що буде вражено. Віддавайте перевагу пакетному видаленню по явних іменах знімків, коли зона ураження важлива.
Помилка 5: Вимірювати неправильний набір даних або властивість
Симптом: Ви видаляєте знімки на pool/app, але пул все ще повний; згодом ви розумієте, що простір зайнятий у pool/app/logs або в іншому наборі.
Виправлення: Використовуйте види на кшталт zfs list -o space (або явні запити властивостей) по дереву наборів даних. Підтвердіть, що набір даних дійсно додає тиск на пул.
Помилка 6: Спочатку видаляти «великі USED» знімки, не думаючи про інкременти
Симптом: Після видалення великого знімка наступна реплікація стає повним send або падає.
Виправлення: Розумійте, які знімки є опорними точками для інкрементальних ланцюгів. Координуйте видалення з розкладом реплікації і станом призначення.
Помилка 7: Запускати масові видалення під час пікового I/O
Симптом: Зростають затримки, iowait піднімається, користувачі скаржаться на повільну роботу, але нічого «не впало».
Виправлення: Обмежуйте швидкість видалень, пакетно їх виконуйте, працюйте в вікна низького навантаження або використовуйте deferred destroy. Видалення знімків інтенсивно працює з метаданими; ставтеся до цього як до технічного обслуговування.
Контрольні списки / покроковий план
Контрольний список A: «Destroy не вдалося» покроково
- Підтвердіть точну назву знімка через
zfs list -t snapshot. - Спробуйте destroy один раз і зафіксуйте текст помилки.
- Якщо в повідомленні йдеться про holds: запустіть
zfs holds, ідентифікуйте теги, вирішіть, чи їх слід знімати. - Якщо йдеться про клони: запустіть
zfs get clones, інспектуйте і або знищіть, або промотуйте клони. - Якщо згадується «busy»: підтвердіть, що ви видаляєте лише знімки, а не набори даних (
-r/-R); перевірте точки монтування, експорти, контейнери і ongoing receives. - Повторіть destroy для одного знімка, щоб переконатися, що блокувальник знято, перш ніж масштабувати операцію.
Контрольний список B: «Знімки видалено, але місце не повернулося» покроково
- Підтвердіть, що кількість знімків дійсно зменшилась:
zfs list -t snapshot | wc -l(або в межах набору даних). - Перевірте розподіл простору набору даних:
zfs get used,usedbysnapshots,usedbydataset,referenced. - Перелічіть залишкові знімки і відсортуйте за
USED, щоб побачити, хто ще тримає блоки. - Розгляньте, чи сама голова набору даних посилається на блоки: видалення знімків не допоможе, якщо живі дані — основний споживач.
- Якщо використовувалося deferred destroy, дозвольте часу й спостерігайте тренд вільного простору пулу.
- Перевірте, чи інший набір даних не є реальним джерелом тиску на пул.
Контрольний список C: Безпечний план масового прибирання (коли потрібно видалити сотні/тисячі)
- Виберіть вікно видалення (поза піком). Оголосіть це як будь-яке інше технічне обслуговування.
- Dry-run вибірки: перелічте імена знімків, які плануєте видалити, перегляньте шаблони (реплікаційні анкори, кінці місяця).
- Перед початком перевірте holds і залежності клонів.
- Видаляйте пакетами; моніторте затримки й активність пулу між пакетами.
- Логуйте, що ви видалили і що не вдалося (і чому). «Ми запустили команду» — це не аудиторський слід.
- Після прибирання переконайтесь, що реплікація/бекапи все ще мають потрібні базові знімки.
Часті питання
1) Чому zfs destroy каже «snapshot has holds»?
Бо на знімку встановлено один або кілька тегів hold. Holds — це явний захист. Використовуйте zfs holds pool/fs@snap, щоб їх перелічити, а потім zfs release TAG pool/fs@snap, щоб зняти конкретний тег, коли це безпечно.
2) Чому не можу видалити знімок, який має залежні клони?
Клон — це набір даних, який використовує цей знімок як origin. Видалення знімка розірве граф посилань клона. Або знищіть клон, або zfs promote клон, щоб перевернути залежність.
3) Я видалив знімки, а df все ще показує повну файлову систему. ZFS бреше?
Зазвичай — ні. Ви порівнюєте різні системи обліку. df показує, що змонтована файлова система вважає доступним, і це може впливатися резервуваннями, квотами, refreservations і загальним тиском пулу. Використовуйте zfs get used,available,usedbysnapshots і рівень пулу zpool list, щоб побачити реальний стан.
4) Чи уповільнюють знімки моє навантаження?
Самі по собі — ні. Витрати з’являються через churn: якщо ви багато перезаписуєте дані, тримаючи багато знімків, пул зберігатиме більше старих блоків, що підвищує тиск простору і потенційну фрагментацію. Масове видалення знімків також може створити сплеск метаданих.
5) У чому різниця між USED у знімка і usedbysnapshots у набору даних?
USED у знімка — це те, що цей знімок унікально додає у порівнянні з іншими. usedbysnapshots у набору даних — це загальний простір, зайнятий знімками, пов’язаними з цим набором. Вони відповідають на різні питання: «який знімок дорогий?» проти «скільки в цілому знімки тут коштують?»
6) Чи безпечне deferred destroy?
Так, у сенсі що це підтримуваний механізм: знімок видаляється, а звільнення блоків відбувається пізніше. Компроміс — видимість операції: люди очікують миттєвого повернення місця. Використовуйте його, коли потрібна швидкість реакції і ви готові до відстроченого звільнення.
7) Чому інколи видалення знімків триває вічно?
ZFS може потребувати обійти метадані, щоб звільнити блоки, і це конкурує з нормальним I/O. Пули з великою кількістю знімків, сильним churn, повільними дисками або важким паралельним навантаженням цю операцію відчують сильніше. Видаляйте пакетами і плануйте на тихі періоди.
8) Чи можу я видаляти знімки на джерелі, не торкаючись приймача (або навпаки)?
Можете, але інкрементальна реплікація залежить від наявності спільних знімків. Видалення «опорного» знімка з будь-якої сторони може змусити наступний раз робити повний пересил або зламати автоматику. Якщо не впевнені, проінспектуйте, які знімки використовуються для реплікації і чи вони затримані/піновані.
9) Як запобігти «безсмертності» знімків надалі?
Використовуйте явні конвенції іменування, теги holds з власністю, моніторинг знімків з holds старших за очікуване, і уникайте довгоживучих клонів без життєвого циклу (вік промоції, TTL або автоматичне прибирання).
Висновок
Знімки ZFS не «відмовляються вмирати» зі злами. Вони зберігаються тому, що ZFS робить саме те, для чого його найняли: зберігати консистентність, шанувати залежності і запобігати небезпечним видаленням. Коли знімок не видаляється, це майже завжди одна з трьох причин — holds, клони або непорозуміння в обліку простору — і виправлення полягає в тому, щоб спершу ідентифікувати причину, перш ніж починати бездумно махати командами.
Якщо ставитись до видалення знімків як до обслуговування (обмежено, під моніторингом і координовано з реаліями реплікації/бекапів), воно знову стане нудним. А нудне сховище — це те, яке ви помічаєте тільки тоді, коли хвалитеся аптаймом, а не коли його пояснюєте.