Вас покликали, бо p99 латентність подвоїлася, API «працює на моєму ноутбуці», і хтось у чаті копіює заклинання sysctl -w з 2013 року. Спокуса налаштувати ядро, ніби це гоночний автомобіль, сильна.
У продакшені це більше схоже на підгонку вантажного потяга: дрібні ручки мають значення, великі ручки можуть зірватися з колій, а більшість рукояток — плацебо.
Ubuntu 24.04 постачається з сучасним ядром і консервативними значеннями за умовчанням, які зазвичай підходять. Зазвичай.
Трюк у тому, щоб знати, які п’ять sysctl варто чіпати, коли вони мають значення і як довести, що ви допомогли, а не просто змінили числа.
Правила роботи: як налаштовувати, не нашкодивши
Налаштування ядра — не хобі. Ставтеся до цього як до зміни в продакшені: вам потрібна гіпотеза, метрика, план відкату
і чітке визначення «краще». Інакше ви просто переставляєте sysctl, поки інцидент не закінчиться з якихось інших причин.
1) Змінюйте один вимір одночасно
Якщо ви змінюєте TCP буфери, пороги dirty pages і розміри conntrack в одному деплої, ви створили детективний роман.
SRE не платять за читання детективів о 03:00.
2) Віддавайте перевагу налаштуванням, що враховують навантаження, а не фольклору
Межі вашої системи зазвичай лежать в іншому місці: насичення CPU, затримка сховища, тайм-аути DNS, head-of-line blocking
або бекпрешур на рівні застосунку. Sysctl може допомогти, але тільки коли ви можете іменувати вузьке місце.
3) Робіть зміни персистентними правильно
Ubuntu 24.04 завантажує sysctl з /etc/sysctl.conf та /etc/sysctl.d/*.conf.
Покладіть зміни в окремий файл, наприклад /etc/sysctl.d/99-local-tuning.conf, і задокументуйте причину.
Не «просто виконуйте sysctl -w» на тестовому сервері й думайте, що все зроблено.
4) Розумійте радіус ураження
Деякі sysctl є на рівні неймспейсу, інші — глобальні. Контейнери можуть це ускладнити.
Якщо ви налаштовуєте хост, ви можете змінити поведінку для всіх подів, VM або сервісів, що використовують це ядро.
5) Не налаштовуйте навколо браку потужності
Ви не зможете вирішити нестачу NIC, насичений масив дисків або базу даних, що робить повні скани, лише за допомогою sysctl.
Налаштування — для тертя. Не для фізики.
Одна цитата, щоб тримати себе в руках: Сподівання — не стратегія.
— парафраз ідеї, яка часто приписується операційним лідерам.
У налаштуванні ядра «надія» виглядає як «поставимо на 1 000 000 і подивимось».
Факти та історія, що пояснюють сучасні значення за замовчуванням
- Інтерфейс «sysctl» з’явився до Linux контейнерів. Багато ручок були розроблені для одновласних машин; мульти-власні хости можуть перетворити «безпечне» на «шумного сусіда».
- Масштабування TCP вікна (RFC 1323) зробило релевантним розмір буферів. До цього великі BDP канали не можна було ефективно заповнити незалежно від налаштувань.
- Linux перейшов від «налаштовувати все» до «автонастроювати більшість речей». Сучасні ядра динамічно підбирають багато TCP буферів, тож старі статичні рецепти часто нічого не дають.
- Модель запису «dirty page» тонувалася десятиліттями. Ці ручки існують, бо буферизація записів може підвищити пропускну здатність, але надмірна буферизація дає грандіозні сплески латентності.
vm.swappinessстала відомою частково через те, що ноутбуки свопили в епоху раннього десктопного Linux. Сервери перейняли фольклор, навіть коли він не підходив.- Conntrack не завжди був «за замовчуванням». Stateful firewall та NAT зробили відстеження з’єднань ключовим питанням масштабування для багатьох флотів.
- Ефемерні порти раніше частіше колізували при високому QPS клієнтів. Діапазон портів і поведінка TIME-WAIT стали операційними темами, коли з’явилися service mesh та агресивні повторні спроби.
- Виснаження файлових дескрипторів завжди було в топ-10 причин інцидентів. Не тому, що Linux крихкий, а тому, що ПЗ креативно «забуває» закривати сокети під частковими збоїми.
Жарт №1: Налаштування ядра — як приправляти суп: можна завжди додати ще солі, але не можна видалити її, коли клієнти вже кричать.
Швидкий плейбук діагностики (першочергово/далі/на третьому)
Перш ніж чіпати sysctl, знайдіть вузьке місце. Це найшвидша «драбина триажу», яку я використовував на Ubuntu кластерах:
вона звужує список підозрюваних за кілька хвилин.
Першочергово: це CPU, тиск пам’яті чи runnable-черга?
- Перевірте load у відношенні до використання CPU: високий load з низьким використанням CPU часто означає I/O wait або contention за локи.
- Перевірте major faults і свопінг: якщо відбувається paging, спочатку маєте проблему з пам’яттю.
- Перевірте довжину run queue: якщо вона постійно вища за кількість ядер, ви CPU-завантажені або обмежені планувальником.
Далі: це затримка сховища або writeback-стали?
- Шукайте високу disk await, насичення та сплески у flush/writeback.
- Шукайте зростання dirty pages і раптові sync-штормі (особливо для баз даних, сплесків логування або NFS клієнтів).
На третьому: це черги мережі, втрачені пакети чи conntrack?
- Перевірте retransmits і drops (вони перетворюють пропускну здатність на смуток).
- Перевірте переповнення backlog сокетів і пропуски у listen queue.
- Перевірте лічильник conntrack у відношенні до max і помилки «insert failed».
Якщо робити це по черзі, ви уникнете класичної помилки: налаштовувати TCP буфери, коли справжня проблема — обмежений NVMe або CPU, завантажений шифруванням.
П’ять sysctl, що мають значення (переважно)
Це ручки, які я насправді бачив, що змінюють метрики в продакшені на Ubuntu 24.04, коли є чітке вузьке місце.
Вони не чарівні. Це важелі, які тягнути, коли вимір показує, що треба.
1) fs.file-max — системний ліміт файлових дескрипторів
Якщо ви запускаєте сервіси з великою кількістю з’єднань (проксі, брокери повідомлень, навантажені HTTP фронтенди),
файлові дескриптори — це кисень. Коли вони закінчуються, система не деградує плавно; вона руйнується так, що виглядає як «випадкові» помилки з’єднання.
За замовчуванням Ubuntu часто підходить до помірних навантажень, але великі флотилії і вузли з великою кількістю з’єднань можуть досягти стелі.
Також: ліміт ядра — лише половина історії; пер-процесні ліміти (ulimit -n) та ліміти systemd часто кусаються першими.
Коли змінювати
- Помилки в логах типу «Too many open files».
- Балансувальники/проксі з десятками тисяч одночасних сокетів на процес.
- Системи з високим churn, багато inotify watcher-ів або велика кількість короткоживучих з’єднань.
Як безпечно встановити
Піднімайте до значення, яке ви можете обґрунтувати, а не «безкінечного». Слідкуйте за використанням дескрипторів і тримайте запас.
Ядру потрібна пам’ять для файлових структур; на малих машинах величезні значення працюють чисто номінально.
2) net.core.somaxconn — ліміт listen backlog
Це обмежує, скільки з’єднань може чекати прийняття на прослуховуваному сокеті (є нюанси: backlog у виклику listen()
і поведінка ядра також важливі). Якщо у вас стрибкоподібний трафік і цикл accept не встигає тимчасово,
низький backlog перетворює сплески у відкинуті SYN або відмовлені з’єднання.
Коли змінювати
- Короткі сплески викликають збої з’єднань, тоді як CPU не насичений.
- Реверс-проксі та API gateway з поривчастим входом.
- Сервіси з дорогим TLS handshake, які не встигають accept під час штормів.
Як безпечно встановити
Підніміть до 4096 або 8192 для навантажених фронтендів, потім перевірте, чи зменшилися listen drops.
Більші backlogs можуть ховати повільність застосунку; не використовуйте їх як заспокійливе.
3) net.ipv4.ip_local_port_range — ефемерні порти для клієнтів
Це важливо для систем, що ініціюють багато вихідних з’єднань: клієнти API, sidecar service mesh,
NAT-шлюзи, скрепери та все, що робить агресивні повторні спроби. Якщо у вас закінчуються ефемерні порти, ви отримуєте помилки connect(),
і застосунок часто трактує це як «віддалений недоступний», додаючи повтори і погіршуючи ситуацію.
Коли змінювати
- Високий вихідний QPS з короткоживучими з’єднаннями.
- NAT або proxy вузли, де багато внутрішніх клієнтів ділять egress.
- Накопичення TIME-WAIT значне і ви не можете швидко перейти на повторне використання з’єднань.
Як безпечно встановити
Розширте діапазон (наприклад, до 10240 60999 або подібного), уникаючи резервованих портів.
Потім перевірте використання портів і підтвердіть, що ви не приховуєте погане повторне використання з’єднань.
4) vm.swappiness — політика свопінгу
Це ручка, до якої всі торкаються першою, і зазвичай це помилка. Але вона має значення в одній дуже поширеній серверній ситуації:
коли у вас багато RAM, але ядро вирішує свопити рідко використовувані сторінки, а потім latency-чутливі сервіси
потім фолтять їх у найневдаліший момент.
Для багатьох сучасних серверних навантажень нижчий swappiness (часто 1–10) зменшує несподівані major faults,
за умови, що у вас достатньо пам’яті і ви не використовуєте swap як розширення ємності.
Коли змінювати
- Сплески латентності корелюють з major page faults або swap-in.
- У вас увімкнений swap «на всякий випадок», і ви хочете, щоб він був крайнім заходом, а не рутиною.
- Ноди бази даних, де кешування важливе, і своп-трещ — фатальний.
Як безпечно встановити
Не ставте reflexively 0. Використовуйте низьке значення і спостерігайте за reclaim-поведінкою.
Якщо у вас обмежена пам’ять, swappiness — не реальне рішення: потрібна ємність або зменшення навантаження.
5) vm.dirty_ratio і vm.dirty_background_ratio — буферизація записів і розподіл болю
Так, це два sysctl, але це одне рішення. Вони визначають, скільки модифікованих (dirty), ще незаписаних даних ядро дозволить
до того, як примусить writeback, і коли має початися фоновий writeback.
Значення за замовчуванням можуть бути прийнятні для загального використання. На інтенсивних у записі системах вони можуть створювати поведінку «спокій якийсь час, а потім все зависає»:
буфери заповнюються, потім ядро змушує синхронний (приблизно) writeback для нещасних потоків. Латентність переходить від плавної до пилкоподібної.
Коли змінювати
- Навантаження з інтенсивними записами і періодичними латентними піками.
- Вузли агрегації логів, пайплайни інжесту, CI воркери, що пишуть артефакти.
- Системи з швидкими сплесками (in-memory), але повільнішою бек-стороною (мережеве сховище, HDD, перевантажений RAID).
Як безпечно встановити
Для багатьох серверів зниження співвідношень (або використання еквівалентів у байтах) згладжує writeback і зменшує tail latency.
Але надто низькі значення будуть необґрунтовано пригальмовувати пропускну здатність. Це те, що потрібно обов’язково валідовувати реальними метриками: dirty bytes, writeback,
latency сховища і p99 застосунку.
Десять sysctl, що не допомагають (переважно)
Це ручки, що з’являються в блог-постах, «Linux hardening» gist-ах або performance cargo cult.
Вони не марні в усіх обставинах. Просто рідко саме вони вирішать вашу проблему на Ubuntu 24.04.
Торкайте їх тільки коли можете пояснити режим відмови і як будете вимірювати покращення.
1) net.ipv4.tcp_tw_reuse — фольклор про TIME-WAIT
Повторне використання TIME-WAIT сокетів раніше було поширеним «фіксом» тиску на ефемерні порти. Сучасні стекі і реальна поведінка NAT/timestamp
роблять цей важіль ризикованим. Повторне використання з’єднань на рівні застосунку (keep-alive, пулінг) — зріле рішення.
2) net.ipv4.tcp_fin_timeout — виглядає корисним, але зазвичай ні
Налаштування FIN timeout рідко вирішує виснаження портів, бо основну масу становить TIME-WAIT, а не FIN-WAIT.
Якщо у вас багато напівзакритих сокетів, запитайте, чому піри не закриваються коректно.
3) net.ipv4.tcp_syncookies — це не tweak продуктивності
Це механізм захисту від SYN-флудів, а не підсилювач пропускної здатності.
Встановіть його відповідно до політики безпеки, а не тому, що ви побачили його в списку налаштувань.
4) net.ipv4.tcp_sack — не перемикайте його легковажно
Вимикання SACK радили під час певних вразливостей. У нормальному житті SACK покращує відновлення після втрат.
Його переключення може погіршити продуктивність у мережах з втратою пакетів і рідко виправдане для загальних серверів.
5) net.core.netdev_max_backlog — пастка «зробіть чергу більшою»
Якщо пакети накопичуються, бо CPU не встигає обробляти чергу, її збільшення може підвищити латентність і джиттер.
Інколи воно потрібне для поглинання сплесків; часто потрібні CPU, IRQ affinity, RPS/RFS налаштування або просто менше пакетів.
6) net.ipv4.tcp_mtu_probing — обхід, а не базова настройка
MTU probing може допомогти на маршрутах з пошкодженим PMTUD. Якщо ви не діагностуєте «чорні діри», не змушуйте стек вгадувати MTU постійно.
7) kernel.pid_max — рідко ваш вузький місце
Якщо ви виснажуєте PID, щось інше глибоко неправильно: fork bomb, runaway супервізори або екстремальний churn процесів.
Виправляйте churn, а не просто піднімайте стелю.
8) vm.vfs_cache_pressure — «ручка кешу», яку неправильно читають
У сучасних ядрах взаємозв’язок між page cache, inode/dentry cache і reclaim більш нюансований, ніж це одне число.
Якщо ви налаштовуєте його без вимірювання reclaim і hit rate кешу, ви здогадуєтесь.
9) kernel.randomize_va_space — ручка безпеки, не швидкодії
ASLR впливає на складність експлойту. Це не ваша проблема латентності, і вимикати її заради продуктивності зазвичай не виправдано.
10) vm.overcommit_memory — неправильно розуміють і застосовують
Поведінка overcommit важлива для деяких алокаційно-інтенсивних навантажень, але змінювати її «для продуктивності» — класичний спосіб перетворити відновлюваний
пам’ятний сплеск у миттєві відмови алокації. Використовуйте її, коли розумієте патерни алокації і обробку збоїв застосунку.
Жарт №2: Встановлювати випадкові sysctl з інтернету — як приймати запит на зміну в продакшені з формулюванням «різні дрібниці»; єдина гарантія — майбутні зустрічі.
Практичні завдання: команди, сенс, рішення (12+)
Це реальні завдання, які я запускаю на Ubuntu-серверах до і після змін sysctl.
Кожне завдання включає: команду, що означає її вивід, і рішення, яке ви приймаєте.
Виконуйте їх з користувачем, що має відповідні привілеї.
Завдання 1: Подивіться, які sysctl насправді встановлені (і звідки)
cr0x@server:~$ sudo systemd-analyze cat-config systemd-sysctl.service
# /usr/lib/systemd/system/systemd-sysctl.service
...
# /usr/lib/sysctl.d/00-system.conf
net.ipv4.ip_forward = 0
...
# /etc/sysctl.d/99-local-tuning.conf
vm.swappiness = 10
Сенс: Це показує, які sysctl-файли застосовані і в якому порядку пріоритету.
Рішення: Якщо ви не можете знайти, звідки береться значення, не змінюйте його поки не виправите гігієну конфігурації.
Завдання 2: Перевірте поточні значення для «п’яти, що важливі»
cr0x@server:~$ sysctl fs.file-max net.core.somaxconn net.ipv4.ip_local_port_range vm.swappiness vm.dirty_ratio vm.dirty_background_ratio
fs.file-max = 9223372036854775807
net.core.somaxconn = 4096
net.ipv4.ip_local_port_range = 32768 60999
vm.swappiness = 60
vm.dirty_ratio = 20
vm.dirty_background_ratio = 10
Сенс: Ви отримали базову лінію. Примітка: деякі дистрибутиви задають дуже великі значення file-max; пер-процесні ліміти все ще мають значення.
Рішення: Якщо swappiness = 60 на латентно-чутливій ноді з увімкненим swap, вона — кандидат на зміну — після підтвердження, що своп використовується.
Завдання 3: Перевірте системний тиск по файлових дескрипторах
cr0x@server:~$ cat /proc/sys/fs/file-nr
12032 0 9223372036854775807
Сенс: Перший номер — виділені файлові дескриптори; третій — максимум.
Рішення: Якщо виділених наближаються до max (або ви бачите помилки алокації), підніміть fs.file-max і проведіть аудит процесів на утечки.
Завдання 4: Перевірте пер-процесні ліміти файлових дескрипторів (systemd часто вирішує)
cr0x@server:~$ systemctl show nginx --property=LimitNOFILE
LimitNOFILE=1024
Сенс: Ваш сервіс може відкривати лише 1024 файли/сокети, незалежно від fs.file-max.
Рішення: Якщо це навантажений фронтенд, підніміть LimitNOFILE в unit override; сам sysctl цього не вирішить.
Завдання 5: Перевірте пропуски listen queue і сигнали SYN backlog
cr0x@server:~$ netstat -s | sed -n '/listen queue/,/TCPBacklogDrop/p'
120 times the listen queue of a socket overflowed
120 SYNs to LISTEN sockets dropped
Сенс: Ядро мало з’єднань швидше, ніж застосунок міг їх прийняти.
Рішення: Розгляньте підняття net.core.somaxconn і профілювання accept loop / TLS / насичення воркерів.
Завдання 6: Подивіться, скільки ефемерних портів реально використовується
cr0x@server:~$ ss -s
Total: 32156 (kernel 0)
TCP: 27540 (estab 1320, closed 24511, orphaned 12, synrecv 0, timewait 23890/0), ports 0
Transport Total IP IPv6
RAW 0 0 0
UDP 316 280 36
TCP 3029 2510 519
INET 3345 3070 275
FRAG 0 0 0
Сенс: Великий TIME-WAIT вказує на churn; це також може означати брак keep-alive/пулінгу.
Рішення: Якщо вихідний churn високий і є помилки підключення, розширте ip_local_port_range і пріоритезуйте повторне використання з’єднань.
Завдання 7: Підтвердіть, що своп використовується (не гадати)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 62Gi 41Gi 2.1Gi 1.2Gi 19Gi 18Gi
Swap: 8.0Gi 1.6Gi 6.4Gi
Сенс: Своп використовується. Це не завжди погано, але підозріло для латентно-чутливих нод.
Рішення: Якщо своп використовується й під час сплесків ви бачите major faults, знизьте vm.swappiness і/або додайте пам’ять.
Завдання 8: Виміряйте активність свопу з часом
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 1677720 2200000 120000 18400000 0 4 2 120 900 2400 12 6 78 4 0
1 0 1677720 2195000 120000 18420000 0 0 0 80 850 2300 11 5 80 4 0
3 1 1677720 2100000 120000 18500000 40 12 200 600 1200 4000 18 8 60 14 0
Сенс: si/so (swap in/out) сплески корелюють з болем у латентності.
Рішення: Якщо swap-in відбуваються під навантаженням, знизьте swappiness і вирішіть проблему пам’яті. Якщо swap стабільний і низький — можливо, це безпечно.
Завдання 9: Інспектуйте поведінку dirty/writeback
cr0x@server:~$ egrep 'Dirty|Writeback|MemAvailable' /proc/meminfo
MemAvailable: 18342172 kB
Dirty: 914832 kB
Writeback: 26304 kB
Сенс: Dirty дані нині ≈900MB. Це може бути нормально або тривожно залежно від RAM і навантаження.
Рішення: Якщо Dirty росте у багатогігабайт і латентність збігається з flush-штормами, налаштуйте dirty ratios (або байтові пороги), щоб згладити writeback.
Завдання 10: Швидко скорелюйте I/O латентність
cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0-xx-generic (server) 12/30/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.40 0.00 5.80 18.20 0.00 63.60
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
nvme0n1 40.0 1024.0 0.0 0.00 2.10 25.6 900.0 32000.0 10.0 1.10 45.00 35.6 41.2 98.0
Сенс: Високий w_await, високий aqu-sz і майже 100% завантаження вказують, що пристрій насичений.
Рішення: Не «налаштовуйте ядро» в першу чергу. Виправте пропускну здатність/латентність сховища або зменшіть тиск на запис; dirty tuning лише перерасподілить біль.
Завдання 11: Перевірте насичення conntrack (класичний кейс NAT/gateway)
cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 252144
net.netfilter.nf_conntrack_max = 262144
Сенс: Ви в межах 4% від max conntrack. Це червона лампочка.
Рішення: Якщо це stateful firewall/NAT/load balancer вузол, підніміть nf_conntrack_max і переконайтесь у запасі пам’яті. Також дослідіть churn з’єднань.
Завдання 12: Підтвердіть conntrack drops у логах ядра
cr0x@server:~$ sudo dmesg -T | tail -n 8
[Mon Dec 30 10:14:02 2025] nf_conntrack: table full, dropping packet
[Mon Dec 30 10:14:02 2025] nf_conntrack: table full, dropping packet
Сенс: Пакети відкидаються, бо conntrack повний. Користувачі відчують тайм-аути і повторні спроби.
Рішення: Підніміть ємність conntrack і зменшіть churn. Це не «можливо»; це підтверджене вузьке місце.
Завдання 13: Перевірте TCP retransmits і drops (не звинувачуйте sysctl за поганий канал)
cr0x@server:~$ netstat -s | sed -n '/Tcp:/,/Ip:/p'
Tcp:
923849 active connection openings
12 failed connection attempts
210938 segments retransmitted
41 resets sent
Сенс: Retransmits — ознака втрат пакетів, конгестії або поганого queueing.
Рішення: Якщо retransmits зростають разом з проблемами продуктивності, дослідіть мережевий шлях, NIC drops, queue disciplines і конгестію — не випадкові TCP sysctl.
Завдання 14: Тимчасово застосуйте зміну sysctl (тільки для валідації)
cr0x@server:~$ sudo sysctl -w net.core.somaxconn=8192
net.core.somaxconn = 8192
Сенс: Зміна застосована негайно, але не персистентна після перезавантаження.
Рішення: Використовуйте це для A/B валідації під час інциденту, потім зробіть персистентним, якщо допомогло.
Завдання 15: Зробіть зміни персистентними та перезавантажте
cr0x@server:~$ sudo tee /etc/sysctl.d/99-local-tuning.conf >/dev/null <<'EOF'
net.core.somaxconn = 8192
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
EOF
cr0x@server:~$ sudo sysctl --system
* Applying /etc/sysctl.d/99-local-tuning.conf ...
net.core.somaxconn = 8192
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
Сенс: Значення тепер персистентні та застосовані в правильному порядку.
Рішення: Задокументуйте change ticket, метрики до/після і план відкату (видалити файл або повернути значення).
Три міні-історії з практики
Міні-історія №1: Інцидент через неправильне припущення (редакція conntrack)
Середня компанія експлуатувала набір «edge» вузлів: HAProxy, NAT і легкий L7 роутинг. Команда вважала вузли безстанними, бо
«лише форвардять». Це було зручно, популярно і неправильно.
Інтеграція партнера запустилася. Трафік не просто виріс; він став більш стрімким. Після короткого збою upstream спалахнув retry-шторм, перетворивши акуратну криву трафіку
в їжака. Edge вузли почали тайм-аутити нові з’єднання. Нічого очевидного в CPU графах. Пам’ять виглядала стабільно. Сховище було не причетне.
Хтось підняв net.core.somaxconn. Інший поклав провину на TLS. Справжня підказка була в логах ядра: conntrack table full, dropping packets.
Ці вузли робили NAT, тож conntrack state не був опціональним; він був продуктом.
Виправлення було нудним: збільшити net.netfilter.nf_conntrack_max і підібрати його відповідно до доступної пам’яті, а потім зменшити churn, виправивши повторне використання з’єднань клієнтів
і додавши circuit breakers, щоб повтори не стали саморуйнуванням. Після цього команда оновила дизайн-док: «stateful edge».
Ця фраза запобігла майбутнім зустрічам «але воно безстанне».
Міні-історія №2: Оптимізація, що дала відкат (dirty ratios і ілюзія швидкості)
Інша організація мала пайплайн логування з локальним дисковим буфером. Під важким інжестом буфер інколи відставав. Інженер помітив,
що диски «не завантажені» більшість часу і захотів батчити записи для пропускної здатності. Він значно підняв vm.dirty_ratio,
очікуючи менше flush-ів і кращу ефективність диска.
Пропускна здатність виглядала чудово — тимчасово. Потім on-call почав бачити періодичні багатосекундні зависання в сервісі інжесту.
Не плавне уповільнення. Повні зупинки. Downstream alert-и спрацювали, бо інжест став бурстовим, що зробило індексування бурстовим, а запити — нестабільними.
Це було як дивитися, як невеликий тремор запускає ланцюг падіння полиць.
Ядру дозволили накопичити величезну купу dirty pages. Коли writeback натиснув нарешті, система оплатила борг відразу.
Деякі потоки, яким треба було виділити пам’ять або записати дрібні метадані, застрягли за writeback-штормом. Графік «диск не завантажений» виявився брехнею:
він був ідл до моменту, а потім 100% завантаження з високим await.
Відкат зміни одразу стабілізував латентність. Довгострокове виправлення було більш тонким: помірні dirty ratios (або байтові пороги),
кращий батчинг у застосунку там, де він контролюється, і більше дискової пропускної здатності. Відомість інциденту дала простий урок:
буферизація ядра може згладжувати; може також приховувати ризик до вибуху.
Міні-історія №3: Нудна, але правильна практика, що врятувала
Фінансова команда вела високонавантажений API зі строгими SLO. Їхня позиція по тюнінгу була майже дратівливо дисциплінована:
кожна зміна sysctl вимагала гіпотезу, посилання на дашборд і план відкату. Вони зберігали невеликий версіонований файл в
/etc/sysctl.d/ з коментарями, що пояснювали кожне відхилення від дефолту.
Одного дня деплой інтroduced subtle socket leak під специфічним шляхом помилок. Файлові дескриптори повільно росли.
У менш дисциплінованої команди першим симптомом став би інцидент і панічне «підніміть ліміти!» як реакція.
Натомість у них було два запобіжні механізми. По-перше: дашборд, що відслідковує /proc/sys/fs/file-nr і пер-процесні кількості відкритих FD.
По-друге: розумний запас у LimitNOFILE і системному file-max, тож витік мав простір для виявлення і відкату до жорсткої відмови.
Їм все одно довелось виправити баг. Але «нудна» робота — задокументовані sysctl, виміряні бази і ліміти, підіграні під реальність — перетворила потенційний інцидент
на рутинний відкат. Ось в чому суть.
Поширені помилки: симптом → корінна причина → виправлення
1) «Випадкові тайм-аути під час сплесків трафіку»
Симптом: Клієнти бачать періодичні помилки з’єднання під час сплесків; CPU не завантажений.
Корінна причина: Переповнення listen backlog (somaxconn занадто низьке) або цикл accept занадто повільний.
Виправлення: Підніміть net.core.somaxconn до 4096–8192 і підтвердіть за допомогою netstat -s лічильників listen queue; профілюйте accept/TLS і насичення воркерів.
2) «Все зависає на секунди, потім відновлюється»
Симптом: Періодичні зупинки; зайнятість диска підскакує до 100%; p99 стає пилкоподібним.
Корінна причина: Накопичення dirty pages і writeback-шторм (dirty ratios занадто високі для пристрою).
Виправлення: Знизьте vm.dirty_background_ratio та vm.dirty_ratio (або використайте пороги в байтах), потім підтвердіть зменшення коливань Dirty/Writeback і покращення I/O await.
3) «Вихідні виклики падають під навантаженням з EADDRNOTAVAIL»
Симптом: Помилки підключення на клієнті, часто під час повторів або сплесків.
Корінна причина: Виснаження ефемерних портів або надмірний TIME-WAIT через churn з’єднань.
Виправлення: Розширте net.ipv4.ip_local_port_range, зменшіть churn з’єднань через keep-alive/пулінг і моніторте TIME-WAIT за допомогою ss -s.
4) «nf_conntrack: table full» і тайм-аути для користувачів
Симптом: Логи ядра показують conntrack drops; вузли NAT/firewall тайм-аутять.
Корінна причина: nf_conntrack_max занадто малий для churn і конкуруючих з’єднань.
Виправлення: Збільшіть net.netfilter.nf_conntrack_max, забезпечте запас пам’яті і зменшіть churn (тайм-аути idle, повторне використання з’єднань, уникання retry-штормів).
5) «Ми встановили swappiness = 1, а все одно повільно»
Симптом: Проблеми з латентністю залишаються; пам’ять щільна; своп може все ще використовуватися.
Корінна причина: Насправді у вас обмежена пам’ять; swappiness не вирішує ємність. Також навантаження може бути I/O або CPU bound.
Виправлення: Виміряйте major faults і swap-in; якщо тиск пам’яті реальний — додайте RAM або зменшіть використання пам’яті. Потім налаштовуйте swappiness як політику, не як порятунок.
6) «Ми підняли всі TCP буфери і стало гірше»
Симптом: Зросла латентність; зросло використання пам’яті; відсутній приріст пропускної здатності.
Корінна причина: Буфери збільшили черги і тиск пам’яті; вузьке місце не в розмірі TCP вікна.
Виправлення: Відкотіть зміни буферів, перевірте retransmits/drops, перевірте CPU і черги NIC; налаштовуйте лише при доведеному BDP-проблемі.
7) «Занадто багато відкритих файлів» незважаючи на великий fs.file-max
Симптом: Помилки застосунку про вичерпання FD; kernel file-max здається величезним.
Корінна причина: Пер-процесні ліміти (systemd LimitNOFILE) низькі.
Виправлення: Підніміть LimitNOFILE для unit-а і перевірте за допомогою systemctl show і /proc/<pid>/limits.
Чеклісти / поетапний план
Чекліст A: Безпечний робочий процес для налаштування sysctl (продакшен)
- Опишіть гіпотезу: «Ми втрачаємо вхідні з’єднання через переповнення backlog; підняття somaxconn зменшить пропуски і покращить p99 часу підключення.»
- Виберіть 2–3 метрики: один сигнал ядра (наприклад, overflow listen queue), один симптом користувача (p99 latency), один ресурсний метрік (CPU, пам’ять, I/O await).
- Зніміть baseline: запустіть відповідні завдання вище; збережіть вивід з часовими мітками.
- Застосуйте зміну тимчасово:
sysctl -wпід час контрольованого вікна або на канарці. - Спостерігайте: покращився сигнал ядра і користувацька метрика без побічних ефектів?
- Зробіть персистентним: запишіть в
/etc/sysctl.d/99-local-tuning.conf, потімsysctl --system. - Задокументуйте: що змінилось, чому і як виглядає відкат.
- Перевірте після перезавантаження: підтвердіть, що значення збереглося і поведінка не змінилася.
Чекліст B: Мінімальний профіль «п’яти sysctl» для типових ролей серверів
Завантажений HTTP інгрех/реверс-проксі
net.core.somaxconn: розгляньте 4096–8192, якщо бачите overflow listen.fs.file-max: забезпечте запас; також підніміть unitLimitNOFILE.net.ipv4.ip_local_port_range: релевантний, якщо вузол багато робить вихідних з’єднань.vm.swappiness: знижуйте, якщо swap-in викликає латентні сплески.vm.dirty_*: лише якщо локальне логування/буферизація викликає зупинки.
Вузол бази даних (латентність-чутливий, інтенсивний у записі)
vm.swappiness: часто 1–10, але лише при підтвердженому впливі свопінгу.vm.dirty_ratio/vm.dirty_background_ratio: налаштовуйте, щоб уникнути writeback-cliff; вимірюйте I/O await і dirty/writeback.fs.file-max: забезпечте запас; БД можуть використовувати багато файлів і сокетів.net.core.somaxconn: рідко головний фактор, хіба що вузол також обслуговує TCP-бурсти.ip_local_port_range: переважно неважливий, якщо БД ініціює багато вихідних з’єднань.
NAT gateway / firewall / Kubernetes node з інтенсивним egress
net.netfilter.nf_conntrack_max: не в «п’ятірці», але для цієї ролі це топ-параметр. Розмірюйте його свідомо.ip_local_port_range: релевантний для вузлів з великою кількістю вихідних з’єднань або спільним egress.fs.file-max: релевантний для проксі та агентів.
Чекліст C: План відкату, який справді працює
- Збережіть копію попередніх значень:
sysctl -aшумний; принаймні зафіксуйте ключі, які змінювали. - Для персистентних змін відкотіть файл у
/etc/sysctl.d/, потім виконайтеsysctl --system. - Якщо потрібно відкотити негайно, використайте
sysctl -w key=valueдля кількох змінених ключів, потім відновіть файл після інциденту. - Підтвердіть за допомогою
sysctl key, що відкат застосувався.
FAQ
1) Чи взагалі варто налаштовувати sysctl на Ubuntu 24.04?
Лише коли у вас є виміряне вузьке місце і чіткий режим відмови. Значення за замовчуванням підходять для загального використання, але не для всіх високонавантажених кейсів на краю.
2) Чому не застосувати «sysctl performance profile» з інтернету?
Бо це зазвичай суміш застарілих порад, перемикань безпеки і налаштувань для конкретних навантажень, подана як універсальна істина.
Ви нічого не виправите і ускладните дебаг.
3) Чи завжди vm.swappiness=1 краще для серверів?
Ні. Це часто добра політика для латентно-чутливих нод з увімкненим swap як аварійним гальмом, але це не вирішить брак пам’яті.
Якщо ви свопите під постійним навантаженням — потрібна пам’ять або зменшення навантаження.
4) Який реальний зв’язок між fs.file-max і ulimit -n?
fs.file-max — це глобальна межа дескрипторів у ядрі. ulimit -n (і systemd LimitNOFILE) обмежують пер-процесні відкриті файли.
Ви можете мати величезний системний ліміт і все одно падати через те, що сервіс обмежений 1024.
5) Я підняв net.core.somaxconn. Чому все ще бачу пропуски?
Бо backlog — не єдина черга: accept loop застосунку може бути повільним, поведінка SYN backlog має значення, а CPU/IRQ насичення може відкидати пакети раніше.
Виміряйте, де саме відбувається втрата і чи процес дійсно встигає accept.
6) Чи варто налаштовувати TCP rmem/wmem на Ubuntu 24.04?
Лише якщо ви довели проблему bandwidth-delay product (високі латентність і широка смуга, де пропускна здатність обмежена розміром вікна)
і авто-підбір недостатній. Інакше ви просто підвищите використання пам’яті і queueing латентність.
7) Чи безпечно налаштовувати vm.dirty_ratio?
Це безпечно, коли ви валідовуєте: дивіться Dirty/Writeback, I/O await і хвостову латентність застосунку.
Небезпечно — коли ви піднімаєте співвідношення до максимуму заради пропускної здатності; це може створити writeback-шторм.
8) Як зберегти зміни sysctl «по-ubuntu’шному»?
Помістіть їх у /etc/sysctl.d/99-local-tuning.conf (або подібний файл), потім виконайте sysctl --system.
Уникайте ad-hoc змін, що зникають після перезавантаження.
9) Чи контейнери ігнорують хостові sysctl?
Деякі sysctl є неймспейсними і можуть бути встановлені для контейнера; інші глобальні для хоста.
На практиці: хостове налаштування може вплинути на всі робочі навантаження. Розглядайте це як зміну інфраструктури.
10) Якщо можна змінити лише одну річ під час інциденту, що найшвидше безпечно?
Залежить, але найнадійніший операційний крок часто — не sysctl: зменшити навантаження (shed traffic), додати ємність або відкотити реліз.
З-поміж sysctl — підняття somaxconn або розширення діапазону портів може бути низькоризиковим, якщо є свідчення.
Наступні кроки, які можна зробити сьогодні
Якщо ви хочете, щоб налаштування ядра покращували надійність, а не породжували фольклор, зробіть цю послідовність:
- Виберіть одну роль ноди (ingress, база даних, NAT, worker). Не «налаштовуйте флот» за відчуттям.
- Запустіть розділ завдань і збережіть виводи як ваш baseline.
- Визначте домінантне вузьке місце використовуючи швидкий плейбук діагностики — CPU, пам’ять, диск або мережа.
- Змініть лише один з п’яти sysctl, що відповідає вузькому місцю, і лише якщо є підтверджуючі сигнали.
- Зробіть це персистентним через sysctl.d і задокументуйте причину в коментарях, а не у племінному знанні.
- Перевірте після одного перезавантаження і одного циклу трафіку. Якщо ви не можете довести покращення — відкотіть і рухайтесь далі.
Найкраще налаштування — те, яке ви можете пояснити через шість місяців без вибачень. Друге найкраще — те, яке вам не було потрібне, бо ви підібрали систему правильно.