Події OOM-killer у Proxmox: чому він вбиває процеси і як задати розумні ліміти

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

Хост Proxmox «працював нормально» місяцями. А потім у вівторок: ВМ ставлять на паузу, інтерфейс гальмує, ssh відповідає з затримкою, а логи нагадують сцену криміналу.
Щось було підбито. Це не диск. Це не мережа. Це пам’ять.

Коли Linux OOM-killer з’являється на хості віртуалізації, це не театральний трюк. Він робить останнє відповідальне дію:
вбити процес, щоб ядро могло вижити. Ваше завдання — перестати запрошувати його в гості.

Що таке подія OOM насправді (і чим вона не є)

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

На Proxmox це особливо болюче, бо хост одночасно є гіпервайзером і платформою для робочих навантажень:
він запускає процеси QEMU (по одному на ВМ), процеси контейнерів LXC, демонів сховища, агенти моніторингу та стек управління Proxmox.
Якщо хост втратить невірний процес, гіпервайзер може вижити, але ваші ВМ можуть призупинитися або впасти, а історія HA перетвориться на інтерпретативний танок.

Пріоритети ядра під час тиску пам’яті

Linux намагається звільнити пам’ять у такому приблизному порядку: звільнити page cache, реклайм анонімної пам’яті (swap), компактувати пам’ять і, нарешті,
викликати OOM-killer. На хості з вимкненим swap і агресивним overcommit ви пропускаєте половину сходинок і стрибаєте прямо до «вбити процес».
Саме цей стрибок ви і бачите як подію OOM.

OOM-killer vs systemd-oomd vs cgroup-ліміти

Три різні механізми «хтось вбив мій процес» зазвичай плутають між собою:

  • Kernel OOM-killer: глобальний, крайнє рішення, обирає жертву за оцінкою «поганості» (badness).
  • cgroup OOM: cgroup контейнера/ВМ досягла свого ліміту пам’яті; ядро вбиває всередині тієї cgroup, а не глобально.
  • systemd-oomd: демон у userspace, який вбиває раніше на основі сигналів тиску (PSI). Він може врятувати або заважати.

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

Сухий факт: якщо ви вважаєте OOM-killer багом, він буде навідуватися далі. Ставтеся до нього як до димової сигналізації.
Димова сигналізація — не ваша проблема; ваші кухонні звички — проблема.

Цитата, яка добре працює в опсах (парафразована думка): John Allspaw: надійність виникає, коли ти володієш тим, як системи відмовляють, і проєктуєш навколо цієї реальності.

Жарт №1: OOM-killer схожий на скорочення кадрів у HR під час дефіциту бюджету — швидко, несправедливо, і всі наполягають, що це «на основі метрик».

Цікаві факти та трохи історії

Це не тривіальні дрібниці заради дрібниць. Кожен факт пояснює, чому ваш хост Proxmox поводиться так, коли пам’ять стискається.

  1. У Linux OOM-killer існує десятиліттями, бо ядро не може просто «повернути NULL» у багатьох критичних шляхах виділення пам’яті без ризику корупції.
  2. Ранні системи Linux сильно покладалися на swap; сучасні кластери часто відключають swap за політикою, що робить події OOM різкими й безжальними.
  3. cgroups змінили правила гри: замість того, щоб уся машина падала, пам’ять можна обмежити по сервісах або контейнерах — і вбивства відбуваються локально.
  4. Оцінка «поганості» враховує використання пам’яті та коригувальники (наприклад, oom_score_adj), тому великий процес QEMU часто стає жертвою.
  5. ZFS ARC не «краде» пам’ять; це кеш, який призначений для росту й стиснення. Але він усе одно може додати тиску при невдалому налаштуванні.
  6. Ballooning існує через overcommit: це спосіб повернути пам’ять гостя під тиском, але воно допомагає лише якщо гості співпрацюють і мають віддавані сторінки.
  7. PSI (pressure stall information) — відносно сучасна фіча Linux, яка вимірює час, коли завдання блокуються через тиск ресурсів; вона увімкнула проактивні «вбивці» як systemd-oomd.
  8. THP (Transparent Huge Pages) може збільшувати затримки виділення й фрагментацію під тиском, що інколи перетворює «сповільнення» на «OOM».
  9. Файловий кеш не є «безкоштовною» пам’яттю у широкому сенсі; Linux його звільнить, але не завжди достатньо швидко для вибухових запитів на виділення.

Як використовується пам’ять на хості Proxmox

Три відра, які мають значення

Думайте про пам’ять хоста як про три відра, які конкурують між собою:

  • Пам’ять гостей: RSS процесів QEMU для ВМ, плюс використання пам’яті контейнерами. Це найбільший відсоток.
  • Сервіси хоста: pveproxy, pvedaemon, corosync, ceph (якщо ви його запускаєте), моніторинг, бекапи та випадковий «маленький агент», що тихо з’їдає 1–2 ГБ.
  • Кеш та ядро: page cache, slab, ZFS ARC, метадані та виділення ядра. Це може бути значно і має бути значним — поки не стане проблемою.

