Набори даних ZFS за робочим навантаженням: прийом управління, що запобігає хаосу

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

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

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

Хитрість: набори даних — межі управління

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

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

Суть не в «більше наборів, бо ZFS може». Суть — в операційному контролі:

  • Ізоляція продуктивності: recordsize, logbias, atime, поведінка sync, special_small_blocks можна налаштувати під кожне навантаження.
  • Ізоляція безпеки: частота знімків, утримання, холди та реплікація можуть бути на рівні окремого набору даних.
  • Дисципліна обсягу: квоти/резервації не дозволяють одному навантаженню з’їсти весь пул і назвати це «тимчасово».
  • Контроль радіусу ураження: можна відкотити або відправити/отримати один набір даних, не торкаючись сусідів.
  • Діагностованість: zfs get, zfs list і статистика на рівні наборів даних набувають сенсу замість загальної каші.

Якщо запам’ятати одну річ: ваш пул ZFS — це бюджет апаратного забезпечення; набори даних — ваші контракти.
Без контрактів приходять несподівані рахунки.

Короткий жарт наостанок: Якщо ваше сховище — «один великий набір даних», то це не архітектура — це емоція.

Цікаві факти та історичний контекст (бо шрами мають історію)

  1. ZFS виник у Sun Microsystems на початку-середині 2000-х років, створений як заміна традиційним менеджерам томів і файловим системам одним інтегрованим рішенням.
  2. Модель copy-on-write була прямою відповіддю на приховану корупцію та проблему «write hole», що переслідувала старі стеки RAID + файлової системи.
  3. Знімки ZFS були задумані як дешеві, бо це всього лише посилання на існуючі блоки — поки ви не зберігаєте їх вічно й не дивуєтеся, чому видалення не звільняє місце.
  4. Успадкування властивостей наборів даних є навмисним: воно дозволяє дерево політик, але також викликає випадкове успадкування політик — наприклад, продакшн-база даних може успадкувати «тестовий» графік знімків.
  5. Стиснення стало стандартною практикою в ZFS, бо воно часто покращує продуктивність, зменшуючи I/O, особливо на обертових дисках і завантажених масивах.
  6. Recordsize існує тому, що «один розмір блоку на всіх» не працює: великі послідовні файли та дрібні випадкові I/O вимагають різних компромісів.
  7. ZVOL — це блочні пристрої на базі ZFS; вони поводяться відмінно від наборів даних і мають свої налаштування, як-от volblocksize.
  8. ARC — це не просто кеш; це споживач пам’яті з політикою вигнання, що може зробити або зламати продуктивність за змішаних навантажень.
  9. OpenZFS став крос-платформеним продовженням після ери Sun, а сучасні функції (наприклад, special vdevs і dRAID) — результат років операційного зворотного зв’язку.

Уся ця історія підводить до однієї операційної істини: ZFS хоче, щоб ви виражали наміри. Набори даних — це спосіб зробити це.

Мапа навантажень: що має бути в окремому наборі даних

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

Добрі межі наборів даних (практично, а не академічно)

  • Бази даних: каталог даних Postgres/MySQL, WAL/binlog і резервні копії часто заслуговують на окремі набори даних.
  • Зберігання VM: один набір даних для образів VM, опціонально на кластер або орендаря. Якщо використовуєте ZVOLs, обробляйте їх як окремі навантаження.
  • Контейнери: шари образів проти записуваних томів проти логів. У них зовсім різні шаблони запису.
  • Кеші збірки/артефактів: висока плинність, низька цінність, багато видалень — ідеальні для окремих правил знімків/утримання.
  • Домашні директорії: квоти, інше утримання й відновлення після випадкового видалення.
  • Зони прийому резервних копій: отримання, довге утримання і «не робити знімки кожні 5 хвилин».
  • Логи: часто добре стискаються, але не завжди потребують знімків; також можуть бути великими й спайковими.

Де люди надмірно дроблять

