Ubuntu 24.04 tmpfs/ramdisk вийшов із-під контролю: як зупинити поїдання RAM (не ламаючи програми)

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

Ви заходите на сервер з Ubuntu 24.04 бо «програма повільна». Пам’ять заповнена на 97%, відбувається активне використання swap, і ядро почало обирати, кого вбивати. Потім ви помічаєте незручну деталь: ніхто не «вилив» пам’ять — її з’їв tmpfs.

tmpfs — чудовий інструмент, поки ним не зловживають. Файли в ньому відчуваються як у RAM, адже фактично там і зберігаються. Коли tmpfs розростається, він робить те саме, що RAM, коли вона закінчується: псує вам день. Давайте зупинимо його, залишивши програми працездатними й робочий процес завантаження нудним.

Що ви бачите (і чому це збиває з пантелику)

tmpfs — це файловa система, яка зберігає дані в пам’яті (page cache / shmem) і може під час тиску використовувати swap. Ubuntu за замовчуванням монтує кілька tmpfs: /run, /dev/shm, іноді /tmp (залежно від конфігурації), а також різні директорії для runtime кожного сервісу.

Найбільш заплутує те, що інструменти показують «Size» tmpfs, яке здається величезним — часто половина RAM — і адміністратори сприймають це як виділену пам’ять. Це не так. Це обмеження, а не використання. Реальне використання — стовпець «Used», і саме воно є реальною пам’яттю (або swap), яка може довести систему до OOM, якщо зросте.

Коли tmpfs «зходить з розуму», звичні кореневі причини зазвичай нудні:

  • Програма записує великі тимчасові файли в /dev/shm або /run, бо це «швидко».
  • Черга або спул-директорія розміщені на tmpfs і навантаження різко зростає.
  • Контейнери монтують tmpfs для /tmp або використовують emptyDir з medium: Memory і логи/метрики «виходять з-під контролю».
  • Неправильно налаштований сервіс генерує runtime-артефакти і ніколи їх не очищає.
  • Хтось «оптимізував» і перетворив /tmp в tmpfs на сервері з малою RAM.

Виправлення — це не один чарівний sysctl. Це (1) виміряти, хто пише, (2) встановити обмеження, які відповідають реальності, і (3) переконатися, що навантаження падає на диск або коректно відмовляється замість того, щоб виводити хост з ладу.

Факти та коротка історія, що справді допомагає

  1. tmpfs в Linux існує десятиліттями і змінив давній підхід «рамдиск повсюди», бо може рости/зменшуватися і може використовувати swap під тиском пам’яті.
  2. Класичні ramdisk заздалегідь виділяли пам’ять: блочний пристрій у RAM з фіксованим розміром. tmpfs — орієнтований на файли і використовує пам’ять за потребою.
  3. На багатьох дистрибутивах значення за замовчуванням для tmpfs виглядає як ~50% RAM (або подібно), але це тільки максимальна допустима величина перед ENOSPC.
  4. Дані tmpfs живуть у тій самій пулі пам’яті, що й усе інше. Вони конкурують з купою додатків, кешем файлової системи та пам’яттю ядра.
  5. Записи в tmpfs змінюють «Shmem» і «Cached» по-різному залежно від відображення та обліку; важливо правильно читати /proc/meminfo.
  6. systemd зробив управління tmpfs більш централізованим: монтажі як /run та runtime-директорії під юнітами — це частина процесу завантаження, а не набір ad-hoc init-скриптів.
  7. /run замінив /var/run у сучасних системах: це tmpfs за задумом, щоб runtime-стан не зберігався після перезавантаження.
  8. /dev/shm — це POSIX shared memory, а не «гарне місце для тимчасових даних». Деяке ПО все одно використовує його як швидкий scratch і воно може з’їсти вашу RAM.
  9. Під тиском пам’яті сторінки tmpfs можуть потрапляти в swap, що «добре», поки не станеться навантаження: затримки й swap-шторм можуть бути гіршими, ніж просто запис на диск для тимчасових файлів.

Одна парафразована ідея від Werner Vogels (CTO Amazon) залишається актуальною в оперуванні: усе ламається весь час — проектуйте й експлуатуйте з такою припущенням (парафраз). tmpfs не виняток; плануйте, що він заповниться.

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

Це порядок дій, який найшвидше виведе вас до винуватця, без занурення в теорію.

1) Підтвердьте, що це tmpfs, а не «таємна пам’ять»

  • Перевірте df -hT, щоб побачити, який tmpfs том заповнюється.
  • Перевірте /proc/meminfo на Shmem, MemAvailable і використання swap.
  • Перегляньте останні записи OOM або сигнали пам’ятного тиску в journalctl.

