ZFS: Чому ваш пул «повний» на 70% (і як виправити планування місця)

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

Ви купили «20 TB raw», зібрали RAIDZ-пул і були задоволені. Потім приблизно на позначці ~70% використання все почало хитатися:
записи сповільнилися, час scrubs зріс, а ваш моніторинг кричав «pool is full», тоді як df стверджував,
що у вас ще залишаються терабайти. Якщо це ваша реальність — ви не нещасливчик, ви просто використовуєте ZFS за його задумом.

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

Проблема 70%: що насправді означає «повний» у ZFS

Пули ZFS часто «відчуваються» повними задовго до того, як вони досягають 100% використання. «Магічні числа», які називають люди — 70%, 80%,
іноді 85% — це не забобон. Це евристика для позначення моменту, коли поведінка аллокатора змінюється з «переважно суміжна,
швидка й передбачувана» на «фрагментована, з великою кількістю метаданих і іноді драматична».

Одночасно вірні дві речі:

  • ZFS може приймати записи вище 70% використання пулу, і багато пулів працюють нормально на 90% — поки не перестають.
  • Продуктивність і надійність ZFS сильно корелюють із вільним простором, особливо для випадкових
    записів, snapshot-ів, RAIDZ і навантажень із великою кількістю метаданих.

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

Ось модель, яка збереже вам роботу: вільний простір не є взаємозамінним.
Десять терабайтів вільного простору, розсипані крихтами по 128 KB, — не те саме, що десять терабайтів у великих чистих шматках.
ZFS — copy-on-write. Воно не перезаписує блоки на місці; воно пише нові блоки і оновлює вказівники.
Це означає, що йому постійно потрібен новий простір для змін — навіть для найменших.

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

То чому саме 70%?

70% — не суворий ліміт; це індикатор тривоги. Вище за нього, особливо на пулах із RAIDZ vdevs, аллокатор має менше великих екстентів.
Фрагментація зростає, кількість метаданих і супутніх оновлень збільшується, і кожен запис стає маленькими перемовинами з фізикою.

«Правильний» поріг залежить від:

  • структури RAID (mirror vs RAIDZ1/2/3)
  • ширини vdev
  • вибору ashift
  • співвідношення recordsize/volblocksize і розміру IO у навантаження
  • частоти та термінів зберігання snapshot-ів
  • використання special vdev (метадані/маленькі блоки)
  • як часто дані перезаписуються (бази даних проти архівів)

Якщо хочете політику, яка не приведе вас в дискусію з ревізійною комісією: плануйте приблизно
~70–80% максимум для RAIDZ-важких пулів із випадковими записами або багатьма snapshot-ами, і дозволяйте
~80–90% лише коли ви добре знаєте своє навантаження і маєте протестований шлях розширення.

Факти та історія, що пояснюють поведінку

Інженери зберігання люблять фольклор. Ось конкретні факти — корисні — які пояснюють, чому ZFS поводиться інакше, ніж
традиційні файлові системи й RAID-стеки.

  1. ZFS з’явився в Sun Microsystems на початку 2000-х як об’єднаний volume manager і filesystem,
    спроєктований, щоб уникнути гри «RAID-контролер проти файлової системи».
  2. Copy-on-write не винайшов ZFS, але ZFS зробив його масовим у корпоративному зберіганні:
    це дозволило дешеві snapshot-и та сильну консистентність на диску без ritual-ів із fsck.
  3. ZFS використовує модель pooled storage: datasets вирізаються логічно, а не статичними розділами.
    Така гнучкість також означає, що quotas, reservations і snapshot-и можуть створювати несподівані сценарії «куди подівся мій простір».
  4. RAIDZ — це не «RAID5 в софті». Він має змінні stripe-записи й іншу поведінку алокації. Він може бути ефективним,
    але й більш чутливий до фрагментації, ніж mirrors.
  5. Аллокатор працює в metaslabs (шматки простору на vdev), і має кілька стратегій залежно від фрагментації та доступних екстентів.
    Коли metaslabs заповнюються, алокація стає дорожчою.
  6. «Slop space» існує навмисно: ZFS резервує шматок простору, щоб система могла продовжувати працювати,
    навіть коли користувачі намагаються його заповнити. Це не марний простір; це антипанічний механізм.
  7. За замовчуванням recordsize 128 KB має історію: це компроміс, дружній до пропускної спроможності для послідовних навантажень,
    але не універсальна істина для баз даних або VM-дисків.
  8. Диски з 4K секторами змінили правила гри: вибір ashift=9 vs ashift=12 впливає на ефективність простору та продуктивність.
    Помилка тут коштує надовго.
  9. Special vdev з’явилися пізніше в еволюції OpenZFS: вони потужні для метаданих/малих блоків, але створюють новий режим відмови — «ви можете першим заповнити не те».

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