Не створюйте набір даних для кожної піддиректорії застосунку тільки тому, що вам сподобалися регулювальні параметри.
Якщо графік знімків і утримання ідентичні, а характеристики продуктивності однакові — тримайте разом.
Керування 400 наборами даних без стандарту найменувань — це як створити музей забутих намірів.

Іменування: зробіть нудним і шукабельним

Використовуйте передбачувану ієрархію. Приклад:
tank/prod/db/postgres, tank/prod/db/postgres-wal, tank/prod/vm, tank/prod/containers,
tank/shared/homes, tank/backup/recv.

Розміщуйте середовище й функцію на початку. Ви будете grep-ити це о 02:00. Не примушуйте Майбутнього Себе розбиратися в поезії.

Властивості, які мають значення (і що вони насправді роблять)

recordsize: мовчазний важіль продуктивності

recordsize контролює максимальний розмір блоку для файлів у наборі даних. Більший підходить для великих послідовних читань/записів (медіа, бекап).
Менший може зменшити ефект read-modify-write при випадкових I/O (бази даних).

Для багатьох баз даних на наборах даних (не ZVOLs) recordsize=16K або recordsize=8K — поширений початковий вибір.
Для образів VM як файлів recordsize=128K часто працює добре. Для потоків резервного копіювання може підходити recordsize=1M.
Але не робіть це за рецептом; вимірюйте.

volblocksize: для ZVOLs, а не файлів

ZVOLs використовують volblocksize. Ви встановлюєте його під час створення, а змінити пізніше складно.
Узгодьте з розміром блоку гостя/файлової системи й очікуваним I/O: наприклад, 8K або 16K для DB-подібних випадкових I/O; більший для послідовних.

compression: функція продуктивності під виглядом економії місця

Використовуйте compression=zstd для більшості наборів даних, якщо немає зрозумілої причини ні.
Зазвичай це швидше за диски й зменшує записи. Також знижує драму «чому мій пул повний».

atime: метадані-записи, які вам, ймовірно, не потрібні

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

sync і logbias: де чесність зустрічається з затримкою

sync=standard — це за замовчуванням і найбезпечніше. sync=disabled може зробити бенчмарки кращими, а продакшн — схожим на постмортем.
logbias=latency vs throughput впливає на намір синхронних записів (і чи допомагає SLOG).

Якщо ви змінюєте поведінку sync, ставтеся до цього як до зміни вашого RPO: зафіксуйте в документації, отримайте погодження і припускайте, що реальність вас перевірить.

primarycache / secondarycache: обирайте, що кешувати

Для наборів даних, що «забруднюють» ARC (наприклад, великі послідовні читання бекапів), розгляньте primarycache=metadata.
Йдеться не про економію ОЗП; йдеться про те, щоб ARC був корисним для навантажень, чутливих до затримки.

special_small_blocks і special vdevs: швидкі метадані, швидкі дрібні I/O

Якщо у вас є special vdev (зазвичай SSD) для метаданих/малих блоків, special_small_blocks може значно покращити роботу з малими файлами.
Але це також може спричинити сильне розчарування, якщо special vdev недостатньо великий. Це одна з тих функцій, де плануйте ємність, як для продакшну — бо так воно і є.

quotas, reservations, refreservation: ємність як політика

quota обмежує зростання. reservation гарантує місце. refreservation може резервувати обсяг, рівний посиланій інформації набору даних,
часто використовується для zvols. Це ваші огорожі від «податку шумного сусіда» у сховищі.

snapshots: важіль відкату, одиниця реплікації, пастка простору-часу

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

Одна цитата, бо вона досі актуальна

Надія — не стратегія.
— генерал Г. Норман Шварцкопф

Практичні завдання: команди + що означає вивід + рішення, які ви приймаєте

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