2) Знайдіть директорію, яка росте

  • Використовуйте du -x на ураженому монтуванні (залишаєтесь в межах файлової системи).
  • Перевірте на видалені, але відкриті файли за допомогою lsof +L1.

3) Ідентифікуйте процес і вирішіть: обмежити, перемістити чи виправити

  • Якщо це сервіс: перегляньте unit, runtime-директорії і змінні оточення, що вказують на /run, /tmp, /dev/shm.
  • Якщо це контейнер: перевірте mounts і tmpfs volumes; застосуйте обмеження пам’яті і обмеження size для tmpfs.
  • Якщо це відомий кеш: налаштуйте зберігання, TTL і забезпечте очищення.

4) Застосуйте найменш ризикований захід спочатку

  • Встановіть розумний tmpfs size= ліміт для ризикового монтування.
  • Перемістіть великі scratch-шляхи на диск (наприклад, /var/tmp або виділену директорію на SSD).
  • Тільки після цього розгляньте агресивне налаштування ядра; більшість спалахів tmpfs — це просто зростання файлів.

Жарт №1: tmpfs не є «безкоштовною пам’яттю». Це та сама пам’ять, просто в файловій формі.

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

Нижче — перевірені в полі команди. Для кожної: що вона показує і яке рішення ви приймаєте.

Завдання 1: Перелік tmpfs і пошук надто великого

cr0x@server:~$ df -hT | awk 'NR==1 || $2=="tmpfs" || $2=="devtmpfs"'
Filesystem     Type     Size  Used Avail Use% Mounted on
tmpfs          tmpfs    3.2G  1.9G  1.3G  60% /run
/dev/nvme0n1p2 ext4      80G   22G   54G  29% /
tmpfs          tmpfs     16G  8.1G  7.9G  51% /dev/shm
tmpfs          tmpfs    5.0M   44K  5.0M   1% /run/lock
tmpfs          tmpfs    3.2G  4.0K  3.2G   1% /run/user/1000

Значення: /dev/shm споживає 8.1G. Це реальний тиск, якщо хост має 16G RAM.

Рішення: Сфокусуйтеся спочатку на цьому монтуванні. Не чіпайте /run/lock; воно маленьке і не стосується проблеми.

Завдання 2: Підтвердьте реальний тиск пам’яті (не тільки великий tmpfs «Size»)

cr0x@server:~$ grep -E 'MemTotal|MemAvailable|Shmem|SwapTotal|SwapFree|Cached' /proc/meminfo
MemTotal:       16329640 kB
MemAvailable:    1128400 kB
Cached:          2413720 kB
Shmem:           8551120 kB
SwapTotal:       4194300 kB
SwapFree:         112340 kB

Значення: Shmem ~8.1G, що збігається з /dev/shm. MemAvailable низький, а swap майже вичерпано.

Рішення: Розглядайте це як інцидент: зупиніть зростання і/або евакуюйте навантаження. Не варто «просто додавати swap» як єдине рішення.

Завдання 3: Перевірте OOM або логи про тиск пам’яті

cr0x@server:~$ journalctl -k -b | egrep -i 'oom|out of memory|memory pressure' | tail -n 8
kernel: Memory cgroup out of memory: Killed process 27144 (python3) total-vm:9652144kB, anon-rss:5132140kB, file-rss:11320kB, shmem-rss:2048kB
kernel: oom_reaper: reaped process 27144 (python3), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
kernel: Out of memory: Killed process 30911 (node) total-vm:7621400kB, anon-rss:2874100kB, file-rss:9020kB, shmem-rss:1780kB

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

Рішення: Зменшіть зростання tmpfs негайно (обмежити, очистити, перезапустити винуватців), потім виправте корінну причину.

Завдання 4: Визначте, що саме знаходиться в tmpfs монтуванні

cr0x@server:~$ sudo du -xh --max-depth=1 /dev/shm | sort -h
0	/dev/shm/snap.lxd
12K	/dev/shm/systemd-private-2d3c...-chrony.service-...
20M	/dev/shm/app-cache
8.1G	/dev/shm/feature-store
8.1G	/dev/shm

Значення: Одна директорія домінує: /dev/shm/feature-store.

Рішення: Знайдіть процес, який її використовує; це ваш важіль. Очищення випадкових systemd-private директорій — не той підхід.

Завдання 5: Зіставте директорію з процесом (швидко і часто достатньо)

cr0x@server:~$ sudo lsof +D /dev/shm/feature-store 2>/dev/null | head -n 10
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
python3  18422  svc   12w  REG   0,37  536870912  131112 /dev/shm/feature-store/chunk-0001.bin
python3  18422  svc   13w  REG   0,37  536870912  131113 /dev/shm/feature-store/chunk-0002.bin
python3  18422  svc   14w  REG   0,37  536870912  131114 /dev/shm/feature-store/chunk-0003.bin