Куди йде простір: механіка за обривом

1) Copy-on-write перетворює оновлення на алокації

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

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

2) RAIDZ-ампліфікація: парітет не безкоштовний, і малі записи також

Mirrors прості: записуємо дві копії — і готово. RAIDZ потребує парітету, і для часткових записів stripe може знадобитися
читання старих даних/парітету для обчислення нового парітету (read-modify-write), якщо неможливо зробити full-stripe write.

Коли вільного простору стає менше і фрагментація зростає, ZFS важче збирає full-stripe алокації.
Результат: більше часткових stripe, більше RMW, більше IO-операцій на логічний запис і підвищена латентність.

3) Фрагментація — це не одне число; це рівень болю для аллокатора

ZFS повідомляє фрагментацію на рівні пулу, але аллокатор відчуває фрагментацію на рівні metaslab.
Пул може показувати «лише» 30% фрагментації і при цьому мати кілька metaslabs, які фактично ворожі до алокації.

Ключова думка: фрагментація зростає природно в COW-файлових системах, коли дані перезаписуються і існують snapshot-и.
Це керовано за наявності вільного простору. Без нього — неприємно.

4) Метадані й непрямі блоки ростуть з snapshot-ами та чурном

Snapshot-и не копіюють дані миттєво; вони зберігають версії блоків. Це означає, що видалення файлів не обов’язково
звільнить простір, якщо snapshot усе ще посилається на старі блоки.

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

5) Резерв «slop space»: пул не бреше — вас просто не запросили

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

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

6) Reservations, refreservations і quotas: мовчазні блокатори простору

ZFS дозволяє резервувати простір для datasets (reservation), а для томів є
refreservation (простір гарантований для цього dataset-у, а не для нащадків).
Це корисний інструмент — доки хтось не встановить reservation «на всяк випадок» і не забуде про нього.

Quotas також можуть зробити «pool free» неактуальним для конкретного dataset-а. Пул може мати простір; ваш dataset усе одно може
отримати жорстку заборону на ріст.

7) Special vdev і малі блоки: метадані можуть закінчитися першими

Special vdevs (часто SSD) зберігають метадані і, опційно, малі блоки. Якщо ви їх занадто мало виділили, пул може опинитися
в поганому стані: special vdev заповнюється, і алокація обмежується, хоча основні data vdevs ще мають місце.

Це одна з тих можливостей, яка потужна в досвідчених руках і можливість кар’єрного росту у недосвідчених.

8) ashift і накладні витрати на паддінг: смерть від вирівнювання

Якщо ви обрали ashift занадто малий відносно фізичного розміру сектора диска, ви можете отримати write amplification і проблеми з продуктивністю.
Якщо обрали занадто великий — втрачаєте простір на маленьких блоках через паддінг. При великій кількості дрібних файлів чи метаданих ця втрата стає помітною рано.

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

Коли хтось каже «ZFS повний» або «ZFS повільний» і треба швидко провести триаж — не блукайте. Дійте в цій послідовності.
Мета — визначити, чи маєте ви справу з (a) реальним виснаженням ємності, (b) блокуванням через snapshot-и, (c)
обмеженням через quota/reservation, (d) насиченням special vdev, або (e) болем аллокатора через фрагментацію.

Спочатку: підтвердьте, що означає «повний»

  1. Перевірте використання пулу та стан: zpool list, zpool status
  2. Перевірте використання dataset-ів проти доступного: zfs list
  3. Перевірте, чи quota/reservation не блокують записи: zfs get quota,reservation,refquota,refreservation

По-друге: знайдіть, що «приковує» простір

  1. Перевірте snapshot-и і їхній «used»: zfs list -t snapshot
  2. Перевірте, чи видалення не звільняє простір через snapshot-и: порівняйте dataset used vs refer