Завдання 1: Перелік наборів даних і хто насправді використовує місце

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -r tank/prod
NAME                        USED  AVAIL  REFER  MOUNTPOINT
tank/prod                   3.21T  5.87T   192K  /tank/prod
tank/prod/db                1.44T  5.87T   192K  /tank/prod/db
tank/prod/db/postgres       812G  5.87T   812G  /var/lib/postgresql
tank/prod/db/postgres-wal   221G  5.87T   221G  /var/lib/postgresql-wal
tank/prod/vm                1.55T  5.87T  1.55T  /tank/prod/vm
tank/prod/containers        218G  5.87T   218G  /var/lib/containers

Що це означає: USED включає знімки/нащадків; REFER — це живі дані в тому наборі даних.

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

Завдання 2: Знайти надмірність знімків по набору даних

cr0x@server:~$ zfs list -t snapshot -o name,used,refer -s used -r tank/prod/db/postgres | tail -n 5
tank/prod/db/postgres@hourly-2025-12-25-00  18.4G   812G
tank/prod/db/postgres@hourly-2025-12-24-23  18.2G   812G
tank/prod/db/postgres@hourly-2025-12-24-22  17.9G   812G
tank/prod/db/postgres@hourly-2025-12-24-21  17.6G   812G
tank/prod/db/postgres@hourly-2025-12-24-20  17.4G   812G

Що це означає: USED знімка — це унікальні блоки, які утримує цей знімок.

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

Завдання 3: Перевірити успадкування властивостей, щоб виявити випадкові значення за замовчуванням

cr0x@server:~$ zfs get -r -o name,property,value,source recordsize,compression,atime,sync,logbias tank/prod/db/postgres
NAME                      PROPERTY     VALUE     SOURCE
tank/prod/db/postgres     recordsize   128K      inherited from tank/prod
tank/prod/db/postgres     compression  zstd      inherited from tank/prod
tank/prod/db/postgres     atime        on        default
tank/prod/db/postgres     sync         standard  default
tank/prod/db/postgres     logbias      latency   local

Що це означає: Ця база даних успадковує recordsize=128K і має atime=on.

Рішення: Встановіть recordsize явно для DB-наборів даних і вимкніть atime, якщо він не потрібний.

Завдання 4: Виправити recordsize та atime для набору даних бази даних

cr0x@server:~$ sudo zfs set recordsize=16K atime=off tank/prod/db/postgres

Що це означає: Майбутні записи використовуватимуть 16K записи; існуючі блоки не будуть перезаписані, поки дані не зміняться.

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

Завдання 5: Перевірити compressratio, щоб дізнатися, чи допомагає стиснення

cr0x@server:~$ zfs get -o name,property,value compressratio -r tank/prod | head
NAME                 PROPERTY      VALUE
tank/prod            compressratio 1.42x
tank/prod/db         compressratio 1.12x
tank/prod/db/postgres compressratio 1.06x
tank/prod/vm         compressratio 1.68x

Що це означає: Образи VM добре стискаються; база даних стискається погано (зазвичай через вже стиснуті сторінки або шифровані дані).

Рішення: Тримайте стиснення, хіба що CPU обмежений. Для DB стиснення не зекономить багато місця, але може зменшити записи трохи.

Завдання 6: Поставити жорстке обмеження на «тимчасове» навантаження

cr0x@server:~$ sudo zfs set quota=500G tank/prod/containers
cr0x@server:~$ zfs get -o name,property,value quota tank/prod/containers
NAME                 PROPERTY  VALUE
tank/prod/containers quota     500G

Що це означає: Набір даних containers не може перевищити 500G.

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

Завдання 7: Забезпечити резерв місця для бази даних, щоб вона могла дихати

cr0x@server:~$ sudo zfs set reservation=1T tank/prod/db
cr0x@server:~$ zfs get -o name,property,value reservation tank/prod/db
NAME         PROPERTY     VALUE
tank/prod/db reservation  1T

Що це означає: Ви гарантуєте місце для DB-наборів даних під tank/prod/db.

Рішення: Використовуйте reservation, коли бізнес-вартість помилок запису в БД вища за вартість «марного» резервованого простору.

Завдання 8: Визначити, чи пул страждає від тиску фрагментації