Значення: PID 18422 записує файли розміром сотні мегабайт у shared memory.

Рішення: Це поведінка програми, а не «Ubuntu випадково використовує RAM». Виправте конфіг програми або змініть шлях для scratch.

Завдання 6: Виявлення видалених, але відкритих файлів tmpfs (пастка «df показує повний, du не сходиться»)

cr0x@server:~$ sudo lsof +L1 | head -n 10
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NLINK    NODE NAME
node    30911  svc   28w  REG   0,37 1073741824     0  140221 /dev/shm/feature-store/tmp.log (deleted)

Значення: 1G файл був видалений з дерева директорій, але все ще утримується процесом. du його не побачить; df — побачить.

Рішення: Перезапустіть або надішліть сигнал процесу, щоб закрив він/відкрив логи; інакше простір не звільниться.

Завдання 7: Перегляньте опції монтування, щоб зрозуміти поточні ліміти

cr0x@server:~$ findmnt -no TARGET,FSTYPE,OPTIONS /dev/shm
/dev/shm tmpfs rw,nosuid,nodev,size=16329640k,inode64

Значення: Ліміт size фактично рівний розміру RAM. Це дозволяє програмі спробувати зайняти весь хост.

Рішення: Встановіть нижчий ліміт, але тільки після перевірки, що потребують спільної пам’яті і скільки.

Завдання 8: Перевірте, як systemd бачить tmpfs-монти і хто ними володіє

cr0x@server:~$ systemctl status dev-shm.mount --no-pager
● dev-shm.mount - POSIX Shared Memory
     Loaded: loaded (/usr/lib/systemd/system/dev-shm.mount; static)
     Active: active (mounted) since Mon 2025-12-29 08:31:12 UTC; 3h 12min ago
      Where: /dev/shm
       What: tmpfs

Значення: Монтаж управляється systemd. Це важливо, бо налаштовувати його слід через drop-in, а не правити випадкові файли, що можуть перезаписатися.

Рішення: Використовуйте systemd override для опцій монтування, якщо хочете зробити зміни стійкими і безпечними під час оновлень.

Завдання 9: Перевірте, чи /tmp — tmpfs (залежить від налаштувань)

cr0x@server:~$ findmnt /tmp
TARGET SOURCE FSTYPE OPTIONS
/tmp   /dev/nvme0n1p2[/tmp] ext4   rw,relatime

Значення: Тут /tmp на диску. Якби це був tmpfs, ви б бачили tmpfs і опцію size= в параметрах монтування.

Рішення: Якщо інцидент пов’язаний з /tmp і воно диск-бекд, tmpfs не винуватець.

Завдання 10: Виміряйте на процеси використання shared memory (коли /dev/shm — гаряча точка)

cr0x@server:~$ ps -eo pid,comm,rss,shr,%mem --sort=-shr | head -n 8
  PID COMMAND        RSS    SHR %MEM
18422 python3     6210040 2101240 38.0
19211 python3     1180240  922120  7.2
 1123 gnome-shell  412320  210800  2.5

Значення: SHR велике для python-процесу. Це не ідеальний показник, але дає корисний напрямок.

Рішення: Фокусуйтеся на цьому сервісі. Якщо проблема на всю флоту, маєте швидкий запит для автоматизації «топ-токерів».

Завдання 11: Перевірте ліміти memory cgroup (поширено в контейнерах і systemd-сервісах)

cr0x@server:~$ systemctl show myapp.service -p MemoryMax -p MemoryHigh -p MemoryCurrent
MemoryMax=infinity
MemoryHigh=infinity
MemoryCurrent=9312415744

Значення: Сервіс не має обмеження по пам’яті. Якщо він активно використовує tmpfs, він може з’їсти весь хост.

Рішення: Розгляньте встановлення MemoryHigh і MemoryMax, щоб сервіс обмежувався/вбивався до того, як впаде весь вузол.

Завдання 12: Перевірте, чи swap ховає проблему (до моменту вибуху)

cr0x@server:~$ swapon --show
NAME      TYPE SIZE USED PRIO
/dev/sda2 partition 4G  3.9G -2

Значення: Swap майже повністю використаний; продуктивність буде жахливою і наступне виділення може спричинити OOM.

Рішення: Зупиніть зростання tmpfs. Додавання swap дає час, але не є відповідальною довгостроковою стратегією.

Завдання 13: Визначте tmpfs-важкі директорії під /run (класичний винуватець: runtime-файли)

cr0x@server:~$ sudo du -xh --max-depth=1 /run | sort -h | tail -n 8
4.0M	/run/systemd
12M	/run/udev
64M	/run/user
1.2G	/run/myapp
1.9G	/run

Значення: /run/myapp величезне. Щось трактує runtime-стан як сховище.