По-третє: діагностуйте біль аллокатора

  1. Перевірте фрагментацію: zpool list -o fragmentation
  2. Перевірте насичення metaslab/special vdev: zpool list -v, zpool status -v
  3. Перевірте затримки IO і черги: zpool iostat -v 1

По-четверте: вирішіть, чи потрібне термінове полегшення, чи структурні зміни

  • Термінове полегшення: видалити snapshot-и, скоротити retention, зняти reservations, додати місце, перемістити «гарячі» datasets.
  • Структурне: переглянути ширину vdev, перемістити БД/VM на mirror-и, правильно додати special vdev, налаштувати recordsize/volblocksize, виправити політику snapshot-ів.

Практичні завдання: команди, виводи та рішення (12+)

Це реальні операційні кроки. Кожне завдання включає: команду, приклад виводу, що це означає і яке рішення прийняти.
Команди передбачають OpenZFS на Linux, але концепти застосовні ширше.

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

cr0x@server:~$ zpool list
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  27.2T  20.1T  7.08T        -         -    41%    74%  1.00x  ONLINE  -

Значення: Пул використаний на 74% і має 41% фрагментації. «ONLINE» — добре; аллокатору ще не боляче.

Рішення: При ~74% і 41% frag розглядайте це як «наближення до обриву» для RAIDZ + навантажень із великим churn.
Почніть планувати полегшення (місце, очищення snapshot-ів, переміщення навантаження).

Завдання 2: Перевірити баланс алокації по vdev (і виявити прихований вузький горлечко)

cr0x@server:~$ zpool list -v tank
NAME         SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank        27.2T  20.1T  7.08T        -         -    41%    74%  1.00x  ONLINE  -
  raidz2-0  18.1T  14.9T  3.22T        -         -    48%    82%      -  ONLINE
  raidz2-1  9.06T  5.23T  3.83T        -         -    19%    57%      -  ONLINE

Значення: Один vdev на 82% і сильно фрагментований, інший — на 57%. Небаланс алокації трапляється;
він болить, бо найзаповненіший vdev стає лімітером.

Рішення: Плануйте ємність на рівні vdev, а не лише на рівні пулу. Розгляньте додавання однакових vdev-ів,
міграцію даних для ребалансування (send/receive) або перегляд дизайну.

Завдання 3: Підтвердити вигляд на рівні dataset (що бачать додатки)

cr0x@server:~$ zfs list
NAME              USED  AVAIL  REFER  MOUNTPOINT
tank              20.1T  5.92T   160K  /tank
tank/prod         18.4T  5.92T  12.2T  /tank/prod
tank/prod/vm      9.80T  5.92T  9.80T  /tank/prod/vm
tank/prod/db      6.10T  5.92T  6.10T  /tank/prod/db
tank/backups      1.62T  5.92T  1.62T  /tank/backups

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

Рішення: Якщо додаток каже «нема місця», а AVAIL виглядає нормально, підозрюйте quotas/reservations або slop space,
а не сире місце.

Завдання 4: Виявити quotas і reservations, що тихо душать вас

cr0x@server:~$ zfs get -r quota,refquota,reservation,refreservation tank/prod
NAME          PROPERTY        VALUE     SOURCE
tank/prod     quota           none      default
tank/prod     refquota        none      default
tank/prod     reservation     none      default
tank/prod     refreservation  none      default
tank/prod/vm  quota           10T       local
tank/prod/vm  refquota        none      default
tank/prod/vm  reservation     none      default
tank/prod/vm  refreservation  9T        local

Значення: tank/prod/vm гарантовано має 9T незалежно від нащадків і обмежено 10T.
Це може позбавити інших datasets простору або зробити «вільний простір» оманливим.

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

Завдання 5: Виявити, що snapshot-и «приковують» простір (класична ситуація: «видалив файли, але нічого не змінилося»)

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation | tail -n 6
tank/prod/db@auto-2026-02-03_0100    0B  6.10T  Mon Feb  3 01:00 2026
tank/prod/db@auto-2026-02-03_0200  18G  6.10T  Mon Feb  3 02:00 2026
tank/prod/db@auto-2026-02-03_0300  22G  6.10T  Mon Feb  3 03:00 2026
tank/prod/db@auto-2026-02-03_0400  25G  6.10T  Mon Feb  3 04:00 2026
tank/prod/db@auto-2026-02-03_0500  27G  6.10T  Mon Feb  3 05:00 2026
tank/prod/db@auto-2026-02-03_0600  31G  6.10T  Mon Feb  3 06:00 2026