cr0x@server:~$ zpool list -o name,size,alloc,free,frag,capacity,health tank
NAME   SIZE  ALLOC  FREE  FRAG  CAPACITY  HEALTH
tank  9.08T  7.24T  1.84T   63%       79%  ONLINE

Що це означає: 79% заповнення і 63% фрагментації — це попереджувальний сигнал, але не вирок. ZFS може працювати в такому стані, але затримки часто зростають.

Рішення: Плануйте ємність: тримайте пули нижче ~80% для змішаних випадкових навантажень. Перемістіть плинні набори даних або додайте vdevs до настання фази «таємничої затримки».

Завдання 9: Слідкувати за затримкою I/O на рівні пулу

cr0x@server:~$ zpool iostat -v tank 2 3
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        7.24T  1.84T    640   1200  78.2M  94.6M
  raidz2-0  7.24T  1.84T    640   1200  78.2M  94.6M
    sda         -      -     80    150  9.70M  11.8M
    sdb         -      -     79    151  9.66M  11.7M
    sdc         -      -     81    148  9.74M  11.6M
    sdd         -      -     80    149  9.69M  11.7M

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

Рішення: Якщо записи постійно високі і навантаження синхронне, оцініть SLOG і налаштування sync для наборів даних (не глобально).

Завдання 10: Інспектувати логічний облік місця по набору даних (і виявити пастки refreservation)

cr0x@server:~$ zfs list -o name,used,refer,logicalused,logicalrefer,compressratio -r tank/prod/vm | head
NAME             USED  REFER  LUSED  LREFER  RATIO
tank/prod/vm     1.55T 1.55T  2.31T  2.31T   1.68x

Що це означає: Логічне використання більше за фізичне завдяки стисненню (і, можливо, розрідженим образам).

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

Завдання 11: Побачити, що «б’є» ARC і чи варто обмежити кешування для набору даних

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  size   c
12:00:01   712   144     20   112   16    32    4     0    0  46.3G  48.0G
12:00:02   690   171     24   139   20    32    5     0    0  46.1G  48.0G
12:00:03   705   162     23   131   19    31    4     0    0  46.0G  48.0G

Що це означає: 20–24% пропусків під навантаженням може бути прийнятним або жахливим залежно від цілей по затримці. Це підказує, що робочий набір може не вміщатися.

Рішення: Якщо великі послідовні завдання трясуть ARC, встановіть primarycache=metadata для таких наборів даних (наприклад, отримання бекапів або архіви медіа).

Завдання 12: Змінити політику кешування для набору бекапів, щоб захистити навантаження, чутливі до затримки

cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup/recv
cr0x@server:~$ zfs get -o name,property,value,source primarycache tank/backup/recv
NAME             PROPERTY      VALUE     SOURCE
tank/backup/recv primarycache  metadata  local

Що це означає: ZFS кешуватиме метадані, але не дані файлів для цього набору даних в ARC.

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

Завдання 13: Створити набір даних з налаштуваннями, придатними для навантаження (правильна нудність)

cr0x@server:~$ sudo zfs create -o compression=zstd -o atime=off -o recordsize=16K tank/prod/db/mysql
cr0x@server:~$ zfs get -o name,property,value,source compression,atime,recordsize tank/prod/db/mysql
NAME                PROPERTY    VALUE  SOURCE
tank/prod/db/mysql  compression zstd   local
tank/prod/db/mysql  atime       off    local
tank/prod/db/mysql  recordsize  16K    local

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

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

Завдання 14: Перевірити дрейф політики знімків (чи ви робите знімки не того?)

cr0x@server:~$ zfs get -r -o name,property,value,source com.sun:auto-snapshot tank/prod | head -n 8
NAME                 PROPERTY              VALUE  SOURCE
tank/prod             com.sun:auto-snapshot true   inherited from tank
tank/prod/db          com.sun:auto-snapshot true   inherited from tank
tank/prod/db/postgres com.sun:auto-snapshot true   inherited from tank
tank/prod/db/postgres-wal com.sun:auto-snapshot true inherited from tank
tank/prod/vm          com.sun:auto-snapshot true   inherited from tank
tank/prod/containers  com.sun:auto-snapshot true   inherited from tank
tank/prod/logs        com.sun:auto-snapshot true   inherited from tank

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