Рішення: Виправте додаток: runtime-директорії повинні мати межі, ротацію або бути переміщені на диск, якщо вони великі.

Завдання 14: Перевірте, чи tmpfs обмежений виснаженням інодів (таке трапляється)

cr0x@server:~$ df -ih /run
Filesystem    Inodes IUsed IFree IUse% Mounted on
tmpfs           800K  792K  8.0K   99% /run

Значення: У tmpfs закінчуються іноди. Ви можете виявитися «повними», навіть коли байти доступні.

Рішення: Знайдіть і видаліть масову кількість файлів. Якщо це очікувана поведінка, збільшіть ліміт інодів через параметри монтування або перемістіть робоче навантаження з tmpfs.

Завдання 15: Перемонтуйте tmpfs з тимчасовим лімітом (мітинг для інциденту)

cr0x@server:~$ sudo mount -o remount,size=4G /dev/shm
cr0x@server:~$ findmnt -no TARGET,OPTIONS /dev/shm
/dev/shm rw,nosuid,nodev,size=4G,inode64

Значення: Ви щойно встановили жорстку межу 4G. Записи понад неї отримають ENOSPC.

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

Завдання 16: Зробіть ліміт постійним через systemd drop-in для mount

cr0x@server:~$ sudo systemctl edit dev-shm.mount
# editor opens; add:
# [Mount]
# Options=rw,nosuid,nodev,size=4G,mode=1777
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart dev-shm.mount
cr0x@server:~$ findmnt -no TARGET,OPTIONS /dev/shm
/dev/shm rw,nosuid,nodev,size=4G,mode=1777,inode64

Значення: Ліміт переживе перезавантаження. mode=1777 зберігає типовий режим дозволів для shared memory.

Рішення: Пропустіть це через change control, якщо у вас є додатки, що покладаються на великі сегменти shared memory (бази даних, браузери, ML-пайплайни).

Як tmpfs насправді використовує пам’ять (і чому «Size» бреше)

tmpfs зберігає вміст файлів у сторінках пам’яті, якими керує ядро. Звучить як «RAM виділяється», але виділення відбувається під час запису. Параметр size=, який ви бачите в df, — це максимум, застосований як квота. Він не резервує пам’ять заздалегідь.

Куди потрапляє пам’ять? Зазвичай в суміші:

  • Shmem у /proc/meminfo (shared memory і сторінки tmpfs).
  • Cached може також бути помітним, бо сторінки tmpfs схожі на page cache, але обліковуються інакше, ніж кеш файлів з блочних пристроїв.
  • Swap, якщо система під тиском і ті сторінки підлягають свапуванню.

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

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

Жарт №2: Якщо ваш план інцидент-репсосу — «хай tmpfs свапиться», ваш диск щойно став у чергові.

tmpfs проти «ramdisk» в Ubuntu

Люди кажуть «ramdisk» абстрактно. Є дві різні речі:

  • tmpfs: динамічний, орієнтований на файли, може свапуватись, використовує пам’ять за потребою до ліміту.
  • brd/ramdisk: блочний пристрій у RAM з фіксованим розміром (тепер рідше в повсякденних операціях Ubuntu).

Більшість «проблем з ramdisk» на Ubuntu 24.04 — це проблеми з tmpfs: /dev/shm, /run або tmpfs, змонтований через systemd чи рантайм контейнера.

Коли зростання tmpfs — це «нормально»

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

  • Високочастотний IPC з використанням POSIX SHM.
  • Системи збірки, що створюють великі тимчасові дерева й виграють від швидкості RAM (на машинах з великою пам’яттю).
  • Деякі ML feature store та кеші для сервінгу моделей (якщо правильно розмірені й обмежені).

«Нормально» має два властиві ознаки: воно обмежене і відновлюване. Необмежений tmpfs — це просто витік пам’яті з додатковими кроками.

Встановлення безпечних обмежень без руйнування програм

Ваша мета — не «зробити tmpfs маленьким». Ваша мета — «локалізувати відмову». Якщо tmpfs не обмежений (або фактично не обмежений), один погано поводжений компонент може позбавити хост усієї пам’яті. Коли tmpfs розумно обмежений, компонент отримає ENOSPC і відмовиться у спосіб, який ваша моніторинг і логіка повторних спроб можуть обробити.

Виберіть правильний об’єкт для обмеження

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

  • /dev/shm: часто використовується як швидкий scratch. Воно також служить для легітимного IPC, тому дійте обережно.
  • /run: не повинно містити величезних даних. Якщо містить — щось не так. Обмеження може допомогти, але важливіше виправити записувача.
  • Користувацькі tmpfs, створені для кешів додатків: такі мають завжди мати явний розмір і ліміти інодів.