Значення: Snapshot-и накопичують «USED» (змінені блоки). Це простір, який не можна звільнити, поки snapshot-и не завершаться.

Рішення: Якщо потрібен терміновий простір, видаліть найбільші/найстаріші snapshot-и першими (обережно), потім виправте політику зберігання.
Якщо «USED» маленький, snapshot-и сьогодні не головний підозрюваний.

Завдання 6: Знайти datasets з підозрілою різницею між USED та REFER

cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | head
NAME           USED  REFER  COMPRESSRATIO
tank/prod      18.4T  12.2T  1.12x
tank/prod/vm   9.80T  9.80T  1.01x
tank/prod/db   6.10T  6.10T  1.37x

Значення: tank/prod USED значно більший за REFER через те, що snapshot-и/нащадки приковують простір.

Рішення: Дослідіть політику snapshot-ів і datasets з високим churn. Перенесіть VM і DB datasets в окремі пули або типи vdev,
якщо вони домінують у перезаписах.

Завдання 7: Переглянути фрагментацію та ємність пулу одночасно

cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag
NAME   SIZE  ALLOC   FREE  CAP  FRAG
tank  27.2T  20.1T  7.08T  74%  41%

Значення: FRAG — грубий сигнал. Після ~50% фрагментації на «гарячому» пулі починає діяти правило «вільний простір є, але швидких записів нема».

Рішення: Якщо FRAG зростає разом з CAP, пріоритет — додати місце або зменшити churn. Фрагментація рідко «заліковується» сама по собі.

Завдання 8: Спостерігати реальний IO-пресинг і який vdev страждає

cr0x@server:~$ zpool iostat -v tank 1 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        20.1T  7.08T    220    980  18.4M  96.7M
  raidz2-0                  14.9T  3.22T    170    770  14.1M  76.2M
  raidz2-1                  5.23T  3.83T     50    210   4.3M  20.5M
--------------------------  -----  -----  -----  -----  -----  -----

Значення: raidz2-0 виконує більшість записів і також є більш заповненим та фрагментованим vdev. Це пастка для продуктивності.

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

Завдання 9: Підтвердити ashift (накладні витрати на простір/продуктивність, які не «підправиш пізніше»)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head -n 8
38:    vdev_tree:
49:            ashift: 12
73:            ashift: 12

Значення: ashift: 12 означає 4K сектори. Добре для сучасних дисків; це запобігає проблемам з невирівнюванням.

Рішення: Якщо ви виявили ashift=9 на 4K дисках, не «надійтесь, що все ок». Плануйте міграцію і перебудову.
Це одна з небагатьох помилок ZFS, яка «приклеюється» надовго.

Завдання 10: Перевірити наявність special vdev і чи він майже повний

cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
config:

        NAME                         STATE     READ WRITE CKSUM
        tank                         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
            sde                      ONLINE       0     0     0
            sdf                      ONLINE       0     0     0
          special
            mirror-1                 ONLINE       0     0     0
              nvme0n1                ONLINE       0     0     0
              nvme1n1                ONLINE       0     0     0

Значення: Існує special vdev (mirrored NVMes). Тепер його потрібно вважати критичним сховищем метаданих.

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

Завдання 11: Побачити алокацію special vdev на верхньому рівні

cr0x@server:~$ zpool list -v tank | sed -n '1,8p'
NAME         SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank        27.2T  20.1T  7.08T        -         -    41%    74%  1.00x  ONLINE  -
  raidz2-0  18.1T  14.9T  3.22T        -         -    48%    82%      -  ONLINE
  special    1.81T  1.55T   260G       -         -    12%    85%      -  ONLINE

Значення: Special vdev на 85% заповнений. Це не «добре». Це пожежна тривога в уповільненому режимі.

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

Завдання 12: Перевірити recordsize і volblocksize (невідповідність навантаженню викликає ампліфікацію)