Рішення: Вимкніть авто-знімки для наборів з сильним чурном (WAL/логи/кеші) або дайте їм інший клас утримання.

Завдання 15: Вимкнути знімки на наборі з сильною плинністю (приклад: WAL)

cr0x@server:~$ sudo zfs set com.sun:auto-snapshot=false tank/prod/db/postgres-wal
cr0x@server:~$ zfs get -o name,property,value,source com.sun:auto-snapshot tank/prod/db/postgres-wal
NAME                      PROPERTY              VALUE  SOURCE
tank/prod/db/postgres-wal com.sun:auto-snapshot false  local

Що це означає: Автоматизація знімків, що поважає цю властивість, пропустить WAL.

Рішення: Захищайте WAL за допомогою вбудованих механізмів бекапу/архівації бази даних, а не файлових знімків кожні 15 хвилин, які утримують плинність вічно.

Завдання 16: Підтвердити ashift і розклад vdev (бо ви не можете налаштуванням виправити неправильну геометрію)

cr0x@server:~$ zdb -C tank | egrep "ashift|vdev_tree|type:|path:"
    ashift: 12
        type: 'raidz'
            type: 'disk'
            path: '/dev/disk/by-id/ata-SAMSUNG_SSD_...'

Що це означає: ashift: 12 означає вирівнювання секторів 4K, зазвичай правильне для сучасних дисків/SSD.

Рішення: Якщо ashift неправильно встановлено (занадто мале), продуктивність і write amplification можуть постраждати назавжди. Виправлення зазвичай вимагає перестроювання пулу.

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

Швидкий playbook діагностики: знайти вузьке місце без тижня нарад

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

Перший крок: чи пул втрачає простір для дихання?

  • Перевірте ємність пулу та фрагментацію: zpool list
  • Перевірте надмірність знімків: zfs list -t snapshot
  • Перевірте, чи один набір даних не вибухає: zfs list -r

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

Другий крок: це IOPS/затримка, пропускна здатність чи CPU?

  • Подивіться на насичення пристроїв і розподіл: zpool iostat -v 1
  • Подивіться на системний CPU steal/iowait: mpstat -P ALL 1, iostat -x 1
  • Перевірте вартість CPU для стиснення, якщо використовуєте важкий алгоритм: zfs get compression і системні метрики CPU

Інтерпретація: Вузькі місця випадкових записів відчуваються як «все повільно». Вузькі місця пропускної здатності — як «великі завдання тривають вічно».
CPU-вузькі місця відчуваються як «сховище повільне», бо стек зберігання чекає на CPU для контрольних сум, стиснення і керування метаданими.

Третій крок: який набір даних — шумний сусід?

  • Визначте найактивніші процеси за читанням/записом: iotop -o або pidstat -d 1
  • Зіставте з mountpoint/набором даних: zfs list -o name,mountpoint
  • Підтвердіть властивості набору даних: zfs get -o name,property,value,source -r ...

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

Четвертий крок: ви боретесь зі своїми політиками?

  • Обсяг автоматизації знімків: перевірте com.sun:auto-snapshot або ваші локальні прапори інструментів знімків
  • Затримка реплікації та прийоми: zfs list -t snapshot і перевірка «застиглих» старих знімків, що зберігаються для send/receive
  • Квоти/резервації: zfs get quota,reservation,refreservation

Інтерпретація: Багато квитків «ZFS їсть простір» насправді означають «знімки роблять саме те, що ми попросили, і ми це погано попросили».

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

1) Інцидент через хибне припущення: «знімки безкоштовні»