Орієнтовні правила розмірів (суб’єктивно)

  • /run: зазвичай кілька сотень МБ вистачає на типовий сервер. Якщо потрібно більше 1–2G, дизайн runtime-директорій слід поставити під сумнів.
  • /dev/shm: розмір базуйте на найбільшому легітимному користувачеві shared memory. Якщо такого немає — обмежте. Типові ліміти: 1G–4G на маленьких/середніх хостах.
  • App-specific tmpfs: зробіть розмір таким, щоб можна було втратити ці дані без свапінгу хоста в непрацездатний стан. Потім реалізуйте очищення/TTL в додатку.

Віддавайте перевагу «перемістити на диск» замість «зробити tmpfs нескінченним»

Якщо додаток хоче записати 20GB «тимчасових» даних, це не випадок для tmpfs. Помістіть їх на диск. Використовуйте:

  • /var/tmp для тимчасових даних, що можуть пережити перезавантаження і бути великими.
  • Виділену директорію на швидкому сховищі (NVMe) з квотами, якщо потрібно.
  • Per-service директорії з власністю та режимом, керовані systemd (StateDirectory=, CacheDirectory=, RuntimeDirectory=).

Режим відмови: ENOSPC — це функція, а не баг

Коли ви обмежуєте tmpfs, додаток врешті отримає «No space left on device». Це добре: помилка явна. Поганий варіант — коли ядро вбиває вашу базу даних, бо побічний процес заповнив /dev/shm.

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

Налаштування systemd, що мають значення в Ubuntu 24.04

Ubuntu 24.04 — це systemd-перший дистрибутив. Це добре: конфігурація централізована і монтажі — це юніти з чіткою власністю. Це також погано, якщо ви продовжуєте правити /etc/fstab з старою звичкою і дивуєтеся, чому зміна не застосовується для манектованого systemd монтування.

Контроль /dev/shm через dev-shm.mount

/dev/shm зазвичай управляється через dev-shm.mount. Використовуйте drop-in override, щоб задати опції як size=, mode= і, за потреби, nr_inodes=, якщо виснаження інодів — реальна проблема.

Якщо обмежуєте його, робіть це свідомо:

  • Знайте, які додатки використовують POSIX SHM (розширення PostgreSQL, інструменти на Chromium, деякі JVM-шляхи IPC, ML-фреймворки).
  • Тестуйте під навантаженням. Помилки у shared memory можуть бути підступними.
  • Поширюйте зміни поступово по флоту. Caps на tmpfs — це те, що «працює в стенді» і потім зустрічається з продуктивним трафіком і несподіваним створенням файлів.

Контроль /run: зазвичай виправляють записувача, а не mount

/run повне — майже завжди наслідок неправильної поведінки: лог файли, спули або кеші в runtime-директорії. Ви можете обмежити /run, але ризикуєте зламати стадію завантаження, якщо критичні сервіси не зможуть створити сокети або PID-файли.

Краще: знайдіть велику директорію (Завдання 13), потім виправте сервіс. Поширені виправлення:

  • Перемістіть великі runtime-артефакти в /var/lib/myapp (стан) або /var/cache/myapp (кеш).
  • Використовуйте директиви systemd: StateDirectory=, CacheDirectory=, RuntimeDirectory=, і встановіть LogsDirectory=, якщо потрібно.
  • Перевіряйте очищення при рестарті: застарілі runtime-файли накопичуються, якщо додаток лише додає.

Захисти на рівні сервісу: MemoryMax, MemoryHigh та інші

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

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

  • MemoryHigh — поріг тиску; він гальмує алокації і дозволяє сервісу відчути біль першим.
  • MemoryMax — жорстка межа; її перевищення може спричинити вбивство процесу.

Це не «вирішує tmpfs», але обмежує радіус ураження. У продакшні саме радіус ураження — більша частина гри.

Контейнери: підводні камені tmpfs у Docker і Kubernetes

tmpfs стає ще складнішим у контейнерах, бо додається щонайменше два рівні непорозумінь:

  • Файлова система контейнера може бути overlayfs, і tmpfs-монти можуть інжектуватись на рівні контейнера.
  • Облік пам’яті ведеться cgroup-ами; хост і контейнер можуть по-різному оцінювати «вільну пам’ять».
  • Kubernetes emptyDir з medium: Memory — це фактично tmpfs. Це не «швидкий тимчасовий диск». Це RAM.

Docker: tmpfs-монти потрібно явно розмірювати

Якщо ви використовуєте Docker tmpfs-монти (наприклад, --tmpfs /tmp), вказуйте size. Інакше ви дозволяєте зростання, яке обмежується в основному лімітами хоста і пам’яті контейнера.

Також пам’ятайте: ліміти пам’яті в контейнері можуть перетворити запис у tmpfs на миттєвий OOM всередині контейнера. Це краще, ніж вбивати хост, але все одно інцидент для сервісу.