cr0x@server:~$ zfs get recordsize,compression tank/prod/db
NAME          PROPERTY     VALUE     SOURCE
tank/prod/db  recordsize   128K      default
tank/prod/db  compression  zstd      local

Значення: DB dataset використовує recordsize 128K. Для багатьох БД з 8K/16K IO це може збільшити read/modify/write і
фрагментацію під час churn.

Рішення: Розгляньте встановлення recordsize=16K (або вирівнювання за розміром сторінки вашої БД) для нових даних,
і перевірте на бенчмарку. Не слідуйте за модою — тестуйте.

Завдання 13: Показати коефіцієнт компресії і вирішити, чи це важливий важіль ємності

cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | grep -E 'db|vm'
tank/prod/vm  9.80T  9.80T  1.01x
tank/prod/db  6.10T  6.10T  1.37x

Значення: VM-дані майже не стискаються; DB-дані помірно стискаються.

Рішення: Компресія не врятує VM-пули. Для БД вона може дати час, але не будьте залежними від неї як від плану ємності.

Завдання 14: Швидко визначити великі snapshot-и (хто накопичує вчора)

cr0x@server:~$ zfs list -t snapshot -o name,used -s used | tail -n 5
tank/prod/vm@weekly-2026-01-05  412G
tank/prod/vm@weekly-2026-01-12  438G
tank/prod/vm@weekly-2026-01-19  451G
tank/prod/vm@weekly-2026-01-26  466G
tank/prod/vm@weekly-2026-02-02  489G

Значення: Щотижневі VM-snapshot-и кожен «приковують» сотні ГБ. Це очікувано при churn, але саме тому ваш пул «заповнюється раніше».

Рішення: Звузьте retention для VM datasets або перемістіть VM-snapshot-и в окремий backup-ціль через реплікацію.

Завдання 15: Перевірити, чи видалення справді відновлює простір (не заблоковано через holds)

cr0x@server:~$ zfs holds tank/prod/vm@weekly-2026-02-02
NAME                            TAG  TIMESTAMP
tank/prod/vm@weekly-2026-02-02  keep  Mon Feb  3 09:12 2026

Значення: Існує hold на snapshot. Хтось «захистив» його. Простір не звільниться, доки hold не буде знятий.

Рішення: Аудитуйте holds. Якщо hold не виправданий — зніміть і видаліть snapshot. Якщо він потрібен за комплаєнсом, прийміть реальність і додайте місце.

Завдання 16: Оцінити логічне проти фізичного використання (і виявити приховані накладні)

cr0x@server:~$ zfs get -o name,property,value -s local,default logicalused,logicalreferenced,used,refer tank/prod
NAME       PROPERTY           VALUE
tank/prod  logicalused        19.9T
tank/prod  logicalreferenced  12.2T
tank/prod  used               18.4T
tank/prod  refer              12.2T

Значення: Logical used більше за фізичне used через компресію, але розрив між USED і REFER вказує на ефекти snapshot-ів/нащадків.

Рішення: Використовуйте логічні метрики для «скільки додаток вважає, що має», і фізичне used для «наскільки повний пул насправді».

Три корпоративні міні-історії з операційної практики

1) Інцидент через хибне припущення: «вільний простір — це просто вільний простір»

Середня компанія запускала внутрішню аналітичну платформу на RAIDZ2-пулі ZFS. Було просто: кілька великих datasets, щомісячні snapshot-и і стабільний потік ETL-джобів. Хтось помітив, що пул довго тримався близько 78% і вирішив, що це «безпечно», бо ніколи не сягав 90%.

Потім додали нове навантаження: write-heavy задачу, що перезаписувала кілька терабайтів щоденно. На папері це вміщалося. На практиці пул почав тайм-аутити. Логи додатку показували періодичні повідомлення «no space left»; пул усе ще мав кілька терабайтів вільних. Люди звинувачували базу даних. Люди звинувачують базу даних так, як моряки звинувачують море.

Справжня проблема була в поведінці аллокатора під churn і з snapshot-ами. ETL-джоб не лише писав нові дані — він перезаписував існуючі блоки. Copy-on-write плюс snapshot-и означали, що старі версії лишалися прикутими. Вільний простір фрагментувався, metaslabs важче виділяли простір, і RAIDZ мав проблеми зі складанням ефективних stripe-ів.
Латентність росла, поки логіка повторних спроб джоба не добила систему.

