Slop-простір ZFS: чому пули здаються заповненими до 100%

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

ZFS має дивну звичку: пул доходить до «90% використано» і раптом поводиться так, ніби у нього немає запасу пального. Записи сповільнюються, алокації відмовляють, і ваш моніторинг миготить, мов ігровий автомат. Але zpool list все ще стверджує, що місце є. Якщо ви коли-небудь бурмотіли «ZFS мені брешe», ви не самотні — і ви не зовсім неправі. ZFS робить математику, яка вашому ментальному моделі, ймовірно, невідома.

Це історія про slop-простір, а також інші невидимі чинники, які змушують пул здаватись повним до 100%: copy-on-write (CoW), метадані, вирівнювання блоків, снапшоти, резервації, special vdev і фрагментація. Ми розглянемо це як довідник оператора, а не рекламний буклет: що відбувається, як виміряти, як виправити і як не повторити той самий інцидент з іншим номером заявки.

Що таке slop-простір (і чим він не є)

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

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

Операційно slop проявляється як: «Available» у zfs list менше, ніж ви очікуєте, і іноді dataset відмовляється від записів, хоча zpool list каже, що місце є. Деталі залежать від платформи (версія OpenZFS, інтеграція ОС), але урок для оператора послідовний: не плануйте використовувати останні кілька відсотків пулу і не ставте «100%» як ціль проєктування.

Чим slop-простір не є

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

Жарт №1: Якщо ви вважаєте, що можете тримати пул ZFS на 99.9% завжди, ви не оптиміст — ви неоплачуваний інженер хаосу.

Чому пули здаються заповненими до 100%

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

1) CoW потребує робочого простору

На CoW-файловій системі невеликий запис користувача може викликати ланцюжок алокацій:

  • Нові блоки даних
  • Нові проміжні блоки (якщо дерево росте або має бути перезаписане)
  • Нові блоки метаданих (вказівники блоків, dnodes, spacemaps, класи алокації)
  • Відкладені звільнення (простір звільняється пізніше, не одразу)

Ось чому «я записую тільки 1 GB» може зазнати невдачі, коли пул показує «2 GB вільного». На останніх відсотках повного пулу аллокатор може потребувати спробувати багато кандидатів, фрагментуючи ще більше і витрачаючи метадані. ZFS не драматизує; він намагається не пошкодити себе.

2) Slop-простір: маржа безпеки на рівні пулу

Slop-простір зазвичай обчислюється як частка від розміру пулу з капом, і точні механіки змінювалися між реалізаціями й версіями. Наслідок: деяка частина вільного простору розглядається як фактично недоступна для звичайних dataset, щоб зберегти працездатність пулу. Коли ви бачите, що dataset показує «0 avail», хоча пул усе ще має «трохи» вільного, часто це дія slop.

Це не фіксовані «3%». На великих пулах може бути кап; на менших — пропорційно значніше. У реальному житті на невеликих лабораторних коробках здається, що ZFS вкрав у вас обідні гроші; на сотнях терабайтів це виглядає як розумний податок за стабільність.

3) 128 KiB записи, 4K сектора та тиранія вирівнювання

Більшість ZFS dataset за замовчуванням мають recordsize близько 128 KiB. Більшість дисків мають фізичні сектори 4K; деякі — 512e; деякі флеш-пристрої мають більші внутрішні сторінки. ZFS має вирівнювати записи під ashift, експоненту розміру сектору пулу. Пул з ashift=12 використовує 4K сектори; ashift=13 — 8K тощо.

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

4) RAIDZ-паритет і проблема «останнього кола»

RAIDZ-алокація — хитра, але вона не чарівна. Коли вільний простір фрагментується, ZFS має менше варіантів зібрати повні смуги. Це може підвищити write amplification і зменшити ефективний вільний простір. Останні 10–20% RAIDZ-пулу можуть відчуватись, як сироп: не тому, що ZFS лінивий, а тому, що знайти «хороші» алокації стає важче, і аллокатору доводиться працювати інтенсивніше, щоб уникнути патологічних розміщень.

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

