fio для ZFS для ВМ: профілі, що відповідають реальності (не маркетингу)

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

Ваші користувачі ВМ не пишуть у тикети «IOPS низькі». Вони пишуть «база зависла», «оновлення Windows йдуть вічно» або «той CI-джоб знову завис на ‘копіюванні артефактів’».
Тим часом ви запускаєте швидкий fio-тест, отримуєте героїчні числа, і всі йдуть додому задоволені — до понеділка.

ZFS робить такі помилки вимірювання простішими, ніж більшість файлових систем, бо вона чесна щодо стійкості й має кілька шарів, які можуть «обдурити» (ARC, compression, зливання записів, transaction groups).
Виправлення не в «запустіть ще fio». Виправлення — запуск fio, який виглядає як реальна поведінка ВМ: змішані I/O, синхронні семантики, реалістичні глибини черги і цілі по затримці.

1) Reality check: what VM I/O really looks like

Більшість навантажень сховища від ВМ — це не «великі послідовні записи» і не «чисті випадкові 4K зчитування».
Це дратівливий коктейль:

  • Малі блоки випадкових читань і записів (4K–64K) від баз даних, метаданих, пакетних менеджерів і фонових сервісів Windows.
  • Сплески синхронних записів (journals, WALs, fsync-шторм, flush-и гостьової ОС).
  • Змішані співвідношення читань/записів, що змінюються по годинах (вікна бекапу, ротація логів, patch Tuesday).
  • Чутливість до затримки важливіша за пропускну здатність. ВМ може «відчувати повільність» при 2 000 IOPS, якщо p99 зростає з 2 ms до 80 ms.
  • Паралелізм нерівномірний: кілька «гарячих» ВМ можуть домінувати, тоді як більшість тихі, але все одно потребують стабільної кінцевої затримки.

Реалістичний fio-профіль для ВМ — це не про максимізацію заголовного числа. Це про вимірювання правильних режимів відмов:
затримка синхронних записів, чергування, write amplification і чи перетворюється ваш «швидкий» пул на гарбуз під час коміту TXG.

Якщо у вашому тесті немає fsync або еквівалентного режиму sync, він не вимірює того болю, що будить людей о 03:00.
Ваш пул може бути придатний для масового інгесту й одночасно жахливим для ВМ.

Жарт №1: Якщо результати fio виглядають надто гарними, щоб бути правдою, вони, ймовірно, прийшли з ARC, а не з дисків — як резюме, написане шаром кешу.

2) Interesting facts (and a little history) that change how you benchmark

Це не тривія. Кожен пункт відповідає за помилку в бенчмаркінгу, яку я бачив у продакшені.

  1. ZFS була спроєктована навколо copy-on-write і transaction groups: записи збираються й комітяться пакетно, що впливає на піки затримки під час TXG sync.
  2. ARC (Adaptive Replacement Cache) — це продуктивність, що базується на пам’яті: «теплий» ARC може зробити fio read-тести схожими на NVMe, навіть якщо пул на шпинделях.
  3. ZIL існує навіть без окремого SLOG: без окремого пристрою ZIL живе на основному пулі й конкурує з усім іншим.
  4. SLOG прискорює синхронні записи, а не всі записи: асинхронні записи обходять шлях ZIL; тестування без sync-семантики може зробити SLOG «марним».
  5. volblocksize задається під час створення zvol: ви практично не «налаштуєте» його пізніше. Це важливо для вирівнювання блоків VM I/O і write amplification.
  6. recordsize — це властивість dataset, а не zvol: змішування dataset і zvol та очікування однакової поведінки — класична помилка бенчмарку.
  7. Compression може збільшити IOPS (іноді драматично) зменшуючи фізичні записи — поки CPU не стане вузьким місцем або дані не перестануть стискатися.
  8. fio за замовчуванням може бути небезпечно «милосердним»: буферизований I/O і дружні глибини черг дають числа, що не переживуть реальної VM-конкуренції.
  9. NVMe кеші запису і захист при втраті живлення мають значення: «швидкий» споживчий NVMe може брехати під sync-навантаженнями, якщо йому бракує PLP-поведінки.

3) The ZFS + VM I/O stack: where your benchmark lies to you

VM I/O — це не один чорний ящик. Це ланцюг рішень і кешів. fio може протестувати будь-яке ланка в тому ланцюгу, і якщо ви тестуєте не ту,
ви опублікуєте бенчмарк для системи, якої фактично не експлуатуєте.

Guest filesystem vs virtual disk vs host ZFS

Гостьова ОС має власний кеш і поведінку writeback. Гіпервізор має власні черги. ZFS має ARC/L2ARC і свій pipeline запису.
Якщо ви запускаєте fio всередині гостьової ОС з buffered I/O, ви в основному бенчмаркуєте гостьову пам’ять і пропускну здатність хоста.
Якщо ви запускаєте fio на хості проти файлу, ви бенчмаркуєте dataset і поведінку recordsize, що може не збігатися з поведінкою zvol.