ВМ: пам’ять QEMU — не просто «RAM ВМ»

Коли ви виділяєте ВМ 16 ГБ, процес QEMU зазвичай відображає цю пам’ять. З KVM значна частина забезпечується хостовою RAM.
Ballooning, KSM і swap можуть змінювати картину, але проста операційна істина:
виділена ВМ пам’ять є резервуванням проти вашого хоста, якщо у вас немає доведеної стратегії overcommit.

Також: бекапи, snapshot-и та інтенсивний I/O можуть створювати транзитні стрибки пам’яті в QEMU і ядрі. Якщо ви плануєте ресурси лише для стейді-стану,
у вас будуть OOM під час «нудних вікон обслуговування» о 02:00. Саме тоді ніхто не дивиться, і все пише.

Контейнери: ліміти — ваш друг, поки вони не фікція

LXC на Proxmox використовує cgroups. Якщо ви задаєте ліміт пам’яті, контейнер не зможе його перевищити. Чудово.
Але якщо ви ставите його занадто низько, контейнер OOM-иться всередині, і це виглядає як «моя програма раптово померла».
Якщо ставите занадто високо, ви насправді нічого не обмежили.

ZFS: ARC — це фічер продуктивності, не безкоштовний буфет

Proxmox любить ZFS, а ZFS любить RAM. ARC росте для покращення швидкості читання та кешування метаданих.
Він зменшується під тиском пам’яті, але не завжди так швидко, як вам потрібно, коли навантаження раптово виділяє кілька гігабайт.
ARC можна налаштувати. Робіть це свідомо. Не робіть через форумну пораду «задати на 50%».

Swap: не гріх, а запобіжник

Swap на хості Proxmox — це не про те, щоб усе тримати на диску. Це про виживання стрибків і про те, щоб дати ядру час на реклайм.
Невелика кількість swap (або zram) може перетворити жорсткий OOM на повільний інцидент, який можна пом’якшити. Так, свопінг — огидна річ. Але й простої — теж.

Жарт №2: Вимкнути swap, щоб «зменшити затримки» — це як зняти подушки безпеки з автомобіля, щоб заощадити вагу — технічно трохи швидше, медично сумнівно.

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

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

1) Підтвердити, хто вбив процес

  • Глобальний kernel OOM-killer?
  • OOM через ліміт cgroup усередині контейнера/сервісу?
  • systemd-oomd — попереджувальне вбивство?

2) Визначити жертву та агресора

Жертва — це те, що було вбито. Агресор — те, що спричинило тиск.
Іноді це те саме. Частіше ні: одна ВМ виділяє пам’ять, іншу вбивають, бо вона виглядає «товстішою».

3) З’ясувати: це проблема ємності чи стрибка

Проблеми ємності показують стале високий анонімний використок пам’яті та використання swap (якщо він є). Проблеми стрибків показують раптові блокування, високі швидкості виділення
і частий реклайм/компаcацію. Ваше рішення відрізнятиметься: ємність — це перебалансування; стрибки — згладжування, лімітування або додавання буфера.

4) Швидко перевірити підозрілі елементи на хості

  • ARC ZFS надто великий або повільно стискається?
  • Зростання slab у ядрі (наприклад, мережа, метадані ZFS, inode/dentry) через витік або шаблон навантаження?
  • Бекап-завдання спричиняють стрибки алокацій?
  • Ballooning налаштовано неправильно або неефективний?

5) Вирішіть: обмежити, перемістити чи додати

Ваші три важелі: (1) задати ліміти, щоб один орендар не знищив будинок, (2) перерасподілити навантаження, і (3) додати RAM/swap.
«Просто додати RAM» не є неправильним, але це не план, якщо ви продовжуєте overcommit без контролю.

Практичні завдання: команди, виводи та рішення

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

Завдання 1: Знайти події OOM-killer у лозі ядра

cr0x@server:~$ journalctl -k -g 'Out of memory\|oom-killer\|Killed process' -n 200
Dec 26 10:41:12 pve kernel: oom-killer: constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0
Dec 26 10:41:12 pve kernel: Out of memory: Killed process 22134 (qemu-system-x86) total-vm:28764564kB, anon-rss:15632240kB, file-rss:12428kB, shmem-rss:0kB, UID:0 pgtables:34912kB oom_score_adj:0
Dec 26 10:41:12 pve kernel: Killed process 22134 (qemu-system-x86) total-vm:28764564kB, anon-rss:15632240kB, file-rss:12428kB, shmem-rss:0kB

Що це означає: Це глобальне вбивство kernel OOM (constraint=CONSTRAINT_NONE). Загинув процес QEMU; ймовірно, ВМ впала.