Kubernetes: emptyDir memory — це не безкоштовно

Kubernetes спрощує створення memory-backed volume. Легко забути вказати:

  • Requests/limits для пам’яті контейнера.
  • Розміри лімітів для volume (залежно від політик кластера і можливостей).
  • Пороги евакуації, щоб вузли не застрягали.

Патерн, якому я довіряю: якщо ви використовуєте memory-backed томи, ставте їх як кеші. Обмежуйте, задавайте TTL і приймайте, що відбувається евакуація.

Swap, zram і ілюзія «tmpfs з’їв RAM»

Ubuntu 24.04 може бути розгорнутий з swap або без нього, і деякі середовища використовують zram. Це змінює відчуття інцидентів tmpfs:

  • Без swap: зростання tmpfs швидше потрапляє в OOM, але принаймні ви не годину перебуваєте у swap-death перш ніж це станеться.
  • Swap на диску: сторінки tmpfs можуть бути свапнуті. Це тримає вузол «живим», поки він стає непридатним для роботи.
  • zram: стиснений swap в RAM може поглинати деякий тиск. Він також може довше ховати runaway tmpfs, а потім виходити з ладу більш заплутано.

Якщо tmpfs використовується для «гарячих» робочих файлів, їх свапування само по собі контрпродуктивне. Таке навантаження має бути на диску (швидкому, але все ж диску) або явно обмежене, щоб не спричинити свапінг.

Операційне правило: якщо використання swap зростає в унісон зі зростанням tmpfs, ви не «зберігаєте пам’ять». Ви сплачуєте відсотки за неї.

Три короткі історії з реального життя

Міні-історія 1: Інцидент через неправильне припущення

У флоті серверів Ubuntu була служба обробки даних. Один інженер прочитав, що tmpfs «використовує до половини RAM», побачив, що /dev/shm має великий Size в df, і вирішив, що це заздалегідь виділена пам’ять. Вони відкрили тікет: «Ubuntu витрачає 32GB на /dev/shm».

«Виправлення» було швидким і впевненим: пофарбувати /dev/shm у щось крихітне по всьому флоту. Це розгорнули в робочий час, бо зміна виглядала безпечною. Наступна пакетна задача запустилась, і компонент, що використовував POSIX shared memory, почав спрацьовувати з переривчастими помилками. Не падіння, а корупція стану при невдалих алокаціях shared memory посеред роботи.

Інцидент був не у використанні shared memory сам по собі. Проблема була в припущенні, що показаний Size — це марна пам’ять. Насправді сервіс використовував shared memory відповідально в нормі; кап його зламав, бо він почав коректно відмовлятися під навантаженням.

Відновились шляхом відкату капа, потім виконали нудну роботу: виміряли реальний Shmem під піком, визначили реальну межу і встановили кап трохи вище. Додали обробку помилок при невдачі алокації, щоб уникнути мовчазної корупції.

Висновок постмортему простий: df показує ліміти, а не алокації. Інциденти з пам’яттю рідко вирішуються одним «глобальним оптимізатором».

Міні-історія 2: Оптимізація, що зіграла злий жарт

Інша організація мала API з чутливими до затримок вимогами. Хтось вирішив прискорити обробку, перемістивши JSON-кеш і тимчасову директорію рендеру в tmpfs. У тестах це працювало: p99 зменшився, диски менше шуміли, графіки були красиві.

Потім маркетингова кампанія потроїла трафік, і кеш перетворився з «корисного» на «необмежений». Кеш не мав евікції; він жив на надії. Під тиском tmpfs заповнився. Пам’ять зросла, swap заповнився, і вузол перетворився на повільне невідповідне місце, де health check-і таймаути робили оркестратор заміни інстансів.

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

Реальне виправлення було не гламурним: перемістити кеш назад на диск (локальний швидкий SSD), реалізувати LRU-евікцію і встановити явні ліміти розміру. Залишили невеликий tmpfs для дійсно гарячих даних — від кілобайт до кількох мегабайт, не гігабайтів. Продуктивність залишилась хорошою, а режим відмови — керованим.

Міні-історія 3: Сумно-правильна практика, що врятувала день

Сервіс поруч з платіжною системою працював на Ubuntu з жорсткими вимогами на надійність. Команда мала чекліст для кожного класу вузлів: перевірити опції tmpfs, капнути /dev/shm відповідно до робочого навантаження і встановити ліміти пам’яті на сервіси через systemd. Це та сама рутина, про яку не пишуть у твітті.

Одного дня нова версія воркера почала писати debug-артефакти в /run. Фіч-флаг був неправильно застосований; замість вибірки 1% трафіку стало фактично все. Файли були маленькі, але їх кількість була величезною, і споживання інодів різко зросло.