Середня компанія використовувала пул ZFS для змішаного парку: VMs, контейнери і кілька баз даних. Хтось увімкнув політику авто-знімків на корені пулу. Щогодинні знімки для всього. Добове утримання на місяць. Це продавалося як «страховка».

Хибне припущення не було злим наміром; воно було оптимістичним. Знімки дешеві у створенні, тому команда вирішила, що вони дешеві у зберіганні.
Тим часом контейнерний набір даних мав агресивну плинність: завантаження образів, видалення шарів, CI-завдання, ротація логів — звична генерація ентропії.
Видалення не звільняло місце. Пул повільно заповнювався, а потім раптово.

Інцидент почався зі симптомів бази даних: зниження швидкості комітів. Потім кластер VM почав відчувати таймаути гостей.
Операції дивилися на графіки і бачили зростання використання диску, що ніколи не опускалося. Хтось намагався видалити старі контейнерні образи — негайного повернення місця не сталося.
На виклику опинилися в тій самій ямі, яку багато хто з нас знає: «чому нічого не звільняється?»

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

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

2) Оптимізація, що відгукнулася: «sync=disabled заради швидкості»

Інший магазин мав проблему продуктивності: їхнє навантаження Postgres страждало від великої затримки комітів після міграції на нове сховище.
Доброзичливий інженер запустив бенчмарк, побачив погані результати і застосував класичне «виправлення»: sync=disabled на наборі даних бази.
Графіки бенчмарку покращилися миттєво. Люди святкували. Квиток закрили з впевненим коментарем про «накладні витрати ZFS».

Через тижні стався незапланований відключник електрики. Не катастрофічна подія — просто такий випадок, через який ви дізнаєтеся, наскільки хороше обслуговування UPS. Системи повернулися. Postgres теж, але не чисто.
База даних потребувала відновлення і показала ознаки корупції в частині останніх транзакцій. Інцидент перетворився на крос-командну вправу з бекапів, архівів WAL і незручних розмов.

Операційний провал полягав не лише в зміні властивості; справа була в відсутності меж наборів даних і документації про наміри.
Набір даних БД успадкував інші значення за замовчуванням від загального дерева наборів даних, і ніхто не відстежував, які набори «брехали» про sync.
Коли настав аудит ризику, команда не знала, що шукати.

Остаточне виправлення було консервативним: повернули sync=standard, оцінили придатність SLOG (або прийняли затримку), і виділили WAL в окремий набір даних з явними властивостями.
Також додали просту перевірку відповідності: zfs get -r sync на прод-пулах, відмічену в моніторингу.

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

3) Нудна, але правильна практика, що врятувала день: квоти + резервації + чітка власність наборів даних

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

На кінець кварталу одна дата-сайнс команда почала збирати великий проміжний обсяг даних у своєму наборі — у відриві від інших це було розумно, але катастрофічно в сукупності.
Навантаження досягло квоти. Завдання впали. Команда викликала платформну команду зі стандартним повідомленням «сховище зламалося».
Але продакшн залишився здоровим. Бази даних продовжували комітити. Образи VM продовжували працювати. Ніхто більше не помітив.

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

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

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

1) Симптом: «Ми видалили терабайти, але пул все ще повний»

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

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

2) Симптом: «Затримки бази даних під час бекапів/реплікації»

Корінна причина: Читання з набору бекапів «забруднює» ARC, витісняючи робочий набір БД; або запис бекапів конкурує за IOPS.

Виправлення: Розмістіть бекапи в окремому наборі даних; встановіть primarycache=metadata для стрімінгових наборів; плануйте важкі завдання; розгляньте окремий пул для бекапів.

3) Симптом: «VM зависають щоразу по годині»

Корінна причина: Завдання знімків/реплікації на наборі даних VM спричинюють сплески метаданих, або scrub/resilver збігаються з піковим навантаженням.

Виправлення: Налаштуйте частоту знімків для набору даних; рознесіть розклади; переконайтеся в розмірі special vdev, якщо він є; запускайте scrub у непіковий час і моніторьте вплив.

