О 02:17 ваш графік робить класичну річ з фільмів жахів: сплески затримки, CPU «steal» виглядає добре, але додаток починає таймаутитися. Диски не заповнені. Мережа нудна. Проте система «відчувається», ніби йде крізь мокрий цемент. Ви заходите по SSH і бачите активність swap та купу dirty-сторінок. Технічно нічого не впало, але все ніяк не працює.
Саме тут дрібні налаштування ядра перестають бути дрібницями й стають кермом. Ubuntu 24.04 постачається із розумними значеннями за замовчуванням для загального використання. Продакшен рідко буває загального призначення. Якщо ваше навантаження вимогливе до пам’яті, інтенсивне по I/O або працює на хмарних томах зі «характером», vm.swappiness і ручки vm.dirty* — одні з небагатьох «малих» налаштувань, які справді можуть змінити результат.
1. Ментальна модель: що ці ручки насправді контролюють
Свопінг — це не просто «закінчилась пам’ять»; це «я вибрав жертву»
Управління пам’яттю в Linux — це переговори між анонімною пам’яттю (heap, стеки, тимчасові алокації) і файловою пам’яттю (page cache, відображені файли). Коли ви бачите активність swap, це не обов’язково означає, що «закінчилась RAM». Це означає, що ядро вирішило, що деякі анонімні сторінки менш цінні, ніж збереження інших сторінок у пам’яті.
vm.swappiness — це не простий вимикач. Це сигнал переваги: наскільки агресивно ядро має звільняти анонімну пам’ять шляхом свопінгу в порівнянні з звільненням файлового кешу (скидання чистих кеш-сторінок). Вищі значення стимулюють більше свопінгу раніше; нижчі значення схиляють до збереження анонімної пам’яті в RAM і жертовності кешу в першу чергу.
Ця упередженість важлива, бо свопінг має жорсткий режим відмови: затримки стають нелінійними. Система може відчуватися ідеальною, а потім раптом стати незручнопридатною, коли переходить у стійку поведінку swap-in/swap-out. Це — «шторм свопінгу»: не одноразовий великий своп, а повторюваний циклічний процес, коли робочий набір більший за ефективну RAM, і ядро постійно виганяє те, що процес невдовзі знову потребуватиме.
Dirty-сторінки — це «IO-борг», який рано чи пізно треба повернути
Коли додаток пише у файл, ядро часто позначає сторінки як dirty у RAM і швидко повертає керування. Це функція продуктивності: пакетний запис дешевший за дрібні синхронні операції IO. Борг у тому, що ці dirty-сторінки треба буде записати на сховище пізніше. Налаштування vm.dirty* визначають, наскільки великим може бути цей борг, як швидко його треба сплатити і наскільки агресивно ядро буде гальмувати записувачів, коли бухгалтерська книга виглядає лякаюче.
Дві пропорції зазвичай домінують у розмові:
vm.dirty_background_ratio: коли dirty-пам’ять перевищує цей відсоток, починається фоновий writeback.vm.dirty_ratio: коли dirty-пам’ять перевищує цей відсоток, процеси, що пишуть, гальмують (вони фактично допомагають скидати дані).
Є також байтові версії (vm.dirty_background_bytes, vm.dirty_bytes), які переважають над відсотками, якщо задані. У продакшені байтові налаштування часто безпечніші, бо відсотки масштабуються з RAM, а сучасні об’єми пам’яті роблять «відсоток від RAM» небезпечно великим числом dirty-сторінок. При 256 ГБ RAM 20% dirty — це багато IO-боргу.
Ці ручки не «прискорюють диски». Вони формують біль
Жоден sysctl не зробить повільне сховище швидким. Те, що роблять ці налаштування — вирішують, коли ви платите вартість тиску пам’яті й writeback, і чи платите ви її у керованому фоні або в катастрофічному передньому затриманні. Ваша мета — нудна передбачуваність:
- Свопувати лише коли це справді менш шкідливо, ніж скидати кеш.
- Повернути dirty-дані поступово, щоб не стикатися з урвищем.
- Гальмувати записувачів перш ніж система стане в заручниках ситуації.
Одна цитата, яка добре живе в операціях: Надія — не стратегія.
(парафразована ідея, приписують генерал-майору Гордон Р. Саллівану). Значення ядра за замовчуванням — це надія. Продакшен — це доказ.
2. Цікаві факти та трохи історії (бо значення за замовчуванням мають свою передісторію)
- Факт 1: Page cache — це не «марнотратна пам’ять». Linux охоче заповнить RAM кешем і миттєво звільнить його, коли програми потребують пам’ять. Плутанина походить від старих інструментів і застарілих моделей мислення.
- Факт 2: Ранні ядра Linux мали дуже інші евристики свопінгу; поведінка свопінгу багаторазово переглядалася в міру зміни сховищ і обсягів RAM.
- Факт 3: Відсотки типу
dirty_ratioмали більше сенсу, коли «багато RAM» означало кілька гігабайт. Сьогодні обмеження на основі відсотків можуть транслюватися у десятки гігабайт dirty-даних — величезні IO-сплески пізніше. - Факт 4: Підсистема writeback спроектована для згладжування IO, але вона може згладити лише те, що сховище здатне витримати. Довбо-бурстові додатки на бурстових хмарних дисках отримують бурстові наслідки.
- Факт 5: Ядро має кілька механізмів звільнення пам’яті: скидати чистий page cache, записувати dirty-cache, компактувати пам’ять і свопувати. Вони взаємодіють; налаштування однієї ручки може змінити шлях, який обирається.
- Факт 6: Swap на SSD став практичним, а потім поширеним, що змінило співвідношення витрат. Swap все ще повільніший за RAM, але це вже не «миттєва смерть» у кожному середовищі.
- Факт 7: Cgroups та контейнери змінили поняття «тиск пам’яті». Свопінг може відбуватися через обмеження cgroup навіть коли хост має багато RAM.
- Факт 8: Налаштування «dirty» також впливають на те, як довго дані можуть сидіти в RAM перед записом — важливо для очікувань щодо стійкості даних і для того, наскільки неприємним може бути відновлення після збою.
3. vm.swappiness: коли свопінг розумний, а коли він незручний
Що насправді впливає swappiness
vm.swappiness може мати значення від 0 до 200 у сучасних ядрах (більшість дистрибутивів використовують конвенції 0–100, але ядро дозволяє 200). За замовчуванням в Ubuntu зазвичай близько 60. Це — середній варіант: певний свопінг допустимий, якщо він зберігає кеш і загальну пропускну здатність.
Ось ключове: swappiness не каже «ніколи свопувати». Він каже «наскільки сильно ядро має уникати свопінгу порівняно зі звільненням кешу». Якщо ви установите його занадто низько, ядро може агресивно скидати кеш, і ви отримаєте більше читань з диска (cache misses) та гіршу продуктивність для сервісів, які залежать від I/O. Якщо ви встановите його занадто високо, можна вигнати в своп пам’ять, яка знадобиться знову незабаром, і затримки стануть спайковими.
Коли нижчий swappiness зазвичай правильний
- Сервіси, чутливі до затримки (API-сервери, інтерактивні системи), де рідкі довгі паузи гірші за трохи більший стабільний I/O.
- Хости з великою кількістю RAM відносно робочого набору, коли свопінг свідчить про невдалу евристику більше, ніж про необхідність.
- Системи, де swap повільний (swap через мережу, перепомпований гіпервізор або дешеві хмарні диски під тиском кредитів).
Коли вищий swappiness може бути виправданим
- Змішані навантаження, де збереження файлового кешу гарячим важливе (машини для збірки, сервери артефактів, читальні бази даних, що покладаються на кеш ОС).
- Переразподіл пам’яті з відомими холодними сторінками (деякі JVM, деякі кеші, деякі пакетні завдання), коли вигнання неактивної анонімної пам’яті менше шкодить, ніж трясіння кешу сторінок.
- Коли swap досить швидкий (NVMe, хороші SSD-масиви) і ви міняєте трохи більшої кількості swap I/O заради пропускної здатності без руйнування хвостових затримок.
Два реалістичних діапазони-цілі
Оціночні рекомендації, які зазвичай витримують продакшен:
- Загальні сервери: 20–60. Почніть з 30, якщо невпевнені і swap увімкнений.
- Сервери, чутливі до затримки: 1–10, але лише після перевірки, що ви не покладаєтесь на swap як запобіжник.
Значення 0 для swappiness часто неправильно розуміють. Це не означає «вимкнути swap». Це означає «уникати свопінгу якомога більше», але під реальним тиском свопінг все одно може статися. Якщо ви дійсно хочете зовсім без свопу, вимкніть swap (і прийміть наслідки: OOM killer стане вашим грубим інструментом).
Жарт #1: Встановити vm.swappiness=1 — це як приклеїти нотатку «будь ласка, будьте люб’язні» для ядра. Іноді воно слухається; іноді воно має свій день.
4. Налаштування vm.dirty: writeback, throttling і чому «буфери» не безкоштовні
Знайомство з квартетом dirty
Найчастіше налаштовуваний набір:
vm.dirty_background_ratio/vm.dirty_background_bytes: починає фонове скидання, коли dirty-сторінок більше за цей поріг.vm.dirty_ratio/vm.dirty_bytes: гальмує передні (foreground) записувачі, коли dirty-сторінок більше за цей поріг.vm.dirty_expire_centisecs: як довго dirty-дані можуть залишатися, перш ніж вважатися достатньо «старими» для запису (в сотих частках секунди).vm.dirty_writeback_centisecs: як часто ядро прокидає флашер-треди для запису (в сотих частках секунди).
Відсотки — це частки від загальної пам’яті. Байти — абсолютні пороги. Якщо ви задаєте байти, відсотки для тієї сторони фактично ігноруються. Байтове налаштування зазвичай більш передбачуване між типами інстансів і при майбутніх апгрейдах RAM.
Режим відмови, який ви намагаєтесь запобігти: writeback-cliff
Якщо пороги dirty високі, ядро може накопичити величезну кількість dirty-даних у RAM, а потім раптово вирішити їх скидати. Це може наситити ваше сховище, викликати черги IO і призупинити записи. Якщо ваш додаток пише через buffered IO (більшість так роблять), затримка проявляється як заблоковані процеси у стані D і підвищений IO wait. Користувачі називають це «система зависла». Інженери називають це «writeback throttling зробив свою роботу, просто ніхто не насолоджується результатом».
На хмарних томах урвина додає веселощів, бо базова пропускна здатність і кредитні бусти можуть змінюватися з часом. Ви можете «вотак протикати» високі dirty-пропорції під час бустів, а потім бути покараним, коли том повертається до бази. Це виглядає випадково, поки не згадаєте, що сховище — це буквально банківський рахунок.
Вибір: відсотки проти байтів
Використовуйте відсотки коли:
- Ви керуєте досить однорідним флотом зі схожими обсягами RAM.
- Ваш часовий проміжок продуктивності масштабується разом із RAM і сховищем.
- Ви хочете простішу ментальну модель і погоджуєтесь на деяку варіативність.
Використовуйте байти коли:
- Обсяги RAM різняться суттєво (поширено в авто-скейлінг групах).
- Пропускна здатність сховища — реальне обмеження і не масштабується з RAM.
- Ви хочете обмежити IO-борг до того, що ваше сховище може предиктовано скидати.
Конкретні початкові точки, що зазвичай поводяться добре
Це не магія, але вони менш імовірно призведуть до урвища, ніж підхід «піднімемо dirty_ratio».
- Для загальних SSD-VM: встановіть
dirty_background_bytesу 256–1024 МБ іdirty_bytesу 1–4 ГБ, залежно від пропускної здатності сховища та «бурстовості» записів. - Для записо-інтенсивних сервісів на скромних дисках: менші пороги (background 128–512 МБ, максимум 512 МБ–2 ГБ) і частіший writeback зменшать спайки.
- Для вузлів з великою пам’яттю: байти майже завжди кращі за відсотки, якщо тільки сховище не масштабується разом із RAM (рідко поза дуже дорогими конфігураціями).
Інтервали expire і writeback: контролі «наскільки грудкувато»
dirty_writeback_centisecs визначає, як часто запускається фоновий writeback. Менші інтервали можуть дати гладкіший writeback, але за рахунок більш частих операцій запису. dirty_expire_centisecs стосується того, як довго дані можуть бути dirty перед тим, як їх вважатимуть «старими» і витиснуть.
Для більшості серверів значення за замовчуванням підходять. Але якщо ви бачите періодичну поведінку «кожні N секунд світ зупиняється», ці параметри підозрілі, особливо в поєднанні з високими dirty-пропорціями. Не робіть різких змін. Маленькі кроки, вимірюйте, повторюйте.
Жарт #2: Dirty-сторінки — як тарілки в раковині: їх можна складати вражаюче високо, але рано чи пізно хтось повинен розібратися з наслідками.
5. Швидкий план діагностики (перший/другий/третій)
Перший крок: вирішіть, чи ви гинете від тиску пам’яті чи від writeback
- Ознаки тиску пам’яті: зростання
pgscan/pgsteal, частий reclaim, наростаючий swap in/out, великі page faults,kswapdзайнятий. - Ознаки тиску writeback: багато завдань у стані
D, високий IO wait, багато dirty-сторінок, у стеках може з’являтисяbalance_dirty_pages, сховище завантажене максимально.
Другий крок: знайдіть рівень вузького місця за 5 хвилин
- Чи справді використовується swap? Якщо так, чи це сталий фон або шторм?
- Чи ростуть dirty-сторінки? Якщо так — чи досягаєте ви порогу throttling?
- Чи насичене сховище? Якщо так — це throughput, IOPS, глибина черги чи затримка?
- Чи це по cgroup/контейнеру? Якщо ви на хості з контейнерами, перевірте обмеження пам’яті та I/O cgroup.
Третій крок: оберіть правильний важіль
- Шторм свопінгу: зменште робочий набір, додайте RAM, відрегулюйте swappiness, виправте витік пам’яті, розгляньте zswap/zram лише з уважністю.
- Writeback-cliff: обмежте dirty-байти, зменшіть dirty-пропорції, згладьте інтервали writeback і виправте вузьке місце сховища (часто реальна відповідь).
- Обидва разом: ймовірно додаток робить інтенсивні буферизовані записи при обмеженій пам’яті. Налаштування допомагають, але планування ємності вирішує проблему.
6. Практичні завдання (команди + значення виводу + рішення)
Завдання 1: Підтвердити поточні swappiness і dirty-настройки
cr0x@server:~$ sysctl vm.swappiness vm.dirty_ratio vm.dirty_background_ratio vm.dirty_bytes vm.dirty_background_bytes vm.dirty_expire_centisecs vm.dirty_writeback_centisecs
vm.swappiness = 60
vm.dirty_ratio = 20
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_background_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
Що це означає: активні відсотки (байти нульові). Dirty-дані можуть лежати ~30 секунд перед тим, як вважатися старими (3000 centisecs). Writeback прокидається кожні 5 секунд (500 centisecs).
Рішення: Якщо RAM велика, а сховище скромне, розгляньте перехід на байтові обмеження dirty, щоб уникнути величезних сплесків.
Завдання 2: Перевірити, чи swap увімкнено і якого типу
cr0x@server:~$ swapon --show --bytes
NAME TYPE SIZE USED PRIO
/swap.img file 8589934592 268435456 -2
Що це означає: swapfile 8 GiB, ~256 MiB використано. Це не обов’язково погано; залежить від трендів.
Рішення: Якщо використання swap постійно зростає під навантаженням і не повертається, ймовірно ви перевищуєте робочий набір або маєте витік пам’яті.
Завдання 3: Швидко перевірити тиск пам’яті (включно з трендами swap)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 31Gi 18Gi 1.2Gi 1.0Gi 12Gi 9.5Gi
Swap: 8.0Gi 256Mi 7.8Gi
Що це означає: «available» — ваш друг; воно оцінює пам’ять, яку можна звільнити без сильного свопінгу. Низьке «free» саме по собі незначуще в Linux.
Рішення: Якщо available падає і swap зростає під нормальним навантаженням, потрібна ємність або зменшення робочого набору, а не чарівний sysctl.
Завдання 4: Побачити реальну активність swap з часом
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 262144 1265000 120000 9500000 0 0 120 180 600 1200 12 4 82 2 0
3 0 262144 1180000 120000 9440000 0 0 100 210 620 1300 14 4 80 2 0
4 1 310000 900000 119000 9300000 2048 4096 90 5000 800 2000 18 6 60 16 0
2 2 420000 600000 118000 9100000 4096 8192 60 9000 1000 2600 20 7 45 28 0
1 2 520000 500000 118000 9000000 4096 8192 40 12000 1100 2800 22 7 40 31 0
Що це означає: si/so (swap-in/out) що зростають вказують на активний свопінг. Зростання b означає заблоковані процеси (часто через IO). Зростання wa — ознака IO wait.
Рішення: Якщо ви бачите стійкий swap-in під час користувацького трафіку, зниження swappiness може допомогти лише якщо система свопує «непотрібно». Якщо робочий набір занадто великий, налаштування не врятує.
Завдання 5: Виявити найбільших споживачів swap
cr0x@server:~$ sudo smem -rs swap -k | head -n 8
PID User Command Swap USS PSS RSS
14231 app /usr/bin/java -jar service.jar 512000 420000 600000 1300000
5123 postgres /usr/lib/postgresql/16/bin/... 128000 300000 380000 700000
2211 root /usr/bin/containerd 24000 35000 42000 90000
1987 root /usr/lib/systemd/systemd 2000 8000 12000 25000
Що це означає: Які процеси реально винесені в swap, а не просто великі.
Рішення: Якщо процес, критичний до затримки, має значний swap, розгляньте налаштування пам’яті в додатку, додавання RAM або зменшення swappiness. Якщо в swap — лише фактично неактивні демоні, панікувати не варто.
Завдання 6: Перевірити рівні dirty-сторінок і активність writeback
cr0x@server:~$ egrep 'Dirty|Writeback|MemTotal' /proc/meminfo
MemTotal: 32734064 kB
Dirty: 184320 kB
Writeback: 16384 kB
WritebackTmp: 0 kB
Що це означає: Dirty зараз невеликий (~180 МіБ). Writeback активний, але скромний.
Рішення: Якщо Dirty росте до кількох гігабайт і залишається таким, ви, ймовірно, накопичуєте IO-борг швидше, ніж сховище встигає скидати.
Завдання 7: Перевірити глобальні пороги dirty ядра (обчислені)
cr0x@server:~$ cat /proc/sys/vm/dirty_background_ratio /proc/sys/vm/dirty_ratio
10
20
Що це означає: Фоновий flush починається на 10% пам’яті; throttling на 20%.
Рішення: На великих системах RAM це часто занадто багато в абсолютних значеннях. Розгляньте байтові обмеження.
Завдання 8: Виміряти заблокований IO і насичення диска
cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0 (server) 12/30/2025 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
14.20 0.00 5.10 18.40 0.00 62.30
Device r/s rkB/s rrqm/s %rrqm r_await w/s wkB/s w_await aqu-sz %util
nvme0n1 120.0 5200.0 2.0 1.6 4.2 980.0 64000.0 22.5 7.8 99.0
Що це означає: Диск завантажений (~99% util) і затримка запису (w_await) висока. Це узгоджується з throttling-ом writeback і/або важкими записами.
Рішення: Налаштування dirty можуть згладити сплески, але якщо диск насичений у базі, потрібне краще сховище, менший обсяг записів або оптимізація на рівні додатка.
Завдання 9: Пошук процесів, застряглих у незавершуваному IO
cr0x@server:~$ ps -eo state,pid,comm,wchan:32 --sort=state | head -n 15
D 9182 postgres io_schedule
D 14231 java balance_dirty_pages
D 7701 rsyslogd ext4_da_writepages
R 2109 sshd -
S 1987 systemd ep_poll
S 2211 containerd ep_poll
Що це означає: Процеси в стані D заблоковані, зазвичай на IO. Побачити balance_dirty_pages — сильний натяк, що активний dirty-throttling.
Рішення: Якщо багато воркерів сидить у balance_dirty_pages, зменшіть dirty-пороги і/або виправте пропускну здатність сховища. Також оцініть, чи ваш додаток робить великі буферизовані записи без усвідомлення зворотного тиску.
Завдання 10: Перевірити обмеження пам’яті cgroup (контейнери тут кусають)
cr0x@server:~$ cat /sys/fs/cgroup/memory.max
max
Що це означає: Цей хост (або поточна cgroup) не обмежена по пам’яті.
Рішення: Якщо ви бачите число замість max, поведінка свопінгу може бути спровокована тиском усередині cgroup, навіть якщо машина виглядає просторною.
Завдання 11: Застосувати тимчасове налаштування безпечно (лише runtime)
cr0x@server:~$ sudo sysctl -w vm.swappiness=30
vm.swappiness = 30
Що це означає: Ви змінили параметр ядра в рантаймі. Після перезавантаження це зміниться назад, якщо ви не збережете постійно.
Рішення: Якщо шторм свопінгу зменшився і cache miss-ів не зросло катастрофічно, розгляньте збереження. Якщо нічого не змінилось, не робіть це за звичкою — шукайте іншу причину.
Завдання 12: Переключити dirty-настройки на байтові ліміти (лише runtime)
cr0x@server:~$ sudo sysctl -w vm.dirty_background_bytes=536870912 vm.dirty_bytes=2147483648
vm.dirty_background_bytes = 536870912
vm.dirty_bytes = 2147483648
Що це означає: Фоновий writeback починається при 512 МіБ dirty, throttling при 2 ГіБ dirty — незалежно від розміру RAM.
Рішення: Якщо це згладжує IO wait і зменшує «writeback cliff», збережіть. Якщо ваше навантаження покладається на величезний кеш для пропускної здатності, ви можете втратити пікову швидкість запису; вирішіть, чи потрібен вам пік чи передбачуваність.
Завдання 13: Зробити sysctl постійним правильно (і перевірити)
cr0x@server:~$ sudo tee /etc/sysctl.d/99-vm-tuning.conf >/dev/null <<'EOF'
vm.swappiness=30
vm.dirty_background_bytes=536870912
vm.dirty_bytes=2147483648
EOF
cr0x@server:~$ sudo sysctl --system | tail -n 6
* Applying /etc/sysctl.d/99-vm-tuning.conf ...
vm.swappiness = 30
vm.dirty_background_bytes = 536870912
vm.dirty_bytes = 2147483648
Що це означає: Налаштування тепер постійні і застосовані. Вивід підтверджує, що завантажено ваші значення.
Рішення: Якщо sysctl --system показує, що ваші значення пізніше перекриваються, у вас є інший файл або інструмент, що конкурує (cloud-init, CM, агент від постачальника). Виправте джерело істини.
Завдання 14: Підтвердити, що dirty-пропорції ефективно вимкнені, коли встановлені байти
cr0x@server:~$ sysctl vm.dirty_ratio vm.dirty_bytes
vm.dirty_ratio = 20
vm.dirty_bytes = 2147483648
Що це означає: Значення відсотка все ще друкується, але dirty_bytes має пріоритет для throttling.
Рішення: Залишайте пропорції за замовчуванням, якщо немає причин їх міняти. Байти — ваш фактичний контроль тепер.
Завдання 15: Перевірити на сплески через delayed allocation в ext4 (контекст, не звинувачення)
cr0x@server:~$ mount | grep ' on / '
/dev/nvme0n1p2 on / type ext4 (rw,relatime,errors=remount-ro)
Що це означає: ext4 з delayed allocation може пакетувати записи, що підсилює writeback-сплески в залежності від навантаження.
Рішення: Не змінюйте опції монтування файлової системи як першу реакцію. Використайте dirty-caps, щоб формувати бурстовість; змінюйте поведінку FS лише після вимірювань і вагомої причини.
7. Оціночні профілі налаштувань, що не вибухають тихо
Профіль A: «Переважно звичайні сервери» (веб + сайдкари + помірні записи)
Використовуйте, коли хочете менше сюрпризів, а не максимальні бенчмарки.
vm.swappiness=30vm.dirty_background_bytes=268435456(256 MiB)vm.dirty_bytes=1073741824(1 GiB)
Чому: Тримає IO-борг у рамках. Починає скидати раніше. Дозволяє буферизацію для пропускної здатності, але не «давайте буферити 30 GB, бо можемо».
Профіль B: «Write-heavy, чутливі до затримки» (логи, інгестія, черги)
vm.swappiness=10(або 20, якщо ви сильно залежите від кешу)vm.dirty_background_bytes=134217728(128 MiB)vm.dirty_bytes=536870912(512 MiB)
Чому: Ви міняєте пік буферизації на менше число хвостових затримок. Це зменшує ймовірність, що раптове скидання зруйнує сервіс.
Профіль C: «Великі машини з нерівномірним сховищем» (класична пастка)
vm.swappiness=20–40залежно від навантаженняvm.dirty_background_bytes=536870912(512 MiB)vm.dirty_bytes=2147483648(2 GiB)
Чому: Велика RAM плюс середні диски — це рецепт випадкового накопичення IO-боргу. Відсотки масштабуються з RAM, але диски — ні.
Чого уникати (бо вас тягне)
- Не підвищуйте
dirty_ratioпросто щоб «поліпшити продуктивність», якщо ви не впевнені, що сховище витримає пізні flush-и. Зазвичай ви купуєте виграш у бенчмарку разом із продакшен-аутеджем. - Не вимикайте swap сліпо на серверах загального призначення. Ви замінюєте градуальне погіршення механізмом миттєвої смерті.
- Не налаштовуйте в темряві. Якщо ви не вимірюєте IO wait, dirty- рівні та активність swap, ви просто переставляєте ядро на відчуття.
8. Три корпоративні міні-історії з польових робіт
Історія 1: Інцидент через хибне припущення
У них був флот серверів Ubuntu, що обслуговував завантажений API, плюс бекграунд-воркер, який періодично писав пакетні експорти на диск. Нічого екзотичного. Команда помітила, що використання swap повільно наростає тижнями і вирішила, що своп — лиходій. Рішення було рішуче: вимкнути swap всюди. «Swap повільний; у нас достатньо RAM.» Речення, що закінчило багато мирних черг виклику.
Два дні потому прийшла хвиля трафіку з невеликим витоком пам’яті в рідко викликаному коді. Витік не був великим, і при наявності swap система б потихеньку працювала та генерувала алерти. Без swap ядро мало тільки один інструмент: OOM killer. Під навантаженням OOM killer зробив свою роботу: убив процес, що виглядав як той, що споживав багато пам’яті. Цей процес виявився API-воркером.
Аутедж не був драматичним одразу. Було гірше: шаблон роллінгових часткових відмов. Інстанси випадали з балансувальника, перезапускалися, прогрівали кеші і вмирали знову. Користувачі бачили флапінг помилок. Інженери бачили «здорову» CPU-графіку і поступово зростаюче почуття особистої зради.
Хибне припущення не було в «swap повільний». Swap дійсно повільний. Хибне припущення було в тому, що swap — лише питання продуктивності. Swap також питання стабільності — він дає час на виявлення витоку й акуратне відновлення. Після інциденту вони знову ввімкнули swap і встановили нижчий swappiness. Потім виправили витік. Найважливіша зміна була культурна: вони перестали вважати swap моральним провалом.
Історія 2: Оптимізація, що дала зворотний ефект
Команда платформи даних мала вузли з великою пам’яттю і write-heavy інгестію. Хтось прочитав, що підвищення dirty-ratio може покращити пропускну здатність, бо ядро може згрупувати більше записів. Вони підняли vm.dirty_ratio і vm.dirty_background_ratio значно. У синтетичному тесті пропускна здатність виглядала відмінно. Дашборди посміхалися. PR швидко змерджили.
В продакшені навантаження не було рівномірним. Воно було спайковим: хвилі записів, потім тиша. При високих dirty-пропорціях система охоче буферизувала величезні сплески в пам’яті. Потім приходила тиша і ядро починало скидати гору dirty-даних. Flush-и були достатньо великими, щоб наситити сховище, що збільшувало IO-затримки для всього: метаданих, читань, навіть дрібних записів від не пов’язаних сервісів.
Користувачі відчували це як «випадкові зупинки». Зупинки співпадали з циклами flush, але тільки якщо нанести dirty-пам’ять і IO-затримку на одній осі. Першою реакцією було звинуватити додаток. Другою — звинуватити провайдера хмари. Третьою — зрештою — визнати, що ядро робило саме те, про що його просили, а оптимізація була налаштована під бенчмарк, а не під бурстовий флот.
Виправлення було нудним: перейти на байтові ліміти dirty, що відображали те, що сховище реально могло скидати без драм. Пропускна здатність трохи впала в найагресивніших бурстах. Хвостова затримка покращилася радикально. Усі прикидалися, що так було заплановано з самого початку — це теж нудно і тому прийнятно.
Історія 3: Нудна, але правильна практика, що врятувала день
Команда, близька до фінансів, експлуатувала хости Ubuntu 24.04 для кінця місяця обробки. Навантаження було передбачуваним: інтенсивні записи кілька годин, потім тиша. Вони не ганялися за піковою продуктивністю; вони хотіли «ніколи не будити мене». Практика була вкрай непримітна: будь-яка зміна в ядрі вимагала canary-rollout, набору вимірювань до/після і плану відкату.
Вони вже стандартизувалися на байтових dirty-капах і помірному swappiness. Також у них були дашборди для dirty-пам’яті, IO-затримки і swap-in/out. Не тому, що це весело, а тому, що це запобігає дебатам на основе відчуттів.
Одного місяця зміна бекенда сховища ввела трохи більшу затримку запису. Нічого катастрофічного, просто повільніше. Під час першого важкого прогону їх метрики показали, що dirty-сторінки ростуть швидше звичайного і передній throttling починає працювати раніше. Але завдяки консервативним dirty-капам система деградувала поступово: обробка займала довше, але хости залишалися відзивними, і нічого не перетворилося на шторм зупинок.
Команда використала докази, щоб відмовитися від зміни сховища і тимчасово скорегувати планування робіт. Ніяких геройств, ніякої археології ядра о 3 ранку. Правильна практика не була магічним sysctl; це було трактування sysctl як конфігурації продакшену з спостережливістю і контролем змін.
9. Поширені помилки: симптом → корінна причина → виправлення
1) Симптом: «Swap використовується, хоча є вільна RAM»
Корінна причина: «Free» не є метрикою. Linux використовує RAM для кешу; рішення про swap залежать від вартості reclaim, а не від «вільних мегабайт». Також холодні анонімні сторінки можуть бути вигнані, щоб кеш лишався гарячим.
Виправлення: Дивіться на available у free, відстежуйте swap-in/out через vmstat. Якщо swap-in близький до нуля і продуктивність добра — нічого робити не потрібно. Якщо swap-in зростає під навантаженням, зменшіть робочий набір або обережно знизьте swappiness.
2) Симптом: періодичні 10–60-секундні паузи; багато процесів у стані D
Корінна причина: Writeback-cliff: накопичується забагато dirty-сторінок, потім великий flush насичує сховище і гальмує записувальників.
Виправлення: Встановіть vm.dirty_background_bytes і vm.dirty_bytes в розумні ліміти; перевірте через /proc/meminfo і інструменти вимірювання IO-затримки (iostat). Розгляньте помірне зниження інтервалів expire/writeback, якщо flush-и занадто грудкуваті.
3) Симптом: високий IO wait, але низька пропускна здатність диска
Корінна причина: Сховище обмежене за затримкою (IOPS-limited, черги, throttling або «шумний сусід»). Dirty-throttling може підсилити це, бо записувачі чекають завершення writeback.
Виправлення: Використайте iostat -xz для перевірки await та розміру черги; зменшіть dirty-капи, щоб уникнути вибуху черги; вирішіть проблему сховища (більший тип тому, більше IOPS, локальний NVMe або зменшення sync-записів).
4) Симптом: свопінг викликає хвостові сплески затримки, але загальний обсяг swap «малий»
Корінна причина: Навіть невеликі темпи swap-in можуть зашкодити, якщо потрапляють на гарячі сторінки під піком. Робочі навантаження, чутливі до затримки, більше ненавидять swap-in, ніж зменшений кеш.
Виправлення: Зменшіть swappiness (наприклад, 10–30), перевірте, що додаток не має витоку пам’яті, і переконайтеся, що swap-пристрій не надто повільний. Розгляньте призначення критичних сервісів у cgroup замість глобальних «костилів».
5) Симптом: після налаштування dirty bytes пропускна здатність впала, а CPU зріс
Корінна причина: Ви занадто сильно зменшили буферизацію, спричинивши частіші менші writeback-и і більші накладні витрати, або змусили foreground-throttling працювати надто часто.
Виправлення: Збільшіть dirty_bytes помірно (наприклад, з 512 МБ до 1 ГБ) і знову виміряйте. Не повертайтесь одразу до величезних відсотків; прагніть мінімальних лімітів, які запобігають урвищам.
6) Симптом: «Налаштування не зберігаються» після перезавантаження
Корінна причина: Ви змінили лише runtime sysctl, або інший компонент перезаписує sysctl під час завантаження.
Виправлення: Помістіть налаштування в /etc/sysctl.d/, запустіть sysctl --system і перегляньте логи завантаження чи інструмент конфігураційного менеджменту, щоб знайти джерело перезапису.
7) Симптом: контейнер багато свопить, а хост виглядає добре
Корінна причина: Обмеження пам’яті cgroup (і можливо обмеження swap) викликають reclaim всередині контейнера.
Виправлення: Перевірте memory.max у cgroup, запити/ліміти пам’яті контейнера та розмір навантаження. Глобальний swappiness не виправить занадто тісний ліміт контейнера.
10. Контрольні списки / покроковий план
Покроково: безпечний робочий процес тюнінгу для продакшену
- Запишіть симптом у оперативних термінах: «p99 latency спайки під час пакетних записів», «хости зависають на 30 секунд», «swap-in зростає після деплою». Якщо ви не можете сформулювати — ви не зможете перевірити зміни.
- Зніміть базові значення: swappiness/dirty-настройки, використання swap, dirty-пам’ять, IO-затримки і заблоковані задачі.
- Доведіть, який тиск домінує: пам’ять проти writeback проти насичення сховища.
- Змінюйте по одному параметру: swappiness або dirty-limits, а не обидва одночасно, якщо ви не впевнені, що обидва впливають.
- Спочатку використовуйтe runtime sysctl: перевірте ефект під реальним навантаженням без коміту.
- Canary rollout: застосуйте до невеликої підмножини; порівняйте при ідентичних патернах навантаження.
- Збережіть через sysctl.d: тримайте один авторитетний файл на роль хоста; уникайте «таємних» налаштувань.
- Перевірка після перезавантаження: підтвердіть, що налаштування лишилися і поведінка відповідає очікуванню.
- Задокументуйте чому: додайте метрику, яку ви цілитеся змінити, і спостережуваний результат.
Контрольний список: як виглядає «добре» після тюнінгу
- Swap-in/out близько нуля під нормальним трафіком (якщо немає очевидної причини).
- Dirty-пам’ять піднімається й опускається плавно, а не у вигляді пилкоподібного циклу з довгими затримками.
- IO-затримки не періодично стрибкують до небес під час фонового flush.
- Мало або зовсім немає потоків додатка, що надовго застрягають у стані
D. - Пропускна здатність прийнятна, а хвостові затримки стабільні.
Контрольний список: план відкату
- Зберігайте останній відомо-хороший файл sysctl в історії CM.
- Майте однолайнер для відкату runtime-налаштувань (
sysctl -wз попередніми значеннями). - Знайте, які графіки повинні поліпшитися за хвилини (dirty/IO wait) і які — за години (тренди swap, поведінка кешу).
11. Питання й відповіді
1) Чи треба ставити vm.swappiness=1 на всіх серверах?
Ні. Для сервісів, чутливих до затримки, це може бути виправдано. Для I/O-інтенсивних серверів, які виграють від кешу, це може негативно вплинути, бо кеш буде скидатися надто агресивно. Починайте з 20–30 і вимірюйте.
2) Чи завжди погано використання swap?
Активність swap під час піку часто шкідлива. Невеликі обсяги використаного swap можуть бути нормальними, якщо ці сторінки дійсно холодні і swap-in залишається близько нуля.
3) Що краще: dirty-ratio чи dirty-bytes?
Dirty-bytes передбачуваніші при різних обсягах RAM і зазвичай безпечніші на машинах з великою пам’яттю. Відсотки простіші, але можуть масштабуватися в абсурдні значення на сучасних серверах.
4) Якщо я обмежу dirty-bytes, чи втратжу я продуктивність?
Ви можете втратити пікову пропускну здатність під час бурстів. Часто ви натомість отримуєте стабільність і нижчі хвостові затримки. Для продакшен-систем цей компроміс зазвичай є правильним.
5) Чи важливі ці налаштування для баз даних?
Іноді. Бази даних з власними буферами і WAL-патернами більше дбати про планувальник I/O, файлову систему і сховище. Але dirty-writeback все одно може впливати на фонові задачі, логи і будь-які буферизовані шляхи IO навколо БД.
6) Чому я бачу високий IO wait при майже простому CPU?
Тому що потоки заблоковані на IO. CPU «idle» не означає, що система здорова; це може означати, що CPU нічого не робить, поки всі чекають на сховище.
7) Чи треба вимикати swap, щоб змусити додаток падати швидко?
Тільки якщо ви справді хочете, щоб OOM killer був вашим основним механізмом контролю і у вас є надійна оркестрація/повторні спроби. Для багатьох флотів swap — це запобіжник, що запобігає каскадним відмовам під короткочасними пам’яттєвими сплесками.
8) Чи треба налаштовувати dirty_expire_centisecs і dirty_writeback_centisecs?
Зазвичай ні. Почніть з dirty-bytes/ratio. Розглядайте інтервали лише якщо у вас є періодичні stalls, пов’язані з flush, і ви вже підтвердили, що dirty-limits — не основна проблема.
9) Я змінив sysctls і нічого не покращилося. Чому?
Бо вузьке місце часто в пропускній здатності/затримці сховища, патернах запису додатка або витоках пам’яті. Sysctl-налаштування формують поведінку на краях; вони не створюють додаткову ємність.
10) Який безпечний спосіб тестувати без ризику для всього флоту?
Runtime-зміни на одному canary-прикладі під репрезентативним навантаженням з чіткими метриками успіху: rate swap-in, dirty-рівні, IO-затримка і латентність запитів.
12. Висновок: практичні наступні кроки
Якщо ваші Ubuntu 24.04 машини зависають і ви бачите або своп-чорніння, або стіну dirty-writeback, вам не потрібні байки. Вам потрібен короткий цикл: спостерігати, обмежити, перевірити.
- Запустіть швидку діагностику: це тиск свопу, writeback чи насичене сховище?
- Якщо історія — writeback-cliff, перейдіть від відсоткових dirty-порогів до байтових лімітів, підібраних під реальність вашого сховища.
- Якщо історія — swap-шторм, обережно знизьте swappiness і вирішіть реальну причину: робочий набір, витоки або недостатність RAM.
- Збережіть налаштування в
/etc/sysctl.d/, виконайте canary, і перевірте після перезавантаження.
Ці налаштування невеликі. У цьому і суть. Малі, цільові зміни, що перетворюють 02:17 на звичайну ніч.