Sync semantics: where the real pain lives

Визначальна різниця між «демо-швидко» і «продакшн-стабільно» — це стійкість. Бази даних і багато файлових систем видають flush-и.
На ZFS синхронні записи йдуть через семантику ZIL; окремий SLOG може зменшити затримку, надаючи низьколатентний журнал.
Але SLOG не чарівний: він має бути низьколатентним, стабільним і безпечним при втраті живлення.

Найгірший гріх fio у бенчмаркінгу ВМ — запускати 1M послідовний запис, побачити 2–5 GB/s і назвати платформу «готовою для баз даних».
Це не профіль ВМ. Це маркетинг-слайд.

Queue depth and parallelism: iodepth is not a virtue signal

Навантаження ВМ часто мають помірну глибину черги на ВМ, але високу конкуруючість між ВМ. fio може симулювати це двома способами:
numjobs (кілька незалежних працівників) і iodepth (глибина черги на працівника).
Для ВМ віддавайте перевагу більшій кількості jobs із помірним iodepth, а не одному job з iodepth=256, якщо ви не моделюєте важку базу даних.

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

4) Principles for fio profiles that match production

Principle A: test the thing users feel (latency), not just the thing vendors sell (throughput)

Фіксуйте p95/p99 latency, а не тільки середнє. Ваші користувачі ВМ живуть в хвості.
fio може звітувати перцентилі; використовуйте їх і ставтеся до них як до основних метрик.

Principle B: include sync write tests

Використовуйте --direct=1, щоб уникнути ефектів сторінкового кешу гостя (коли тестуєте всередині гостя), і застосовуйте механізм sync:
--fsync=1 (або --fdatasync=1) для файлових навантажень, або --sync=1 для деяких engine-ів.
На сирих блочних пристроях можна апроксимувати, використовуючи --ioengine=libaio і примусово викликаючи flush-и, але найчистіша модель для «fsync-штормів» ВМ — це тест із реальною файловою системою + fsync.

Principle C: ensure the working set defeats ARC when you intend to test disks

Якщо ви хочете виміряти продуктивність пулу, розмір тесту має бути значно більший за ARC.
Якщо ARC — 64 GiB, не запускайте 10 GiB read-тест і не називайте це «швидкістю диска».
Альтернативно, тестуйте так, щоб фокус був на записах (особливо sync-записах), де ARC не може повністю приховати поведінку фізичних носіїв.

Principle D: match block sizes to what guests do

Випадкові I/O ВМ зазвичай зосереджуються на 4K, 8K, 16K і 32K. Великі 1M блоки — для резервних копій і стрімів мультимедіа.
Використовуйте кілька розмірів блоків або розподіл, якщо можете. Якщо потрібно обрати один: 4K random і 128K sequential — це робочі конячки.

Principle E: use time-based tests with ramp time

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

Principle F: pin down the test environment

CPU governor, балансування переривань, налаштування virtio, властивості dataset і zvol — усе це має значення.
Відтворюваність — це особливість. Якщо ви не можете повторити тест через місяць і пояснити різниці, це не бенчмаркінг — це відчуття.

Одна цитата, яку варто тримати на стікері біля моніторингу:
Everything fails, all the time. — Werner Vogels

5) Realistic fio profiles (with explanations)

Це не «найкращі» профілі. Це чесні профілі. Використовуйте їх як блоки для побудови і налаштовуйте під вашу суміш ВМ.
Для кожного профілю вирішіть, чи ви тестуєте всередині гостя, на хості проти zvol, або на хості проти файлу в dataset.

Profile 1: VM boot/login storm (read-heavy, small random, modest concurrency)

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

cr0x@server:~$ fio --name=vm-boot --filename=/dev/zvol/tank/vm-101-disk0 \
  --rw=randread --bs=16k --iodepth=8 --numjobs=8 --direct=1 \
  --time_based --runtime=180 --ramp_time=30 --group_reporting \
  --ioengine=libaio --percentile_list=95,99,99.9
vm-boot: (groupid=0, jobs=8): err= 0: pid=21233: Sat Dec 21 11:02:20 2025
  read: IOPS=42.1k, BW=658MiB/s (690MB/s)(115GiB/180s)
    slat (usec): min=3, max=2100, avg=12.4, stdev=18.9
    clat (usec): min=90, max=28000, avg=1480, stdev=2100
     lat (usec): min=105, max=28150, avg=1492, stdev=2102
    clat percentiles (usec):
     | 95.00th=[ 3600], 99.00th=[ 8200], 99.90th=[18000]

Що це означає: 42k IOPS виглядає чудово, але реальний сигнал — це p99 і p99.9 затримки.
Бут-шторм відчувається погано, коли p99 знаходиться в десятках мілісекунд.
Рішення: якщо p99.9 високий, шукайте конкуренцію (інші навантаження), спеціальні потреби vdev або завеликі/повільні vdev.

