Нічого так не псує ранок, як Linux VM, яка «працює нормально», поки раптом не стане гальмувати: запрошення в shell зависають, коміти MySQL призупиняються, journald затримується, а графік латентності вашого додатку набуває гострих зубів. Ви дивитеся на CPU і RAM — усе добре. Мережа — теж. Потім відкриваєте iostat і бачите це: випадкові латентності запису підстрибають до секунд. Гість наче працює на обертовому диску, що живиться настроєм.
Це зазвичай виправно. Часто проблема не в тому, що «сховище повільне», а в тому, що ви обрали неправильний віртуальний дисковий контролер, неправильний режим кешу або випадково включили режим QEMU, який перетворює хвилю дрібних записів на затор. Хороша новина: Proxmox дає вам важелі керування. Погана новина: налаштування за замовчуванням не завжди відповідають вашому навантаженню.
Швидкий план діагностики
Якщо ваша VM «підвисає», потрібно вирішити, звідки приходить латентність: гість, QEMU, ядро хоста, бекенд сховища або фізичний пристрій/кластер. Не гадати. Тріаж.
Спочатку: доведіть, що це латентність сховища (не CPU steal або нестача пам’яті)
-
У гості: перевірте, чи затримки корелюють із очікуванням диска.
cr0x@server:~$ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 812340 22012 903112 0 0 120 980 310 510 4 2 92 2 0 0 2 0 810112 22020 904000 0 0 0 4096 280 420 2 1 58 39 0 0 1 0 809980 22020 904120 0 0 0 2048 270 410 2 1 70 27 0Що це означає: високий
waвказує на час, проведений у очікуванні IO. Якщоst(steal) високий, ви перенавантажили CPU; виправте це перед тим, як занурюватися в налаштування диска.Рішення: якщо
waпідскакує під час підвисань іstнизький, продовжуйте розслідування сховища.
Далі: знайдіть повільний шар (черга гостя чи пристрій хоста)
-
На хості Proxmox: спостерігайте латентність і насичення по пристроях.
cr0x@server:~$ iostat -x 1 5 Linux 6.8.12-pve (pve01) 12/26/2025 _x86_64_ (32 CPU) avg-cpu: %user %nice %system %iowait %steal %idle 10.12 0.00 3.01 8.33 0.00 78.54 Device r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util nvme0n1 22.0 480.0 1.2 46.8 192.0 3.8 7.9 1.2 8.2 0.4 20.5 sda 0.0 95.0 0.0 6.0 128.0 18.2 191.3 0.0 191.3 1.1 99.8Що це означає:
await— це латентність запиту;%utilблизько 100% з високимawaitкричить «пристрій завантажено». Тутsda— винуватець.Рішення: якщо латентність погана на пристрої хоста, підгонки контролера/кешу у VM вас не врятують. Виправте бекенд (міграція, швидший диск, кращий розподіл пулу) перед тонким налаштуванням VM.
-
На хості: перевірте, чи потоки QEMU блокуються в IO.
cr0x@server:~$ pgrep -a qemu-system-x86_64 | head -n 1 24891 /usr/bin/kvm -id 103 -name vm103 -m 8192 ... -drive file=/dev/zvol/rpool/vm-103-disk-0,if=none,id=drive-scsi0,format=raw,cache=none,aio=native ...Що це означає: рядок запуску QEMU показує режим кешу, режим aio і чи використовується virtio-scsi/virtio-blk.
Рішення: якщо бачите
cache=writebackна хості без UPS і без стабільного кешу записів, ви ризикуєте втратою даних; якщо бачитеaio=threadsіз високою завантаженістю CPU і латентністю, розгляньтеaio=native, якщо бекенд це підтримує.
Третє: підтвердіть, що гість бачить адекватний віртуальний пристрій і модель черг
-
У гості: ідентифікуйте тип диска і чи це virtio.
cr0x@server:~$ lsblk -o NAME,MODEL,TYPE,SIZE,ROTA,DISC-MAX,DISC-GRAN,MOUNTPOINTS NAME MODEL TYPE SIZE ROTA DISC-MAX DISC-GRAN MOUNTPOINTS vda QEMU HARDDISK disk 200G 0 2G 4K ├─vda1 part 1G 0 └─vda2 part 199G 0 /Що це означає:
vdaзазвичай означає virtio-blk.sdaвсередині VM часто означає емульований SATA/SCSI і зазвичай повільніший/має вищі накладні витрати.Рішення: якщо ви на емульованих контролерах, заплануйте вікно технічного обслуговування для міграції на virtio.
Цей план навмисно короткий. Решта статті пояснює «чому» і дає важелі, які надійно знижують сплески латентності, не створюючи нових проблем.
Що насправді означає «підвисання диска» у VM
Підвисання — це не про середню пропускну здатність. Моніторинг покаже 50 MB/s і всі похвалять сховище. Тим часом ваша VM зависає на 800 мс кожні кілька секунд, бо дрібні синхронні записи накопичуються за якимось дорогим бар’єром.
У віртуалізованому сховищі є кілька черг і точок очищення:
- Кеш сторінок гостя: буферизовані записи накопичуються, доки ядро не вирішить їх скинути.
- Блоковий шар гостя: об’єднує та планує IO (менш драматично на новіших ядрах, але все ще реально).
- Черга віртуального контролера: virtio-blk і virtio-scsi мають різні моделі черг і поведінку переривань.
- Блоковий шар QEMU: режим кешу контролює, чи використовує QEMU кеш сторінок хоста і куди потрапляють flush-операції.
- Файлова система / менеджер томів хоста: ZFS, LVM-thin, ext4 на SSD, Ceph RBD — кожен має різну характеристику латентності.
- Пристрій/кластер: реальний диск, RAID-контролер, NVMe, SAN чи Ceph OSDs.
Підвисання зазвичай буває одного з таких сценаріїв:
- Шторм flush-операцій: пакет записів наштовхується на бар’єр синхронізації (
fsync(), commit журналу, commit бази даних) і латентність підіймається. - Задушення однієї черги: модель диска/контролера використовує одну чергу, тож паралельні навантаження серіалізуються.
- Тиск пам’яті на хості: кеш сторінок хоста трясеться, і IO стає «неочікувано синхронним».
- Подвійне записування на бекенді: тонке надавання, copy-on-write або реплікація роблять дрібні записи дорогими.
Фраза, яку варто тримати в голові: гість вважає, що розмовляє з диском; насправді ви запускаєте розподілену систему з черг.
Парафразована ідея від Werner Vogels (надійність/операції): «Все зрештою ламається; проектуйте так, щоб збої були очікуваними і керованими.»
Жарт №1: Продуктивність сховища — як плітки: усі швидко, поки не попросиш синхронізацію.
Факти та історія, які пояснюють дивні значення за замовчуванням
Деякі «таємничі» опції Proxmox/QEMU стають значно зрозумілішими, коли знаєш, звідки вони походять.
- Virtio народився, щоб уникнути емуляції апаратного забезпечення. Ранній віртуалізація часто емулювала IDE/SATA; virtio з’явився пізніше як паравіртуалізований інтерфейс для зменшення накладних витрат.
- virtio-blk передував virtio-scsi. virtio-blk був простішим і широко підтримуваним; virtio-scsi з’явився, щоб запропонувати можливості, ближчі до справжнього SCSI (кілька LUN, hotplug, кращі шаблони масштабування).
- Бар’єри запису та flush-операції стали суворішими з часом. Файлові системи та бази даних стали менш довірливими до кешів після болючих історій корупції в 2000-х; семантика flush має більше значення нині.
- Кеш сторінок хоста колись був простим виграшем. На обертових дисках і системах з малою ОЗП використання кешу хоста (writeback) могло згладжувати IO; на сучасних SSD/NVMe з багатьма VM це може створювати конкуренцію кешів між орендарями.
- AIO в Linux має дві особистості. «native» AIO (ядровий AIO) поводиться інакше, ніж емульований на потоках; що краще — залежить від бекенда і вирівнювання.
- NCQ і multi-queue не завжди були звичними. Багато фольклору налаштувань походить з ери одночергових SATA і обмеженої паралельності.
- ZFS став популярним у віртуалізації завдяки снапшотам/клонам. Компроміс: copy-on-write і контрольні суми додають накладні витрати; це того варте, але потрібно поважати поведінку синхронних записів.
- Ceph популяризував «сховище як кластерну функцію». Чудово для стійкості і масштабування; латентність — це продукт кворуму, мережі і навантаження OSD, а не лише диска.
Це не дрібниці. Вони пояснюють, чому один регулятор вирішує проблему в вашому середовищі, а в колеги робить гірше.
Вибір контролера: virtio-scsi vs virtio-blk (і коли SATA все ще корисний)
Якщо ви працюєте з Proxmox і дисковий пристрій Linux VM всередині показується як sda з контролером «Intel AHCI», ви платите за апаратний костюм. Емульовані контролери існують задля сумісності. Продуктивність — не їхня задача.
Що використовувати за замовчуванням
- Використовуйте virtio-scsi-single для більшості сучасних Linux VM. Це надійний дефолт: хороша підтримка функцій, масштабування і передбачувана поведінка з iothreads.
- Використовуйте virtio-blk для простих налаштувань або коли потрібно менше складових. virtio-blk може бути дуже швидким. Він також простіший, що іноді важливо для налагодження або сумісності драйверів у рідкісних гостях.
- Використовуйте SATA лише для встановлення або дивних сценаріїв з інсталяційними носіями. Після встановлення перемкніться на virtio.
virtio-scsi: що ви отримуєте
virtio-scsi моделює SCSI HBA з virtio-транспортом. У Proxmox ви побачите опції типу:
- virtio-scsi-pci: може приєднувати кілька дисків; може ділити чергу, залежно від конфігурації.
- virtio-scsi-single: створює окремий контролер на диск (або фактично ізолює черги), часто зменшуючи contention замків і покращуючи справедливість між дисками.
На практиці: virtio-scsi-single часто найпростіший спосіб отримати передбачувану латентність для кількох навантажених дисків. Якщо у вас є диск бази даних і диск логів, ви не хочете, щоб вони «грабували» один одного у спільній черзі.
virtio-blk: що ви отримуєте
virtio-blk — це паравіртуалізований блочний пристрій. Він лаконічний. Може давати високу пропускну здатність і низькі накладні витрати. Але він може бути менш гнучким, коли потрібні SCSI-подібні можливості, і історично деякі поведінки (наприклад, discard/unmap) були простішими з virtio-scsi.
Коли вибір контролера дійсно фіксує підвисання
Зміни контролера зменшують підвисання, коли вузьке місце — у чергованні віртуального пристрою або обробці переривань. Типові ознаки:
- Латентність
awaitна хості низька (бекенд в порядку), але у гостя високий IO wait і періодичні паузи. - Одна VM отримує хвилі хорошої пропускної здатності, а потім повну тишу, хоча сховище хоста не насичене.
- Кілька навантажених дисків в одній VM заважають один одному (логи блокують коміти БД, тимчасові файли гальмують записи додатку).
Практична рекомендація
Якщо у вас є підвисання і ви не впевнені: перемкніть контролер диска VM на virtio-scsi-single, увімкніть iothread для цього диска і використовуйте cache=none, якщо немає дуже конкретної причини інакше.
Це не «єдино правильна настройка». Це та, яка найчастіше виправляє p99 сплески латентності, не перетворюючи цілісність даних на питання стилю життя.
Режими кешу: параметри, що формують p99 латентність
Режим кешу — це місце, де продуктивність і безпека нерішуче тиснуть руки одне одному. У термінах Proxmox/QEMU ви вирішуєте, чи стоїть кеш сторінок хоста між гостьовою системою і сховищем, і як обробляються flush-операції.
Поширені режими (що вони насправді означають)
- cache=none: QEMU використовує direct IO (де можливо). Кеш сторінок хоста переважно оминається. Кеш гостя все ще існує. Flush-операції відображаються більш прямо на бекенд. Часто найкраще для передбачуваної латентності і уникнення подвійного кешування.
- cache=writeback: записи QEMU потрапляють у кеш сторінок хоста і підтверджуються швидко; пізніше вони скидаються на сховище. Швидко — поки не перестане бути. Небезпечно без захисту від втрати живлення або стабільних кешів, бо гість вважає дані «безпечними» раніше, ніж вони справді записані.
- cache=writethrough: записи проходять через кеш хоста, але перед завершенням форсуються на диск. Безпечніше за writeback, зазвичай повільніше, іноді створює підвисання при великій кількості синхронних операцій.
- cache=directsync: намагається робити кожний запис синхронним. Зазвичай чудовий спосіб навчитися терпіти.
- cache=unsafe: не робіть цього. Існує для бенчмарків і жалю.
Чому writeback може викликати підвисання (навіть якщо він «швидкий в бенчмарках»)
Writeback часто перетворює вашу проблему на проблему таймінгу. Кеш сторінок хоста поглинає записи швидко — тож гість генерує більше записів. Потім хост вирішує, що час на flush. Flush стається пакетно, і ці пакети можуть блокувати нові записи або відбирати ресурси у читань. Ваша VM сприймає це як періодичні зависання.
На легкозавантаженому хості з однією VM writeback може виглядати чудово. У продакшні з багатьма VM та змішаними навантаженнями це еквівалент того, як усі водії одночасно намагаються влитися в одну смугу.
Чому cache=none — нудний у найкращому сенсі
З cache=none ви зменшуєте подвійне кешування і тримаєте уявлення гостя про стійкість ближчим до реальності. Це часто стабілізує латентність. Гість все ще активно кешує, тож це не «без кешу». Це «один кеш, в одному місці, з меншими сюрпризами».
Flush-операції, fsync і чому бази даних показують погані налаштування
Бази даних викликають fsync(), бо їм не до вподоби втрата даних. Журнальні файлові системи також виконують бар’єри/flush для порядку операцій. Якщо ваш режим кешу й бекенд перетворюють flush-операції на дорогі глобальні операції, ви отримуєте класичний патерн підвисання: VM працює нормально до точки коміту, потім усі зупиняються.
Нотатка про безпеку (бо ви любите свої вихідні)
cache=writeback може бути прийнятним, якщо у вас є:
- UPS, що реально працює і інтегрований (хост правильно вимикається),
- сховище з захистом від втрати живлення (PLP) або контролер з батарейним кешем,
- і ви розумієте режими відмов.
Інакше «швидке» налаштування швидке аж до моменту розтину постмортему.
AIO та iothreads: як перетворити паралельний IO на реальну паралельність
Навіть із правильним контролером і режимом кешу ви все ще можете отримувати підвисання, бо обробка IO в QEMU серіалізована або конкурує. Дві опції значно впливають: AIO-режим і iothreads.
aio=native vs aio=threads
QEMU може подавати IO, використовуючи нативний Linux AIO або пул потоків, який виконує блокуючі виклики IO. Що кращe — залежить від бекенду і поведінки ядра, але правило таке:
- aio=native: часто менше накладних витрат і краще для шляхів direct IO; може зменшити джиттер, коли підтримується бекендом чисто.
- aio=threads: більш сумісний; іноді дає більший CPU-витрат і може вводити планувальний джиттер при навантаженні.
Якщо ви на ZFS zvols або сирих пристроях, native AIO зазвичай гарна ідея. Якщо ви на деяких файлових образах або незвичних конфігураціях, threads можуть поводитися краще. Вимірюйте, не вгадуйте.
iothreads: стабілізатор латентності, який справді варто використовувати
Без iothreads QEMU може обробляти IO в головному event-loop потоці (і деяких допоміжних), що означає конкуренцію між дисковими операціями, емульованою роботою і обробкою переривань. З iothreads кожен диск може мати свій IO-потік, зменшуючи contention і вирівнюючи латентність.
У Proxmox ви можете увімкнути iothread для кожного диска. Це особливо корисно коли:
- у вас є кілька навантажених дисків в одній VM,
- маєте один навантажений диск з багатьма дрібними синхронними записами,
- намагаєтеся утримати p99 під контролем, а не виграти в тесті послідовної пропускної здатності.
Скільки iothreads?
Не створюйте 20 iothreads тому, що можете. Кожний потік — це витрати на планування. Створюйте iothreads для дисків, що мають значення: том бази даних, журнал-важлива ФС, диск логів. Для VM з одним диском зазвичай достатньо одного iothread.
Жарт №2: Додавання iothreads схоже на наймання більше барист — чудово, доки кав’ярня не перетвориться на нараду про те, як варити каву.
Наслідки для бекендів сховища (ZFS, Ceph, LVM-thin, файли)
Ви можете підібрати ідеальний контролер і кеш та все одно отримати підвисання, бо бекенд робить щось дороге. Proxmox абстрагує сховище, але фізика не вражена.
ZFS: синхронні записи, ZIL/SLOG і питання «чому мій NVMe все ще повільний?»
ZFS відмінний для цілісності та снапшотів. Він також чесний щодо синхронних записів. Якщо ваше гостьове навантаження робить синхронні записи (бази даних, журнальні ФС, додатки з fsync), ZFS має їх закомітити безпечно. Без виділеного швидкого SLOG-пристрою з захистом від втрати живлення синхронні навантаження можуть підвисати навіть на швидких пулах.
Ключові моменти:
- zvol vs dataset: ВМ на zvol зазвичай поводяться прогнозованіше, ніж файлові qcow2 на dataset для важкого IO, хоча обидва варіанти можуть працювати.
- поведінка sync: якщо гість виконує flush-операції, ZFS ставиться до них серйозно. Якщо ви «вирішили проблему» через
sync=disabled, ви міняєте надійність на швидкість. - recordsize/volblocksize: невідповідність може збільшити write amplification. Для zvol VM значення
volblocksizeважливе під час створення.
Ceph RBD: латентність — властивість кластера
Ceph стійкий і масштабований. Він також має більше місць, де може з’явитися латентність: мережа, навантаження OSD, backfill/recovery, PG peering і клієнтські черги.
Шаблони підвисання на Ceph часто походять від:
- recovery/backfill, що насичує диски або мережу,
- OSD з нерівною продуктивністю (один повільний диск тягне за собою реплікований запис),
- клієнтські опції, що роблять flush-операції дорогими,
- шумного сусіда на загальних вузлах OSD.
Вибір контролера/кешу все ще важливий, але він не перекриє кластер, який відновлюється в найневдалий момент. (Він завжди відновлюється у найгірший час.)
LVM-thin: тиск на метадані і поведінка discard
LVM-thin швидкий і простий, але тонке виділення вводить запис метаданих і може стати спазматичним при важких випадкових записах або коли thin pool майже повний. Discard/TRIM може допомогти звільнити місце, але також створює вибухи роботи залежно від конфігурації.
Файлові qcow2: снапшоти гарні, але дивіться за накладними витратами
qcow2 зручний: снапшоти, стиснення, шифрування. Він також додає метадані copy-on-write і ризик фрагментації. При випадкових записах qcow2 може помітно підвисати порівняно з raw на блочному пристрої.
Якщо вам потрібна продуктивність і стабільна латентність, віддавайте перевагу raw на блочному бекенді. Якщо потрібні функції — використовуйте qcow2, але погодьтеся на необхідність перебільшення ресурсів і періодичну дефрагментацію/конвертацію.
Практичні завдання: команди, виводи та рішення (12+)
Це реальні завдання, які ви можете виконати сьогодні. Кожне містить: команду, реалістичний вивід, що це означає, і рішення, яке ви приймаєте.
Завдання 1: Визначити контролер диска VM і режим кешу в конфігурації Proxmox
cr0x@server:~$ qm config 103
boot: order=scsi0
cores: 4
memory: 8192
name: api-prod-03
net0: virtio=DE:AD:BE:EF:10:03,bridge=vmbr0
ostype: l26
scsihw: virtio-scsi-single
scsi0: rpool:vm-103-disk-0,cache=none,discard=on,iothread=1
agent: 1
Що це означає: scsihw — модель контролера; рядок диска показує cache=none, discard увімкнено і iothread увімкнено.
Рішення: якщо бачите sata0 або scsihw: lsi з інтенсивним IO, заплануйте міграцію на virtio-scsi-single або virtio-blk. Якщо кеш — writeback, перевірте захист від втрати живлення і домени відмов.
Завдання 2: Підтвердити, що QEMU дійсно запущено з очікуваними параметрами (trust but verify)
cr0x@server:~$ ps -p $(pgrep -f "kvm -id 103") -o pid,cmd --cols 200
PID CMD
24891 /usr/bin/kvm -id 103 -name vm103 -m 8192 -smp 4 ... -device virtio-scsi-pci,id=scsihw0 ... -device scsi-hd,drive=drive-scsi0 ... -drive file=/dev/zvol/rpool/vm-103-disk-0,if=none,id=drive-scsi0,format=raw,cache=none,aio=native ...
Що це означає: підтверджує ефективний режим кешу і AIO; іноді GUI-настройки відрізняються від реальності через старі конфіги чи переопреділення.
Рішення: якщо очікували aio=native, а бачите threads, дослідіть сумісність бекенда і версію Proxmox.
Завдання 3: Перевірити латентність і насичення сховища хоста під навантаженням
cr0x@server:~$ iostat -x -d 1 3
Device r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
nvme0n1 40.0 620.0 2.8 62.5 205.0 5.2 8.4 0.3 19.0
Що це означає: пристрій не насичений (%util низький), латентність прийнятна.
Рішення: якщо латентність хоста низька, а гість підвисає, підозрюйте контролер/кеш/черги всередині віртуалізації.
Завдання 4: Визначити, який процес генерує IO на хості
cr0x@server:~$ pidstat -d 1 3
Linux 6.8.12-pve (pve01) 12/26/2025 _x86_64_ (32 CPU)
# Time UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
12:01:01 0 24891 120.00 54000.00 0.00 12 kvm
12:01:01 0 1820 0.00 220.00 0.00 1 zfs
Що це означає: процес QEMU вашої VM відповідає за більшість записів; ви дивитесь у правильному напрямку.
Рішення: якщо IO іде від pvestatd, бекапів або реплікаційних задач, сплануйте/обмежте їх перед тонкою настройкою дисків VM.
Завдання 5: Перевірити латентність диска і глибину черги у гості
cr0x@server:~$ iostat -x 1 3
Device r/s w/s rkB/s wkB/s aqu-sz await r_await w_await %util
vda 5.0 220.0 320.0 22000.0 8.4 38.0 2.0 38.8 98.0
Що це означає: гість бачить диск майже насиченим з високим часом очікування запису. Може бути реальна латентність бекенда або віртуальна черга/поведінка flush.
Рішення: зіставте з iostat хоста. Якщо латентність хоста низька, а гостя висока, підозрюйте чергування в контролері/емуляції або шторм flush-операцій.
Завдання 6: Перевірити, чи гість робить багато flush-операцій
cr0x@server:~$ grep -R . /sys/block/vda/queue/* 2>/dev/null | egrep "write_cache|nomerges|max_sectors_kb"
/sys/block/vda/queue/max_sectors_kb:1280
/sys/block/vda/queue/nomerges:0
/sys/block/vda/queue/write_cache:write through
Що це означає: write through вказує, що пристрій представляє write-through кеш; поведінка flush має значення.
Рішення: якщо навантаження fsync-важке і ви бачите шторм flush-операцій, віддайте перевагу cache=none і переконайтеся, що бекенд добре обробляє синхронні записи (ZFS SLOG, налаштування Ceph тощо).
Завдання 7: Виміряти поведінку commit журналу у гості (тиснення журналювання)
cr0x@server:~$ dmesg | tail -n 8
[ 9123.112233] EXT4-fs (vda2): re-mounted. Opts: (null)
[ 9450.774411] INFO: task jbd2/vda2-8:341 blocked for more than 120 seconds.
[ 9450.774419] Tainted: G W 6.5.0-28-generic #29-Ubuntu
[ 9450.774425] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Що це означає: потік журналу ext4 заблокований — класичний симптом затримок сховища, часто на шляху flush/commit.
Рішення: ставте це як серйозну ознаку. Виправляйте латентність IO перш за все; не «налаштовуйте ext4», щоб приховати проблему.
Завдання 8: Перевірити тиск пам’яті на хості (тріаж кешу хоста)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 125Gi 98Gi 1.2Gi 2.0Gi 26Gi 18Gi
Swap: 16Gi 12Gi 4.0Gi
Що це означає: використання swap високе; хост може відчувати тиск пам’яті, що робить IO повільнішим і більш джиттерним.
Рішення: зменшіть overcommit, додайте ОЗП або перестаньте використовувати кеш сторінок хоста для дисків VM. cache=none допомагає зменшити залежність від кешу хоста, але достатньо пам’яті все одно потрібно.
Завдання 9: Перевірити стан пулу ZFS і індикатори латентності (якщо використовуєте ZFS)
cr0x@server:~$ zpool status -v
pool: rpool
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
rpool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Що це означає: пул здоровий; немає очевидних несправних пристроїв, що викликають повторні спроби.
Рішення: якщо бачите деградовані vdev або зростання кількостей помилок, припиніть налаштування VM і спочатку замініть обладнання / проведіть resilver.
Завдання 10: Перевірити налаштування sync у ZFS і чи ви «читаєте» стійкість
cr0x@server:~$ zfs get -o name,property,value -s local,default sync rpool
NAME PROPERTY VALUE
rpool sync standard
Що це означає: синхронні записи обробляються за стандартом.
Рішення: якщо хтось встановив sync=disabled для «прискорення», розглядайте це як ризик. Якщо продуктивність неприйнятна зі sync=standard, додайте належний SLOG або переробіть архітектуру.
Завдання 11: Підтвердити, що discard/TRIM увімкнено наскрізь (тонкі пули й SSD)
cr0x@server:~$ qm config 103 | grep -E "discard|ssd|iothread"
scsi0: rpool:vm-103-disk-0,cache=none,discard=on,iothread=1
Що це означає: discard увімкнено на віртуальному шарі диска.
Рішення: якщо ви використовуєте тонке надавання (LVM-thin, приблизна thin поведінка ZFS, Ceph), розгляньте discard, щоб уникнути роздування використання простору — але тестуйте, бо discard може викликати сплески роботи бекенда.
Завдання 12: Перевірити, чи гість використовує драйвери virtio і multiqueue (на стороні гостя)
cr0x@server:~$ lsmod | egrep "virtio_blk|virtio_scsi|scsi_mod" | head
virtio_scsi 28672 2
scsi_mod 274432 3 virtio_scsi,sd_mod,sg
virtio_pci 32768 0
virtio_ring 40960 2 virtio_net,virtio_scsi
Що це означає: virtio-scsi завантажено; ви не на емульованих SATA-драйверах.
Рішення: якщо модулі virtio відсутні, можливо, ви використовуєте неправильний контролер або старий initramfs. Виправте доступність драйверів перед зміною інших параметрів.
Завдання 13: Виміряти поведінку при багатьох flush-операціях швидким чесним тестом fio (на хості або в гості)
cr0x@server:~$ fio --name=syncwrite --filename=/var/lib/testfile --size=1G --rw=randwrite --bs=4k --iodepth=1 --numjobs=1 --direct=1 --sync=1 --time_based --runtime=20
syncwrite: (groupid=0, jobs=1): err= 0: pid=2123: Thu Dec 26 12:10:01 2025
write: IOPS=3200, BW=12.5MiB/s (13.1MB/s)(250MiB/20001msec)
clat (usec): min=120, max=42000, avg=310.42, stdev=900.12
Що це означає: максимальна латентність завершення досягає 42ms у цьому прогоні; у випадках підвисання ви побачите сотні мс або секунди.
Рішення: якщо максимальна латентність величезна, зосередьтеся на синхронному шляху: режим кешу, обробка sync на бекенді (ZFS SLOG), здоров’я Ceph або насичення хоста.
Завдання 14: Перевірити налаштування черги блоку на хості (інколи тихий ліміт)
cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] none kyber bfq
Що це означає: хост використовує mq-deadline, розумний дефолт для багатьох SSD/NVMe навантажень.
Рішення: якщо ви на ротаційних дисках, bfq або deadline можуть змінити латентність. Для NVMe зміна планувальника зазвичай не перше виправлення, але може допомогти справедливості при змішаних навантаженнях.
Завдання 15: З’ясувати, чи бекапи/реплікація збігаються з IO VM
cr0x@server:~$ systemctl list-timers --all | egrep "pve|vzdump" || true
Thu 2025-12-26 12:30:00 UTC 20min left Thu 2025-12-26 12:00:01 UTC 9min ago vzdump.timer vzdump backup job
Що це означає: запланована задача бекапу і може виконуватись часто; бекапи можуть створювати читальні шторми і накладні витрати на снапшоти.
Рішення: якщо підвисання корелює з вікном бекапів, обмежте/переплануйте бекапи, використовуйте режими снапшотів, що підходять для бекенду, та ізолюйте IO бекапів де можливо.
Три корпоративні історії з полів IO
Міні-історія 1: Інцидент через хибне припущення
Команда успадкувала кластер Proxmox, який «переважно працював» для внутрішніх сервісів. Новий клієнтський API розгорнули у VM з невеликим екземпляром PostgreSQL. Навантаження не було масивним; воно було просто постійним. За добу on-call почали бачити періодичні сплески латентності і дивні таймаути. Графіки CPU виглядали спокійно. Мережа — нудно. VM була «здоровою».
Хтось припустив, що бекенд сховища повільний і зробив звичне: «увімкніть writeback cache, це згладить». І справді — тимчасово. Пропускна здатність покращилась. Підвисання стало рідшим, що зробило зміну «геніальною». А потім хост несподівано перезавантажився після короткого стрибка живлення, що було достатнім, щоб уникнути коректного вимкнення, але достатнім, щоб зіпсувати ваш вечір.
База даних повернулася з ознаками корупції: відсутні WAL-сегменти і несумісний стан. Postgres зробив свою справу і відмовився робити вигляд, що все гаразд. Відновлення спрацювало, але це не було швидко і не було приємно. Неприємна частина не була простою відсутністю сервісу; це усвідомлення, що «фікс продуктивності» змінив контракт надійності без явної згоди.
Те, що реально вирішило підвисання пізніше, було не writeback. Вони перенесли диск VM з емульованого контролера на virtio-scsi-single, увімкнули iothread, використали cache=none і виправили sync-латентність бекенду належним пристроєм, оптимізованим для записів. Латентність вирівнялась. Історія із стійкістю залишилася недоторканою. Урок засвоїли: ніколи не «припускайте», що режим кешу — це просто регулятор швидкості.
Міні-історія 2: Оптимізація, що обернулась проти
Інша організація мала змішані навантаження: CI-runner-и, pipeline інгесту логів і кілька станисних сервісів. Вони помітили, що CI-завдання повільні на фазах з інтенсивними дисковими операціями, і агресивно налаштували систему. Змінили диски на virtio-blk, задали великий iodepth у гості і увімкнули discard скрізь, щоб тонкі пули залишалися охайними.
Бенчмарки виглядали краще. Команда CI святкувала. Через два тижні система інгесту логів почала підвисати під час пікових годин. Це не був CPU. Це не була мережа. Це були гострі періодичні сплески латентності IO — гострі, періодичні і складні для кореляції. Найгірше: проблема з’явилась у багатьох VM одночасно.
Корінь проблеми — комбінація «хороших ідей», що співпали невдалим чином: discard від багатьох VM генерував сплески роботи бекенду, і iodepth збільшив розміри черг, тож хвости латентності подовжились при конкуренції. Кластер не став «повільнішим», він став більш джиттерним. p50 покращився, а p99 — погіршився, і саме так вас можуть обдурити усереднення.
Виправлення було прозаїчним: обмежити discard (запланувати замість постійного), зменшити глибину черги для чутливих до латентності VM, повернути virtio-scsi-single для певних мультидискових гостів і вибірково використовувати iothreads. Вони зберегли покращення для CI без шкоди для інгесту логів. Реальна перемога — усвідомлення, що тюнінг продуктивності — це переговори між навантаженнями, а не одна величина.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Компанія, близька до фінансів, запускала Proxmox з ZFS-дзеркалами на NVMe. Нічого гламурного, не дешево, але розумно. У них була звичка, що виглядала як бюрократія: у кожної VM був невеликий runbook з типом контролера, режимом кешу, чи увімкнений iothread і чому. Нічого зайвого — достатньо, щоб припинити імпровізації.
Одного дня оновлення ядра плюс зміна навантаження гостя спричинили періодичні затримки в критичній VM. VM була брокером повідомлень і ненавиділа сплески латентності. Користувачі бачили затримки обробки. Інженери почали шукати, «що змінилося». Runbook зробив пошук швидким: не було недавніх змін у конфігурації VM. Контролер і кеш були як очікувалося. Це відсікло половину підозр.
Вони пішли одразу до метрик хоста і побачили підвищену латентність на одному NVMe пристрої під час сплесків. Пристрій не падав, але поводився дивно. Підозрювали баг у прошивці. Оскільки налаштування VM були послідовними, вони відтворили проблему цілеспрямовано і ізолювали її до пристрою, а не до QEMU-флагів.
Міграція дисків VM з підозрілого пристрою, оновлення прошивки і перевірка тими ж fio-профілями, що використовувалися в тестах прийняття, дали чисте рішення. Аварія була локалізована. Постмортем короткий. Нікому не довелося пояснювати, чому о 3 ранку хтось «швидко» ввімкнув writeback. Нудні практики не трендують у соцмережах; вони зберігають дохідний контакт з реальністю.
Типові помилки: симптом → причина → виправлення
1) Симптом: періодичні зависання 1–5 секунд; середня пропускна здатність виглядає нормально
Причина: шторм flush-операцій, викликаний fsync/комітами журналу + режим кешу/бекенд, який перетворює flush у затримку.
Виправлення: використайте cache=none, увімкніть iothread, переконайтеся, що бекенд обробляє синхронні записи (ZFS з правильним SLOG, Ceph здоровий і не виконує відновлення), уникайте qcow2 для важких sync-навантажень.
2) Симптом: у гостя високий await, а на хості низький await
Причина: віртуальне чергування/контенція контролера (спільна черга), contention в головному циклі QEMU або неідеальний AIO-режим.
Виправлення: перейдіть на virtio-scsi-single або перевірте multiqueue в virtio-blk; увімкніть iothread; розгляньте aio=native для шляхів direct IO.
3) Симптом: підвисання почалося після увімкнення writeback «для швидкості»
Причина: кеш сторінок хоста, throttling записів і підсилення проблем за тиску пам’яті.
Виправлення: поверніть cache=none, якщо у вас немає захисту від втрати живлення; додайте ОЗП або зменшіть overcommit; ізолюйте IO-важкі VM на виділене сховище.
4) Симптом: тонкий пул заповнюється непередбачувано; VM повільнішає при майже повному пулі
Причина: тиск на метадані LVM-thin і поведінка біля повного стану; discard не увімкнено або неефективно.
Виправлення: моніторте використання thin-пулу, тримайте запас місця, ввімкніть discard обдумано, періодично запускайте fstrim у гостях і уникайте надмірного overcommit тонких пулів для write-heavy навантажень.
5) Симптом: Ceph-backed VM підвисає вдень, вночі все нормально
Причина: recovery/backfill або нерівномірна продуктивність OSD під навантаженням робочого дня; мережеві обмеження.
Виправлення: налаштуйте планування/ліміти відновлення, виправте повільні OSD, забезпечте виділену мережу для сховища і перевірте, що клієнтські опції кешу/flush не погіршують хвости латентності.
6) Симптом: мультидискова VM; навантаження одного диска порушує інший
Причина: спільний контролер/черга і відсутність iothread-ізоляції; ефекти merge/scheduler.
Виправлення: використовуйте virtio-scsi-single, увімкніть iothread для кожного важливого диска і розділяйте диски за призначенням (DB vs логи) з ясними пріоритетами.
7) Симптом: бенчмарки показують відмінну послідовну пропускну здатність, але додаток все одно підвисає
Причина: неправильний профіль тестування; реальне навантаження — дрібні випадкові синхронні записи з жорсткими вимогами до латентності.
Виправлення: тестуйте 4k/8k випадкові записи, низький iodepth і синхронні/fsync шаблони. Оптимізуйте хвости латентності, а не пікові MB/s.
Чеклисти / покроковий план
Покроково: план «виправити підвисання без ігор з даними»
- Спочатку виміряйте. Зніміть
iostat -xу гості і на хості під час підвисання. Збережіть виводи у тікеті. - Перевірте тип контролера. Якщо ви на SATA/IDE/емуляції LSI без причини, заплануйте зміну на virtio.
- Виберіть контролер:
- За замовчуванням — virtio-scsi-single для загальноцільових Linux VM.
- Використовуйте virtio-blk, якщо хочете простоти і маєте один навантажений диск, і ви перевірили латентність.
- Встановіть режим кешу
none. Це найбезпечніший варіант продуктивності для більшості продакшн-систем. - Увімкніть iothread для навантажених дисків. Почніть з одного на важливий диск.
- Підтвердьте AIO-режим. Віддавайте перевагу
aio=native, коли підтримується і стабільний; інакше прийміть threads і спирайтеся на iothreads. - Перевірте пам’ять хоста. Якщо хост свапить — матимете дивні IO-ефекти. Виправте тиск пам’яті.
- Бекенд-специфічні виправлення:
- ZFS: переконайтеся, що шлях синхронних записів достатньо швидкий; розгляньте правильний SLOG для синхронних VM.
- Ceph: переконайтеся, що кластер здоровий і не виконує відновлення; знайдіть повільні OSD.
- LVM-thin: тримайте вільний простір, стежте за метаданими; керуйте discard.
- Перетестуйте з реалістичним IO. Використовуйте fio з синхронними шаблонами, а не лише послідовними записами.
- Розгортайте поступово. Змініть одну VM за раз, перевіряйте p95/p99 латентність, потім стандартизуйте.
Чеклист: чого уникати, коли ви втомлені і на виклику
- Не вмикайте
cache=unsafe. Ніколи. - Не встановлюйте ZFS
sync=disabledяк «тимчасове» рішення без письмового погодження ризику. - Не налаштовуйте тільки під пропускну здатність; хвости латентності визначають відчуття користувачів.
- Не вмикайте discard скрізь, не розуміючи поведінку тонкого бекенду.
- Не думайте, що «NVMe» означає «низька латентність» автоматично. Це означає «швидше голосно відмовляє, коли насичено».
Швидкі приклади змін (з командами)
Ці приклади — типовi CLI операції Proxmox. Завжди робіть це в вікні технічного обслуговування і з планом бекапу/снапшоту, відповідним до вашого бекенду.
cr0x@server:~$ qm set 103 --scsihw virtio-scsi-single
update VM 103: -scsihw virtio-scsi-single
Що це означає: встановлює модель SCSI-контролера.
Рішення: продовжуйте, якщо гість підтримує virtio і ви можете перезавантажити при потребі.
cr0x@server:~$ qm set 103 --scsi0 rpool:vm-103-disk-0,cache=none,iothread=1,discard=on
update VM 103: -scsi0 rpool:vm-103-disk-0,cache=none,iothread=1,discard=on
Що це означає: встановлює режим кешу і вмикає iothread для цього диска.
Рішення: після перезавантаження перевірте латентність гостя і поведінку додатка.
Часті питання
1) Що обрати: virtio-scsi-single чи virtio-blk для Linux VM?
За замовчуванням обирайте virtio-scsi-single, якщо вам важлива передбачувана латентність і у вас кілька дисків або змішані IO. Використовуйте virtio-blk для простих одно-дискових VM, якщо ви виміряли і бачите гарну поведінку.
2) Чи завжди cache=none — найкращий вибір?
Для більшості продакшн Proxmox налаштувань — так. Це уникає подвійного кешування і зменшує побічний вплив кешу хоста на пам’ять. Винятки рідкі — зазвичай коли ви навмисно використовуєте кеш хоста для read-heavy навантажень і маєте достатньо ОЗП і дисципліну операцій.
3) Чому cache=writeback зробив мою VM «швидшою», але менш стабільною?
Тому що він підтверджує записи раніше, буферизуючи їх у RAM хоста, а потім скидає пізніше пакетами. Це може створити періодичні зависання і підвищений ризик при втраті живлення або падінні хоста.
4) Чи завжди iothreads допомагають?
Вони часто допомагають з латентністю і конкурентністю, особливо при навантаженні. Вони можуть бути нейтральними або трохи негативними при дрібних навантаженнях, де витрати на потоки домінують. Увімкніть їх для тих дисків, які мають значення, а не автоматично для кожного диска у кожній VM.
5) Як зрозуміти, чи підвисання викликане бекендом (ZFS/Ceph) чи налаштуваннями VM?
Порівняйте латентність пристрою на хості (iostat -x на хості) з латентністю гостя. Якщо хост в порядку, а гість ні — підозрюйте віртуальні контролери/черги/режим кешу. Якщо латентність хоста погана — виправляйте бекенд спочатку.
6) Чи можу я «вирішити» підвисання ZFS через sync=disabled?
Ви можете «вирішити» це так само, як можна вирішити звукову сигналізацію, вийнявши батарейку. Це міняє надійність на швидкість. Якщо вам потрібна продуктивність при sync-важких навантаженнях, використовуйте належний SLOG-пристрій з захистом від втрати живлення або переробіть архітектуру.
7) Чи викликає qcow2 підвисання?
Може, особливо при випадкових записах і коли образ фрагментовано. Якщо вам потрібна стабільна латентність для баз даних, віддавайте перевагу raw на блочному бекенді (zvol, LVM LV, RBD), якщо вам не дуже потрібні фічі qcow2.
8) Чи варто вмикати discard/TRIM для віртуальних дисків?
Часто так для SSD-backed тонкого надавання, але робіть це свідомо. Безперервний discard може створювати сплески роботи бекенду. Багато команд віддають перевагу періодичному fstrim у гості плюс discard увімкнений на віртуальному шарі, а потім моніторять.
9) Чому мій диск у гості називається sda, хоча я обрав virtio?
Всередині гостя ім’я залежить від драйвера і контролера. virtio-blk часто з’являється як vda. virtio-scsi часто з’являється як sda, але все ще використовує virtio-транспорт. Перевіряйте lspci/lsmod, а не лише ім’я пристрою.
10) Яка найпоширеніша причина «випадкового» підвисання у Proxmox?
Контенція на рівні хоста: бекапи, реплікація, scrub-и, відновлення Ceph або насичений диск. Друга за частотою причина: writeback кеш плюс тиск пам’яті, що створює шторм flush-операцій.
Висновок: кроки, які можна зробити сьогодні
Якщо у вашої Proxmox Linux VM повільний диск і підвисання, не починайте з фольклору. Почніть з короткого циклу вимірювань: гостя vmstat/iostat, хоста iostat і реальні прапори QEMU для VM. Потім зробіть зміни, що надійно покращують хвости латентності:
- Перейдіть з емульованих контролерів; використовуйте virtio-scsi-single або virtio-blk.
- Віддавайте перевагу cache=none, якщо не можете обґрунтувати writeback з захистом живлення і операційними гарантіями.
- Увімкніть iothread для дисків, які створюють біль проблеми.
- Робіть бекенд-специфічні виправлення замість звинувачення VM: шлях sync у ZFS, здоров’я Ceph, запас тонкого пулу.
Зробіть це для однієї VM, виміряйте p95/p99 латентність до і після, а потім стандартизуйте шаблон. Якщо проблема залишиться — вітаємо: ви усунули легкі причини і тепер маєте зайнятися справжньою інженерією. Це і є робота.