5) Метадані — це реальний простір

ZFS зберігає багато метаданих: вказівники блоків, dnodes, проміжні блоки, spacemaps, контрольно-суми та (залежно від фіч) додаткові структури. Чим більше файлів, снапшотів, клонів і маленьких блоків, тим більше метаданих ви тягнете за собою.

Якщо ви додаєте special vdev для метаданих і маленьких блоків, облік змінюється: метадані можуть більше не споживати «звичайний» простір, але тепер у вас може з’явитись інша стінка — ємність special vdev. Коли special vdev заповнюється, пул може бути фактично неробочим, навіть якщо основні vdev мають простір.

6) Снапшоти: простір, який ви не бачите, доки не стане запізно

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

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

7) Резервації, refreservation і підводні камені volblocksize

Резервації (для файлових систем) і refreservations (часто для zvol) відтинають простір, щоб dataset міг продовжувати писати навіть під тиском. Це добре — але це також може зробити інші dataset виглядати заповненими раніше, бо залишковий простір уже обіцяний комусь іншому.

Zvol додають ще один рівень: блоковий розмір тому (volblocksize) взаємодіє з навантаженням і ashift. Невідповідність може витрачати простір і підвищувати write amplification, що робить «хвіст» пулу ще гіршим.

8) Відкладені звільнення та таймінги транзакційних груп

ZFS пакетно обробляє роботу в транзакційні групи (TXGs). Звільнення можуть бути відкладені; простір може бути «логічно звільнений», але не відразу доступний для повторного використання, поки відповідний TXG не закомітиться і синк не завершиться. У пулі під сильним навантаженням записів ви можете побачити розрив між тим, що застосунки вважають звільненим, і тим, що аллокатор може реально використати зараз.

Жарт №2: «Кажуть, 5% вільно» — це аналогічно до «приїду за п’ять хвилин» у світі сховищ — може бути правдою, але на неї не варто ставити виробничий реліз.

Цікаві факти та історія

Декілька контекстних пунктів, що пояснюють, чому ZFS поводиться так, як він поводиться — і чому «обрив повного пулу» є відомим, запроектованим компромісом, а не багом.

  1. ZFS з’явився в середині 2000-х з end-to-end checksumming і CoW як базовими принципами, пожертвувавши простотою перезапису заради цілісності й семантики снапшотів.
  2. «Правило 80%» передує ZFS: класичні Unix-файлові системи (як UFS/FFS) резервували простір (часто ~10%) для root і щоб зменшити фрагментацію. Різний механізм, та сама мета: уникнути смерті через повний диск.
  3. RAIDZ не те саме, що RAID5: RAIDZ інтегрує алокацію і паритет у файлову систему, уникаючи write hole через CoW і транзакційну семантику.
  4. ashift — назавжди: після створення пулу вибір розміру сектору фактично стає постійним для того vdev. Люди дізнаються про це відразу після того, як дізнаються, що таке ashift.
  5. Special vdev змінив правила гри: метадані і маленькі блоки можна перенаправити на швидші пристрої, але це також ввело новий режим «пул здається заповненим»: виснаження special vdev.
  6. Спорадичність снапшотів — сучасний вбивця ємності: ZFS зробив снапшоти дешевими у створенні, що призвело до звичок «snapshot everything» — і до усвідомлення, що видалення не означає звільнення.
  7. Стиснення змінило планування ємності: з lz4 і подібними «логічне використання» й «фізичне використання» розходяться, що ускладнює інтуїцію і робить «вільний простір» непослідовним.
  8. Copy-on-write означає write amplification: особливо під фрагментацією і на RAIDZ, малі випадкові записи можуть коштувати більше фізичних IO і оновлень метаданих, ніж їхній розмір підказує.
  9. Облік простору еволюціонував між версіями OpenZFS: поля на кшталт usedby* і покращення в звітуванні полегшили віднесення простору, але також виявили, в скільки відер простір може впасти.

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

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

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

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