Виправлення було непоказним. Вони призупинили нові snapshot-и для churn-датасету, скоротили retention і перемістили ETL scratch-простір у пул на mirror-ах, спроєктований для випадкових записів.
Урок: «вільний пул» — це не те саме, що «здоровий пул», а %CAP не гарантує продуктивності.

2) Оптимізація, що повернулася бумерангом: «ввімкнемо dedup; зберігання дороге»

Інша організація хотіла зменшити витрати на зберігання для образів VM і беккопій. Вони увімкнули dedup на dataset-і з великою кількістю схожих даних. Перший тиждень виглядав чудово — поки не почалися проблеми з пам’яттю, і коробки почали свопити під навантаженням.

Dedup у ZFS — це не просто перемикач оптимізації. Це проєктна відповідальність: він створює таблицю дедуплікації, яку треба перевіряти при записах, і йому потрібна RAM та низьколатентний доступ до метаданих.
Вони не мали достатньо RAM, і їхні метадані жили на тих самих обертових дисках, що й усе інше.

Коли пул наповнився, таблиці dedup і активність метаданих лише погіршили ситуацію. Латентність зросла, продуктивність стала нестабільною, а видалення snapshot-ів — нормальна господарська задача — перетворилося на нічну марівню. Вони не просто зробили пул меншим; вони ускладнили ним керування в умовах обмеженого простору.

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

3) Нудна, але правильна практика, що врятувала день: «тримайте запас і план росту»

Фінансово орієнтована firma підтримувала клієнтські API на пулі ZFS зі строгими SLO на латентність. Інженер зберігання не користувався любов’ю на етапі бюджету, бо наполягав на 25–30% вільного простору і письмовому плані зростання. Це здавалося консервативним. І воно було.

Раз на квартал зміни продукту підвищили write amplification: більше індексів, більше дрібних оновлень, більше churn-у snapshot-ів.
Вони побачили ранні сигнали — фрагментація підповзала, special vdev ставав повнішим, час scrub-ів подовжувався — і відреагували як на планове явище, а не як на аварію.

Вони виконали попередньо погоджений план: додали новий набір vdev-ів, ребалансували, мігрувавши найгаласливіший dataset до свіжого пулу реплікацією, потім відкоригували retention для churn-датасетів. Користувачі нічого не помітили.
Керівництво теж не помітили — а це найвища похвала для операцій.

Нудна практика була не просто «тримати запас». Це була інструментована політика плюс правило прийняття рішень: якщо CAP > 75% і frag > 35% на гарячому пулі, стартує тикет на розширення. Ніяких героїчних вчинків. Без ночних підрахунків.

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

1) «Я видалив купу даних, але простір не повернувся»

Симптом: Dataset показує менше живих даних, але ALLOC пулу майже не змінився.

Корінь: Snapshot-и все ще посилаються на блоки; видалення лише прибирає поточні посилання.

Виправлення: Знайдіть snapshots з високим USED (zfs list -t snapshot -o name,used -s used), видаліть або скоротіть retention, зніміть holds, якщо доречно.

2) «Пул показує 10% вільного, але записи падають з ENOSPC»

Симптом: Додатки помиляються; zpool list ще показує якийсь FREE.

Корінь: Slop space reserve, quota/refquota або виснаження metaslab-ів на певному vdev (загальний вільний пул не допоможе, якщо один vdev є лімітером).

Виправлення: Перевірте quotas/reservations; перевірте CAP по vdev; додайте ємність або мігруйте дані. Не намагайтеся «перетерпіти».

3) «Продуктивність впала після перетину ~80%»

Симптом: Стрибки латентності, падіння пропускної здатності, зростання CPU у шляхах IO ядра.

Корінь: Фрагментація + RAIDZ часткові stripe-записи + churn метаданих.

Виправлення: Створіть запас (видаліть snapshot-и, додайте vdev-и, перемістіть churn-ні навантаження). У довгому терміні використовуйте mirror-и для навантажень із великою кількістю випадкових записів.

4) «Один vdev майже заповнений, але пул ні»

Симптом: CAP пулу виглядає нормально, але один топ-рівневий vdev має дуже високі CAP/FRAG.