Рішення: Розглядайте як виснаження пам’яті хоста, а не проблему локального ліміту контейнера. Перейдіть до перевірки ємності хоста та overcommit.

Завдання 2: Визначити, чи це було OOM через ліміт cgroup

cr0x@server:~$ journalctl -k -g 'Memory cgroup out of memory' -n 50
Dec 26 10:13:07 pve kernel: Memory cgroup out of memory: Killed process 18872 (node) total-vm:1876540kB, anon-rss:824112kB, file-rss:1220kB, shmem-rss:0kB, UID:1000 pgtables:3420kB oom_score_adj:0
Dec 26 10:13:07 pve kernel: Tasks in /lxc.payload.104.slice killed as a result of limit of /lxc.payload.104.slice

Що це означає: Це локальна подія у cgroup (тут — контейнер LXC). Хост не обов’язково був без пам’яті.

Рішення: Збільшити ліміт контейнера, виправити пам’ять додатку або додати swap всередині контейнера, якщо це доречно. Не «виправляйте» це тонуванням ARC хоста.

Завдання 3: Перевірити, чи systemd-oomd вбив щось

cr0x@server:~$ journalctl -u systemd-oomd -n 100
Dec 26 10:20:55 pve systemd-oomd[782]: Killed /system.slice/pveproxy.service due to memory pressure (memory pressure 78.21%).
Dec 26 10:20:55 pve systemd-oomd[782]: system.slice: memory pressure relieved.

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

Рішення: Налаштуйте політики oomd або виключіть критичні сервіси; все одно розслідуйте, чому тиск піднявся.

Завдання 4: Побачити поточний розклад пам’яті (включно з кешем і swap)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            62Gi        54Gi       1.2Gi       1.1Gi       6.9Gi       2.8Gi
Swap:           8.0Gi       6.4Gi       1.6Gi

Що це означає: Доступної пам’яті мало; swap сильно використовується. Ви у стійкому тиску.

Рішення: Імовірно проблема ємності або runaway-навaнтаження. Розгляньте міграцію ВМ, збільшення RAM і перегляд розподілу пам’яті між гостями. Не просто перезапускайте сервіси.

Завдання 5: Перевірити PSI для пам’яті

cr0x@server:~$ cat /proc/pressure/memory
some avg10=12.34 avg60=9.88 avg300=4.21 total=9349221
full avg10=3.21 avg60=2.10 avg300=0.88 total=1883211

Що це означає: «some» показує час, протягом якого задачі затримувалися на реклайм пам’яті; «full» — системні загальні затримки.

Рішення: Якщо «full» несправедливо високий у нормальному навантаженні, потрібен буфер (swap/zram), менше overcommit або перерасподілення навантаження.

Завдання 6: Швидко знайти топ-споживачів пам’яті

cr0x@server:~$ ps -eo pid,comm,rss,vsz,oom_score_adj --sort=-rss | head -n 12
  PID COMMAND           RSS    VSZ OOM_SCORE_ADJ
22134 qemu-system-x86 15632240 28764564 0
 9921 qemu-system-x86  8241100 16544320 0
18772 pveproxy           612332  1054320 0
 1443 pvedaemon          244120   612044 0

Що це означає: Процеси QEMU домінують. Це нормально, але означає, що ваше overcommit-математичне планування має бути вірним.

Рішення: Зіставте PID з VMID, то вирішіть, які гості занадто великі або ведуть себе неправильно.

Завдання 7: Зіставити PID QEMU з VMID

cr0x@server:~$ pgrep -a qemu-system-x86_64 | head -n 3
22134 /usr/bin/kvm -id 101 -name vm101,debug-threads=on -m 16384 ...
9921 /usr/bin/kvm -id 102 -name vm102,debug-threads=on -m 8192 ...

Що це означає: PID 22134 — це VM 101 з 16 ГБ виділеної пам’яті.

Рішення: Проінспектуйте навантаження VM 101, налаштування balloon і чи виправдана виділена пам’ять.

Завдання 8: Перевірити конфіг ВМ на ballooning і пам’ять

cr0x@server:~$ qm config 101
agent: 1
balloon: 4096
boot: order=scsi0;net0
cores: 8
memory: 16384
name: vm101
ostype: l26
scsi0: rpool:vm-101-disk-0,size=200G

Що це означає: ВМ має 16 ГБ максимум, balloon target 4 ГБ. Це дозволяє забрати приблизно ~12 ГБ, якщо гість співпрацює.

Рішення: Якщо тиск на хості частий, ballooning може допомогти, але лише якщо гість має драйвер/агента і може звільняти пам’ять. Інакше не покладайтеся на ballooning як на порятунок.

Завдання 9: Перевірити, чи ballooning реально активний (virtio-balloon)

