Ви заходите в систему і все відчувається… «липким». SSH відповідає на секунду довше, ніж зазвичай. «Швидкий» запуск apt тягнеться.
Дашборди показують, що CPU у нормі, диск не здається завантаженим, і все ж затримки ростуть, наче хтось влаштував забіг по сходах.
Потім ви помічаєте це: використання swap збільшується. Спочатку повільно, а потім невпинно. Коробка не впала, проте вона й не «жива».
На Debian 13 це зазвичай не «swap — зло», а скоріше «система торгується з фізикою». Хороша новина: ви можете швидко діагностувати тиск пам’яті,
обрати правильне втручання і перестати ставитися до swap як до забобону. Погана новина: деякі поширені «налаштування» роблять ситуацію гіршою.
Зробімо виправлення, які витримують роботу в продакшені.
Швидкий план діагностики (перевірте це першочергово)
Коли swap росте і продуктивність падає, потрібно швидко відповісти на три запитання:
(1) Чи ми дійсно під тиском пам’яті, чи просто тримаємо кеш?
(2) Якщо тиск реальний, що його спричиняє — один процес, обмеження cgroup чи поведінка reclaim у ядрі?
(3) Чи уповільнення через swap I/O, direct reclaim блокування, чи й те й інше?
Перше: підтвердьте тиск і тип проблеми
- Перегляньте індикатори пам’яті + swap + reclaim:
free,vmstatта PSI у/proc/pressure. - Перегляньте найбільших споживачів: сортування RSS через
ps, далі — cgroups. - Перевірте затримки: високі
si/so(swap in/out), високеwaта PSI «some/full».
Друге: визначте межу
- Якщо ви на bare metal/VM без обмежень cgroup, межею є «RAM + swap».
- Якщо ви на хості з контейнерами, межею може бути
memory.maxcgroup; swap може зрости на хості, поки контейнер заважає. - Якщо systemd-oomd увімкнено, воно може вбивати (або не вбивати) процеси на основі сигналів PSI та конфігурації юніта.
Третє: оберіть клас втручання
- Утеча пам’яті або неконтрольований ріст: виправити додаток, безпечно перезапустити, додати захисні межі (ліміти/oomd).
- Навантаження, яке важить кеш: налаштувати reclaim і уникати панічних «очищень кешу».
- Swap thrash: зменшити свопінг (swappiness, memcg налаштування) або зробити swap швидшим (zram/NVMe), поки виправляєте корінь.
- Неправильно розмірена система: додати RAM або зменшити паралельність. Так, іноді вирішення — витратити гроші.
Парафразована ідея від John Allspaw (операції та надійність): «Ви не виправляєте інциденти, полюючи за єдиним ‘коренем’; ви виправляєте умови, що дозволили помилці статися.»
Це як раз про зростання swap: зазвичай це набір умов, а не один злодій.
Що насправді відбувається, коли swap росте
Linux використовує RAM не лише для купи вашого додатка. Він зберігає дані файлів у page cache, тримає метадані файлової системи
і утримує анонімні сторінки (пам’ять процесів). Коли RAM заповнюється, ядро виконує reclaim: видаляє чисті сторінки кешу,
записує «брудні» сторінки на диск і, якщо треба, переміщує анонімні сторінки в swap.
Зростання swap не означає автоматично, що система «закінчилася пам’ять». Це також може означати, що ядро вирішило, що деякі сторінки холодні
і відправило їх у swap, щоб звільнити місце для кешу, який підвищує пропускну здатність. Це звучить логічно — поки не перестане бути так.
Три режими відмов, які ви реально бачите
- Безпечний swap: swap використовується трохи, але швидкість swap-in залишається низькою. Машина чуйна. Можна ігнорувати.
-
Swap thrash: швидкості swap-in/out стрибають, затримки вибухають, CPU показує час у I/O wait, а інтерактивні задачі «повзуть».
Це сценарій «продуктивність падає». -
Direct reclaim блокування без масивного swap I/O: ядро витрачає час на reclaim сторінок; PSI пам’яті «some/full» зростає.
Коробка відчувається повільною навіть якщо swap I/O не величезний, особливо при певних навантаженнях і фрагментації пам’яті.
На Debian 13 зазвичай сучасне ядро і systemd. Це добре — доступний PSI, cgroup v2 поширений, і є systemd-oomd.
Але це також означає, що багато «розумних налаштувань» можуть взаємодіяти так, що кожне окремо розумне, а разом — дурне.
Короткий жарт №1: Swap як шухляда зі сміттям — норм, поки вам не треба знайти щось швидко, і воно заховано під старими меню з доставки.
Факти та контекст: swap, reclaim і чому Debian поводиться так
-
Факт 1: Linux swap передує сучасній парадигмі «RAM дешева»; він був спроектований у час, коли пам’яті було мало і навантаження були менші,
тож політики reclaim еволюціонували десятиліттями, а не місяцями. -
Факт 2: Ядро Linux розрізняє анонімну пам’ять (сторінки процесів) і файлово-підтримувану пам’ять (page cache).
Файлові сторінки можна викинути; анонімні сторінки потребують swap (або смерті через killer). -
Факт 3: PSI (Pressure Stall Information) з’явився для вимірювання часу, коли задачі стоять через брак ресурсу (пам’ять/CPU/I/O),
і це більш дієвий показник, ніж «вільна пам’ять» для діагностики затримок. -
Факт 4: cgroup v2 змінив семантику контролю пам’яті і зробив
memory.highручкою для throttling замість простої жорсткої межі;
це важливо, бо throttling може виглядати як «загадкова повільність». -
Факт 5: OOM killer — це рівень ядра і грубий інструмент; systemd-oomd — користувацький простір і політика,
часто спрацьовує на сигнали PSI замість очікування повного OOM. -
Факт 6: Значення за замовчуванням для
vm.swappinessзмінювалося з часом і між дистрибутивами; це не моральне рішення,
а регулювальний важіль, і дефолти орієнтовані на «загальне призначення», а не на ваше конкретне навантаження. - Факт 7: Свопінг на швидкий NVMe може «працювати», але все одно руйнувати хвостову латентність, бо swap-in часто знаходиться на критичному шляху обробки запиту.
-
Факт 8: Transparent Huge Pages (THP) можуть збільшувати пам’ятну «підписку» і ускладнювати поведінку reclaim; для деяких баз даних це корисно,
для інших — постійне джерело болю. -
Факт 9: Фрагментація пам’яті може викликати відмови алокації або затримки reclaim, навіть коли «вільна пам’ять» виглядає ненульовою,
особливо для великих суміжних алокацій (включно з huge pages).
Практичні завдання: команди, виводи та рішення (реальна робота)
Це не «запустіть це і почувствуйте себе краще» команди. Кожна з них: команда → що означає вивід → яке рішення ви приймаєте.
Виконуйте їх по черзі, доки картина не стане очевидною.
Завдання 1: Встановіть базовий стан пам’яті та swap
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 62Gi 51Gi 1.2Gi 1.5Gi 10Gi 3.4Gi
Swap: 16Gi 9.8Gi 6.2Gi
Що це означає: «available» — ключова рядок, а не «free». Тут лише 3.4Gi швидко доступні без серйозної шкоди.
Swap вже близько ~60% використаний.
Рішення: Продовжуйте: треба з’ясувати, чи swap активно використовується (thrash), чи просто алокований і «охолоджений».
Завдання 2: Перевірте активність swap в реальному часі (thrash чи відкладені сторінки)
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
2 0 10032128 120064 89216 8675328 12 18 22 40 820 2150 12 6 80 2 0
1 0 10032256 118720 89216 8675456 0 0 0 8 790 2080 10 5 84 1 0
3 1 10110464 93248 89216 8629312 420 980 120 640 1100 3200 14 7 62 17 0
2 1 10135680 90112 89216 8622336 510 1200 180 920 1180 3400 15 8 55 22 0
1 0 10136576 95040 89216 8622912 10 40 16 88 840 2250 12 6 79 3 0
Що це означає: Дивіться на si/so (swap in/out) і wa (I/O wait). Середні зразки показують інтенсивний swap I/O і високий wait.
Це thrash, а не безпечний swap.
Рішення: Визначте, хто це змушує, і чи тиск глобальний, чи всередині cgroup.
Завдання 3: Знайдіть топ споживачів RSS
cr0x@server:~$ ps -eo pid,comm,rss,vsz,%mem --sort=-rss | head -n 12
PID COMMAND RSS VSZ %MEM
4121 java 18842340 25011240 29.6
2337 postgres 9241120 10012560 14.5
2875 python3 4021152 5120032 6.3
1998 node 2568896 3101024 4.0
1552 dockerd 812224 1887744 1.3
911 systemd-journald 392448 566288 0.6
Що це означає: RSS — «resident set», те що фактично в RAM. VSZ включає відображені, але не обов’язково resident сторінки.
Якщо один процес домінує в RSS, він — основний підозрюваний.
Рішення: Для великих процесів перевірте, чи зростання пам’яті очікуване (heap, cache) чи це витік; також перевірте cgroup ліміти.
Завдання 4: Підтвердіть, чи ви використовуєте cgroup v2 (поширено на сучасному Debian)
cr0x@server:~$ stat -fc %T /sys/fs/cgroup/
cgroup2fs
Що це означає: cgroup v2 активний. Тиск пам’яті може відбуватися в межах юніта/контейнера, навіть якщо хост виглядає «ок», або навпаки.
Рішення: Перегляньте використання пам’яті та ліміти по юнітах або контейнерам.
Завдання 5: Знайдіть юніти з найгіршим тиском пам’яті через systemd
cr0x@server:~$ systemctl status --no-pager --full user.slice
● user.slice - User and Session Slice
Loaded: loaded (/usr/lib/systemd/system/user.slice; static)
Active: active since Mon 2025-12-29 08:12:10 UTC; 2h 21min ago
Docs: man:systemd.special(7)
Tasks: 246 (limit: 76800)
Memory: 14.8G
CPU: 1h 10min 32.920s
CGroup: /user.slice
├─user-1000.slice
│ ├─session-4.scope
│ │ ├─4121 java
│ │ └─...
Що це означає: systemd може показувати пам’ять по slice. Якщо slice або сервіс розростається, ви можете його обмежити.
Рішення: Заглибтесь у конкретний service unit (або контейнерний scope) і перевірте його контролі пам’яті.
Завдання 6: Перевірте ліміти пам’яті та поточне використання для конкретного юніта
cr0x@server:~$ systemctl show myapp.service -p MemoryCurrent -p MemoryPeak -p MemoryMax -p MemoryHigh -p ManagedOOMMemoryPressure
MemoryCurrent=20521447424
MemoryPeak=22231822336
MemoryMax=infinity
MemoryHigh=infinity
ManagedOOMMemoryPressure=auto
Що це означає: Жодної жорсткої межі. Якщо додаток росте, він штовхатиме систему в reclaim і swap.
Рішення: Якщо це мульти-орендний хост, додайте MemoryHigh/MemoryMax захисні межі і визначте політику OOM.
Завдання 7: Прочитайте PSI тиск пам’яті (це корелює з «все повільно»)
cr0x@server:~$ cat /proc/pressure/memory
some avg10=18.42 avg60=12.01 avg300=6.55 total=922337
full avg10=3.21 avg60=1.70 avg300=0.62 total=120044
Що це означає: «some» — час, коли як мінімум одна задача застрягла через пам’ять. «full» — час, коли система повністю «застряла» (без прогресу).
Це достатньо високо, щоб пояснити сплески затримок і скарги користувачів.
Рішення: Розглядайте це як проблему надійності, а не «тонке» налаштування. Зменшіть тиск або встановіть ліміти.
Завдання 8: Перевірте swap-пристрої і пріоритет (повільний swap може вбити)
cr0x@server:~$ swapon --show --bytes
NAME TYPE SIZE USED PRIO
/dev/nvme0n1p3 partition 17179865088 10522624000 -2
Що це означає: Swap на розділі NVMe, але пріоритет низький (-2). Якщо у вас також є zram або інші бекенди swap, пріоритет має значення.
Рішення: Якщо swap неминучий, віддавайте перевагу швидшому swap (часто zram для сплесків) та встановлюйте пріоритети навмисно.
Завдання 9: Спостерігайте major faults і swap faults по процесу
cr0x@server:~$ pidstat -r -p 4121 1 3
Linux 6.12.0 (server) 12/29/2025 _x86_64_ (32 CPU)
09:41:10 UID PID minflt/s majflt/s VSZ RSS %MEM Command
09:41:11 1000 4121 1200.00 35.00 25011240 18842340 29.6 java
09:41:12 1000 4121 1305.00 42.00 25011240 18850012 29.7 java
09:41:13 1000 4121 1104.00 38.00 25011240 18861020 29.7 java
Що це означає: Major faults (majflt/s) часто означають диск‑I/O для повернення сторінок — swap-in є поширеною причиною.
Сталі major faults разом з активністю swap — явний сигнальний доказ thrash.
Рішення: Зменшити working set або додати RAM; також розглянути налаштування heap і ліміти пам’яті.
Завдання 10: Перевірте статистику reclaim ядра (чи ми перемелюємо page cache?)
cr0x@server:~$ egrep 'pgscan|pgsteal|pswpin|pswpout' /proc/vmstat | head
pgscan_kswapd 18223344
pgscan_direct 992311
pgsteal_kswapd 17199812
pgsteal_direct 801223
pswpin 621044
pswpout 1200033
Що це означає: Високий pgscan_direct свідчить про direct reclaim (блокування задач). Високі pswpin/out вказують на своп‑чарн.
Рішення: Якщо direct reclaim значний, тиск шкодить латентності. Пріоритетно зменшити working set і встановити ліміти; сам по собі tuning swappiness не врятує.
Завдання 11: Перевірте swappiness та інші VM-ручки (не налаштовуйте всліпу)
cr0x@server:~$ sysctl vm.swappiness vm.vfs_cache_pressure vm.dirty_ratio vm.dirty_background_ratio
vm.swappiness = 60
vm.vfs_cache_pressure = 100
vm.dirty_ratio = 20
vm.dirty_background_ratio = 10
Що це означає: swappiness=60 — загальний дефолт. На latency‑чутливих серверах це може бути надто схильним до свопінгу під тиском.
Показники dirty впливають на writeback-сплески; неправильне налаштування може створити I/O‑конгестивність, що підсилює біль від swap.
Рішення: Якщо ви бачите реальний thrash (високі si/so), варто протестувати зменшення swappiness (обережно) і перевірити, щоб dirty‑параметри не створювали writeback‑бурі.
Завдання 12: Перевірте налаштування THP (може збільшувати footprint і ускладнювати reclaim)
cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
Що це означає: THP увімкнено «always». Це може бути нормально, але для деяких навантажень (особливо деяких БД/кешів) це викликає сплески латентності або пам’ятне роздування.
Рішення: Якщо бачите reclaim‑застрявання і непередбачувані латентності, протестуйте перехід на madvise (або never) в контрольованому режимі.
Завдання 13: Підтвердіть кореляцію дискового I/O wait і swap I/O
cr0x@server:~$ iostat -xz 1 3
Linux 6.12.0 (server) 12/29/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.10 0.00 6.50 18.40 0.00 63.00
Device r/s w/s rKB/s wKB/s avgrq-sz avgqu-sz await svctm %util
nvme0n1 85.00 140.00 9200.0 18000.0 210.0 4.20 18.50 0.45 92.00
Що це означає: %iowait високий, NVMe біля насичення. Swap I/O конкурує з усім іншим і псує хвостову латентність.
Рішення: Якщо swap на тому ж пристрої, що й база даних або лог‑інтенсивні томи, ви запрошуєте крос‑затримки. Розділіть пристрої або зменшіть свопінг.
Завдання 14: Перевірте журнали ядра і journal на OOM та події тиску пам’яті
cr0x@server:~$ journalctl -k --since "1 hour ago" | egrep -i 'oom|out of memory|memory pressure|kswapd' | tail -n 20
Dec 29 09:02:14 server kernel: oom_reaper: reaped process 4121 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
Dec 29 09:02:14 server kernel: Out of memory: Killed process 4121 (java) total-vm:25011240kB, anon-rss:18842340kB, file-rss:10240kB, shmem-rss:0kB, UID:1000 pgtables:52000kB oom_score_adj:0
Що це означає: Якщо ви бачите OOM‑kills, ви не «виправили swap», ви його пережили. Також зауважте: OOM ядра — останній рубіж, часто після довгого thrash.
Рішення: Впровадьте політику (ліміти, oomd), щоб падіння відбувалося раніше і передбачувано, і виправте розмір/витік пам’яті.
Завдання 15: Якщо ви запускаєте контейнери, перевірте події пам’яті (тихий вбивця)
cr0x@server:~$ cat /sys/fs/cgroup/system.slice/docker.service/memory.events
low 0
high 1242
max 0
oom 0
oom_kill 0
Що це означає: Багато «high» подій означає, що група часто потрапляє під memory.high throttling. Це виглядає як випадкова повільність.
Рішення: Збільшіть поріг memory.high, зменшіть конкурентність навантаження або виправте відбиток сервісу. Throttling корисний, але все одно болючий.
Виправлення, які справді допомагають (і чому)
Є дві категорії «виправлень»: структурні та тактичні. Структурні виправлення усувають причину, чому working set не вміщується в RAM.
Тактичні зменшують радіус ураження, коли це все ж трапляється. Потрібні обидва. І треба уникати культу‑навіювання налаштувань.
1) Встановіть захисні межі пам’яті там, де проблема мешкає (systemd юніти / cgroups)
Якщо сервіс може з’їсти хост, він рано чи пізно це зробить — випадково, під час піка трафіку або через фічу, яку хтось задеплоїв о 16:55 у п’ятницю.
На Debian із systemd використовуйте контролі пам’яті на рівні юніта.
cr0x@server:~$ sudo systemctl edit myapp.service
[Service]
MemoryHigh=18G
MemoryMax=22G
ManagedOOMMemoryPressure=kill
ManagedOOMMemoryPressureLimit=20%
Як це думати:
MemoryHigh — це «почати застосовувати тиск» (reclaim/throttling). MemoryMax — це «жорстка зупинка». Якщо ваш сервіс не переживе reclaim,
краще вбити і перезапустити його, ніж дозволити йому отруїти весь вузол.
2) Налаштуйте systemd-oomd свідомо (або відключіть явно)
systemd-oomd — не той же OOM killer ядра. Це движок політик на основі метрик тиску. Це може бути чудово — якщо ви налаштуєте його під вашу топологію.
Також воно може вбити не ту річ, якщо сприймати дефолти як даність.
cr0x@server:~$ systemctl status systemd-oomd --no-pager
● systemd-oomd.service - Userspace Out-Of-Memory (OOM) Killer
Loaded: loaded (/usr/lib/systemd/system/systemd-oomd.service; enabled)
Active: active (running) since Mon 2025-12-29 08:12:11 UTC; 2h 35min ago
Рішення: Якщо ви запускаєте мульти‑сервісний хост, увімкнення oomd з коректними політиками per‑service зазвичай краще, ніж чекати ядра OOM.
Якщо ви маєте однопрофільний аплайнс з жорстким контролем на рівні аплікації, можете відключити oomd, щоб уникнути несподіваних вбивань — але тоді потрібні інші захисні механізми.
3) Знизьте swappiness для latency‑чутливих сервісів (але не вважайте це панацеєю)
vm.swappiness контролює, наскільки агресивно ядро буде свопити анонімну пам’ять відносно викидання файлового кешу.
На серверах, де важлива латентність більше, ніж кожен кеш‑хіт, нижчий swappiness може допомогти зменшити swap churn.
Це не зробить занадто малий хост придатним.
cr0x@server:~$ sudo sysctl -w vm.swappiness=10
vm.swappiness = 10
cr0x@server:~$ sudo tee /etc/sysctl.d/99-swappiness.conf >/dev/null <<'EOF'
vm.swappiness=10
EOF
Рішення: Якщо ви спостерігаєте реальний thrash (високі si/so), варто протестувати зниження swappiness.
Якщо ж PSI «full» і direct reclaim високі, все одно потрібно зменшувати попит на пам’ять або ставити ліміти.
4) Виправте найбільший working set спочатку: обмежте heap, налаштуйте кеші, зупиніть подвійне кешування
Найпоширеніший «swap росте назавжди» винуватець — додаток з еластичним heap або кешем, якому ніколи не задали межі.
Java, Node, Python‑сервіси з агресивним кешуванням та бази даних можуть поводитися так.
- Java: виставляйте
-Xmxпродумано; не дозволяйте йому «пливти» до всього, що дозволяє ОС. - PostgreSQL: тримайте
shared_buffersрозумним і пам’ятайте, що ОС page cache вже є кешем. - Elasticsearch / кеші: обмежуйте пам’ять; не дозволяйте кешуванню бути «безкінечним», лише тому що хост має swap.
Класична пастка — подвійне кешування: ваш додаток кешує дані в heap, а ядро кешує ті самі дані в page cache. Ви платите двічі, потім свопитесь.
Ієрархія пам’яті — не шведський стіл.
5) Розгляньте zram для короткочасних піків (особливо на менших VM)
Якщо ваш хост інколи відчуває нестачу пам’яті, zram (стиснутий swap у RAM) може поглинути сплески без обрушення на диск.
Це не безкоштовно — компресія вимагає CPU — але може бути вигідним, коли дискова латентність більший ворог.
cr0x@server:~$ sudo apt-get update
Hit:1 deb.debian.org stable InRelease
Reading package lists... Done
cr0x@server:~$ sudo apt-get install -y zram-tools
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
zram-tools
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Рішення: Якщо swap thrash відбувається на повільному диску або спільному сховищі, zram може стати сильним тактичним пом’якшувачем.
Якщо ваші навантаження вже CPU‑зв’язані, zram може погіршити ситуацію. Вимірюйте.
6) Розмістіть swap на правильному сховищі з правильними очікуваннями
Swap на обертовому диску — машина часу в 2009 рік. Swap на мережевому блоці — пригода, якої ви не замовляли.
Swap на NVMe може бути робочим, але якщо він ділиться пристроєм з латентно‑критичною базою даних, це все одно зруйнує хвостову латентність.
Добрі практики:
- Віддавайте перевагу виділеному swap-пристрою або принаймні відокремленому від DB WAL/лог-важких томів.
- Переконайтесь, що swap увімкнений рано і має правильний пріоритет, якщо є кілька бекендів swap.
- Правильно розмірюйте swap. Якщо вам потрібний величезний swap, щоб просто залишатися в роботі, ви маскуєте проблему ємності.
7) Налаштуйте dirty writeback, щоб уникнути I/O‑бур, що підсилюють swap‑біль
Тиск пам’яті + writeback‑сплески = поганий час. Якщо брудні сторінки накопичуються, ядро рано чи пізно примусово виконає writeback,
що може наситити I/O саме тоді, коли потрібен swap‑in.
Так ви отримуєте сервер, який і свопить, і «раптом» повільний, навіть після зупинки росту swap.
cr0x@server:~$ sudo sysctl -w vm.dirty_background_ratio=5 vm.dirty_ratio=10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
Рішення: Нижчі коефіцієнти можуть згладити writeback на системах з вибуховими записами і чутливістю до латентності.
Але не налаштовуйте це ізольовано; потрібно перевірити пропускну здатність сховища та модель записів додатка.
8) Уникайте «drop caches» як звички
Так, echo 3 > /proc/sys/vm/drop_caches зробить «free memory» красивішою. Але це також знищить корисний кеш і може викликати лавину повторних читань.
Це останній засіб для налагодження, а не виправлення. Якщо ви робите це щотижня, ви працюєте в продакшені «на відчуттях».
9) Не відключайте swap, якщо не готові до раптової смерті процесів
Вимкнення swap може зменшити thrash, але змінює режим відмови з «повільно» на «вбивство». Іноді це бажано. Частіше — ні.
Якщо ви вимикаєте swap, вам потрібно:
- строгі ліміти пам’яті по сервісах
- чітку політику перезапуску
- сповіщення при наближенні до меж і PSI
- план на випадок піків трафіку
Короткий жарт №2: Вимикати swap, щоб виправити тиск пам’яті — як забрати пожежний датчик, бо він пищить.
10) Визнайте, коли машина занадто мала
Якщо ваш steady‑state working set перевищує RAM, ви або свопитеся, або застрягаєте, або отримуєте вбивства процесів. Налаштування можуть визначити, який дискомфорт ви оберете;
вони не перепишуть арифметику. Додайте RAM, зменшіть паралельність, шардьте навантаження або виведіть важкі сервіси з вузла.
Три корпоративні міні-історії (як це зазнає фіаско в реальному житті)
Міні‑історія 1: Інцидент через неправильне припущення
Команда запускала клієнтський API на Debian‑хостах з «достатньо пам’яті». Вони налаштували swap, бо «це безпечніше».
Дашборди відстежували CPU, частоту запитів і використання диска. Пам’ять була представленa одним рядком: «free».
Зазвичай все здавалось ок. Потім латентність подвоїлась на десять хвилин кілька разів на день без очевидного шаблону.
Неправильне припущення було тонким: вони вірили, що якщо хост не закінчився пам’яттю, то пам’ять не є проблемою.
Використання swap повільно зростало під час піків трафіку, але оскільки нічого не падало, це не викликало тривоги.
Тим часом ядро свопило холодні сторінки Java‑сервісу. Під подальшими сплесками ті сторінки вже не були холодними.
Swap‑інти впали на той самий NVMe, що використовувався базою даних для WAL.
Інцидент проявився як «база даних повільна», бо це було перше, що помічали: запити чекали на I/O і блокування.
Але корінна умова була в тиску пам’яті, що спричинив swap I/O, який конкурував з WAL і підсилював хвостову латентність.
Команда тиждень ганялася за індексами і connection pool.
Виправлення не було екзотичним. Вони додали сповіщення на основі PSI, знизили swappiness, відокремили swap від диска бази даних,
і найголовніше — обмежили Java heap і кеші в процесі. Після цього swap переважно стояв на місці,
а коли тиск повертався, це було очевидно на ранній стадії, а не щоденний whodunit.
Міні‑історія 2: Оптимізація, що підвела
Інша організація хотіла «ефективно використовувати пам’ять» і напирала на максимізацію page cache для читально‑інтенсивних навантажень.
Хтось запропонував підвищити swappiness «щоб Linux тримав файловий кеш гарячим». Також увімкнули THP повсюди, бо «більші сторінки швидші».
На папері все виглядало чисто: більше кеш‑хітів, менше читань з диска. На практиці два сервіси ділили хост:
кешуючий API і batch‑завдання, що щодня піком штовхало пам’ять.
З підвищеним swappiness анонімні сторінки активно свопилися під тиском; з THP відбиток пам’яті став менш передбачуваним.
Batch завдання викликало reclaim; reclaim викликав swap; swap відбирав I/O‑пропускну здатність; і інтерктивне API платило ціну.
Найгірше: оптимізація покращила пропускну здатність в ізоляції.
Вона провалилася лише під змішаним навантаженням, яке є типовим для корпоративних серверів.
Команда бачила «добрий показник кеш‑хітів» і думала, що все ок, поки клієнтська латентність падала.
Откат був простим: повернули swappiness, THP перевели на madvise, batch‑джоб помістили в systemd scope з жорстким лімітом пам’яті.
Також перестали співрозміщувати batch і latency‑чутливі API на одному класі вузлів.
Оптимізація не була злою; вона була неправильно застосована і без меж.
Міні‑історія 3: Нудна, але правильна практика, що врятувала день
Платформена команда керувала флотом Debian‑серверів з кількома внутрішніми сервісами. Нічого гламурного: логи, метрики, кілька API
і декілька фонових воркерів. У них була нудна правило: кожен сервіс отримує systemd‑юнит з межами пам’яті, і кожен вузол експортує PSI метрики з алертами. Ніяких винятків. Розробники скаржилися. Мовчки, всі дотримувалися правил.
Одного дня розгортання внесло витік пам’яті у Python‑воркер. RSS повільно росло кілька годин.
На вузлах без захисту такий витік перетворюється на повільний відкат: swap росте, латентність підвищується, люди звинувачують мережу,
і лише потім щось падає.
Тут воркер спочатку влучив у MemoryHigh і почав отримувати throttling. PSI підскочив для юніта, а не всього вузла.
Спрацювали алерти з чітким контекстом: «юнит воркера має тиск пам’яті», а не «хост повільний».
systemd-oomd убив воркер при перевищенні ліміту. systemd перезапустив його, відновивши сервіс.
Ніхто не святкував. Оце і є суть. Система деградувала передбачувано і відновилася без перетворення всього вузла на swap‑піч.
Подальша робота була цілеспрямованою: виправити витік, додати регресійний тест і зберегти захисні межі. Нудно — перемогло. Знову.
Поширені помилки: симптом → корінь проблеми → фікс
1) «Swap використано високо, отже ми вийшли з пам’яті»
Симптом: Swap ненульовий або росте, але система відчувається нормально.
Корінь проблеми: Бенінний swap: холодні анонімні сторінки перенесені у swap; швидкість swap‑in незначна.
Фікс: Перевірте vmstat на si/so і PSI. Якщо swap‑in близький до нуля і PSI низький, не чіпайте.
2) «Виправимо, поставивши swappiness=1»
Симптом: Swap зменшується, але система починає OOM‑ити або застопорюється під час reclaim.
Корінь проблеми: Working set все ще не влазить; ви просто змінили, які сторінки жертвуються. Низький swappiness не запобігає тиску пам’яті.
Фікс: Обмежте пам’ять додатка, встановіть memory.high/memory.max, зменшіть паралельність або додайте RAM. Використовуйте swappiness як тонке налаштування, а не парашут.
3) «Вимкнемо swap — і все буде добре»
Симптом: Зник thrash, але з’являються раптові вбивства процесів і перезапуски; іноді OOM ядра вбиває не ту службу.
Корінь проблеми: Режим відмови змінився з «деградації» на «раптове». Без політик — хаос.
Фікс: Якщо вимикаєте swap, застосуйте per‑service ліміти і передбачувану політику kill/restart (systemd-oomd або супервізія сервісів).
4) «Це проблема диску; давайте налаштуємо файлову систему»
Симптом: Високий iowait, високе використання сховища, але корінна подія — тиск пам’яті.
Корінь проблеми: Swap I/O конкурує з реальним I/O; диск виглядає винним, бо він використовується як аварійна RAM.
Фікс: Спочатку зменшіть свопінг. Якщо потрібно, відокремте пристрій під swap. Потім перегляньте тюнінг диска.
5) «Просто скинемо кеші»
Симптом: Короткострокове покращення, потім гірша продуктивність і більше I/O.
Корінь проблеми: Ви знищили корисний page cache; система мусить перечитати дані, збільшуючи I/O і тиск.
Фікс: Не робіть цього, хіба що як контрольований експеримент. Виправте розмір пам’яті і відбиток додатка.
6) «Контейнер має ліміт пам’яті, отже хост захищений»
Симптом: Swap хоста росте; контейнер сповільнюється; немає явних OOM‑подій.
Корінь проблеми: memory.high throttling і reclaim всередині cgroup; хостовий page cache і інші сервіси все ще конкурують; ліміти не зупиняють вплив на сусідів.
Фікс: Налаштуйте sensibly і memory.high, і memory.max; ізолюйте шумні сервіси; дивіться cgroup PSI і memory.events.
7) «Swap повільний, бо на NVMe; це не може бути причиною»
Симптом: NVMe швидкий, але латентність жахлива під свопінгом.
Корінь проблеми: Swap‑in — на критичному шляху; черги і конкуренція ламають хвостову латентність навіть на швидких пристроях.
Фікс: Зупиніть свопінг, зменшивши working set. Використовуйте zram для сплесків, якщо CPU дозволяє. Відокремлюйте swap від гарячих I/O‑шляхів.
Контрольні списки / покроковий план
Чекліст A: Коли ви на чергуванні і вузол «повільний»
- Запустіть
free -hі зафіксуйте «available» та використаний swap. - Запустіть
vmstat 1 10. Якщоsi/soстрибає абоwaвисокий, підозрюйте swap thrash. - Прочитайте
/proc/pressure/memory. Якщо «full avg10» значущий, розглядайте як інцидент. - Перевірте
iostat -xz 1 3, щоб побачити, чи swap насичує основний диск. - Знайдіть топ RSS споживачів через
ps; підтвердіть через метрики сервісів/cgroup. - Якщо один сервіс runaway: безпечно перезапустіть і додайте тимчасовий ліміт (MemoryHigh/Max), щоб зупинити повтор.
- Відкрийте follow‑up задачу: план ємності, per‑service memory caps і PSI‑оповіщення.
Чекліст B: Постійні виправлення на мульти‑сервісному Debian 13 хості
- Інвентаризуйте сервіси і встановіть межі пам’яті:
MemoryHighдля м’якого тиску,MemoryMaxдля жорсткої зупинки. - Вирішіть політику: використовуйте systemd-oomd для вбивства юнітів при тривалому тиску, або залиште kernel OOM як останню інстанцію (не рекомендовано для multi‑tenant).
- Встановіть swappiness тестовано (часто 10–30 для latency‑чутливих хостів).
- Оцініть zram для поглинання сплесків; перевірте запас CPU.
- Розмістіть swap на відповідному сховищі; уникайте спільного використання з DB/WAL.
- Перевірте поведінку THP; переключіть на
madvise, якщо це зменшить сплески латентності. - Сповіщайте по PSI пам’яті «some/full» і по швидкостях swap‑in/out, а не лише за використовуваним swap.
Чекліст C: Якщо ви підозрюєте витік
- Захопіть тренд росту RSS у часі: знімки
psабоpidstat -r. - Підтвердіть поведінку swap‑in через major faults:
pidstat -rmajflt. - Додайте жорсткий ліміт пам’яті, щоб запобігти впливу на весь хост під час дебагу.
- Перезапустіть сервіс для відновлення стабільності; підготуйте інцидент‑репорт з доказами: тренд RSS, PSI, vmstat і логи.
FAQ
1) Чи завжди використання swap погане?
Ні. Активність swap — ось що вас вбиває. Якщо використання swap велике, але vmstat показує майже нульові si/so і PSI низький, це, ймовірно, нормально.
2) Яка найкраща одинична метрика для «пам’ять нищить латентність»?
PSI тиск пам’яті (/proc/pressure/memory). Він показує, скільки часу задачі стоять. Це краще корелює з «незадоволеними користувачами», ніж «вільна RAM».
3) Чи варто знижувати swappiness на Debian 13 серверах?
Часто — так, для latency‑чутливих сервісів, але лише після підтвердження, що у вас саме swap thrash. Почніть з 10–30, вимірюйте swap‑in/out і PSI.
Не використовуйте swappiness, щоб приховати занадто великий working set.
4) Чи варто повністю відключати swap?
Лише якщо ви приймаєте раптові OOM‑вбивства і маєте захисні межі (per‑service ліміти, політики перезапуску і моніторинг). Вимкнення swap може покращити передбачуваність
в деяких кластерах, але це не універсальне поліпшення.
5) Що краще: OOM killer ядра чи systemd-oomd?
Для мульти‑сервісних хостів systemd-oomd з явними політиками юнітів часто кращий, бо може діяти раніше на основі тиску і цілити потрібний юніт.
OOM ядра — останній засіб і може вбити щось критичне після хвилин thrash.
6) Чому продуктивність падає, навіть коли swap I/O не масивний?
Direct reclaim блокування. Ядро може витрачати час на сканування і reclaim сторінок, поки задачі чекають. PSI «some/full» зазвичай це показує.
Фрагментація і взаємодія з THP також можуть додавати проблем.
7) Чи безпечний zram для серверів?
Так, при свідомому використанні. Він чудовий для поглинання короткочасних піків і для систем, де дисковий swap повільний або контестований.
Це коштує CPU; якщо ви вже CPU‑зв’язані, ви можете перенести вузьке місце.
8) У мене контейнер має ліміт пам’яті; чому хост свопить?
Бо хост все одно керує глобальною пам’яттю і може свопити через загальний тиск. Також cgroup throttling (memory.high) може спричиняти сповільнення без OOM‑подій.
Перевірте memory.events і PSI на рівні юніта.
9) Скільки swap має бути на Debian 13 сервері?
Достатньо, щоб обробляти короткі сплески і дозволяти впорядковану поведінку, але не настільки багато, щоб маскувати хронічне недовлаштування. Універсальної відповіді немає.
Якщо ви регулярно покладаєтесь на глибокий swap, вам потрібна більше RAM або менші working sets.
10) Чому swap продовжує рости і ніколи не зменшується?
Linux не наполягає на агресивному поверненні сторінок у пам’ять, якщо їх не використовують. Якщо сторінки дійсно холодні, вони залишаються в swap.
Це не баг. Проблема виникає, коли ці сторінки перестають бути холодними і ви починаєте своп‑ін під навантаженням.
Висновок: практичні наступні кроки
Якщо swap росте і продуктивність Debian 13 падає, розглядайте це як те, чим воно є: тиск пам’яті, що перетворюється на затримки і I/O‑конкуренцію.
Не сперечайтеся з графіком swap. Використайте його як підказку, потім доведіть режим відмови за допомогою активності swap і PSI.
- Прямо зараз: запустіть
vmstat,free, PSI іiostat. Визначте, чи це thrash або безпечний swap. - Протягом години: знайдіть топ RSS споживача і чи він обмежений cgroups/systemd‑юнітами.
- Протягом дня: додайте захисні межі пам’яті (
MemoryHigh/MemoryMax) і обдуману політику OOM. - Протягом тижня: виправте working set (heap/cache sizing), оцініть zram для сплесків і перевірте розміщення swap, щоб він не конкурував з критичними I/O.
- Завжди: ставте алерти на PSI і швидкості swap‑in/out, а не лише на використаний swap. Користувачі відчувають затримки, а не статистику обліку.
Кінцевий стан, до якого треба прагнути, не «swap = 0». Це «тиск видимий, обмежений і відновлюваний» — і машина залишається чуйною, коли це важливо.