Корінь: Дисбаланс алокації від додавання невідповідних розмірів vdev-ів або історичних рішень по алокації.

Виправлення: Додавайте vdev-и, що відповідають існуючим за розміром; розгляньте міграцію даних до нового пулу для перерозкладки.

5) «Special vdev заповнився і тепер усе дивно»

Симптом: Записи повільні або падають; операції над метаданими зависають; пул ще має місце на HDD vdev-ах.

Корінь: Special vdev занадто малий для метаданих/малих блоків; це жорстка залежність.

Виправлення: Припиніть агресивне відвантаження малих блоків, підвищте розмір special vdev додаванням ємності, якщо безпечно, або перебудуйте пул. Моніторте CAP special vdev як кисень.

6) «Ми виставили reservations на всяк випадок і тепер місця немає»

Симптом: Деякі datasets мають місце, інші не можуть рости; pool free виглядає неправдиво.

Корінь: Reservations/refreservations збирають простір незалежно від реального використання.

Виправлення: Видаліть або підберіть reservations; залишайте їх тільки для по-справжньому критичних dataset-ів з жорсткими гарантіями.

7) «Ми налаштували recordsize і стало гірше»

Симптом: Більше IO, більше фрагментації, гірша латентність після «зміни продуктивності».

Корінь: Невідповідність recordsize/volblocksize навантаженню, плюс існуючі дані не перезаписані під новий розмір.

Виправлення: Протестуйте на репрезентативних IO-патернах; застосовуйте на рівні dataset; перезапишіть дані, якщо нова геометрія блоків має стати реальною.

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

Політика планування місця (що робити, а не що обговорювати)

  1. Встановіть цільовий CAP для типу пулу:
    • RAIDZ + snapshot-и + випадкові записи: ціль ≤ 75–80% використання.
    • Mirror-и + переважно послідовні записи + низький churn: ціль ≤ 85–90% використання (з моніторингом).
  2. Плануйте на рівні vdev: один «гарячий» vdev може визначити поріг відмови.
  3. Припускайте, що snapshot-и ростимуть: зберігання — це споживач ємності, а не пункт у чеклісті.
  4. Документуйте дії зростання: що додаєте, скільки часу це займає і хто погоджує.
  5. Моніторте special vdev окремо: ставтеся до нього як до пулу в середині пулу.

План екстреного звільнення простору (коли потрібно місце сьогодні)

  1. Підтвердьте, що вас не блокують quotas/reservations; спочатку зніміть ненавмисні.
  2. Видаліть найбільш USED snapshot-и, які можна безпечно прибрати; враховуйте holds.
  3. Тимчасово припиніть створення нових snapshot-ів для churn-датасетів.
  4. Перенесіть тимчасові/scratch-навантаження з обмеженого пулу.
  5. Додайте ємність (нові vdev-и), якщо це безпечно і швидко; інакше реплікуйте на свіжий пул.

План структурного виправлення (щоб це не повторилося)

  1. Класифікуйте навантаження: VM-диски, бази даних, логи, бекупи, архіви медіа.
  2. Підігнати дизайн під навантаження:
    • Для випадкових записів: mirror-и (і достатньо vdev-ів для IOPS).
    • Переважно послідовні, ємнісні дані: RAIDZ2/3 з планованим запасом.
  3. Встановлюйте recordsize/volblocksize на рівні dataset відповідно до IO-профілю, а не за відчуттями.
  4. Snapshot-и зі змістом: коротше зберігання для churn-датасетів, довше для майже-append-only даних.
  5. Автоматизуйте алерти «CAP + FRAG» з жорсткою операційною політикою: при перевищенні порогів починається розширення.

Ідея з надійності, яку варто запозичити

Парафразована ідея, з посиланням: Робота Richard Cook «How Complex Systems Fail» стверджує, що безпека — це проблема контролю, а не компонентів.

Планування простору — це мініатюра цього: надійності не досягають, сподіваючись, що пули залишаться акуратними; її отримують, контролюючи запас і швидкість змін.

Поширені питання

1) Чи завжди правило «тримайте ZFS нижче 80%» правильне?

