Короткий ответ

Активный swap сам по себе не авария: Ubuntu часто имеет swap-файл и редкие si/so. Авария — когда MemAvailable падает, PSI full растёт, диск swap молотит, SSH начинает отвечать секундами. Сначала докажите давление: free -h, vmstat 1, cat /proc/pressure/memory. Потом решите: снизить consumers (кэш приложения, workers), добавить RAM, не swapoff под нагрузкой и не vm.swappiness=0 как магическая кнопка.

Хост host.example (10.0.20.10), пользователь admin.

Симптомы и как отличить

  • free -h : available сотни МБ при total 8 ГБ, swap used растёт;
  • vmstat колонки si/so десятки тысяч блоков в секунду;
  • нагрузка «как CPU», но mpstat показывает iowait, а не usr — swapping;
  • пользователи: таймауты, PHP 502, postgres terminating connection due to administrator command после OOM.
НаблюдениеСкорее
cache большой, available тоже большойнорма, page cache
available мал, cache мал, swap used растётнастоящая нехватка
OOM в dmesgуже убийство, не «ещё потерпеть» — OOM
hang без si/soзависание

Возможные причины

  1. Утечка в приложении / рост heap JVM без -Xmx.
  2. Слишком много php-fpm pm.max_children × memory_limit.
  3. PostgreSQL shared_buffers + work_mem × соединений.
  4. File cache + анонимная память на узком хосте — ядро вытесняет anon в swap.
  5. tmpfs /tmp забит (это RAM).
  6. systemd-oomd ещё не сработал, PSI уже красный.
  7. Swap на том же медленном диске, что и БД — двойной iowait.

Диагностика

free -h
swapon --show
cat /proc/swaps
cat /proc/meminfo
cat /proc/pressure/memory
vmstat 1 15

PSI: строки some и full, поля avg10 avg60 avg300. full > 0 устойчиво — процессы стоят в ожидании памяти.

Кто ест анонимную память:

ps -eo pid,user,rss,vsz,cmd --sort=-rss | head -n 20
sudo smem -tk 2>/dev/null | head
grep -s VmRSS /proc/*/status | sort -n -k2 | tail

RSS не суммируется напрямую из‑за shared; для cgroup:

systemd-cgtop -m -n 1
systemctl show app.service -p MemoryCurrent,MemoryMax,MemoryHigh

tmpfs:

df -h -t tmpfs
sudo du -sh /tmp /dev/shm /run

Если /tmp на диске, это не RAM — но spill туда огромных сортировок всё равно бьёт диск.

Ядро и oomd:

systemctl status systemd-oomd --no-pager
journalctl -u systemd-oomd -b --no-pager | tail
dmesg -T | grep -iE 'oom|killed process' | tail

Решение

Сценарий A. Понятный потребитель, сервис можно снизить

Уменьшите workers, перезапустите один unit после дампа диагностики:

sudo systemctl restart app.service
free -h

Зафиксируйте MemoryMax= в drop-in, чтобы повторение упёрлось в cgroup, а не в весь хост.

Сценарий B. Нужно пережить пик

Временно остановите некритичное: batch, apt, snapshot. Не трогайте sshd и systemd-journald.

Сценарий C. Swap слишком маленький или на переполненном диске

Добавление swap-файла — только если на диске есть место (df -h) и вы понимаете, что это костыль латентности:

df -h /
sudo fallocate -l 2G /swapfile.extra
sudo chmod 600 /swapfile.extra
sudo mkswap /swapfile.extra
sudo swapon /swapfile.extra

Не кладите второй swap на тот же SSD, который уже 100% util.

Сценарий D. Хочется крутить swappiness

vm.swappiness на сервере с 32 ГБ и БД часто ставят 10, не 0. Ноль увеличивает риск жёсткого OOM. Меняйте sysctl после замеров PSI, не вместо них.

Сценарий E. Утечка подтверждена

Дамп heap/coredump, откат релиза. Reboot очистит RAM на часы — это не фикс.

Как выбрать шаг: рестарт, MemoryMax, RAM, swap

Порядок безопасных шагов после того, как PSI full ненулевой:

  1. Зафиксировать топ RSS и cgroup MemoryCurrent в заявку.
  2. Остановить расходный batch (apt, backup, CI), не sshd.
  3. Если есть явный unit-утечка — рестарт его с дампом.
  4. Поставить временный MemoryHigh/MemoryMax, чтобы не убить хост.
  5. Добавлять RAM на гипервизоре, если это постоянный рабочий набор, а не утечка.
  6. Swap — только чтобы пережить пик на диске, где iostat не в 100% util.

На Ubuntu 22.04 systemd-oomd включён в desktop-образах чаще, чем на minimal server; на 24.04 server проверяйте systemctl is-enabled systemd-oomd. Если oomd выключен, давление дольше живёт в swap и выглядит как «диск умер» из‑за iowait.

MemAvailable учитывает reclaimable cache. Если Available 2 ГБ, а free колонка 100 МБ — не паникуйте и не drop_caches. Если Available 80 МБ и si/so кипят — паникуйте.

Для PostgreSQL смотрите shared_buffers плюс work_mem × активных сортировок. Формула «25% RAM в shared_buffers» на 8 ГБ хосте вместе с java рядом даёт OOM. Считайте сумму сервисов, не каждый по отдельности как на выделенном сервере.

Как проверить, что проблема устранена

free -h
cat /proc/pressure/memory
vmstat 1 10
swapon --show
ssh admin@10.0.20.10 'uptime'

si/so около нуля под типичной нагрузкой, available с запасом (ориентир >15–20% RAM для смешанных узлов). Приложение отвечает без 502.

Если не помогло

  • После рестарта RSS снова растёт линейно: утечка, профилируйте код.
  • PSI полный, free врёт из‑за huge pages/hugepages не освобождённых: grep Huge /proc/meminfo.
  • Гость видит ballooning гипервизора: смотрите хост.
  • Уже есть Killed process — статья OOM, не эта.

Профилактика

  • Мониторинг MemAvailable, PSI full, swap I/O отдельно.
  • MemoryMax на потенциально дырявых unit.
  • Размер swap: аварийный тормоз, не «вторая RAM».
  • Не запускать CI-раннеры на том же 4 ГБ узле, что и postgres.

FAQ

Почему cache «съел» всю память?

Так задумано. Ядро отдаст cache под anon. Смотрите available.

Нужен ли swap на машине с 64 ГБ?

Небольшой swap полезен для редких пиков и чтобы oom killer получил время. Нулевой swap = более жёсткий OOM.

smem не установлен

ps + cgroup достаточно. Не ставьте случайные PPA в инцидент.

Можно ли echo 3 > drop_caches?

Это сбросит page cache и на секунды улучшит free, ухудшив I/O. Не диагностика давления anon.

systemd-oomd убил ssh?

Редко, если ssh в том же cgroup user.slice под давлением. Проверьте oomctl / логи oomd и не держите тяжёлые job в user-сессии admin.