4) Симптом: «Усе повільно, як тільки пул досягає ~85% заповнення»

Корінна причина: Алокатор має менше свободи; фрагментація зростає; write amplification збільшується, особливо на RAIDZ з випадковими записами.

Виправлення: Тримайте запас вільного місця; додавайте vdevs; перемістіть плинні навантаження в окремий пул; обрізайте знімки; застосовуйте квоти.

5) Симптом: «Після увімкнення стиснення CPU зростає і затримка погіршується»

Корінна причина: Використовують дорогий рівень/алгоритм стиснення для CPU-обмеженої системи, або стискають вже стиснуті/шифровані дані.

Виправлення: Використовуйте zstd на розумному рівні (дефолт платформи); перевірте запас CPU; розгляньте залишення стиснення ввімкненим, але уникайте крайніх рівнів.

6) Симптом: «Записи додатку повертають ENOSPC, хоча zpool показує вільне місце»

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

Виправлення: Перевірте zfs get quota,reservation; налаштуйте квоти/резервації; комунікуйте власність — це робота політики, а не хитрість ZFS.

7) Симптом: «Малі файли повільні до болю»

Корінна причина: Метадані і малі блоки на повільних дисках; відсутній special vdev; recordsize не головний фактор — питання в IOPS.

Виправлення: Використовуйте special vdev з достатньою ємністю; налаштуйте special_small_blocks відповідно; переконайтеся, що atime вимкнено; виділіть окремий набір даних для дрібних файлів.

8) Симптом: «Реплікація не може видалити старі знімки»

Корінна причина: Холди на знімках або залежні інкрементальні ланцюги, що вимагають їх збереження.

Виправлення: Перевірте холди; перевірте інструмент реплікації; акуратно розірвіть і перезапустіть реплікацію; не знищуйте потрібні знімки навмання.

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

Крок за кроком: міграція з «одного великого набору даних» до наборів даних на навантаження

  1. Інвентаризація навантажень і mountpoint-ів.
    Визначте межі на основі поведінки: DB, WAL, образи VM, контейнери, логи, бекапи, домашні директорії.
  2. Визначте мінімальний набір політик.
    Для кожного навантаження: частота/утримання знімків, стиснення, recordsize/volblocksize, atime, квоти/резервації, політика кешування.
  3. Створіть набори даних з явними властивостями.
    Не покладайтеся на успадкування для критичних налаштувань; успадкування придатна для дефолтів, але не для контрактів.
  4. Перемістіть дані з планом.
    Для живих сервісів використовуйте rsync з вікном простою або міграцію, обізнану про застосунок; для великих наборів даних розгляньте send/receive знімків.
  5. Оновіть fstab/systemd mount units.
    Зробіть монтовання явним і послідовним; уникайте несподіванок з mountpoint-ами.
  6. Впровадьте інструменти знімків за класами наборів даних.
    Один графік для даних БД, інший для образів VM, ні для кешів, якщо це не має цінності.
  7. Застосуйте квоти там, де живуть «тимчасові» дані.
    Контейнери, CI, кеші, логи. Якщо це можна відтворити — воно має ліміт.
  8. Резервуйте для навантажень, які не повинні падати.
    Бази даних та критичні сховища стану.
  9. Додайте моніторинг на дрейф.
    Сповіщення про несподіване sync=disabled, зростання кількості знімків, або зменшення запасу ємності пулу.
  10. Проведіть game day.
    Практикуйте відновлення файлу зі знімків; відкат набору даних з клонів; прийом реплікації в правильний набір даних.