Profile 2: OLTP database-ish (mixed random, sync writes matter)

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

cr0x@server:~$ fio --name=oltp-mix-fsync --directory=/tank/vmtest \
  --rw=randrw --rwmixread=70 --bs=8k --iodepth=4 --numjobs=16 \
  --direct=1 --time_based --runtime=300 --ramp_time=60 \
  --ioengine=libaio --fsync=1 --group_reporting --percentile_list=95,99,99.9
oltp-mix-fsync: (groupid=0, jobs=16): err= 0: pid=21901: Sat Dec 21 11:12:54 2025
  read: IOPS=18.4k, BW=144MiB/s (151MB/s)(42.2GiB/300s)
    clat (usec): min=120, max=95000, avg=2900, stdev=5200
    clat percentiles (usec):
     | 95.00th=[ 8200], 99.00th=[22000], 99.90th=[62000]
  write: IOPS=7.88k, BW=61.6MiB/s (64.6MB/s)(18.0GiB/300s)
    clat (usec): min=180, max=120000, avg=4100, stdev=7800
    clat percentiles (usec):
     | 95.00th=[12000], 99.00th=[34000], 99.90th=[90000]

Що це означає: з fsync хвости затримок пишуться першими. Середнє може виглядати «нормально», тоді як p99.9 псує транзакції.
Рішення: якщо p99.9 write latency жахлива, перевірте SLOG, sync-настройки і поведінку кешу запису пристрою.

Profile 3: Windows update / package manager (metadata-heavy, small random reads/writes)

Тут спеціальні vdev для метаданих і малих блоків можуть виправдати свою вартість — якщо у вас справді відповідний пул.

cr0x@server:~$ fio --name=metadata-chaos --directory=/tank/vmtest \
  --rw=randrw --rwmixread=60 --bs=4k --iodepth=16 --numjobs=8 \
  --direct=1 --time_based --runtime=240 --ramp_time=30 \
  --ioengine=libaio --group_reporting --percentile_list=95,99,99.9
metadata-chaos: (groupid=0, jobs=8): err= 0: pid=22188: Sat Dec 21 11:18:22 2025
  read: IOPS=55.0k, BW=215MiB/s (226MB/s)(50.4GiB/240s)
    clat percentiles (usec): 95.00th=[ 2400], 99.00th=[ 6800], 99.90th=[16000]
  write: IOPS=36.0k, BW=141MiB/s (148MB/s)(33.0GiB/240s)
    clat percentiles (usec): 95.00th=[ 3100], 99.00th=[ 9200], 99.90th=[24000]

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

Profile 4: Backup/restore stream (sequential, large blocks, checks “can we drain?”)

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

cr0x@server:~$ fio --name=backup-write --filename=/tank/vmtest/backup.bin \
  --rw=write --bs=1m --iodepth=8 --numjobs=1 --direct=1 \
  --size=50G --ioengine=libaio --group_reporting
backup-write: (groupid=0, jobs=1): err= 0: pid=22502: Sat Dec 21 11:24:10 2025
  write: IOPS=1450, BW=1450MiB/s (1520MB/s)(50.0GiB/35s)

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

Profile 5: “No cheating” disk test (working set bigger than ARC, random reads)

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

cr0x@server:~$ fio --name=arc-buster --filename=/tank/vmtest/arc-buster.bin \
  --rw=randread --bs=128k --iodepth=32 --numjobs=4 --direct=1 \
  --size=500G --time_based --runtime=240 --ramp_time=30 \
  --ioengine=libaio --group_reporting --percentile_list=95,99
arc-buster: (groupid=0, jobs=4): err= 0: pid=22791: Sat Dec 21 11:31:12 2025
  read: IOPS=3100, BW=387MiB/s (406MB/s)(90.7GiB/240s)
    clat percentiles (usec):
     | 95.00th=[ 16000], 99.00th=[ 32000]

Що це означає: нижчі IOPS і вищі затримки тут нормальні; ви нарешті торкаєтесь дисків.
Рішення: якщо це несподівано жахливо, перевірте vdev layout, ashift і стан дисків перед тим, як сперечатися про прапорці fio.

6) Practical tasks: commands, what output means, and what you decide

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

Task 1: Identify whether you’re benchmarking a zvol or a dataset (and what properties apply)

cr0x@server:~$ zfs list -o name,type,volblocksize,recordsize,compression,sync tank
NAME                     TYPE     VOLBLOCKSIZE  RECORDSIZE  COMPRESS  SYNC
tank                     filesystem       -        128K     lz4       standard
tank/vmdata              filesystem       -        128K     lz4       standard
tank/vm-101-disk0         volume        16K          -      lz4       standard