О 02:13 збільшилась латентність записів. О 02:17 кілька VM повідомили про помилки диска. О 02:19 пул почав кидати ENOSPC для dataset, який «повинен був мати місце». Він не обманював: dataset досяг точки, де ZFS більше не віддає останній шматок резервного простору пулу для звичайних алокацій. Slop-простір плюс фрагментація означали, що «вільний» не означає «алокований».

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

Міні-історія №2: Оптимізація, що відкотилася назад

Команда сховищ захотіла «випередити» скарги на продуктивність. У них були NVMe, що простоювали, тож додали special vdev, щоб прискорити метадані і малий IO. На папері все було ідеально: швидші операції в директоріях, нижча латентність для малих читань і щасливіша віртуалізаційна платформа.

Працювало — поки не перестало. Місяцями special vdev тихо заповнювався. Не користувацькими даними, не «великими файлами», а метаданими, маленькими блоками і накопиченням «корисних» файлових функцій. Ніхто не відслідковував окремо розподіл special vdev; пул все ще мав багато місця на основному RAIDZ. Моніторинг бачив 60% використання пулу і заспокоювався.

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

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

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

Інша команда використовувала ZFS для баз даних і логів. Нічого екзотичного. Їхній лід по сховищам мав правило: жоден пул не повинен проводити значний час вище ~75%, якщо немає письмового плану розширення. Також було правило: снапшоти мають явні політики збереження, і кожен dataset повинен вказати, чи він «snapshot-heavy» або «snapshot-light».

Одного дня розробник випадково задеплоїв збірку, що подвоїла обсяг логів. Dataset логів розрісся. Система моніторингу не тільки пейджила за «пул на 85%». Вона пейджила за «пул прогнозується досягне 80% за 6 годин» і за «тренд використання снапшотів пришвидшується». Це не магія; це нудна математика і консистентне маркування.

Інженер на чергуванні обмежив інтенсивність логування, зменшив частоту снапшотів для не критичних dataset і підвищив збереження тільки для бази даних. У них також був попередньо схвалений runbook: тимчасово підвищити квоту dataset для критичної служби, одночасно знизивши квоту менш критичного dataset. Ніяких екстрених закупівель. Ніякого караоке із взаємних звинувачень опівночі.

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

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

Мета тут не в тому, щоб кидати команди на стіну. Мета — побудувати ментальну модель, яка відповідає обліку ZFS: реальність на рівні пулу, обіцянки на рівні dataset і «чому я не можу алокувати, хоча здається, що можу». Приклади припускають OpenZFS на типового Linux-сервері, але концепції переносяться.

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

cr0x@server:~$ zpool list
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  87.2T  71.4T  15.8T        -         -    41%    81%  1.00x  ONLINE  -

Інтерпретація: CAP — це грубий інструмент. FRAG — сигнальна лампочка, а не вирок, але коли фрагментація зростає, «останні 10–15%» стають менш придатними. Пул на 81% з 41% frag може вже відчуватись тісним для випадкових операцій запису.

Завдання 2: Подивитися деталі vdev алокації і ashift

cr0x@server:~$ zdb -C tank | sed -n '1,120p'
MOS Configuration:
        version: 5000
        name: 'tank'
        vdev_tree:
            type: 'root'
            id: 0
            guid: 1234567890123456789
            children[0]:
                type: 'raidz'
                id: 0
                ashift: 12
                nparity: 2
                children[0]:
                    type: 'disk'
                    path: '/dev/disk/by-id/ata-DISK0'
                children[1]:
                    type: 'disk'
                    path: '/dev/disk/by-id/ata-DISK1'
                children[2]:
                    type: 'disk'
                    path: '/dev/disk/by-id/ata-DISK2'

Інтерпретація: ashift каже вам мінімальний блок алокації. Якщо він більший, ніж ваші реальні фізичні потреби, ви назавжди зменшили використовувану ємність. Якщо він менший за реальність (рідко з сучасними дефолтами), продуктивність і write amplification можуть постраждати.

Завдання 3: Порівняти вигляд пулу і dataset щодо доступного простору

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used tank
NAME         USED  AVAIL  REFER  MOUNTPOINT
tank        71.4T  9.82T    96K  /tank
tank/vm     52.1T  2.40T  42.0T  /tank/vm
tank/backup 18.9T  7.40T  18.9T  /tank/backup

