Якщо ви запускаєте ВМ на ZFS, напевно вже підганяли recordsize для датасетів, сперечалися про SLOG у чаті та принаймні раз звинувачували «шумних сусідів». Потім одного дня ви відкриваєте volblocksize на zvol і розумієте, що їздили з ручником — тихо, дорого і з незрозумілим виглядом на графіки IOPS.
volblocksize — одне з тих налаштувань, що здається занадто дрібним, щоб мати значення, як вкладка «Додатково», у яку ніхто не клацає. Але воно визначає, як ZFS розбиває I/O віртуального диска ВМ, що впливає на write amplification, що впливає на затримку, і врешті-решт — чи вирішить ваша база даних сьогодні таймаутити. Це не теорія; це видно у 99-го перцентиля затримок у продакшені в найгірший момент.
Що таке volblocksize насправді (і чого воно не є)
zvol — це ZFS, яке прикидається блоковим пристроєм. Ви створюєте його через zfs create -V ..., і воно з’являється як щось на кшталт /dev/zvol/pool/vm-101-disk-0. Ваш гіпервізор форматують його (або передає «raw»), і гостьова ОС вважає, що це диск.
volblocksize — це внутрішній розмір блоку, який ZFS використовує для даних цього zvol. Коли гість пише 4K, 8K, 64K або 1M шматків, ZFS має відобразити ці записи у свої власні блоки. Для датасету таким регулятором зазвичай є recordsize. Для zvol схожим регулятором є volblocksize.
Спокуса така: «просто виставити 4K для ВМ» або «128K, бо ZFS любить великі блоки». Обидва — напівправди, що погано старіють. Правильна відповідь залежить від типу I/O (рандомні чи послідовні), файлової системи гостя, розміру сторінки БД, поведінки sync-записів і латентності апаратури.
Чого це не є:
- Це не розмір блоку файлової системи гостя. Гість може використовувати 4K блоки на zvol з
volblocksize16K; це буде «працювати», просто може працювати дорого. - Це не
ashiftпулу. Вирівнювання важливе, алеashiftстосується фізичного розміру сектора, який ZFS вважає для vdev. - Це не магічний вимикач IOPS. Це регулятор компромісу між ефективністю IOPS, накладними витратами на метадані, коефіцієнтом стиснення та ризиком хвостової затримки.
Перший жарт, бо ми цього заслужили: Змінювати volblocksize після того, як дані записані у zvol — як намагатися змінити розмір піци після того, як її з’їли: є методи, але жоден не відчувається як «змінити розмір».
Чому воно визначає IOPS і затримку
IOPS і затримка — це не просто «наскільки швидкі диски». У збереженні ВМ це також питання, скільки операцій ви змушуєте стек зберігання виконувати на кожну операцію гостя. volblocksize змінює цей коефіцієнт множення.
Write amplification, яку можна реально проаналізувати
Припустимо, гість робить 4K випадкового запису. Що робить ZFS, залежить від volblocksize:
- Якщо
volblocksize=4K, ZFS може оновити один 4K блок (плюс метадані). Це «буквальне» відображення. - Якщо
volblocksize=16K, ZFS має оновити 16K блок. Якщо змінився лише 4K, ZFS все одно запише новий 16K блок (copy-on-write), що означає логічну операцію read-modify-write: потрібно зібрати новий вміст 16K, а потім записати. Залежно від кешування та того, як збирається блок, ви витрачаєте пропускну здатність і додаєте ризик затримки. - Якщо
volblocksize=128K, ви потенційно перезаписуєте 128K за 4K зміну — щонайменше логічно. Якщо дані добре стискаються або більшість запису — нулі, можна пощастити. Але «можливо стискається» — не стратегія.
Тепер переверніть для послідовного I/O. Якщо ваша ВМ стрімує великі читання/записи (резервне копіювання, лог-реплікація, великі копіювання файлів): великі блоки можуть знизити накладні витрати і підвищити пропускну здатність, бо ZFS робить менше операцій I/O на мегабайт.
Затримка — це гра хвостів
Середня затримка вас лестить. Хвостова (tail) затримка принижує. Великі блоки збільшують обсяг роботи на логічну зміну й розширюють розподіл затримок, коли система під тиском: більше байтів переміщати, більше часу в черзі за іншими записами, більше часу очікування commit txg і більше шансів на колізії з вимогами sync-записів.
У продакшені найгірші моменти передбачувані: шквал знімків (snapshots), вікна бекапів, scrub, resilver і той квартальний батч, про який ніхто не повідомив SRE. Вибір volblocksize, що «проходить у бенчмарку», може стати генератором відмов у 99.9-му перцентилі, коли пул заповнений на 70% і фрагментований.
Метадані та CPU теж не безкоштовні
Малі блоки означають більше блоків, а отже більше метаданих: більше вказівників блоків, більше непрямих блоків, більше операцій контрольних сум, більше рішень про компресію, більше роботи для ARC. Ви цілком можете створити систему, яка «швидко по IOPS», але CPU-зв’язана в шарі зберігання, особливо з шифруванням або інтенсивною компресією.
Цікаві факти та історичний контекст
Інженери зберігання люблять фольклор; ось конкретні моменти, що справді мають значення:
- ZFS народився у світі обертових дисків, де послідовна пропускна здатність була важливою, а затрати на позиціювання — величезними. Великі блоки мали сенс.
- Copy-on-write — суперсила та податкова збирачка ZFS. Це дозволяє знімки та консистентність, але також означає, що «малі зміни можуть перезаписувати великі блоки», якщо ви вибрали великі блоки.
- zvol створювали, щоб забезпечити блочні пристрої для iSCSI, дисків ВМ і swap-подібних випадків — навантажень, що не поводяться як великий файловий NAS.
- Сектори 4K виграли індустрію, але перехід був складним (512e диски, Advanced Format). Невирівнювання могло тихо зменшити продуктивність записів удвічі.
- Гіпервізори змінили характер I/O. Thin provisioning, snapshot-инг на шарі гіпервізора та вибухи випадкових записів стали нормою, а не виключенням.
- Розміри сторінок баз даних часто 8K або 16K (залежно від движка і конфігурації). Коли розмір блоку зберігання конфліктує зі сторінкою БД, ви платите двічі: за I/O і за WAL/redo поведінку.
- SSD зробили випадковий I/O дешевшим порівняно з HDD, але не безкоштовним. Латентність нижча, але write amplification (і на стороні ZFS, і в FTL SSD) все ще має значення.
- NVMe зменшив затримку настільки, що «програмні витрати» стали видимими. Раптом вибір розміру блоку проявляється як витрати CPU та contention замість лише очікування диску.
- Компресія в ZFS стала мейнстрімом, бо процесори прискорилися, а місце зберігання залишилося дорогим. Компресія сильно взаємодіє з розміром блоку: більші блоки зазвичай краще стискаються, але можуть підсилювати малі записи.
Ментальна модель, яку можна використовувати о 3 ранку
Думайте про volblocksize як про «мінімальну одиницю перезапису» для zvol всередині ZFS. Гість може записати 4K, але якщо ZFS зберігає дані у шматках по 64K, ZFS відповідає за створення нової 64K версії цього шматка при кожній зміні.
Є три великі наслідки:
- IOPS для випадкових записів: менший volblocksize зазвичай допомагає, бо ви перезаписуєте менше байтів на запис.
- Послідовна пропускна здатність: більший volblocksize може допомогти, бо ви амортизуєте витрати на метадані та контрольні суми.
- Хвостова затримка: менші блоки зазвичай більш передбачувані в змішаних навантаженнях, тоді як більші можуть різко стрибати при поєднанні sync-записів і навантаження пула.
Другий і заключний жарт: Налаштування зберігання — як приготування еспресо: на одну щонайтонку й усе зупиняється; на одну щонайгрубіше — смак жалю.
Синхронні записи, SLOG і чому volblocksize змінює біль
ВМ часто генерують sync-записи навіть якщо ви їх не просили. Бази даних викликають fsync. Журнальні файлові системи комітять. Гіпервізори можуть виконувати flush. І якщо ви експортуєте zvol по iSCSI/NFS (або вмикаєте деякі налаштування гіпервізора), «sync» може стати поведінкою за замовчуванням.
У ZFS sync-записи повинні бути зроблені на диск перед тим, як система підтвердить їх. Без окремого SLOG-пристрою основні vdev пулу мають зафіксувати записи intent log на стійке сховище швидко. З хорошим SLOG ви можете швидко віддавати підтвердження, а пізніше скидати в пул під час txg commit.
Де тут грає роль volblocksize? Двома способами:
- Скільки даних «малий» запис перетворюється всередині ZFS перед тим, як його можна зафіксувати. Більші блоки можуть збільшити обсяг роботи для безпечного представлення тієї зміни.
- Скільки фрагментації та churn ви створюєте в основному пулі, що впливає на час txg commit і отже на sync-затримку під навантаженням.
Класичний шаблон болю: все добре, поки не починається вікно бекапів. Пул зайнятий великими послідовними читаннями/записами, часи txg commit ростуть, і раптом fsync бази даних підскакує від «нормально» до «додаток впав». Якщо ваше volblocksize занадто велике для випадкових записів дисків ВМ, ви збільшуєте обсяг роботи на кожен fsync під тиском.
Важлива нота: продуктивність sync-записів — не лише про volblocksize. Це також про властивість sync, logbias, якість SLOG, компоновку пулу (mirrors vs RAIDZ) і загальне насичення сховища. Але volblocksize — один із небагатьох важелів, що змінює фундаментальну гранулярність зміни.
Компресія, контрольні суми та прихований податок на CPU
Компресія на zvol не завжди неправильна. Але вона не завжди безкоштовна.
Більші блоки часто краще стискаються, бо компресор бачить більше повторюваних шаблонів. Це може зменшити фізичні записи, що компенсує витрати на перезапис більших логічних блоків. У реальному житті все залежить від міксу:
- Диски ОС: часто добре стискаються (текст, бінарники, багато нулів). Компресія може допомогти, і вибір розміру блоку може бути менш болісним.
- Бази даних з уже стисненими сторінками: можуть погано стискатися. Тоді більші блоки просто означають більше байтів для перезапису без вигоди.
- Зашифровані гості: якщо гість шифрує файлову систему, компресія на рівні ZFS може стати неефективною. Не покладайтеся на компресію, щоб врятувати вас від поганого розміру блоку, коли дані мають високу ентропію.
Контрольні суми та (опційне) шифрування також масштабуються з «кількістю блоків» і «байтів для обробки». Занадто малий розмір блоку може стати CPU-важким при високих IOPS; занадто великий — створювати затримки при випадкових записах. Ви шукаєте «ліктьовий» перелом кривої для вашого середовища.
ashift, розмір секторів і вирівнювання
Якщо volblocksize — логічна одиниця перезапису zvol, то ashift — це припущення пулу про фізичне вирівнювання. Більшість сучасних пулів має бути принаймні з ashift=12 (4K). Деякі середовища віддають перевагу ashift=13 (8K) для певних пристроїв.
Невирівнювання — тихий вбивця: якщо ZFS думає, що сектор пристрою менший, ніж насправді, це може викликати read-modify-write на рівні диска. Це той тип проблеми з продуктивністю, що виглядає як «випадкові стрибки затримки» і виживає місяці нарад.
Правило великого пальця: переконайтеся, що ashift пулу правильний при його створенні. Змінити його пізніше без перебудови неможливо. Потім обирайте volblocksize як кратне розміру сектора (4K, 8K, 16K…). Більшість розгортань вважають 4K або 8K безпечними базовими значеннями для дисків ВМ з випадковими записами.
Вибір volblocksize для реальних робочих навантажень ВМ
Поговоримо про вибір, а не ідеологію.
Поширені стартові точки
- Універсальні диски ОС ВМ:
volblocksize=8K— поширений компроміс: не надто важкий для метаданих, не надто призводить до ампліфікації.4Kможе бути відмінним для чутливих до латентності змішаних навантажень, але може коштувати більше метаданих і CPU в масштабі. - Диски ВМ для баз даних (інтенсивні випадкові записи): почніть з
8Kабо16Kзалежно від розміру сторінки БД та спостережуваного I/O. Якщо не знаєте —8Kчасто безпечніший за128K. - Обсяги з послідовною інтенсивністю (цілі бекапу всередині ВМ, медіа, великі об’єкти):
64Kабо128Kможуть мати сенс, якщо навантаження дійсно велеблокове послідовне і не робить багато дрібних випадкових перезаписів.
Ось частина, яку люди пропускають: ви не обираєте volblocksize базуючись на тому, що «ZFS любить», ви обираєте його за тим, що реально робить ваш гість. Якщо гість весь день робить 4K випадкові записи, давати йому 128K одиницю перезапису — фактично підписуватися на непотрібну роботу й варіативність.
Mirrors проти RAIDZ змінюють ставки
На mirror vdev випадкові записи зазвичай «дружніші». На RAIDZ малі випадкові записи дорожчі через паритет і патерни read-modify-write на рівні vdev. Надто малий volblocksize на RAIDZ може бути караючим. Надто великий — караючим іншим способом (амліфікація). «Правильне» значення більш чутливе на RAIDZ.
Якщо ви запускаєте зберігання ВМ на RAIDZ і вам важлива латентність — ви вже граєте на складному режимі. Це може працювати, але налаштування та резерви по ємності мають бути нудно дисципліновані.
Три корпоративні міні-історії
1) Інцидент через хибне припущення: «ZFS сам впорається»
Мігрували завантажену внутрішню SaaS-платформу з legacy SAN на кластер з ZFS. Команда зробила розумні речі: mirror vdev, нормальний RAM, SSD та окремий SLOG, бо база даних активно викликала fsync. Пілот виглядав добре. Перемикання було заплановано, ризики оцінені, тикет зміни погоджено, кава заварена.
Припущення було простим: «ZFS розумний; він адаптується». zvol створили з великим volblocksize, бо хтось пам’ятав, що ZFS любить великі блоки для пропускної здатності, і в презентації вендора був графік, що хвалив послідовну продуктивність. Ніхто не зіставив I/O-профіль ВМ з розміром блоку.
Інцидент не трапився одразу. Він зачекав, поки система пропрацює трохи: з’явилися знімки, churn і реальна фрагментація. Потім, під час рутинного батча, латентність комітів бази почала хитатися. Потоки додатку накопичувалися. Ретрайи множилися. Пул не «впав», він став просто повільним так, що все інше виглядало зламаним.
On-call перевірили CPU (нормально), мережу (нормально), SLOG (нормально) і все одно бачили сплески latency для sync-записів. Прорив стався, коли хтось швидко запустив zfs get volblocksize і порівняв з I/O-розмірами гостя з iostat і коротким запуском fio. Гості писали багато 8K і 16K; одиниця перезапису zvol була набагато більшою. Під навантаженням кожний маленький коміт тягнув за собою потяг перезапису більшого блока.
Виправлення не було кнопкою. Потрібно було створити нові zvol з адекватним volblocksize і мігрувати диски живцем де можливо, офлайн де ні. Це була довга ніч, але корисна: ZFS розумний, так. Але він не ясновидець.
2) Оптимізація, що відкотилася: «Давайте 4K усюди»
Інша організація, інша проблема. У них була флотилія чутливих до затримок маленьких ВМ (CI воркери, build агенти, тестові бази) і вони хотіли більше IOPS. Хтось прочитав, що 4K volblocksize покращує випадкові записи, і вирішили стандартизуватися на ньому. Без винятків. Послідовність втішає, особливо в корпоративному середовищі, де будь-яке виняток перетворюється на зустріч.
Перший тиждень виглядав чудово. Бенчмарки покращилися. Дашборди посміхалися. Люди тихо святкували в Slack з тим емодзі, яке використовують, коли не хочуть, щоб менеджмент помітив важливу зміну.
Потім реальність з’явилася у вигляді тиску на CPU і пам’ять. Ноди зберігання почали витрачати більше часу на метадані та обчислення контрольних сум. ARC почав частіше переміщатися, бо робоча множина містила набагато більше блоків і вказівників. Деякі хости дійшли до стіни, де пул не був насичений по пропускній здатності, але завершення I/O сповільнилося, бо програмний шлях виконував більше роботи на операцію.
Це не впало катастрофічно; воно впало бюрократично. Розробники скаржилися, що збірки «інколи повільні». Тестові завдання стали нестабільні. SRE тижнями ганялися за фантомними «шумними сусідами», поки не помітили спільну нитку: універсальний 4K розмір блоку для навантажень, що включали багато послідовного I/O та роботу з великими файлами.
Зрештою політика стала більш зрілою: 4K для конкретних zvol, доведено випадковими записами; 8K або 16K для загальних ОС-дисків; більші лише для відомих послідовних обсягів. Урок не в «4K — погано». Урок — догма дорога.
3) Нудна але правильна практика, що врятувала день: «Стандартні профілі + шлях міграції»
Ця команда вже була обпікана, тому зробила нешикарну річ: створила невеликий набір профілів зберігання і зробила їх дефолтними. Кожен профіль мав документований volblocksize, налаштування компресії, очікування щодо sync/logbias і коротку раціоналізацію. Нічого екзотичного. Просто рішення, записані наперед до інциденту.
Коли нова ВМ створювалася, запитувач обирав «general», «db-heavy» або «sequential». Якщо не обирався — дефолтувало на general. Це одне вже запобігло багатьом випадковим невідповідностям.
Але справжній виграш — шлях міграції. Вони прийняли, що volblocksize фактично незмінний для існуючих даних, і налагодили операційні процеси для переміщення дисків: створити новий zvol з потрібним volblocksize, реплікувати або копіювати блоками, переключити, валідувати, видалити старий. Вони практикували це вдень, а не у відчаї під час кризи.
Тож коли на кластер прийшла VM від вендора з важким sync-навантаженням, фікс не став воєнною кімнатою. Це була запланована міграція з профілю «sync-latency» і контрольований cutover. День врятував нудний підхід: повторювана процедура і дисципліна її виконання.
Практичні завдання: команди, виводи та інтерпретація
Ось завдання, які я реально виконую, коли хтось каже «зберігання ВМ повільне» і є лише скриншот червоного графіка. Команди припускають Linux-хост з утилітами ZFS. Підлаштуйте імена пулів і zvol під себе.
Завдання 1: Визначити, чи диск ВМ — це zvol і знайти його ім’я в ZFS
cr0x@server:~$ ls -l /dev/zvol/tank/vm-101-disk-0
lrwxrwxrwx 1 root root 13 Dec 24 10:20 /dev/zvol/tank/vm-101-disk-0 -> ../../zd0
Інтерпретація: Ви маєте справу з zvol. Добре — volblocksize застосовується. Бекінг-пристрій тут — /dev/zd0.
Завдання 2: Перевірити volblocksize (і кілька суміжних властивостей)
cr0x@server:~$ zfs get -H -o property,value volblocksize,compression,logbias,sync,refreservation tank/vm-101-disk-0
volblocksize 128K
compression lz4
logbias latency
sync standard
refreservation none
Інтерпретація: 128K велике для загального диска ВМ, якщо ви не знаєте, що він в основному послідовний. Компресія ввімкнена (lz4), що може допомогти або ні. sync=standard означає, що flush/ fsync гостя має значення.
Завдання 3: Підтвердити ashift пулу (базове вирівнювання)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
52: vdev_tree:
76: ashift: 12
104: ashift: 12
Інтерпретація: ashift=12 (4K сектори) — адекватна база. Якщо бачите ashift=9 на сучасних дисках, імовірно знайдена фундаментальна проблема.
Завдання 4: Швидко подивитися розміри I/O та затримку на хості
cr0x@server:~$ iostat -x 1 5 /dev/zd0
Linux 6.8.0 (server) 12/24/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
6.12 0.00 3.45 8.90 0.00 81.53
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
zd0 5.00 80.0 0.00 0.00 4.20 16.0 220.00 3520.0 10.00 4.35 18.50 16.0 3.20 92.00
Інтерпретація: Середній розмір write request приблизно 16K, не 128K. Якщо volblocksize 128K, очікуйте додатковий churn на дрібних записах, особливо під sync-навантаженням. w_await близько 18ms вже натякає на біль у latency.
Завдання 5: Спостерігати ZFS-рівневу латентність через iostat на vdev
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 4.2T 3.1T 80 900 12.0M 55.0M
mirror 2.1T 1.6T 40 450 6.0M 27.5M
nvme0n1 - - 20 230 3.0M 14.0M
nvme1n1 - - 20 220 3.0M 13.5M
mirror 2.1T 1.6T 40 450 6.0M 27.5M
nvme2n1 - - 20 225 3.0M 13.8M
nvme3n1 - - 20 225 3.0M 13.7M
Інтерпретація: Пропускна здатність не дуже велика, але операцій багато. Це типовий малоблоковий випадок випадкового I/O. Якщо ви бачите багато ops і помірні MB/s — розмір блоку має значення.
Завдання 6: Перевірити тиск txg sync (чи зайняті коміти занадто довго?)
cr0x@server:~$ cat /proc/spl/kstat/zfs/txgs
1 0x01 137 14592 0 0 0 0 0 0 0
2 0x01 137 14593 0 0 0 0 0 0 0
Інтерпретація: Поля різняться за платформою, але мета — помітити затримки txg і чи накопичуєте ви dirty-дані швидше, ніж можете їх синкнути. Якщо часи sync txg ростуть під час сплесків затримки — ви в «території тиску комітів».
Завдання 7: Перевірити, чи zvol ефективно використовує окремий лог (SLOG)
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
mirror ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 0 0
log
mirror ONLINE 0 0 0
nvme4n1 ONLINE 0 0 0
nvme5n1 ONLINE 0 0 0
errors: No known data errors
Інтерпретація: Є mirrored SLOG. Чудово. Але пам’ятайте: SLOG допомагає лише для sync-записів. Якщо ваше навантаження асинхронне або вузьке місце в іншому місці, SLOG не врятує.
Завдання 8: Перевірити, чи гість змушує sync (або ви випадково змусили його)
cr0x@server:~$ zfs get -H -o property,value sync tank/vm-101-disk-0
sync standard
Інтерпретація: standard означає, що ZFS шанує O_SYNC/fsync/flush семантику. Якщо хтось встановив sync=always, латентність зросте драматично для багатьох ВМ-навантажень. Якщо встановлено sync=disabled, латентність виглядає чудово, аж поки ви не втратите живлення і отримаєте дуже пізнавальний день.
Завдання 9: Створити тестовий zvol з кандидатом volblocksize (не змінюйте prod навмання)
cr0x@server:~$ sudo zfs create -V 50G -b 8K -o compression=lz4 tank/test-vm-disk-8k
cr0x@server:~$ zfs get -H -o property,value volblocksize tank/test-vm-disk-8k
volblocksize 8K
Інтерпретація: Використовуйте тестовий zvol для бенчмарку з fio з гостя або хоста. Вибір по даним краще за вибір по думках.
Завдання 10: Запустити швидкий fio тест випадкових записів проти zvol (на хості)
cr0x@server:~$ sudo fio --name=randwrite --filename=/dev/zvol/tank/test-vm-disk-8k \
--direct=1 --ioengine=libaio --rw=randwrite --bs=8k --iodepth=32 --numjobs=1 --time_based --runtime=20 --group_reporting
randwrite: (g=0): rw=randwrite, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=libaio, iodepth=32
fio-3.36
randwrite: write: IOPS=42.1k, BW=329MiB/s (345MB/s)(6580MiB/20001msec)
lat (usec): min=70, max=22450, avg=610.42, stdev=380.12
clat percentiles (usec):
| 1.00th=[ 150], 10.00th=[ 250], 50.00th=[ 520], 90.00th=[ 980], 99.00th=[ 1800], 99.90th=[ 4200]
cpu : usr=3.20%, sys=11.40%, ctx=820k, majf=0, minf=12
Інтерпретація: Дивіться на перцентилі, а не лише IOPS. Якщо 99.9-й перцентиль поганий — додаток рано чи пізно поскаржиться.
Завдання 11: Порівняти з більшим volblocksize на тестовому zvol (той самий fio)
cr0x@server:~$ sudo zfs create -V 50G -b 128K -o compression=lz4 tank/test-vm-disk-128k
cr0x@server:~$ sudo fio --name=randwrite --filename=/dev/zvol/tank/test-vm-disk-128k \
--direct=1 --ioengine=libaio --rw=randwrite --bs=8k --iodepth=32 --numjobs=1 --time_based --runtime=20 --group_reporting
randwrite: write: IOPS=18.7k, BW=146MiB/s (153MB/s)(2920MiB/20001msec)
lat (usec): min=95, max=78410, avg=1380.12, stdev=1220.55
clat percentiles (usec):
| 1.00th=[ 220], 10.00th=[ 420], 50.00th=[ 1100], 90.00th=[ 2600], 99.00th=[ 6800], 99.90th=[ 32000]
Інтерпретація: Той самий 8K робочий набір, значно гірші хвостові затримки і менше IOPS. Ось і проявляється податок ампліфікації.
Завдання 12: Перевірити логічне/фізичне використання zvol (ефекти компресії і паддінгу)
cr0x@server:~$ zfs list -o name,volsize,used,refer,compressratio tank/test-vm-disk-8k tank/test-vm-disk-128k
NAME VOLSIZE USED REFER COMPRESSRATIO
tank/test-vm-disk-8k 50G 3.2G 3.2G 1.45x
tank/test-vm-disk-128k 50G 6.8G 6.8G 1.10x
Інтерпретація: Більші блоки тут не стиснулися краще; вони використали більше місця. Реальні навантаження різні, але саме тому ви вимірюєте.
Завдання 13: Підтвердити, чи zvol тонкий (sparse) і чи refreservation маскує це
cr0x@server:~$ zfs get -H -o property,value volsize,volmode,refreservation tank/vm-101-disk-0
volsize 500G
volmode default
refreservation none
Інтерпретація: Поведінка тонкого розподілення залежить від середовища. Refreservation може уникнути «несподіванок під час заповнення», але за рахунок ефективності ємності.
Завдання 14: Виміряти реальний розподіл розмірів I/O гостя (семплінг на хості)
cr0x@server:~$ sudo blktrace -d /dev/zd0 -w 10 -o - | blkparse -i - | head -n 12
8,16 3 1 0.000000000 1234 Q WS 12345678 + 16 [qemu-kvm]
8,16 3 2 0.000010000 1234 G WS 12345678 + 16 [qemu-kvm]
8,16 3 3 0.000020000 1234 P WS 12345678 + 16 [qemu-kvm]
8,16 3 4 0.000080000 1234 C WS 12345678 + 16 [0]
8,16 3 5 0.000100000 1234 Q WS 12345710 + 32 [qemu-kvm]
8,16 3 6 0.000120000 1234 C WS 12345710 + 32 [0]
Інтерпретація: «+ 16» секторів тут — 8K (16 × 512B секторів) у схемі звітування 512-секцій. Це показує, що VM реально відправляє, а не те, що вам хочеться.
Завдання 15: Запланувати безпечну міграцію на новий volblocksize (create + copy)
cr0x@server:~$ sudo zfs create -V 500G -b 8K -o compression=lz4 tank/vm-101-disk-0-new
cr0x@server:~$ sudo dd if=/dev/zvol/tank/vm-101-disk-0 of=/dev/zvol/tank/vm-101-disk-0-new bs=16M status=progress conv=fsync
104857600000 bytes (105 GB, 98 GiB) copied, 310 s, 338 MB/s
Інтерпретація: Це грубий інструмент. Потребує простою (або принаймні узгодженої стратегії snapshot на рівні гіпервізора). Але це надійно і зберігає семантику блочного пристрою простою.
Швидкий план діагностики
Коли затримка висока і люди кричать, потрібна коротка послідовність, що швидко звужує простір пошуку. Ось моя для ZFS zvol-backed ВМ.
Перше: чи пул справді насичений або просто «повільний»?
- Запустіть
zpool iostat -v 1і подивіться на ops і bandwidth. - Перевірте, чи один vdev гарячіший за інших (дисбаланс може виглядати як «випадкова латентність»).
- Шукайте високе використання з помірним throughput: це часто означає дрібний випадковий I/O або тиск sync.
Друге: це біль від sync-записів?
- Перевірте
zfs get sync,logbiasна zvol. - Перевірте, чи є SLOG і чи він здоровий (
zpool status). - Корелюйте сплески латентності з тиском комітів txg (kstats залежать від платформи; на Linux допомагають SPL kstats і системні логи).
Третє: чи співпадає volblocksize з I/O-профілем?
- Перевірте
zfs get volblocksizeдля ураженого zvol. - Відберіть реальні розміри I/O за допомогою
iostat -xна пристрої zvol і, при потребі,blktrace. - Якщо гостьові записи в основному 4K–16K, а volblocksize 64K–128K — припускайте ампліфікацію, поки не доведете протилежне.
Четверте: чи ви обмежені ємністю/фрагментацією?
- Перевірте заповненість пулу:
zfs list/zpool list. Велике використання підвищує фрагментацію і уповільнює алокації. - Перевірте фонова роботу: статус scrub/resilver у
zpool status. - Кількість знімків: велика кількість snapshot може збільшити накладні витрати на метадані і сповільнити деякі операції.
П’яте: валідуйте контрольним мікро-бенчмарком
- Створіть тестовий zvol з кандидатськими volblocksize.
- Запустіть fio з розмірами блоків, що відповідають навантаженню ВМ, і виміряйте хвостову латентність.
- Не бенчмарьте на вже палаючому пулі, якщо вам не подобається отримувати тикети самостійно.
Поширені помилки, симптоми та виправлення
Помилка 1: Використання 128K volblocksize для дисків ВМ з випадковими записами
Симптоми: Відмінна послідовна пропускна здатність, але бази даних тайм-аутять під змішаним навантаженням; високі 99-го/99.9-го перцентилі; IOPS нижчі, ніж очікувалось; «все добре, поки не бекап».
Виправлення: Мігруйте на новий zvol з volblocksize=8K або 16K (залежно від навантаження). Перевірте fio і метрики додатку. Не очікуйте, що зміна властивості in-place переробить існуючі дані.
Помилка 2: Встановлення 4K volblocksize скрізь без перевірки витрат CPU/метаданих
Симптоми: Випадкові IOPS покращилися, але вузли зберігання показують більший system CPU; ARC churn; флуктуації продуктивності на послідовних навантаженнях; «швидко, але нестабільно».
Виправлення: Використовуйте профілі. Тримайте 4K для справді випадкових записів; 8K/16K для універсальних дисків ОС; більші — лише для відомо послідовних томів.
Помилка 3: Плутанина між налаштуванням dataset (recordsize) і zvol (volblocksize)
Симптоми: Хтось ставить recordsize=16K на батьківський датасет і чекає змін у продуктивності диска ВМ. Нічого не відбувається; звинувачення летять вгору.
Виправлення: zvol не те саме, що файли у датасеті. Використовуйте zfs get volblocksize на самому zvol.
Помилка 4: Використання sync=disabled щоб «виправити латентність»
Симптоми: Графіки латентності виглядають чудово; керівництво щасливе; потім некоректне завершення приводить до пошкодження бази або втрачених транзакцій.
Виправлення: Тримайте sync=standard заради коректності. Якщо потрібна продуктивність для sync, виправте SLOG, архітектуру пулу і вирівнювання volblocksize відповідно до навантаження.
Помилка 5: Ігнорування заповненості пулу та фрагментації
Симптоми: Те саме навантаження стає повільнішим за місяці; латентність записів повільно зростає; алокації дорожчають; «ZFS був швидкий на старті».
Виправлення: Підтримуйте запас по ємності. Плануйте розширення до паніки. Ставте дохідливість знімків і ретеншен як бюджет, а не хобі.
Помилка 6: Бенчмаркування з неправильним розміром блоку і називання цього «доказом»
Симптоми: fio показує величезні MB/s з 1M послідовними записами, але продакшен повільний; починаються аргументи.
Виправлення: Бенчмаркуйте з розмірами блоків і sync-семантикою, які використовує ваша ВМ. Включайте перцентилі затримки і змішані read/write патерни.
Чеклісти / покроковий план
Чекліст A: Пропозиція нового диска ВМ (на zvol)
- Класифікуйте навантаження: загальний ОС, DB-heavy (випадкові записи) або послідовно-важкий.
- Оберіть профіль:
- General:
volblocksize=8K(часто) зcompression=lz4 - DB-heavy:
8Kабо16K; тестуйте якщо можливо - Sequential-heavy:
64Kабо128K, якщо дійсно послідовне
- General:
- Створіть zvol явно (не покладайтеся на дефолти, які ви не аудіювали).
- Підтвердіть властивості через
zfs get. - Задокументуйте вибір у метаданих/тикеті ВМ, щоб майбутній ви не копирсалися археологічно.
Чекліст B: Коли підозрюєте, що volblocksize неправильний
- Підтвердіть поточний
volblocksizezvol. - Виміряйте реальні розміри I/O (
iostat -x, опційноblktrace). - Створіть тестовий zvol з кандидатом меншим/більшим volblocksize.
- Бенчмаркуйте з fio з розмірами блоків, що відповідають навантаженню.
- Складіть план міграції:
- Вікно простою або інструменти live-міграції
- Метод копіювання на рівні блоків (dd, реплікація на шарі гіпервізора або копіювання з гостя)
- Кроки валідації (перевірка файлової системи, перевірки додатку, тест продуктивності)
- Переключіться, моніторьте хвостову латентність, потім виведіть з експлуатації старий zvol.
Чекліст C: «Нудні запобіжники» для зберігання ВМ на ZFS
- Тримайте використання пулу в межах здорового запасу; уникайте роботи на межі заповнення.
- Плануйте scrubs, але не накладайте їх на найважчі вікна записів, якщо не хочете тестувати стійкість.
- Відстежуйте перцентилі затримки, а не лише середні значення.
- Стандартизуйте невелику кількість профілів volblocksize і дотримуйтесь їх.
- Прокачуйте процедуру міграції до того, як вона знадобиться.
Питання та відповіді
1) Який volblocksize за замовчуванням, і чи варто йому довіряти?
За замовчуванням залежить від платформи і версії, і вони можуть не відповідати вашому навантаженню ВМ. Сприймайте дефолти як «безпечні для когось», але не «оптимальні для вас». Завжди перевіряйте, що використовує ваше середовище, і бенчмаркуйте за своїм I/O-профілем.
2) Чи можна змінити volblocksize на існуючому zvol без міграції?
Ви часто можете змінити властивість, але це не переробить існуючі блоки в новий розмір чисто і миттєво. На практиці, якщо вам потрібна поведінка іншого volblocksize, плануйте створити новий zvol і мігрувати дані.
3) Чи 4K завжди найкращий для дисків ВМ?
Ні. 4K може бути відмінним для випадкових записів і передбачуваної латентності, але він збільшує накладні витрати на метадані і CPU. Для універсальних дисків ОС часто кращий баланс — 8K або 16K.
4) Як volblocksize пов’язаний з блоковим розміром файлової системи гостя?
Це незалежні шари. Розмір блоку гостя впливає на I/O, який він випускає. volblocksize zvol впливає на те, як ZFS зберігає і перезаписує ці записи. Коли вони не співпадають (наприклад, гість пише 8K сторінки, а zvol використовує 128K), ви отримаєте ампліфікацію і варіативність затримок.
5) Якщо у мене швидкий NVMe-пул, чи volblocksize все ще має значення?
Так. NVMe робить латентність настільки низькою, що програмні витрати і ампліфікація стають помітними. Ви можете легко змарнувати продуктивність NVMe поганим вибором розміру блоку, особливо для sync-важких або випадково-записних навантажень.
6) Чи компресія змінює рекомендований volblocksize?
Може. Компресія може зменшити фізичні записи і пом’якшити витрати більших блоків, але це залежить від навантаження. Зашифровані або вже стиснені дані часто не отримують вигоди. Вимірюйте compressratio і перцентилі затримок під реальним навантаженням.
7) А RAIDZ — чи це штовхає мене до більшого чи меншого volblocksize?
RAIDZ робить малі випадкові записи дорожчими через паритет і read-modify-write. Це не означає «завжди використовуйте гігантські блоки», але означає, що треба бути особливо обережним і тестувати. Mirrors загалом дружніші для випадкового I/O ВМ.
8) Чи може SLOG виправити поганий вибір volblocksize?
Гарний SLOG може значно покращити latency sync-записів, але він не усуне ампліфікацію і фрагментацію від невідповідного volblocksize. Думайте про SLOG як «допомагає швидко підтвердити sync», а не «виправляє неефективні записи».
9) Мій гіпервізор використовує QCOW2 або інший формат образу — чи має значення volblocksize?
Так, але стек ускладнюється. Формати образів можуть вводити власну гранулярність алокації і метадані, що може масштабуватися разом із поведінкою ZFS. Якщо вам потрібна передбачувана латентність, raw-томи простіше аналізувати, і volblocksize стає зрозумілішим важелем.
10) Який найбезпечніший volblocksize, якщо треба стандартизувати?
Якщо змушені вибрати одне для загальних дисків ВМ без знання навантажень — 8K часто «менш хибний» вибір у багатьох середовищах. Але чесна відповідь: стандартизуйте профілі, а не одне значення.
Висновок
volblocksize не гламурне, і саме тому воно боляче. Це низькорівневе рішення, яке тихо формує, скільки роботи ваш стек зберігання виконує на кожен запис ВМ, наскільки стабільною є ваша латентність під тиском і як швидко ваш «швидкий пул» перетворюється на вибачення.
Операційно зрілий підхід також найпростіший: вимірюйте реальні розміри I/O, обирайте невеликий набір адекватних профілів, бенчмаркуйте хвостову латентність і приймайте, що зміна volblocksize зазвичай — проєкт міграції, а не перемикач. Робіть так, і ви отримаєте те, в чому ZFS справді гарний: передбачувану коректність, стабільну продуктивність і поведінку зберігання, яку можна пояснити іншому інженеру під час інциденту.