Що це означає: zvol мають volblocksize; dataset — recordsize.
Змішування їх результатів — це спосіб випадково «оптимізувати» неправильну річ.
Рішення: обирайте ціль fio відповідно: /dev/zvol/... для zvol VM-дисків або файл на dataset, якщо ваші VM зберігаються у файлах.

Task 2: Check pool topology (your vdev layout is your performance contract)

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

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          mirror-0                  ONLINE       0     0     0
            nvme0n1                 ONLINE       0     0     0
            nvme1n1                 ONLINE       0     0     0
          mirror-1                  ONLINE       0     0     0
            nvme2n1                 ONLINE       0     0     0
            nvme3n1                 ONLINE       0     0     0
        logs
          nvme4n1                   ONLINE       0     0     0

errors: No known data errors

Що це означає: Mirrors поводяться інакше, ніж RAIDZ під випадковим I/O. logs вказує на наявність окремого SLOG-пристрою.
Рішення: якщо ви тестуєте sync-записи, перевірте, чи logs присутні і здорові; якщо RAIDZ — очікуйте нижчих IOPS на малі випадкові записи і плануйте відповідно.

Task 3: Check pool free space and fragmentation risk signals

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint tank
NAME         USED  AVAIL  REFER  MOUNTPOINT
tank        42.8T  6.10T   192K  /tank

Що це означає: Пули, що працюють «гарячими» по простору, зазвичай мають гіршу поведінку алокації і гірші хвости затримки.
ZFS тут не унікально погана; вона просто чесно показує наслідки.
Рішення: якщо запас майже закінчився, припиніть «бенчмаркинг» і почніть роботу з ємності. Будь-який fio-тест тепер вимірює систему, що вже у стресі.

Task 4: Validate that sync settings aren’t quietly lying to you

cr0x@server:~$ zfs get -r -o name,property,value sync tank/vmdata
NAME        PROPERTY  VALUE
tank/vmdata sync      standard

Що це означає: sync=standard означає, що sync-запити виконуються. sync=disabled робить бенчмарки гарними і аудитів — злими.
Рішення: якщо хтось тимчасово поставив sync=disabled, розцінюйте всі результати продуктивності як «зашумлені».

Task 5: Verify ashift (because 4K disks don’t forgive 512-byte fantasies)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
120:        ashift: 12

Що це означає: ashift=12 означає 4K сектори. Неправильний ashift може назавжди погіршити продуктивність через read-modify-write.
Рішення: якщо ashift неправильний, плануйте міграцію. Ви не «налаштуєте» себе з цього питання.

Task 6: Check ARC size vs test size (are you benchmarking RAM?)

cr0x@server:~$ arcstat 1 1
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
11:40:22  128K   56K     43   10K   8%   40K  31%    6K   4%   64G   80G

Що це означає: ARC — 64G з таргетом 80G. Якщо файл fio менший за це, читання «покращуватимуться» з часом.
Рішення: для дискових тестів використовуйте файл, що в кілька разів перевищує ARC, або фокусуйтесь на sync-записах, де ARC не може повністю маскувати затримку.

Task 7: Watch ZFS I/O and latency at the pool level during fio

cr0x@server:~$ zpool iostat -v tank 1 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        42.8T  6.10T  8.20K  3.10K   410M   220M
  mirror-0                  21.4T  3.05T  4.10K  1.55K   205M   110M
    nvme0n1                      -      -  2.05K    780   102M    55M
    nvme1n1                      -      -  2.05K    770   103M    55M
  mirror-1                  21.4T  3.05T  4.10K  1.55K   205M   110M
    nvme2n1                      -      -  2.04K    780   102M    55M
    nvme3n1                      -      -  2.06K    770   103M    55M
--------------------------  -----  -----  -----  -----  -----  -----

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

Task 8: Confirm SLOG is actually being used for sync writes

cr0x@server:~$ zpool iostat -v tank 1 2 | sed -n '1,18p'
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        42.8T  6.10T  2.10K  6.40K   120M   210M
  mirror-0                  21.4T  3.05T  1.05K  3.10K    60M   105M
  mirror-1                  21.4T  3.05T  1.05K  3.30K    60M   105M
logs                             -      -     2  9.80K   512K   310M
  nvme4n1                         -      -     2  9.80K   512K   310M

Що це означає: Високі операції запису на log-пристрої під час sync-важкого fio вказують, що трафік ZIL йде на SLOG.
Рішення: якщо записи в логи не зростають під час sync-тестів, або навантаження не sync, або встановлено sync=disabled, або SLOG не налаштований.

Task 9: Check CPU bottlenecks and IRQ pressure during “fast” benchmarks

cr0x@server:~$ mpstat -P ALL 1 2
Linux 6.8.0 (server)  12/21/2025  _x86_64_  (32 CPU)

