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

Load average: три числа й одна поширена помилка

uptime
cat /proc/loadavg
nproc

Три числа — середні за хвилину, за п’ять і за п’ятнадцять хвилин. Порівнювати їх треба не з нулем, а з кількістю ядер, яку покаже nproc. Значення 8 на восьмиядерній машині — це повне, але здорове навантаження: роботи рівно стільки, скільки сервер здатен виконати. Значення 2 на одноядерному VPS — черга вдвічі довша за можливості, і кожен запит чекає своєї черги.

Співвідношення чисел між собою вказує напрямок. Хвилинне помітно вище за п’ятнадцятихвилинне — навантаження зростає просто зараз. Навпаки — пік уже минув, і ви дивитеся на хвіст.

Тепер помилка, через яку ці цифри найчастіше розуміють неправильно. Load average у Linux — це не «завантаження процесора». На відміну від інших Unix-систем, Linux рахує в нього не лише процеси, що виконуються або готові виконуватися, а й ті, що перебувають у стані D — непереривному сні. А це очікування диска або мережевої файлової системи. Звідси сервер, у якого load 12, а процесор майже простоює: робота не йде, усі стоять у черзі.

Процесор чи диск

Розділити ці два випадки — перше, що варто зробити:

vmstat 1 5
iostat -x 1 3

У виводі vmstat дивляться на три колонки. r — скільки процесів стоїть у черзі на процесор, b — скільки заблоковано в очікуванні введення-виведення, wa — частка часу, яку процесор простояв, чекаючи на диск. Стійке wa вище 10–15 % за невеликих us і sy означає, що сервер упирається в диск: додавати ядер немає сенсу, вони й так вільні.

Хто саме чекає, видно поіменно:

ps -eo state,pid,comm,wchan:30 | awk '$1 ~ /^D/'

Приклад із власної практики. Suricata зі стандартним конфігом пише три десятки типів подій і ротації за собою не налаштовує — на робочому сервері це дало 15 ГБ логів за дві доби. Навантаження при цьому виражалося не в зайнятому процесорі, а саме в очікуванні диска: система безперервно писала, усе інше стояло в черзі за нею. Лікується це не збільшенням тарифу, а обмеженням переліку подій і налаштованою ротацією.

Зворотний випадок, суто процесорний і значно менш очевидний. Вебсторінка викликала apt від імені користувача, під яким працює PHP-FPM. Root тримає бінарний кеш /var/cache/apt/pkgcache.bin — на хості з десятком репозиторіїв це 70 МБ — і під’єднує його до пам’яті безкоштовно. Звичайний користувач писати в цей каталог не може і перебудовує кеш заново при кожному виклику. Замір на одній і тій самій машині: 0,01 секунди процесорного часу від root проти 4,2 секунди від непривілейованого користувача. Одна й та сама команда, різниця в чотириста разів, і множилася вона на кожне відкриття сторінки.

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

Пам’ять: free показує не те, що здається

free -h

Колонка used сама по собі майже ні про що не говорить, а колонка free вводить в оману: Linux віддає незайняту пам’ять під кеш сторінок і повертає її застосункам на першу вимогу. Дивитися треба на available — скільки пам’яті можна зайняти, не йдучи у swap. Великий buff/cache при цьому не проблема, а ознака нормально працюючої системи.

Зі swap важлива не поточна величина, а форма. Зріс і повернувся до нуля — був короткочасний пік. Зріс одного разу й лишився — пік уже стався, сторінки витіснено, і ніхто їх назад не піднімає: сервер має спокійний вигляд, хоча пам’яті йому в якийсь момент забракло. Чи йде обмін просто зараз, показують колонки si і so у vmstat; їхні ненульові значення — найпомітніша для користувача форма гальмування.

Якщо під час нічного провалу процес просто зник, пояснення зазвичай тут:

journalctl -k --since yesterday | grep -i "out of memory"
dmesg -T | grep -i "killed process"

Рядок на кшталт Out of memory: Killed process 1234 (mysqld) закриває питання краще за будь-які графіки: база не «впала сама», її зупинило ядро, бо пам’ять скінчилася.

Хто це робить

ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10

Одне застереження: %CPU у ps — середнє за весь час життя процесу, тому короткий сплеск у цьому списку губиться. Для нього потрібен top або pidstat 1 5, які рахують за проміжок.

Коли навантаження стає питанням безпеки

Рівні 100 % процесора ночами, з процесом, у якого невиразна назва і робочий каталог у /tmp чи /dev/shm, — це класична картина майнера, а не «сайт виріс». Сплеск мережевого прийому разом зі зростанням запису в логи — перебір паролів, що триває. Різко підскочивши обсяг журналів — зазвичай шторм хибних спрацювань фільтрів.

Окрема категорія — самі засоби захисту. На одному з наших серверів load тримався близько трьох за цілковитої відсутності відвідувачів, і у верхівці ps за накопиченим процесорним часом стояли не сайт і не база, а CrowdSec, fail2ban, Falco і Suricata. Це не аварія і не привід їх вимикати, але ціну захисту корисно знати в цифрах: на слабкому VPS вона помітна.

Сенс спостереження

Усе перелічене відповідає на питання «що відбувається зараз». Ранкове «що було о третій ночі» цими командами не закривається: даних за минулу ніч ніде немає, якщо їх ніхто не записав. При цьому розгортати заради одного VPS зв’язку Prometheus і Grafana сумнівно — стек, що спостерігає, виходить важчим за сервер, за яким спостерігають.

Достатньо рядка в базі раз на п’ять хвилин і однієї сторінки, яка малює за ним останню добу: load average, завантаження процесора й окремо I/O wait, пам’ять і swap, читання та запис диска, заповнення розділу, inodes, файлові дескриптори, з’єднання. В одному масштабі часу пари читаються очима: високе wa за спокійного процесора, swap, який не повернувся до нуля, дескриптори, що підійшли до ліміту, — майбутня помилка too many open files стає видимою за кілька годин до того, як станеться. Як це виглядає зібраним — на сторінці демо нижче.