cr0x@server:~$ qm monitor 101 --cmd 'info balloon'
balloon: actual=16384

Що це означає: Гість зараз утримує повні 16 ГБ; ballooning не звільняє пам’ять.

Рішення: Якщо ви очікували зменшення, перевірте драйвери/агент у гості і чи гість справді використовує RAM. Інакше припиніть покладатися на ballooning для порятунку хоста.

Завдання 10: Перевірити ліміт пам’яті LXC та поточне використання

cr0x@server:~$ pct config 104 | egrep 'memory|swap|cores'
cores: 4
memory: 2048
swap: 512
cr0x@server:~$ cat /sys/fs/cgroup/lxc.payload.104.slice/memory.current
1938479104

Що це означає: Ліміт контейнера — 2 ГБ; він використовує ~1.8 ГБ. Майже до стелі.

Рішення: Якщо він OOM-иться всередині, підніміть ліміт або виправте навантаження. Якщо він ніколи не має перевищувати 2 ГБ, збережіть ліміт і налаштуйте додаток.

Завдання 11: Подивитися події пам’яті cgroup (кількість OOM)

cr0x@server:~$ cat /sys/fs/cgroup/lxc.payload.104.slice/memory.events
low 0
high 0
max 3
oom 3
oom_kill 3

Що це означає: Cgroup досягла max і тричі вбивала завдання.

Рішення: Це не проблема хоста. Підвищте пам’ять контейнера і/або розслідуйте ріст пам’яті всередині контейнера.

Завдання 12: Перевірити статус swap і який у вас тип swap

cr0x@server:~$ swapon --show
NAME      TYPE SIZE USED PRIO
/dev/sda3 partition 8G  6.4G -2

Що це означає: Swap є і ним користуються. Добре: це дає простір для дихання. Погано: можлива трешинг.

Рішення: Якщо swap постійно зайнятий і латентність висока, зменшіть overcommit і приведить гості до розумних розмірів. Якщо swap відсутній і ви OOM-итесь, додайте невеликий swap-пристрій.

Завдання 13: Перевірити розмір ZFS ARC та ліміти (якщо використовуєте ZFS)

cr0x@server:~$ arcstat -f time,arcsz,arc_max,hit%,miss% 1 1
    time  arcsz     arc_max   hit%  miss%
10:44:10   28.4G      48.0G    92     8

Що це означає: ARC займає 28.4 ГБ, дозволено до 48 ГБ. Це багато на хості з 62 ГБ, що вже напружений.

Рішення: Розгляньте обмеження ARC. Але тільки після підтвердження реального тиску пам’яті, а не тимчасового стрибка, і розуміння вашого навантаження на сховище.

Завдання 14: Перевірити налаштування overcommit ядра

cr0x@server:~$ sysctl vm.overcommit_memory vm.overcommit_ratio
vm.overcommit_memory = 0
vm.overcommit_ratio = 50

Що це означає: Стандартний евристичний overcommit. Багато БД і JVM можуть поводитися погано під тиском незалежно.

Рішення: Не «виправляйте OOM» перемиканням опцій overcommit без розуміння ризику. Спочатку використовуйте ліміти та планування ємності; потім тонко налаштовуйте.

Завдання 15: Подивитися, чи ядро витрачає час на компактування пам’яті (проблема фрагментації)

cr0x@server:~$ grep -E 'compact|pgscan|pgsteal' /proc/vmstat | head
compact_migrate_scanned 184921
compact_free_scanned 773212
compact_isolated 82944
pgscan_kswapd 29133211
pgsteal_kswapd 28911220

Що це означає: Висока активність сканування/зняття та компактування може свідчити про інтенсивний реклайм і фрагментацію.

Рішення: Очікуйте латентності. Додайте буфер (swap), зменшіть великі алокації, перегляньте THP і зменшіть overcommit.

Завдання 16: Перевірити, чи ввімкнені Transparent Huge Pages

cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

Що це означає: THP встановлено на always. Для деяких хостів віртуалізації й певних сумішей БД/ВМ це збільшує тиск на компактування.

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

Завдання 17: Підтвердити, які гості сконфігуровані і скільки пам’яті ви пообіцяли

cr0x@server:~$ qm list
 VMID NAME                 STATUS     MEM(MB)    BOOTDISK(GB) PID
  101 vm101                running    16384             200.00 22134
  102 vm102                running     8192             100.00  9921
  103 vm103                running     4096              80.00  7744