12:01:10 PM  CPU   %usr  %nice   %sys %iowait  %irq  %soft  %idle
12:01:11 PM  all   18.2   0.0   22.9    0.8    0.0   6.1   52.0
12:01:11 PM   7    4.0   0.0   78.0    0.0    0.0   0.0   18.0

Що це означає: Один CPU з високим %sys може вказувати на вузьке місце одного ядра/IRQ (NVMe, virtio, мережа або блокування).
Рішення: якщо ви зажаті по CPU, припиніть «налаштовувати» диски. Виправте чергування, IRQ affinity або збільшіть паралелізм.

Task 10: Verify guest flush behavior (why “sync” keeps showing up)

cr0x@server:~$ qemu-img info /tank/vmimages/vm-101.qcow2
image: /tank/vmimages/vm-101.qcow2
file format: qcow2
virtual size: 200 GiB (214748364800 bytes)
disk size: 36.1 GiB
cluster_size: 65536

Що це означає: qcow2 має власну метадані й поведінку алокації. Воно може підсилювати записи і додавати додаткові flush-и в залежності від налаштувань.
Рішення: якщо затримки погані під sync-навантаженнями, розгляньте raw/zvol або тонко налаштуйте qcow2 cache/IO mode (і протестуйте знову).

Task 11: Check ZFS compression ratio and decide whether CPU is helping or hurting

cr0x@server:~$ zfs get -o name,property,value,source compressratio,compression tank/vmdata
NAME        PROPERTY       VALUE  SOURCE
tank/vmdata compression    lz4    local
tank/vmdata compressratio  1.62x  -

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

Task 12: Verify zvol block alignment expectations for VM I/O

cr0x@server:~$ lsblk -o NAME,PHY-SEC,LOG-SEC,MIN-IO,OPT-IO,ROTA /dev/zvol/tank/vm-101-disk0
NAME                     PHY-SEC LOG-SEC MIN-IO OPT-IO ROTA
zd0                        4096    4096   4096      0    0

Що це означає: 4K логічні/фізичні сектори відповідають сучасним очікуванням. Невирівнювання викликає RMW і піки затримки.
Рішення: якщо ви бачите 512 логічних секторів поверх 4K пристроїв, виправляйте це на етапі проєктування (ashift/volblocksize). Інакше ви будете «налагоджувати» вічно.

Task 13: Measure TXG sync pressure signals

cr0x@server:~$ cat /proc/spl/kstat/zfs/txgs
1 0x01 0x00000000 136 13440 105155148830 0

Що це означає: Цей файл може змінюватися залежно від впровадження, але якщо часи TXG sync або бэклог зростають під навантаженням, ви побачите хвильові затримки.
Рішення: якщо хвости затримок корелюють з поведінкою TXG sync, досліджуйте ліміти «брудних» даних, write latency vdev і ефективність SLOG замість біганини за прапорцями fio.

Task 14: Check device error counters and latency outliers before blaming ZFS

cr0x@server:~$ smartctl -a /dev/nvme0n1 | sed -n '1,25p'
smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.8.0] (local build)
=== START OF INFORMATION SECTION ===
Model Number:                       ACME NVMe 3.2TB
Firmware Version:                   1.04
Percentage Used:                    2%
Data Units Read:                    19,442,112
Data Units Written:                 13,188,440
Media and Data Integrity Errors:    0
Error Information Log Entries:      0

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

7) Fast diagnosis playbook: find the bottleneck in minutes

Це «перестаньте сперечатись, почніть ізоляцію» playbook. Використовуйте його, коли затримки високі або результати fio не співпадають із продакшном.
Мета — визначити, чи ви прив’язані гостем, гіпервізором, ZFS, vdev layout або одним хворим пристроєм.

First: decide what you are actually testing

  • fio in guest, buffered I/O → в основному поведінка кешу гостя та пам’яті.
  • fio in guest, direct I/O → ближче до поведінки віртуального диска (все ще через черги гіпервізора).
  • fio on host against a zvol → тестує шлях блочного об’єму ZFS, обходячи гістьову FS.
  • fio on host against a file in dataset → тестує шлях dataset ZFS і поведінку recordsize.

Якщо ціль тесту не відповідає шляху зберігання ВМ, зупиніться. Виправте тест.

Second: ask “is it sync?”

  • Запустіть sync-важкий fio-профіль (--fsync=1 або еквівалент) і дивіться zpool iostat -v на предмет активності логів.
  • Перевірте zfs get sync на рівні dataset/zvol.

Якщо p99 write latency вибухає лише при sync, ваша проблема в поведінці ZIL/SLOG, безпечності кешу запису пристрою або write latency на vdev.

Third: determine whether ARC is masking reads

  • Порівняйте ARC-busting read-тест з маленьким read-тестом.
  • Стежте за показниками arcstat miss rates під час прогону.

Якщо «читання з диска» швидкі, але пропуски (misses) низькі, ви не читаєте диски. Ви читаєте RAM і називаєте це сховищем.