На менш дисциплінованому середовищі /run заповнилося б і заблокувало базові системні функції. У них же /run моніторився за інодами і сервіс мав memory ceiling. Сервіс почав відмовляти, алерти вказали на «inode pressure /run», і хост залишився достатньо здоровим, щоб оператори змогли зайти без боротьби з напівмертвою системою.

Ремедіація була швидкою: відключили фіч-флаг, задеплоїли патч і очистили runtime-директорію. Ніякого каскадного авариї. Сумна практика не запобігла помилці, але обмежила її.

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

1) «df показує /dev/shm великим, отже Ubuntu витрачає RAM»

Симптом: df -h показує tmpfs Size = половина (або більше) RAM.

Корінна причина: Плутанина між максимальним розміром tmpfs і фактичним використанням.

Виправлення: Перевірте Used у df і Shmem у /proc/meminfo. Дійте лише якщо використання зростає.

2) «Хост OOM-ить, але du показує, що tmpfs малий»

Симптом: df повідомляє про повний tmpfs; du не сходиться.

Корінна причина: Видалені, але відкриті файли на tmpfs.

Виправлення: Використовуйте lsof +L1, перезапустіть винуватий процес і переконайтеся, що простір звільнений.

3) «Ми обмежили /dev/shm і тепер випадкові програми падають»

Симптом: Переривчасті помилки, помилки IPC, дивні падіння після зміни.

Корінна причина: Легітимний споживач POSIX shared memory перевищив новий кап.

Виправлення: Виміряйте пік SHM, підніміть кап відповідно або переналаштуйте додаток на дисково-бекедний тимчасовий простір там, де це прийнятно.

4) «/run заповнилося і машина стала вести себе наче проклята»

Симптом: Сервіси не стартують, PID-файли зникли, сокети не створюються, логіни дивні.

Корінна причина: Хтось записав великі дані або забагато файлів у /run (байти або іноди).

Виправлення: Визначте великі піддиректорії (du -x, df -ih), перемістіть писача в /var/lib або /var/cache, додайте очищення і ротацію.

5) «Ми зробили /tmp tmpfs для швидкості і тепер збірки падають»

Симптом: Компілятори й пакувальні інструменти падають з ENOSPC або OOM під час великих збірок.

Корінна причина: Тимчасові файли збірки великі; tmpfs — поганий вибір для цієї RAM-ємності.

Виправлення: Тримайте /tmp на диску або надайте виділений швидкий диск і вкажіть інструментам шлях до нього.

6) «Kubernetes pods OOMKilled після того, як ми перемкнули emptyDir на пам’ять»

Симптом: Під час навантаження pods перезапускаються; пам’ять вузла виглядає щільною.

Корінна причина: emptyDir з medium: Memory споживає пам’ять, що враховується в лімітах pod/контейнера.

Виправлення: Встановіть requests/limits пам’яті, обмежте tmpfs або поверніться до дисково-бекедного emptyDir і оптимізуйте навантаження.

7) «Ми перемонтували tmpfs меншим і тепер записи падають»

Симптом: Миттєві помилки ENOSPC після пом’якшення.

Корінна причина: Робочий набір вже перевищував ваш новий ліміт; перемонтування не зменшує використані сторінки само по собі.

Виправлення: Спочатку зменшіть використання (видаліть файли, перезапустіть процеси, що тримають видалені файли), потім перемонтуйте з меншим лімітом.

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

Утримання інциденту (15–30 хвилин)

  1. Знайдіть монтування: запустіть df -hT і знайдіть tmpfs з високим Used%.
  2. Підтвердіть тиск пам’яті: перевірте /proc/meminfo на MemAvailable, Shmem і swap.
  3. Знайдіть велику директорію: du -xh --max-depth=1 на тому монтуванні.
  4. Знайдіть писача: lsof +D або таргетований lsof / fuser.
  5. Перевірте видалені, але відкриті файли: lsof +L1.
  6. Зупиніть зростання: призупиніть задачу, зменшіть масштаб деплойменту або перезапустіть сервіс, що утримує простір.
  7. Якщо потрібно — страховка: перемонтуйте tmpfs з тимчасовим size= лімітом, лише якщо ви витримуєте ENOSPC.
  8. Стабілізація: переконайтесь, що swap не зафіксований; якщо так — розгляньте перезапуск найгірших винуватців, щоб швидко звільнити пам’ять.

Постійне виправлення (того ж дня)

  1. Вирішіть, які дані належать в tmpfs: тільки IPC і маленькі гарячі кеші.
  2. Перемістіть великий scratch на диск: змініть конфіг додатків, щоб використовували /var/tmp або виділену директорію.
  3. Встановіть явні tmpfs-ліміти: використовуйте systemd mount overrides для /dev/shm або ваших кастомних монтувань.
  4. Додайте очищення: TTL, евікція за розміром або ротація. «Почистимо пізніше» — так tmpfs стає зброєю.
  5. Додайте моніторинг: алерт на Used% tmpfs і використання інодів (df -ih), а також MemAvailable і swap.
  6. Додайте контролі радіусу ураження: systemd MemoryHigh/MemoryMax для найгірших винуватців.