Інтерпретація: Pool FREE був 15.8T, але top-level dataset показує 9.82T avail. Ця прогалина — там, де живуть slop-простір, резервації та реалії обліку. Число, яке цікавить додатки, часто — це AVAIL dataset, а не FREE пулу.

Завдання 4: Перевірити reservation та refreservation

cr0x@server:~$ zfs get -H -o name,property,value,source reservation,refreservation tank tank/vm tank/backup
tank        reservation     none    default
tank        refreservation  none    default
tank/vm     reservation     2T      local
tank/vm     refreservation  none    default
tank/backup reservation     none    default
tank/backup refreservation  none    default

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

Завдання 5: Розподіл простору по снапшотах, дочірнім dataset і refreservation

cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation tank/vm
NAME     USED  USEDBYSNAPSHOTS  USEDBYDATASET  USEDBYCHILDREN  USEDBYREFRESERVATION
tank/vm  52.1T            8.4T          43.7T             0B                   0B

Інтерпретація: 8.4T, зафіксовані снапшотами, не «звільняються», поки снапшоти не будуть видалені. Якщо ви видалили образи VM і простір не повернувся, зазвичай причина в цьому.

Завдання 6: Перелік снапшотів і пошук «важких» споживачів

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used | tail -n 8
tank/vm@auto-2025-12-20  210G  39.8T  Sat Dec 20 02:00 2025
tank/vm@auto-2025-12-21  240G  40.1T  Sun Dec 21 02:00 2025
tank/vm@auto-2025-12-22  310G  40.5T  Mon Dec 22 02:00 2025
tank/vm@auto-2025-12-23  480G  41.0T  Tue Dec 23 02:00 2025
tank/vm@auto-2025-12-24  1.2T  42.0T  Wed Dec 24 02:00 2025
tank/vm@pre-migration     3.6T  38.2T  Wed Dec 24 18:11 2025
tank/vm@quarter-end       6.1T  37.9T  Thu Dec 25 01:05 2025
tank/vm@baseline          7.8T  36.4T  Fri Dec 12 03:00 2025

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

Завдання 7: Перевірити коефіцієнт стиснення та логічне vs фізичне використання

cr0x@server:~$ zfs get -H -o name,property,value compressratio,compression tank/vm
tank/vm  compressratio  1.45x  -
tank/vm  compression    lz4    local

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

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

cr0x@server:~$ zpool get -H -o name,property,value frag,capacity,free tank
tank  frag      41%   -
tank  capacity  81%   -
tank  free      15.8T -

Інтерпретація: FRAG, що росте разом із CAP — нормально; FRAG, що швидко росте при помірному CAP, часто вказує на невідповідність навантаження (віртуальні машини з випадковими записами на RAIDZ, інтенсивні снапшоти або постійний churn).

Завдання 9: Перевірити алокацію special vdev (якщо є)

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

        NAME                                STATE     READ WRITE CKSUM
        tank                                ONLINE       0     0     0
          raidz2-0                          ONLINE       0     0     0
            ata-DISK0                       ONLINE       0     0     0
            ata-DISK1                       ONLINE       0     0     0
            ata-DISK2                       ONLINE       0     0     0
        special
          mirror-1                          ONLINE       0     0     0
            nvme-NVME0                      ONLINE       0     0     0
            nvme-NVME1                      ONLINE       0     0     0

errors: No known data errors
cr0x@server:~$ zpool list -v tank
NAME         SIZE  ALLOC   FREE
tank        87.2T  71.4T  15.8T
  raidz2-0  87.2T  68.9T  18.3T
special      1.8T   2.5T   0B

Інтерпретація: Якщо special показує «0B free», у вас проблема, навіть якщо основний vdev має простір. Пул може відмовляти в алокаціях, які вимагають блоків special-класу (метадані/малі блоки залежно від властивостей). Це патерн «оптимізація, що відкотилася назад».