Fourth: locate the choke point with live stats

  • zpool iostat -v 1 показує розподіл по vdev і чи використовуються логи.
  • mpstat 1 показує насичення CPU і навантаження на одне ядро.
  • iostat -x 1 показує використання пристроїв і затримки на рівні блоку.

Якщо один пристрій зашпигований або має високе await, ізолюйте його. Якщо CPU завантажений, припиніть шукати швидші SSD.

Fifth: check pool health and allocation reality

  • Пул майже повний? Очікуйте гіршої поведінки.
  • Нещодавній resilver? Scrub працює? Очікуйте перешкод.
  • Помилки? Припиніть роботу з продуктивністю і виправляйте цілісність спочатку.

8) Common mistakes: symptoms → root cause → fix

Mistake 1: “fio shows 1M IOPS but VMs are slow”

Симптоми: Масивні read IOPS у fio, але реальні додатки мають високу затримку і зависання.

Корінь проблеми: ARC/page cache в бенчмарку. Тест-файл поміщається в RAM; fio читає кеш, а не сховище.

Виправлення: Використовуйте --direct=1, зробіть робочий набір більшим за ARC і слідкуйте за arcstat miss% під час прогону.

Mistake 2: “SLOG did nothing”

Симптоми: Додавання SLOG не показало покращення; затримка sync-записів не змінилася.

Корінь проблеми: Навантаження не було sync (немає fsync/flush), або sync=disabled, або лог-пристрій неактивний.

Виправлення: Запустіть fsync-важкий fio, перевірте zpool status на наявність logs і підтвердіть операції запису логів в zpool iostat -v.

Mistake 3: “We increased iodepth and got better numbers, so we’re done”

Симптоми: IOPS бенчмарку покращилися з iodepth=256; у продакшені все одно проблеми.

Корінь проблеми: Штучне чергування ховає затримку. Ви вимірюєте throughput насичення, а не service time.

Виправлення: Використовуйте iodepth, які відповідають поведінці ВМ (часто 1–16 на job) і відстежуйте p99/p99.9 затримки.

Mistake 4: “Random writes are terrible; ZFS is slow”

Симптоми: Тести малих випадкових записів погані, особливо на RAIDZ.

Корінь проблеми: Накладні витрати RAIDZ при паритеті плюс витрати COW при малих випадкових записах. Це фізика, яку варто очікувати.

Виправлення: Для VM-орієнтованих навантажень з випадковими I/O використовуйте mirrors (або спеціальні vdev-дизайни) і масштабування кількості vdev для IOPS, а не для сирої місткості.

Mistake 5: “Latency spikes every so often like a heartbeat”

Симптоми: p99 затримка періодично стрибає під час стабільного навантаження.

Корінь проблеми: Поведінка TXG sync, throttling «брудних» даних або повільний пристрій, що створює періодичні простої.

Виправлення: Зіставте спайки з ZFS-статистикою і await дисків; перевірте firmware пристрою і подумайте про зниження write latency (кращі vdev, кращий SLOG).

Mistake 6: “We tuned recordsize for VM disks”

Симптоми: Зміни recordsize не впливають на продуктивність zvol для VM.

Корінь проблеми: recordsize не застосовується до zvol; застосовується volblocksize.

Виправлення: Створюйте zvol з відповідним volblocksize одразу; мігруйте за потреби.

Mistake 7: “Compression made it faster in fio, so it must be better”

Симптоми: IOPS стрибнули з compression; CPU піднявся; під реальним навантаженням затримки погіршилися.

Корінь проблеми: Вузьке місце по CPU або дані не стискуються. Compression може допомогти, але не безкоштовно.

Виправлення: Виміряйте запас CPU при реалістичній конкуренції; перевірте compressratio; зберігайте compression, якщо він реально зменшує записи без навантаження CPU.

Жарт №2: Змінити sync=disabled, щоб «поліпшити продуктивність», — це як зняти пожежну сигналізацію, бо вона вас будить.

9) Three corporate mini-stories from the trenches

Story A: An incident caused by a wrong assumption (cache ≠ disk)

Середня SaaS-компанія розгорнула новий кластер ВМ для внутрішнього CI і кількох клієнтських баз даних.
Сховище було ZFS на пристойних NVMe mirror-ах. Доказ готовності — fio-тест, що показував абсурдні random read IOPS.
Усі заспокоїлися. Procurement отримав золоту зірку.

Через два тижні канал інцидентів загорівся: піки затримки баз даних, CI-ранери таймаутяться, випадкові «hung task» попередження в гостьових ядрах.
Відповідальний на чергуванні запустив той самий fio і знову отримав великі числа. Це створило особливий вид мук: коли метрики кажуть «швидко», а люди — «повільно», ви втрачаєте години на суперечки про реальність.