Зміцнення флоту (на наступний спринт)

  1. Уніфікуйте політики монтувань: для кожного класу вузлів визначте caps для tmpfs і задокументуйте, які додатки потребують більшої shared memory.
  2. Політика контейнерів: вимагайте явних memory limits і забороніть необмежені memory-backed томи для даних, що не є кешем.
  3. Тестуйте режими відмов: штучно викликайте ENOSPC на tmpfs у стенді і перевіряйте, що додатки деградують передбачувано (не корумпуються і не зависають).
  4. Аудит /run: runtime-директорії не повинні містити великих наборів даних; контролюйте це в code review і при пакуванні.

Питання й відповіді

1) Чи tmpfs фактично використовує RAM чи тільки «віртуальну» пам’ять?

Воно використовує реальні сторінки пам’яті в міру запису. Ці сторінки можуть бути свапнуті під тиском, але вони все одно враховуються в системній пам’яті.

2) Чому tmpfs показує Size рівний половині мого RAM?

Це максимальний розмір за замовчуванням. Це не резервування. Стовпець Used — ось що має значення, плюс Shmem у /proc/meminfo.

3) Чи слід монтувати /tmp як tmpfs на Ubuntu 24.04?

Лише якщо ви розміряли його і протестували ваші робочі навантаження. На універсальних серверах безпечніше тримати /tmp на диску. Якщо ж використовуєте tmpfs, встановіть cap і плануйте ENOSPC.

4) Чи можна просто збільшити swap, щоб вирішити спалахи tmpfs?

Збільшення swap дає час, а не правильність. Це також може перетворити чисту відмову на повільний brownout. Виправляйте писача і встановлюйте ліміти.

5) Де найбезпечніше розміщувати великі тимчасові файли?

На диску: /var/tmp або виділена директорія на швидкому сховищі. Якщо потрібна продуктивність, використовуйте NVMe і тримайте tmpfs для маленьких гарячих даних.

6) Чому «df каже повний», а «du каже маленький» на tmpfs?

Зазвичай це видалені, але відкриті файли. Записи у директорії зникли, але процес все ще тримає дескриптор. Використовуйте lsof +L1 і перезапустіть процес.

7) Якщо я обмежу /dev/shm, чи зламаю я щось?

Можливо. Деяке ПО легітимно використовує shared memory. Виміряйте реальне використання під піком, встановіть кап вище цього показника і протестуйте. Випадкове «512M здається нормальним» — шлях до нічної роботи.

8) Як постійно обмежити tmpfs на Ubuntu 24.04?

Для systemd-управляємих монтувань як /dev/shm створіть drop-in override через systemctl edit dev-shm.mount і задайте Options=...size=....

9) /run має бути tmpfs?

Так. Це для runtime-стану, що не має зберігатися після перезавантаження. Якщо воно велике — хтось зловживає.

10) У tmpfs теж є обмеження інодів?

Так. Ви можете вичерпати іноди раніше, ніж байти. Перевіряйте за допомогою df -ih. Якщо інодів замало — знайдіть спам файлів і виправте генератор.

Наступні кроки, які можна зробити сьогодні

tmpfs не є багом в Ubuntu 24.04. Це гострий інструмент. Відмови відбуваються, коли ви ставитеся до нього як до нескінченного сховища і забуваєте, що він ділиться тією ж RAM, якої потребують ваші програми.

Зробіть це в порядку:

  1. Визначте, який tmpfs росте (df -hT), потім перевірте, що це реальний тиск (/proc/meminfo).
  2. Знайдіть директорію й процес, відповідальні (du, lsof, lsof +L1).
  3. Перемістіть великі тимчасові дані на диск і капніть tmpfs там, де ймовірні зловживання (особливо /dev/shm) через systemd overrides.
  4. Додайте моніторинг за байтами і інодами tmpfs, а також за пам’яттю/swap, щоб це було інцидентом, який видно заздалегідь — а не загадковим відключенням.
  5. Обмежте радіус ураження per-service memory limits там, де це доцільно.

Якщо нічого іншого не зробите: свідомо капніть ризикові tmpfs-монти і навчіть додатки коректно реагувати на ENOSPC. Ваше ядро скаже вам «дякую», коли не доведеться вибирати жертву о 3 ранку.

← Попередня
Debian 13 «Segfault» аварії після оновлення: знайдіть точну невідповідність бібліотеки (Випадок №55)
Наступна →
MySQL проти PostgreSQL при високій конкуренції: хто першим впирається в стіну і чому

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