Завдання 10: Визначити dataset, які використовують спеціальні класи алокації

cr0x@server:~$ zfs get -H -o name,property,value special_small_blocks tank tank/vm tank/backup
tank        special_small_blocks  0     default
tank/vm     special_small_blocks  16K   local
tank/backup special_small_blocks  0     default

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

Завдання 11: Перевірити квоти та refquotas, які можуть створити відчуття «припинився простір» на рівні dataset

cr0x@server:~$ zfs get -H -o name,property,value quota,refquota tank/vm
tank/vm  quota     55T   local
tank/vm  refquota  none  default

Інтерпретація: Квота обмежує загальне використання (включно зі снапшотами залежно від типу); refquota обмежує референтований простір. Якщо dataset досяг квоти, він виглядає повним, незалежно від стану пулу.

Завдання 12: Підтвердити, чи звільнення відкладені (простір не звільняється миттєво)

cr0x@server:~$ zpool iostat -v tank 2 3
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        71.4T  15.8T    210    980  82.1M  311M
  raidz2-0  68.9T  18.3T    200    940  80.4M  305M
  special    2.5T     0B     10     40  1.7M   6.1M
----------  -----  -----  -----  -----  -----  -----
tank        71.4T  15.8T    190   1020  75.2M  340M
  raidz2-0  68.9T  18.3T    180    980  73.6M  333M
  special    2.5T     0B     10     40  1.6M   6.6M
----------  -----  -----  -----  -----  -----  -----

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

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

cr0x@server:~$ zfs destroy -nvp tank/vm@baseline
would destroy tank/vm@baseline
would reclaim 7.8T
cr0x@server:~$ zfs destroy -vp tank/vm@baseline
will destroy tank/vm@baseline
will reclaim 7.8T
destroyed tank/vm@baseline

Інтерпретація: Використовуйте -nvp спочатку: dry-run плюс verbose плюс парсабельний вивід. Якщо ви намагаєтесь вирватися з повного пулу, це один з найчистіших важелів — за умови, що снапшот безпечний для видалення.

Завдання 14: Виявити «простір, обіцяний інакше» через logicalused/available

cr0x@server:~$ zfs get -H -o name,property,value logicalused,logicalavailable tank
tank  logicalused       102T  -
tank  logicalavailable  14.2T -

Інтерпретація: При стисненні логічне і фізичне розходяться. Якщо logicalavailable низький, хоча ви «думаєте», що маєте фізичний простір, ви можете стикнутися з обмеженнями на кшталт slop, резервацій або виснаження special vdev.

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

Це послідовність, коли дзвонить пейджер. Мета — визначити, який саме тип «повноти» у вас: пул повний, dataset обмежений, снапшот фіксує блоки, простір обіцяний іншому, special vdev вичерпано або фрагментація/помилки алокації.

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

  1. Помилка додатку: ENOSPC? EIO? timeouts? Не приймайте ENOSPC за «немає байтів вільних»; це може означати «немає алокованого місця під політику».
  2. Обсяг: один dataset чи все? Один zvol чи всі файлові системи?
  3. Час появи: почалося після створення снапшоту, реплікації, великого видалення чи додавання special vdev?

По-друге: перевірити реальність пулу

cr0x@server:~$ zpool list tank
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  87.2T  71.4T  15.8T        -         -    41%    81%  1.00x  ONLINE  -
cr0x@server:~$ zpool status -x tank
pool 'tank' is healthy

Рішення: Якщо HEALTH не ONLINE/healthy — зупиніться і виправте це спочатку. Ігри з ємністю не мають значення, якщо ви деградовані і йде resilver.

По-третє: порівняти avail dataset з free пулу

cr0x@server:~$ zfs list -o name,used,avail,mounted -r tank | head
NAME        USED  AVAIL  MOUNTED
tank       71.4T  9.82T  yes
tank/vm    52.1T  2.40T  yes
tank/backup 18.9T  7.40T yes

Рішення: Якщо pool free «непоганий», але dataset avail крихітний, швидше за все ви маєте справу зі slop, резерваціями, квотами або обмеженнями special vdev.