cr0x@server:~$ awk '/^memory:/{sum+=$2} END{print sum " MB assigned to VMs"}' /etc/pve/qemu-server/*.conf
28672 MB assigned to VMs

Що це означає: Ви можете порахувати «обіцяні» VM пам’яті. Це крок нуль у математиці overcommit.

Рішення: Порівняйте обіцяні пам’яті плюс сервіси хоста та потреби ARC/cache з фізичною RAM. Якщо ви близько до ліміту, потрібні ліміти і вільний запас.

Завдання 18: Перевірити cgroup пам’яті хоста і системні slice (хто захищений)

cr0x@server:~$ systemctl show -p MemoryMax -p MemoryHigh -p ManagedOOMMemoryPressure system.slice
MemoryMax=infinity
MemoryHigh=infinity
ManagedOOMMemoryPressure=auto

Що це означає: Немає обмежень пам’яті на рівні slice; oomd може керувати тиском автоматично.

Рішення: Якщо oomd вбиває важливі сервіси Proxmox, можливо, потрібно відрегулювати поведінку oomd або захистити критичні юніти через OOMScoreAdjust і політики.

Встановлення розумних лімітів: VM, контейнери, хост і ZFS

Єдине правило, якого варто дотримуватися

Залишайте запас пам’яті на хості. Реальний запас, а не теоретичний. На вузлі Proxmox резервувати 15–25% RAM для хоста
— це розумна початкова точка при використанні ZFS і суміші гостей. Якщо ви запускаєте Ceph на тому ж вузлі (так роблять), резервуйте більше.

Якщо ви не можете дозволити собі такий запас, ви не можете дозволити собі таку щільність робочих навантажень. Це не філософія. Це фізика.

Розмір пам’яті ВМ: припиніть ставитися до нього як до «списку бажань»

Для KVM ВМ є три поширені підходи:

  • Статичне виділення (без ballooning): найбільш передбачувано. Найкраще для важливих навантажень.
  • Ballooning з реалістичним мінімумом: підходить для загальних флітів, але не як єдиний захід безпеки.
  • Агресивний overcommit: лише якщо ви довели це метриками і погоджуєтесь на періодичні колапси продуктивності під тиском.

Рекомендована політика для ВМ (думка автора)

  • Для продукційних БД: без ballooning, виділена пам’ять, без сильного overcommit на хості.
  • Для загальних аплікаційних серверів: ballooning дозволено, але задавайте мінімум balloon не нижче пікового споживання додатку.
  • Для build-агентів, dev-боксів: ballooning і більш жорсткі caps прийнятні; вони мають бути дешевими і трохи дратівливими.

Ліміти для LXC: робіть це свідомо

Контейнери діляться ядром хоста. Це робить їх ефективними, але також робить погані ліміти особливо болючими, бо ядро жорстко їх застосує.
Якщо у контейнері працює Java з неправильно налаштованим heap, ліміт 2 ГБ означатиме, що ядро врешті-решт вб’є процес.
Це «працює як задумано». Просто ваш дизайн поганий.

Для LXC простий базовий набір:

  • Задайте memory відповідно до пікового навантаження додатку в нормі.
  • Задайте swap невеликий (наприклад, 25–50% від пам’яті), якщо навантаження стрибкоподібне, якщо немає жорстких вимог до латентності.
  • Слідкуйте за OOM через memory.events. Якщо бачите зростання oom_kill, ви недопостачили або маєте витік.

Swap на хості: нудно, але ефективно

Для вузла Proxmox помірний swap-пристрій зазвичай — правильне рішення. Не тому, що ви хочете свапити. А тому, що ви хочете виживання.
4–16 ГБ — звичний діапазон залежно від розміру вузла та волатильності навантаження. Якщо у вас ультранизькі вимоги до латентності, розгляньте zram або погодьтесь на ризик.

Swappiness і чому не варто на цьому фанатіти

Люди ставлять vm.swappiness=1, ніби це відзнака. На хості віртуалізації ви хочете, щоб ядро віддавало перевагу RAM,
але також щоб воно використовувало swap як аварійний клапан. Значення близько 10–30 часто розумні.
Правильне число — те, яке підходить вашому навантаженню та бюджету інцидентів.

ZFS ARC: обмежуйте його, коли хост — передусім гіпервайзер

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

Практичний підхід:

  • Виміряйте ARC під типовим навантаженням. Не налаштовуйте за тихої години.
  • Визначте головний запас хоста спочатку (наприклад, 20%).
  • Обмежте ARC так, щоб «VM + контейнери + сервіси хоста + ARC + буфер» вміщувалися з комфортом.

Як задати максимум ARC (приклад)

На Debian/Proxmox зазвичай задають опцію модуля. Приклад: обмежити ARC до 16 GiB.

cr0x@server:~$ echo "options zfs zfs_arc_max=17179869184" | sudo tee /etc/modprobe.d/zfs-arc.conf
options zfs zfs_arc_max=17179869184
cr0x@server:~$ update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.8.12-5-pve

Що це означає: Максимум ARC застосується після перезавантаження (або перезавантаження модуля, чого зазвичай не роблять у продуктиві, якщо не люблять адреналін).

Рішення: Перезавантажте у вікні техобслуговування, потім перевірте поведінку ARC і продуктивність ВМ. Якщо затримки читання зросли, ви обмежили занадто сильно.

Захист хоста від гостей (і від самого себе)

Дві практичні захисні міри:

  • Не допускайте, щоб хост регулярно працював при 95% «available» пам’яті під звичайним навантаженням. Якщо «available» регулярно менше декількох ГБ, ви вже в інциденті.
  • Зробіть так, щоб площина управління Proxmox було важко вбити. Якщо pveproxy помирає, відновлення стає повільнішим і ризикованішим, особливо в HA-кластерах.

Налаштування OOM-score: використовуйте виважено, але застосовуйте для критичних сервісів

Linux використовує oom_score_adj для ухилення від вибору жертви. Ви можете зробити критичні сервіси менш ймовірними жертвами.
Не робіть усе «незавершуваним», якщо не хочете, щоб ядро впало або вбило щось по-справжньому важливе.

cr0x@server:~$ systemctl edit pveproxy.service
[Service]
OOMScoreAdjust=-800
cr0x@server:~$ systemctl daemon-reload
cr0x@server:~$ systemctl restart pveproxy.service

Що це означає: pveproxy менш імовірно буде обраний як жертва.

Рішення: Захистіть сервіси управління та кластеризації. Не захищайте «пожирачів» пам’яті.

Три корпоративні історії з практики

1) Інцидент через помилкове припущення: «available memory — це вільна пам’ять»

Середня компанія вела кластер Proxmox для внутрішніх додатків: тикетинг, CI-ранери, кілька баз даних і кілька «тимчасових» аналітичних машин.
Графіки вузла показували RAM на рівні 90–95% більшу частину часу. Ніхто не панікував, бо «Linux використовує пам’ять для кешу».
Це правда. Але неповна. Ключовий показник був available, а не used.

Була розгорнута нова ВМ з вендорським аплайенсом. Там був великий Java-сервіс, який мав звичку виділяти пам’ять стрибками під час інгесту логів.
Під час першого інгесту хост увійшов у реклайм, потім swap, потім — ядру треба було вибирати: вбити щось.
Воно вбило QEMU іншої ВМ, бо він мав найбільший RSS. Та ВМ була базою даних тикетингу.

Постмортем був брудним, бо нову ВМ звинуватили («це нова річ»), але вона насправді не «зловживає» у власному сенсі.
Хост просто працював з надто малим запасом. Page cache не був лиходієм; він був канаркою.
Неправильне припущення полягало в тому, що високе «used» — ок, якщо це кеш. Кеш відновлюваний, але його відновлення не безкоштовне, і при стрибковому навантаженні воно не завжди швидке.

Виправлення не було екзотичним: резервувати запас хоста, обмежити ARC, додати невеликий swap-партіцій, і застосувати розумні ліміти контейнерів.
Також перестали вдавати, що ballooning врятує, коли гості справді використовують свою RAM. OOM-и припинилися, і нічні загадки теж.

2) Оптимізація, що повернулася бумерангом: «swap вимкнули через продуктивність»

Інший магазин обслуговував сервіси з вимогами до латентності й вирішив, що swap — ворог. Консультант давно порадив вимикати swap на bare-metal БД.
Хтось застосував ту саму пораду до вузлів Proxmox. Звучало правдоподібно. Було контекст-нечутливе.

Деякий час усе працювало швидше. Менше swap — менше несподіваних затримок у нормальному режимі. Перемога.
Потім додали нічну рутинну задачу бекапу, яка робила експорт зі snapshot-ів, компресію й шифрування.
Вузол почав стрибати за пам’яттю, реклайм наростав, і — немає swap — OOM приходив.
Він вбивав QEMU, що еквівалентно гіпервайзеру обрізати гальма, щоб зменшити вагу автомобіля.

«Оптимізація продуктивності» перетворила переживаний стрибок у фатальний крах. Гірше, вона зробила патерн інциденту непередбачуваним:
іноді хост виживав, іноді вбивав різні ВМ, іноді — демон хоста, ускладнюючи розслідування.
Вимкнення swap не було злом за своєю суттю; злочином було робити це без стратегії буфера.

Зрештою прийшли до нудного й ефективного компромісу: знов увімкнули swap, але встановили консервативний swappiness і моніторинг PSI.
Також перенесли бекап-роботу на виділений вузол з більшим запасом RAM. Чутливі до латентності сервіси залишилися задоволені, а нічна рулетка закінчилася.

3) Нудна, але правильна практика, що врятувала день: «ліміти і запас — непорушні»

Третя команда вела Proxmox для змішаних навантажень по відділах. Вони не були надзвичайно вмілі — вони були надзвичайно дисципліновані.
Кожен запит на ВМ мав бюджет пам’яті і обґрунтування. Контейнери мали явні обмеження пам’яті й swap.
І кожен вузол мав фіксований «резерв для хоста», що трактувався як податок на ємність, а не опція.

Одного кварталу вендор випустив оновлення, що внесло витік пам’яті в агента, який працював у кількох контейнерах.
Витік був повільний і ввічливий — поки не став таким, що не було. Контейнери почали досягати своїх cgroup-лімітів і вмирали.
Дратує, але локалізовано. Хост залишився стабільним. Жодних глобальних OOM-подій, жодного краху гіпервайзера, жодних каскадних відмов.

Їхній моніторинг підловив зростання лічильників oom_kill по контейнерах, і команда откотила версію агента.
Декілька сервісів перезапустилися, клієнти помітили незначні пробої, і інцидент завершився за ту ж годину.
«Нудна» практика — виконання лімітів і резервів — перетворила потенційну відмову хоста на керований баг додатку.

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

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

1) Симптом: «Випадкові падіння ВМ; хост стоїть»

Корінна причина: Глобальний kernel OOM-killer вбив процес QEMU; ВМ померла.

Виправлення: Зменшити overcommit, додати запас хоста, перевірити стратегію swap і розглянути капінг ARC. Підтвердити через journalctl -k.

2) Симптом: «Контейнери перезапускаються; пам’ять хоста виглядає нормально»

Корінна причина: OOM через ліміт cgroup усередині контейнера; локальне вбивство.

Виправлення: Підвищити memory/swap для контейнера, налаштувати додаток (heap limits) або забезпечити кращу розмірність навантаження. Перевірити через memory.events.

3) Симптом: «Веб-інтерфейс Proxmox помирає під час тиску; ssh працює»

Корінна причина: systemd-oomd або kernel OOM вбив pveproxy/pvedaemon, бо вони були доступними для вбивства і мали не нульовий RSS.

Виправлення: Захистити критичні сервіси через OOMScoreAdjust; зменшити тригери тиску; налаштувати oomd, якщо він увімкнений.

4) Симптом: «OOM відбувається під час бекапів або реплікації»

Корінна причина: Транзитні стрибки пам’яті від компресії/шифрування, операцій з snapshot-ами і поведінки кешу файлів.

Виправлення: Обмежити паралельність, перемістити бекапи з вузла, додати swap-буфер, зарезервувати більше запасу.

5) Симптом: «Немає swap, ніяких попереджень, миттєвий OOM при стрибку»

Корінна причина: Swap вимкнено; реклайм не встигає.

Виправлення: Додати помірний swap або zram; налаштувати swappiness; використовувати PSI для виявлення тиску до OOM.

6) Симптом: «ZFS ARC «з’їдає» пам’ять; OOM після інтенсивних читань»

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

Виправлення: Обмежити ARC на основі вимірювань; забезпечити резерв хоста; перевірити затримки та коефіцієнт попадань після зміни.

7) Симптом: «OOM при достатку вільної пам’яті раніше»

Корінна причина: Проблеми фрагментації/компакції (THP, великі алокації) або раптовий темп анонімних виділень.

Виправлення: Розгляньте THP=madvice, додайте swap, зменшіть шаблони великих алокацій і уникайте надмірного упакування вузла.

8) Симптом: «Виправили додаванням RAM; OOM повертається через місяці»

Корінна причина: Немає лімітів, немає бюджету; споживання виросло відповідно до ємності.

Виправлення: Впровадьте обмеження по орендарях, цільові запаси пам’яті та періодичні рев’ю розмірів. Додайте моніторинг assigned vs used.

Чек-листи / покроковий план

Негайний чек-лист інциденту (перші 15 хвилин)

  1. Підтвердити вбивцю: kernel OOM vs cgroup OOM vs systemd-oomd (journalctl запити).
  2. Визначити, що померло (PID, команда, VMID/слайс контейнера).
  3. Захопити поточний стан: free -h, swapon --show, PSI (/proc/pressure/memory).
  4. Перелік топ-споживачів (ps --sort=-rss) і зіставлення QEMU PID з VMID.
  5. Якщо хост трешить: негайно зменшити навантаження (мігрувати/вимкнути некритичні гості), замість перезапуску демонів.

Чек-лист стабілізації (наступні 24 години)

  1. Встановити цільовий запас хоста (почніть з 20% RAM для змішаних навантажень).
  2. Переконатися, що swap є; встановити розумний swappiness (уникати крайнощів).
  3. Аудит призначених ресурсів гостей: виділена пам’ять vs реальна потреба; прибрати «комфортну» RAM.
  4. Для контейнерів: задати memory і swap ліміти; відстежувати memory.events для OOM.
  5. Для ВМ: вирішити, де ballooning прийнятний; перевірити ефективність balloon driver у гостях.
  6. Якщо ZFS: виміряти ARC і встановити cap, що враховує запас гіпервайзера.

Чек-лист закріплення (наступний спринт)

  1. Додати алерти на: низьку available пам’ять хоста, тренд використання swap, PSI full avg60, події kernel OOM, лічильники cgroup OOM.
  2. Захистити критичні сервіси Proxmox через OOMScoreAdjust і перевірити політики systemd-oomd.
  3. Зменшити паралельність в запланованих роботах: бекапи, реплікація, scrub-и та важкі батч-задачі.
  4. Задокументувати політику overcommit: що дозволено, що ні, і як це вимірюється.
  5. Запустити контрольоване навантаження: симулювати стрибки алокацій і перевірити, що хост деградує плавно, а не вбиває QEMU.

FAQ

1) Чому Linux вбиває «випадковий» процес замість того, що спричинив проблему?

Це не випадково. Ядро обчислює оцінку поганості на основі використання пам’яті та інших факторів. Агресор може бути меншим за «товстішу» жертву.
Тому ліміти по орендарях такі цінні: вони локалізують зону ураження.

2) Чи нормально, що Proxmox працює при 90% використаній пам’яті?

«Used» — оманливе число, бо включає кеш. Важливі показники — available, тренд swap і сигнали тиску.
Вузол може виглядати «заповненим» і бути здоровим, або виглядати «добре» і бути в одному стрибку від OOM.

3) Чи варто вимикати swap на Proxmox?

Зазвичай ні. Невеликий swap — це запас безпеки. Якщо ви вимикаєте його, ви обираєте жорсткі вбивства замість уповільнень.
Якщо у вас жорсткі вимоги до латентності, розгляньте інші стратегії буфера (наприклад, zram) і визнайте ризик явно.

4) Чи викликає ZFS ARC OOM?

ARC спроектовано так, щоб стискатися, але він все одно може додати тиску, якщо система вже overcommit або якщо навантаження стрибає швидше, ніж ARC встигає зменшитись.
Обмеження ARC — розумний крок на вузлах, де гіпервайзер — пріоритет.

5) Чи запобігає ballooning OOM?

Може допомогти, але це не гарантія. Ballooning вимагає співпраці гостя і наявності сторінок, які можна віддати.
Якщо гості справді використовують свою RAM, ballooning не створить вільної пам’яті з повітря.

6) Як визначити, чи OOM стався всередині контейнера чи на хості?

Повідомлення про cgroup OOM містять «Memory cgroup out of memory» і посилаються на шлях cgroup (наприклад, /lxc.payload.104.slice).
Глобальні OOM-повідомлення виглядають як «oom-killer: constraint=CONSTRAINT_NONE» і можуть вбивати QEMU або демони хоста.

7) Який найнадійніший спосіб швидко зменшити ризик OOM?

Додайте невеликий swap-буфер, введіть ліміти контейнерів, припиніть екстремальний overcommit і забезпечте запас хоста.
Якщо використовуєте ZFS, капайте ARC лише після вимірювань. Потім валідируйте через PSI і поведінку навантаження під час бекапів.

8) Якщо я встановлю OOMScoreAdjust дуже низький для всього, чи зупинить це OOM-вбивства?

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

9) Чому OOM-и часто відбуваються під час бекапів, scrub-ів або реплікації?

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

10) Який «розумний» коефіцієнт overcommit для пам’яті ВМ?

Універсального числа немає. Починайте консервативно: не перевищуйте фізичну RAM мінус резерв хоста для «гарантованих» алокацій критичних ВМ.
Якщо ви overcommit-ите, робіть це на гостях низького пріоритету, з ballooning там, де воно доречне, і з моніторингом, що доводить безпеку.

Висновок: практичні кроки далі

Події OOM-killer на Proxmox рідко містять таємницю. Це рахунок за рішення щодо пам’яті, які ви приймали місяцями:
overcommit без лімітів, відмова від swap як моральної слабкості і «ми налаштуємо ZFS пізніше».
Ядро все зібрало.

Зробіть наступне, у цьому порядку:

  1. Класифікуйте подію (kernel OOM vs cgroup OOM vs systemd-oomd) за допомогою запитів у логах, наведених вище.
  2. Перерахуйте бюджет заново: резерв хоста, призначена пам’ять гостей, swap-буфер, ціль ARC ZFS.
  3. Впровадьте ліміти для контейнерів і прийміть політику підбору пам’яті для ВМ згідно з критичністю навантаження.
  4. Додайте спостережуваність: алерти за PSI, available пам’ять, тренд swap і лічильники OOM.
  5. Перевірте під час обслуговування, а не лише у тихі години.

Мета не в «повністю ніколи не OOMитися». Мета — зробити тиск пам’яті передбачуваним, локалізованим і прозорим. У продакшні «нудно» — це комплімент.

← Попередня
Чому ігри хочуть один CPU, а рендеринг — інший
Наступна →
ZFS mountpoint: пастка монту, що змушує датасети «зникати»

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