Неправильне припущення було простим: «fio read IOPS = швидкість диска». Тест-файл був малим.
ARC був величезним. У стійкому навантаженні ВМ гарячий робочий набір нестабільний і sync-записи штовхають поведінку TXG у видимі хвилі затримок.
fio бенчмаркував пам’ять.

Виправлення було не екзотичним. Вони перебудували набір fio: time-based тести, файли значно більше за ARC і змішане навантаження з fsync.
Числа стали «гіршими», що виявилося найкращим — тепер вони відповідали продакшну. Потім знайшли один NVMe з непостійною write latency.
Його заміна стабілізувала p99.9 і «магічно покращила додаток», що є єдиним бенчмарком, який дійсно має значення.

Story B: An optimization that backfired (the sync shortcut)

Платформа, пов’язана з фінансами, мала ферму ВМ з message bus і кількома PostgreSQL кластерами.
Під час репетиції пік-сезону вони помітили збільшення latency commit-ів. Хтось запропонував «тимчасову» зміну ZFS:
встановити sync=disabled на dataset з VM-дисками, щоб прискорити коміти.

Це спрацювало відразу. Графіки latency впали. Репетиція пройшла. Зміна залишилась.
Команда не була безпечністю; вони були зайняті, і у платформи не було культури огляду зміни конфігурацій.
Через місяці подія з відключення живлення в одній стійці. Хости перезавантажилися чисто. ВМ повернулися. Декілька сервісів — ні.

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

Повернення назад — це не лише outage. Це технічний борг:
їм довелося перевірити кожен dataset, перезаснувати продуктивність з увімкненим sync, валідувати SLOG-апарат і переучити команди ставитися до налаштувань стійкості як до засобів безпеки.
Остаточне рішення для продуктивності включало кращі лог-пристрої і більше mirror-ів — не обман стека зберігання.

Story C: A boring but correct practice that saved the day (repeatable baselines)

Інша організація — з надзвичайно зрілим процесом змін — зберігала невеликий набір fio-профілів у версії разом з інфраструктурним кодом.
Ті самі версії fio. Ті самі job-файли. Той самий runtime. Цілі dataset-и. Кожна зміна, що стосується зберігання, вимагала прогона і прикріпленого звіту.
Нікому це не подобалося. Це не було гламурно.

Одного кварталу вони оновили firmware HBA під час вікна обслуговування. Більше нічого не змінилося.
Наступного дня кілька ВМ почали повідомляти про інколи виникаючі затримки. Не достатньо для повного інциденту, але достатньо, щоб викликати занепокоєння.
Команда запустила стандартний набір fio і порівняла з минуломісячним базлайном. p99 write latency значно погіршився у sync-важких профілях.

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

Рятівний крок тут був нудним: контрольовані, відтворювані тести з перцентилями затримки і sync-семантикою.
Це дозволило їм розглядати продуктивність як проблему регресії, а не філософський диспут.

10) Checklists / step-by-step plan

Step-by-step: build a VM-reality fio suite for ZFS

  1. Описуйте шлях зберігання ВМ. Це zvol, raw файли, qcow2 чи щось інше?
  2. Зніміть властивості ZFS для релевантних dataset/zvol: compression, sync, recordsize/volblocksize.
  3. Виберіть три основні профілі:
    • 4K/8K змішані випадкові з fsync (фокус на затримці)
    • 16K випадковий read storm (поведінка бут/логіну)
    • 1M послідовний запис (пропускна здатність бекапу/відновлення)
  4. Вирішіть структуру job-ів: віддавайте перевагу numjobs для конкуруючості і тримайте iodepth помірним.
  5. Використовуйте time-based прогони (3–10 хвилин) з ramp time (30–60 секунд).
  6. Міряйте перцентилі (95/99/99.9) і вважайте p99.9 «проксі болю користувача».
  7. Розмір тестових файлів має перевищувати ARC, якщо ви хочете виміряти читання диска.
  8. Запускайте тести в трьох режимах:
    • Host → zvol
    • Host → файл в dataset
    • Guest → файловa система (direct I/O і fsync)
  9. Записуйте оточення: версії kernel/ZFS, CPU governor, топологію пулу і чи були запущені scrub/resilver.
  10. Повторіть мінімум двічі і порівняйте. Якщо результати сильно різняться, сама варіабельність — це знахідка.

Operational checklist: before trusting any fio number

  • Використовується --direct=1, коли потрібно?
  • Чи профіль включає fsync/flush для моделювання баз даних або стійкості ВМ?
  • Чи тестовий файл більший за ARC (для тестів читання)?
  • Чи відстежуєте ви p99/p99.9 затримки?
  • Чи дивитесь на zpool iostat -v і CPU під час тесту?
  • Чи пул здоровий (немає помилок, жодних деградованих vdev)?
  • Чи пул не майже заповнений?
  • Чи тест запускали на реальному шляху зберігання ВМ?