По-четверте: швидко атрибутувати простір

cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation -r tank | head -n 15
NAME        USED  USEDBYSNAPSHOTS  USEDBYDATASET  USEDBYCHILDREN  USEDBYREFRESERVATION
tank       71.4T              0B            96K          71.4T                   0B
tank/vm    52.1T            8.4T          43.7T             0B                   0B
tank/backup 18.9T           1.1T          17.8T             0B                   0B

Рішення: Якщо domina снапшоти, звільнення простору означає зміну політики снапшотів, а не видалення файлів.

По-п’яте: перевірити special vdev і обмеження класів алокації

cr0x@server:~$ zpool list -v tank
NAME         SIZE  ALLOC   FREE
tank        87.2T  71.4T  15.8T
  raidz2-0  87.2T  68.9T  18.3T
special      1.8T   2.5T   0B

Рішення: Якщо special заповнений, вважайте це інцидентом високої важливості. Звільнення «звичайного» простору не обов’язково допоможе, якщо аллокатор потребує блоків special-класу.

По-шосте: перевірити фрагментацію і форму навантаження

cr0x@server:~$ zpool get frag tank
NAME  PROPERTY  VALUE  SOURCE
tank  frag      41%    -

Рішення: Високий frag плюс високий CAP означає, що вам терміново потрібен запас. Найшвидше виправлення — зазвичай видалити снапшоти, перемістити dataset або розширити пул. Дефрагментація — це не кнопка; це дисципліна щодо ємності і життєвого циклу.

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

Помилка 1: Вважати «pool FREE» як «dataset може писати»

Симптом: zpool list показує терабайти вільних, але записи відмовляють на dataset або zvol.

Ймовірні причини: досягнуто порогу slop-простору; dataset досяг квоти; резервація десь ще; special vdev повний.

Виправлення: Порівняйте AVAIL цільового dataset через zfs list; перевірте квоти/резервації; інспектуйте використання special vdev; звільніть простір у потрібному місці.

Помилка 2: Dataset з великою кількістю снапшотів без дисципліни збереження

Симптом: Видалення файлів не звільняє простір; usedbysnapshots росте; використання пулу зростає «таємничо».

Виправлення: Використовуйте zfs list -t snapshot -o used, щоб знайти важкі снапшоти; безпечно очистіть; переробіть політику збереження (менше снапшотів, коротша політика або стратегія реплікації, що не фіксує вічно).

Помилка 3: Додавання special vdev без розрахунку розміру і тривог

Симптом: Пул здається в порядку, але операції з метаданими/малими файлами відмовляють; special vdev показує майже повний або повний.

Виправлення: Моніторити алокацію special vdev; розширити special vdev (у дзеркалі); розглянути зміну special_small_blocks для dataset; уникати створення choke-point для ємності.

Помилка 4: Надмірна оптимізація ashift або recordsize заради «продуктивності»

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

Виправлення: Обирайте ashift на основі фізичної реальності, а не фольклору. Налаштовуйте recordsize/volblocksize під навантаження. Якщо ви вже помилились — плануйте міграцію; ashift не переключається як перемикач.

Помилка 5: Запускати RAIDZ-пули «надто гарячими» з випадковими записами

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

Виправлення: Тримайте більше запасу; розгляньте mirrors для навантажень з випадковими записами (VMs, БД); обережно використовуйте special vdev; зменшуйте churn снапшотів.

Помилка 6: Припущення, що frees миттєві при сильному churn

Симптом: Ви видаляєте багато, але AVAIL не зростає швидко; миттєві записи все ще відмовляють.

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

Контрольні списки / покроковий план

Контрольний список A: Звіт про ємність, що відповідає реальності

  1. Відзвітуйте zpool CAP, zpool FRAG і zpool FREE.
  2. Відзвітуйте топ dataset з USED, AVAIL і usedbysnapshots.
  3. Окремо вкажіть алокацію special vdev (якщо є).
  4. Включіть резюме квот/резервацій для «критичних» dataset.
  5. Трендуйте зростання, використовуючи фізичне використання (не лише логічне), якщо вкл. стиснення.