Операційний чекліст: стандартні налаштування для наборів даних (стартовий набір)

  • DB дані (файли): recordsize=16K, compression=zstd, atime=off, знімки часті, але з коротким утриманням.
  • DB WAL/binlog: recordsize=16K (або специфічно для навантаження), atime=off, знімки зазвичай вимкнені або з мінімальним утриманням.
  • Образи VM (файли): recordsize=128K, compression=zstd, знімки щоденно/щотижнево залежно від RPO/RTO.
  • ZVOL для VM: встановіть volblocksize під час створення; документуйте; ставтеся до змін як до міграції.
  • Контейнери: квоти обов’язкові; знімки опціональні й зазвичай короткі; розгляньте контролі кешу при високій плинності.
  • Бекапи/receive: recordsize=1M часто доречний, primarycache=metadata, довге утримання але під контролем реплікації.
  • Логи: стиснення увімкнено, знімки рідко потрібні, ротація/квоти обов’язкові.

Чекліст управління: запобігти поверненню хаосу

  • Кожен набір даних має власника і призначення в імені.
  • Кожен прод-набір має явну політику знімків, задокументовану в коді/runbooks.
  • Квоти існують для всього некритичного або відтворюваного.
  • Резервації існують для критичного стану, де ENOSPC неприйнятний.
  • Дрейф властивостей виявляється: планові звіти про sync, recordsize, compression, кількості знімків.

FAQ

1) Чи означає «набір даних на навантаження» один набір даних на застосунок?

Не обов’язково. Це означає один набір даних на поведінку і політику. Один застосунок може потребувати кілька наборів даних (дані БД проти WAL проти завантажень).
Десять застосунків можуть ділити один набір даних, якщо вони дійсно ділять цикл життя і політику (рідко, але можливо).

2) Скільки наборів даних — забагато?

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

3) Чи треба ставити recordsize=8K для кожної бази даних?

Не робіть універсально. Багато СУБД пишуть сторінки 8K/16K, але навантаження має значення. Почніть з 16K для DB-наборів даних, вимірюйте та налаштовуйте.
Якщо ви використовуєте ZVOLs, recordsize не той параметр — там volblocksize.

4) Чи безпечне стиснення для баз даних?

Загалом так, і часто корисно. Ризик — навантаження на CPU в системах, вже перевантажених CPU.
Виправлення — не «вимкнути стиснення всюди», а «використовувати розумне стиснення і моніторити CPU».

5) Чи варто колись використовувати sync=disabled?

Лише якщо ви свідомо погоджуєтесь втратити останні синхронні записи при аварії/відключенні живлення — зазвичай для тимчасових даних, де коректність не потрібна.
Якщо це продакшн-стан, ставтеся до sync=disabled як до зняття пасків безпеки заради кращого часу в дорозі.

6) Чому видалення великої директорії не звільнило місце одразу?

Знімки і клоні можуть тримати блоки. Перевірте використання знімків на цьому наборі даних. Місце повернеться, коли останнє посилання зникне.

7) Чи шкодять квоти продуктивності?

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

8) Якщо я встановлю recordsize зараз, чи перезапише це існуючі дані?

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

9) Чи можна використовувати один графік знімків для всього і забути?

Можна, так само як можна використовувати один пароль для всього. Шкода виявляється пізніше, і завжди в найгірший момент.
Графіки знімків повинні відповідати цінності та плинності даних.

10) Який найшвидший виграш, якщо ми вже в хаосі?

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

Висновок: наступні кроки, які справді працюють

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

Практичні наступні кроки:

  1. Намалюйте мапу навантажень і позначте, де політики відрізняються. Це й будуть межі ваших наборів даних.
  2. Створіть або рефакторіть набори даних так, щоб кожна межа мала явні властивості: recordsize/volblocksize, compression, atime, знімки, квоти/резервації.
  3. Впровадьте швидкий триаж (ємність → насичення → шумний сусід → дрейф політик) і потренуйте його.
  4. Додайте виявлення дрейфу: сповіщення, якщо прод-набір змінює sync=disabled, якщо кількість знімків вибухає, або якщо запас ємності пулу падає нижче порогу.

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

← Попередня
Debian 13: Відмова в доступі на bind-монтах — виправте відображення UID/GID чистим способом (case #41)
Наступна →
Docker: Blue/green на одному хості — найпростіший підхід, що працює

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