Change checklist: when tuning ZFS for VM workloads

  • Спочатку не чіпайте стійкість. Не міняйте sync, якщо не бажаєте ретроспективи інцидентів.
  • Віддавайте перевагу рішеням по розкладці, а не мікро-налаштуванням. Mirrors vs RAIDZ — це архітектурний вибір, а не sysctl.
  • Валідуйте SLOG з sync-важким fio і підтвердіть його використання.
  • Вирівняйте volblocksize під реальність гостя при створенні zvol.
  • Міряйте ризик регресії базовим набором після кожної значної зміни.

11) FAQ

Q1: Should I run fio inside the VM or on the host?

Обидва варіанти — за різних причин. Всередині гостя ви бачите те, що відчуває гість (включно з чергами гіпервізора і поведінкою файлової системи гостя).
На хості ви ізолюєте поведінку ZFS. Якщо вони не збігаються — це підказка: вузьке місце віртуалізації або кешування.

Q2: What fio flags matter most for VM realism?

--direct=1, реалістичні --bs, помірний --iodepth, кілька --numjobs, time-based прогони,
і --fsync=1 (або еквівалент) для стійкістних навантажень. Також: --percentile_list, щоб перестати пильно дивитися на середні.

Q3: Why does my random read test get faster over time?

ARC (або сторінковий кеш гостя) прогрівається. Ви переходите з диска в пам’ять. Якщо ви тестуєте диски, збільшіть робочий набір і стежте за ARC miss rates.

Q4: How do I know if my SLOG is helping?

Запустіть sync-важкий fio-профіль і дивіться операції запису логу в zpool iostat -v. Також порівнюйте p99 write latency з SLOG і без нього.
Якщо ваше навантаження не sync — SLOG не має допомагати, і це не є проблемою.

Q5: Is RAIDZ “bad” for VM storage?

RAIDZ не поганий; він просто не є IOPS-монстром для малих випадкових записів. Для VM-орієнтованих OLTP-подібних навантажень mirror-и зазвичай безпечніші.
Якщо вам потрібен RAIDZ за економією місткості, плануйте продуктивність і тестуйте з sync + random writes.

Q6: Should I change recordsize for VM performance?

Тільки для dataset-ів, що використовуються як файли (наприклад qcow2/raw файли). Для zvol-backed VM-дисків recordsize не застосовується; діє volblocksize.

Q7: What’s a good target for p99 latency?

Залежить від навантаження, але за правилом: якщо p99 sync write latency регулярно заходить у десятки мілісекунд, бази даних скаржитимуться.
Використайте SLO вашого додатку, щоб встановити поріг; потім налаштуйте дизайн (vdev, SLOG), щоб його виконувати.

Q8: How do I stop fio from destroying my pool performance for everyone else?

Проводьте тести в вікнах обслуговування, обмежуйте кількість jobs/iodepth і моніторьте. fio — генератор навантаження, не ввічливий гість.
Якщо тест у продакшені необхідний, використовуйте коротші прогони і віддавайте перевагу профілям, чутливим до затримки, а не насиченню пропускної здатності.

Q9: Does enabling compression always help VM workloads?

Часто допомагає, бо дані ВМ (OS файли, логи) стискуються і зменшують фізичні записи. Але якщо CPU стає вузьким місцем або дані несжимаємні,
compression може погіршити хвости затримки. Перевірте compressratio і CPU під реалістичним навантаженням.

Q10: Why do my fio results differ between zvols and dataset files?

Різні шляхи коду і властивості. Dataset-и використовують recordsize і файлові метадані; zvol-и — volblocksize і презентують блочний пристрій.
Платформи VM також поводяться по-різному залежно від того, чи ви використовуєте raw файли, qcow2 або zvol-и.

12) Practical next steps

Якщо ви хочете, щоб ваші fio-результати передбачали реальність ВМ, зробіть наступне в цьому порядку:

  1. Оберіть один VM-диск (zvol або файл) і побудуйте три fio-профілі: boot storm, OLTP mixed з fsync, backup stream.
  2. Запустіть їх time-based з перцентилями, і записуйте p95/p99/p99.9, а не тільки IOPS.
  3. Під час кожного прогона захопіть zpool iostat -v, arcstat і статистику CPU.
  4. Валідуйте sync path: підтвердіть активність SLOG (якщо є) і що жоден dataset не має sync=disabled, що ховає проблеми.
  5. Перетворіть результати в базовий набір і проганяйте після кожної значної зміни: firmware, kernel, версія ZFS, топологія і формат зберігання ВМ.

Мета — не отримати красиві числа. Мета — перестати дивуватися продакшну.
Коли ваш fio-набір почне робити боляче те саме, про що скаржаться користувачі, ви нарешті тестуєте систему, яку реально експлуатуєте.

← Попередня
ZFS xattr: вибір сумісності, що змінює продуктивність
Наступна →
Заголовки про 0-day: чому одна вразливість викликає миттєву паніку

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