Контрольний список B: Коли dataset каже «немає місця»

  1. Перевірте AVAIL dataset: zfs list dataset.
  2. Перевірте quota/refquota: zfs get quota,refquota.
  3. Перевірте reservation/refreservation: zfs get reservation,refreservation.
  4. Перевірте фіксацію снапшотами: zfs list -o usedbysnapshots і перелік снапшотів.
  5. Перевірте здоров’я пулу і вільний простір special vdev.
  6. Якщо потрібен терміновий простір: видаліть снапшоти з dry-run; призупиніть churn-навант; перемістіть дані з пулу, якщо можливо.

Контрольний список C: Як уникнути «хвостового ризику»

  1. Встановіть пороги тривоги на pool CAP (раніше) і на rate of change (ще раніше).
  2. Встановіть пороги тривоги на FRAG і на використання special vdev.
  3. Визначте цільову максимальну заповненість пулу залежно від навантаження (VM потребують більше запасу, ніж архіви).
  4. Тримайте політики снапшотів явними, переглядайте і виконувати.
  5. Тестуйте сценарії «видалити дані»: підтвердіть, що простір повертається очікувано (і дізнайтеся, коли ні).
  6. Плануйте розширення до того, як вони знадобляться; екстрені розширення — шлях до дивної геометрії vdev назавжди.

FAQ

1) Чи налаштовується slop-простір?

Іноді, залежно від платформи і версії OpenZFS, є параметри, що впливають на поведінку. Операційно ставтесь до slop як до функції безпеки, а не як до статті бюджету. Якщо ви «потанцюєте з ним», ви міняєте передбачувану подушку на непередбачувані режими відмов під навантаженням.

2) Чому zpool list FREE не збігається з zfs list AVAIL?

Тому що pool free — це не те саме, що алокований простір для dataset. Різницю можуть складати slop-простір, резервації для інших dataset і іноді обмеження класів алокації, як special vdev. Завжди довіряйте AVAIL dataset для питання «чи можу я тут писати?».

3) Я видалив величезний каталог. Чому простір не повернувся?

Найпоширеніша причина: снапшоти. Блоки все ще реферуються снапшотами, тому їх не можна звільнити. Перевірте usedbysnapshots і перелік снапшотів, відсортований за USED.

4) Чи справді фрагментація має значення в ZFS?

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

5) Чи правило «тримати пул під 80%» реальне?

Це правило великого пальця, а не фізика. Деякі навантаження можуть комфортно працювати вище 80% довго (холодні архіви з небагатьма перезаписами). Інші стають нещасними вже на 70% (віртуалки з випадковими записами і інтенсивними снапшотами). Правильне число залежить від навантаження, типу vdev і оперативної толерантності до ризику.

6) Чи може заповнення special vdev зламати весь пул?

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

7) Чи допомагають резервації з проблемами slop-простору?

Резервації допомагають гарантувати, що dataset зможе писати, коли пул стискається, але вони не створюють додаткового простору. Вони змінюють черговість, хто впаде першим. Для мульти-оренних пулів це часто правильно — просто розумійте компроміс.

8) Чому записи драматично повільні поблизу заповнення?

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

9) Якщо я додам дисків, чи зникне проблема зі «slop»?

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

10) Яка найкраща звичка, щоб уникнути сюрпризів «здається заповненим»?

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

Висновок

Пули ZFS здаються заповненими до 100% тому, що ZFS — це не тупий контейнер блоків. Це транзакційна CoW-система, якій потрібно місце для маневру: місце для нових блоків, місце для метаданих, місце щоб уникати фрагментаційних пасток і місце, щоб утримувати свої обіцянки (резервації) без краху під навантаженням. Slop-простір — явна версія цієї реальності; снапшоти, special vdev і поведінка RAIDZ — неявні версії.

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

← Попередня
RDP між офісами без відкритих портів: безпечна конфігурація «тільки RDP через VPN»
Наступна →
Заміна дисків у ZFS: безпечний робочий процес без драм у пулі

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