Ні. Це налаштування політики. Для RAIDZ-пулів з churn і snapshot-ами 70–80% — надійний дефолт.
Для mirror-пулів з переважно послідовними записами і низьким churn-ом можна працювати вище — за умови моніторингу фрагментації і латентності та наявності протестованого шляху розширення.

2) Чому ZFS сповільнюється більше, ніж ext4 при нестачі місця?

Copy-on-write означає, що оновлення вимагають нових алокацій, а RAIDZ прагне великих, добре вирівняних записів для ефективності.
Коли вільний простір фрагментується, ZFS витрачає більше зусиль на пошук місця, записи стають менш суміжними, і робота парітету зростає.

3) Чому df і zfs list показують різні дані про вільний простір?

Вони звітують про різні шари. df показує те, що монтувана файлова система рекламує, тоді як zfs list звітує про облік dataset-ів в пулі,
що зазнає впливу quotas, reservations і поведінки slop space.

4) Чи snapshot-и споживають простір, якщо нічого не змінюється?

Сам по собі snapshot дешевий, але зміни після створення snapshot-а коштують простору, бо старі версії блоків треба зберегти.
Якщо dataset переважно додатковий, snapshot-и ростуть повільно. Якщо дані перезаписуються — snapshot «USED» зростає швидко.

5) Чи можна «дефрагментувати» ZFS-пул?

Не на місці, як старі інструменти дефрагментації. Практичний дефраг — це міграція: zfs send | zfs receive на свіжий пул або dataset,
або переписування даних у нові блоки. Запас простору — справжній інструмент проти фрагментації.

6) Чи вирішить додавання vdev фрагментацію?

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

7) Яка найпоширеніша помилка планування місця зі special vdev-ами?

Недостатнє їхнє виділення і забуття, що тепер вони критичні. Якщо special vdev заповниться, можуть виникнути серйозні проблеми з продуктивністю або відмови алокації.
Розмірюйте їх з запасом, дзеркальте і моніторте окремо.

8) Чи варто використовувати компресію як стратегію ємності?

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

9) Чому мої щотижневі VM-snapshot-и такі великі?

VM-диски інтенсивно змінюються: оновлення ОС, логи, swap, бази даних у ВМ і випадкові записи. Snapshot-и зберігають старі блоки, тож churn дорівнює росту snapshot-ів.
Виправлення — політика retention, стратегія реплікації і, іноді, переміщення VM-даних на дизайн, який краще витримує churn.

10) Яка найбезпечніша негайна дія, коли пул наближається до повного?

Створіть запас: скоротіть retention snapshot-ів і видаліть найбільш USED snapshot-и, які можна безпечно прибрати, потім додайте ємність.
Не чекайте на тригер «100% used»; ZFS може опинитися в операційній пастці раніше.

Наступні кроки, які можна зробити цього тижня

  1. Встановіть явну політику CAP для кожного пулу (за робочим навантаженням і типом vdev). Якщо ви не можете пояснити, чому це 85% замість 75% — робіть 75%.
  2. Реалізуйте алерт «CAP + FRAG» за допомогою zpool list, і повістіть людей до дій раніше, ніж користувачі почнуть надсилати звернення.
  3. Аудитуйте політику snapshot-ів для churn-датасетів (VM, БД). Якщо snapshot-и — ваша стратегія бекапу, добре — але нехай таргет бекапу платить за це, а не продакшн.
  4. Аудитуйте quotas/reservations. Приберіть все, що є «на всяк випадок». «На всяк випадок» — це як помирає сховище тихо.
  5. Перевірте використання special vdev. Якщо воно вище 70–80%, вважайте це терміновою інженерною задачею.
  6. Напишіть план зростання: хто погоджує розширення, як ви додаєте vdev-и, і коли обираєте міграцію замість розширення.

Другий короткий жарт, бо знадобиться під час наступного огляду ємності: Єдина річ більш оптимістична, ніж прогноз продажів — це пул, запланований на 99%.

Основна дисципліна проста: ZFS хоче запас так само, як бази даних хочуть індекси — бо альтернатива — хаос,
і хаос завжди знаходить вікно технічного обслуговування, яке ви не запланували.

← Попередня
SMB Multichannel: коли допомагає (і коли шкодить)
Наступна →
Dovecot: IMAP працює повільно — 7 налаштувань, що справді мають значення

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