Коротко. Начните с uptime и nproc — load average сравнивают с числом ядер, а не с единицей. Затем vmstat 1 разделяет три разные «нагрузки»: очередь на CPU (колонка r), ожидание диска (b и wa) и свопинг (si/so). Дальше — free -h, df -h и df -i, iostat -x 1, ss -s. Виновника ищут через ps aux --sort=-%cpu и логи nginx, PHP-FPM и базы.
«Сервер тормозит» — это не диагноз, а симптом. За одинаковой картиной (сайт отвечает медленно или отдаёт 502/503) стоят принципиально разные причины: нехватка процессора, ожидание диска, свопинг, забитый раздел, всплеск соединений, один тяжёлый SQL-запрос или наплыв парсеров. Лечение у них разное, и добавление ядер помогает ровно в одном случае из шести. Ниже — последовательность команд, которая за 15 минут сужает круг до одного слоя, и то, что делать дальше.

Первые 60 секунд: load average, top и vmstat
Задача первой минуты — не найти виновника, а понять, какой ресурс исчерпан. Три команды дают 80% ответа.
uptime
nproc
vmstat 1 5
free -h
df -h; df -i
Как читать load average и почему «load 4» на 4 ядрах — не всегда плохо
Вывод uptime заканчивается тремя числами: средняя нагрузка за 1, 5 и 15 минут.
$ uptime
14:22:31 up 82 days, 3:11, 2 users, load average: 4.12, 3.87, 2.05
$ nproc
4
Load average в Linux — это среднее число задач, которые либо выполняются на процессоре, либо стоят в очереди на него, либо находятся в состоянии непрерываемого сна (D — обычно ожидание дискового ввода-вывода). Это принципиальное отличие от классического UNIX: в Linux в load попадает и диск, поэтому load 30 при простаивающем CPU — нормальная картина для сервера, который упёрся в диск или в сетевое хранилище.
Нормализуйте load на число ядер. Load 4.12 на четырёх ядрах — это загрузка около 100%: система работает на пределе, но очередь ещё не растёт лавинообразно. Тот же 4.12 на 16 ядрах — четверть мощности, повода для тревоги нет. Тот же 4.12 на одном ядре означает, что каждая задача в среднем ждёт в очереди в три раза дольше, чем выполняется.
Сравнение трёх чисел важнее их абсолютного значения. Ряд 4.12 / 3.87 / 2.05 означает рост — нагрузка началась недавно и усиливается. Ряд 2.05 / 3.87 / 4.12 означает спад: пик уже прошёл, и, возможно, вы диагностируете последствия, а не причину. Ровный ряд — устойчивое состояние, и его надо сравнивать с историей, а не с ощущениями.
Load average сам по себе не отвечает на вопрос «почему». Он отвечает только на вопрос «сколько задач ждут». Пока вы не разделили ожидание CPU и ожидание диска, любые действия — угадывание.
Что смотреть в top и htop
В top полезны не все колонки. Смотрите на четыре:
- %CPU — доля одного ядра. 100% означает полностью занятое одно ядро, а не весь сервер. Многопоточный процесс легко покажет 380% на четырёхъядерной машине, и это нормально.
- RES — резидентная память, реально занятая в RAM. Именно её, а не VIRT, надо смотреть при подозрении на утечку. VIRT включает отображённые файлы и зарезервированные диапазоны и почти всегда выглядит пугающе большим.
- S — состояние процесса.
R— работает или готов,S— спит,D— непрерываемый сон, то есть ждёт диск или сеть на уровне ядра,Z— зомби. Несколько процессов вDодновременно — почти всегда проблема хранилища, а не процессора. - Строка %Cpu(s) — распределение по типам:
us(пользовательский код),sy(ядро),wa(ожидание ввода-вывода),st(steal — время, украденное гипервизором у виртуальной машины).
В top нажмите 1, чтобы развернуть строку CPU по ядрам: часто выясняется, что нагружено одно ядро из восьми, а значит проблема в однопоточном процессе, и апгрейд на 16 ядер ничего не изменит. htop удобнее для чтения, но по составу данных ничего принципиально нового не даёт.
Высокий st (единицы процентов и выше, устойчиво) на VPS означает, что гипервизор не отдаёт вам обещанные такты — это проблема соседей по хосту или переподписки тарифа, и внутри виртуальной машины она не лечится.
vmstat 1: колонки r, b, si, so и wa
Одна команда, которая разделяет три вида нагрузки. Первую строку вывода игнорируйте — это среднее с момента загрузки системы.
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
6 0 0 152344 84120 2210436 0 0 0 28 912 1840 94 4 2 0 0
5 0 0 151980 84120 2210436 0 0 0 0 880 1792 96 3 1 0 0
- r — число процессов в очереди на выполнение. Устойчиво больше числа ядер — упёрлись в CPU.
- b — число процессов в непрерываемом сне (ждут ввод-вывод). Больше нуля постоянно — упёрлись в диск.
- si / so — страницы, читаемые из свопа и записываемые в своп, килобайт в секунду. Ненулевые значения в моменте — активный свопинг, самая болезненная разновидность тормозов.
- wa — доля времени CPU в ожидании ввода-вывода. Высокий
waпри низкомus— процессор простаивает, ждёт диск. - cs — переключения контекста. Резкий рост при неизменной нагрузке говорит о слишком большом числе воркеров или потоков.
Три сценария читаются мгновенно:
| Картина в vmstat | Что это | Куда копать |
|---|---|---|
| r больше числа ядер, wa около 0, us высокий | Нехватка CPU | Какой процесс: ps aux --sort=-%cpu, PHP, компиляция, антивирус, крон |
| b больше 0, wa высокий, us низкий | Нехватка I/O | iostat -x 1, iotop, процессы в состоянии D |
| si и so ненулевые, free мал | Свопинг | Нехватка RAM: free -h, кто съел — ps aux --sort=-%mem |
| sy высокий, cs огромный, us низкий | Накладные расходы ядра | Слишком много воркеров, шторм прерываний, сетевой флуд |
| st устойчиво больше нуля | Steal на виртуалке | Переподписка хоста или лимит тарифа — вопрос к провайдеру |
Память: free -h, page cache, своп и OOM killer
Самая частая ложная тревога: «свободной памяти почти нет». В Linux это норма. Ядро использует всю неиспользуемую память под page cache — кеш файлов на диске. Эта память отдаётся приложениям мгновенно, как только понадобится.
$ free -h
total used free shared buff/cache available
Mem: 7.7Gi 3.1Gi 210Mi 180Mi 4.4Gi 4.1Gi
Swap: 2.0Gi 512Mi 1.5Gi
Смотреть надо на available, а не на free. Available — это оценка ядра, сколько памяти реально можно выделить новому процессу без свопинга, с учётом освобождаемой части кеша. Здесь free = 210 МиБ выглядит страшно, а available = 4.1 ГиБ говорит, что памяти достаточно.
Тревожно, когда available падает до единиц процентов от total и одновременно в vmstat ненулевые si/so. Это уже свопинг: система вытесняет страницы приложений на диск и читает их обратно, каждое обращение к памяти превращается в дисковую операцию, и производительность падает на порядки.
Своп и swappiness
Параметр vm.swappiness задаёт, насколько охотно ядро вытесняет анонимные страницы вместо сброса page cache. Значение по умолчанию на большинстве дистрибутивов — 60.
cat /proc/sys/vm/swappiness
sudo sysctl -w vm.swappiness=10
# постоянно:
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
# кто именно ушёл в своп (по процессам)
for f in /proc/*/status; do
awk '/^Name:|^VmSwap:/{printf "%s ", $2} END{print ""}' "$f"
done | sort -k2 -h -r | head -15
Снижение swappiness до 10 разумно для серверов баз данных и веб-серверов: своп остаётся страховкой от OOM, но перестаёт использоваться «профилактически». Полное отключение свопа — плохая идея: без свопа ядро при нехватке памяти сразу зовёт OOM killer, и вместо замедления вы получаете убитый процесс.
OOM killer: как найти следы
Если процесс «просто исчез», а в логах приложения ничего нет — почти наверняка сработал OOM killer.
sudo dmesg -T | grep -i -E 'out of memory|killed process|oom-kill'
sudo journalctl -k --since "2 hours ago" | grep -i oom
sudo journalctl -u php-fpm --since today | grep -i -E 'exited|killed'
В строке ядра будет имя убитого процесса и его total-vm/rss на момент смерти. Это точная улика: вы знаете и что убили, и сколько оно занимало. Дальше вопрос в том, почему процесс вырос — утечка, слишком большой memory_limit в PHP при большом числе воркеров, или неудачный запрос, тянущий в память миллион строк.
Классическая ошибка на веб-сервере:
pm.max_childrenв PHP-FPM выставлен «с запасом», аmemory_limit— 512 МБ. При всплеске трафика сумма воркеров превышает RAM, начинается свопинг, потом приходит OOM killer и убивает базу данных как самый жирный процесс. Считайтеmax_childrenкак доступная память, делённая на реальное среднее потребление воркера.

Диск: свободное место, иноды и удалённые открытые файлы
Забитый раздел даёт самые разнообразные симптомы: 502/503 от веб-сервера, отказ базы стартовать, «белый экран» PHP, невозможность записать сессию или лог. Проверка занимает пять секунд.
df -h
df -i
df -h /var /tmp /var/lib/mysql
df -h против df -i: почему «No space left on device» при свободном диске
Файловые системы семейства ext хранят метаданные файлов в иноды, число которых фиксируется при форматировании. Если приложение создало миллионы мелких файлов — кеш, сессии, очередь писем, временные файлы — иноды кончатся раньше места. Тогда df -h покажет 40% занятости, а любая запись будет падать с «No space left on device».
$ df -i /
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 3276800 3276794 6 100% /
Ищите каталог с миллионами файлов:
# сколько файлов в каждом подкаталоге верхнего уровня
sudo du -xh --max-depth=1 / | sort -h | tail -20
sudo find /var -xdev -type d -exec sh -c 'echo "$(ls -U "$1" | wc -l) $1"' _ {} \; 2>/dev/null | sort -rn | head
Типичные виновники: /var/lib/php/sessions, /var/spool/postfix, каталоги кеша CMS (bitrix/cache, wp-content/cache, var/cache), почтовые очереди, каталоги превью изображений.
Поиск того, что съело место
# сверху вниз, не выходя за пределы одной файловой системы
sudo du -xh --max-depth=1 / | sort -h
sudo du -xh --max-depth=1 /var | sort -h
# интерактивно — удобнее
sudo ncdu -x /
# журналы systemd умеют разрастаться
journalctl --disk-usage
sudo journalctl --vacuum-size=200M
Флаг -x обязателен: без него du уйдёт в /proc, сетевые монтирования и примонтированные тома контейнеров и даст бессмысленные числа.
Удалённые, но открытые файлы
Ситуация, которая ставит в тупик: du насчитал 20 ГБ, df показывает 90 ГБ занято. Значит, кто-то удалил большой файл, но процесс держит его дескриптор открытым — место освободится только после перезапуска процесса.
sudo lsof +L1
# или короче, только крупные:
sudo lsof -nP +L1 2>/dev/null | awk '$7 > 100000000'
Чаще всего это ротированный, но не переоткрытый лог: логи удалили руками вместо logrotate, а nginx или приложение продолжает писать в удалённый inode. Лечение — nginx -s reopen, systemctl reload сервиса или его перезапуск. И настроить logrotate, чтобы это не повторялось; подробнее — в материале о работе с логами.
Держите 15–20% свободного места на разделе с базой данных и на корне. На ext4 около 5% блоков зарезервировано под root, и когда обычные процессы уже не могут писать, система ещё загружается — это ваш запас на спасение, а не рабочее состояние.
Дисковый ввод-вывод: iostat -x 1, %util и await
Если vmstat показал ненулевую колонку b и высокий wa, переходите к iostat (пакет sysstat).
iostat -x 1
# только интересующее устройство
iostat -xd 1 vda
Ключевые колонки:
| Колонка | Что показывает | Когда тревожно |
|---|---|---|
r/s, w/s | Операций чтения и записи в секунду (IOPS) | Упирается в лимит тарифа или диска |
rkB/s, wkB/s | Пропускная способность | Постоянный потолок при низком IOPS — упор в полосу |
r_await, w_await | Среднее время ответа операции, мс | Для SSD/NVMe — единицы мс норма, десятки уже плохо |
aqu-sz | Средняя глубина очереди | Устойчиво больше 1–2 — устройство не успевает |
%util | Доля времени, когда устройство обслуживало запросы | 100% на HDD — насыщение; на NVMe число вводит в заблуждение |
На NVMe и на сетевых дисках %util почти бесполезен: устройство параллельно обслуживает десятки очередей, и 100% не означает исчерпания. Ориентируйтесь на await и aqu-sz.
Кто именно делает ввод-вывод:
sudo iotop -oPa # только активные, по процессам, с накоплением
pidstat -d 1 # то же без интерактивности
ps -eo state,pid,ppid,comm,wchan | awk '$1 ~ /^D/'
Частые причины всплеска I/O на веб-сервере: ночной бэкап или репликация, полное пересканирование антивируса, перестроение поискового индекса, запрос без индекса, который читает таблицу целиком, ротация и сжатие логов, и — на переподписанных VPS — соседи по хосту.
Сеть и соединения: ss -s и всплеск TIME-WAIT
Нагрузка бывает не в ресурсах, а в числе соединений. Симптом — сервер отвечает медленно или отдаёт ошибки, при этом CPU и диск свободны.
ss -s
ss -tan state time-wait | wc -l
ss -tan state established | wc -l
ss -ltn # что слушает и какие очереди
ss -tan state syn-recv | wc -l
В выводе ss -ltn смотрите колонки Recv-Q и Send-Q для слушающих сокетов: Recv-Q — текущая длина очереди принятых, но ещё не обработанных соединений, Send-Q — настроенный backlog. Если Recv-Q упирается в Send-Q, приложение не успевает принимать соединения, и часть клиентов получает таймауты или сброс.
Десятки тысяч сокетов в TIME-WAIT сами по себе не катастрофа: это нормальное состояние после закрытия соединения, оно уходит примерно за минуту. Проблема начинается, когда их число близко к диапазону локальных портов, и исходящие соединения (к базе, к API, к upstream) перестают устанавливаться.
cat /proc/sys/net/ipv4/ip_local_port_range
# лимит таблицы conntrack, если включён netfilter
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
sudo dmesg -T | grep -i 'nf_conntrack: table full'
Правильное лечение всплеска TIME-WAIT — не крутить sysctl вслепую, а включить keep-alive между nginx и upstream, чтобы соединения переиспользовались. Практика описана в разборе тюнинга nginx. Если очередь соединений переполняется под наплывом — смотрите стратегии ограничения частоты запросов.
Кто конкретно грузит сервер: процессы, nginx, PHP-FPM, MySQL
Ресурс определён — теперь имя виновника.
ps aux --sort=-%cpu | head -15
ps aux --sort=-%mem | head -15
pidstat -u -p ALL 1 5 | sort -k8 -rn | head
systemd-cgtop # нагрузка по юнитам и контейнерам
nginx: найти медленные запросы
По умолчанию nginx не пишет время ответа в лог. Добавьте его — без этого искать нечего.
log_format timed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'uct=$upstream_connect_time uht=$upstream_header_time '
'urt=$upstream_response_time rt=$request_time';
access_log /var/log/nginx/access.log timed;
После перезагрузки конфига (nginx -t && nginx -s reload) медленные URL находятся одной строкой:
# топ-20 самых долгих запросов
awk -F'rt=' '{print $2, $0}' /var/log/nginx/access.log | sort -rn | head -20
# сколько запросов в минуту сейчас
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2,3 | uniq -c | tail -10
PHP-FPM: slowlog и число воркеров
; в пуле, например /etc/php-fpm.d/www.conf
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s
request_terminate_timeout = 60s
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
Slowlog пишет полный стек PHP на момент превышения таймаута — это готовый ответ на вопрос «в какой функции висим». Смотреть статус пула:
sudo systemctl status php-fpm
# при включённом pm.status_path
curl -s http://127.0.0.1/status?full | head -40
grep -c 'pool www' /var/log/php-fpm/www-slow.log
Если в статусе постоянно виден listen queue больше нуля, воркеров не хватает. Но прежде чем повышать pm.max_children, посчитайте: воркеры × реальное потребление памяти воркером должно помещаться в RAM с запасом на базу и кеш. Иначе вы просто меняете медленный ответ на OOM. Про то, как это выглядит снаружи, — в разборах ошибки 502 и ошибки 503.
База данных: PROCESSLIST и медленные запросы
-- что выполняется прямо сейчас
SHOW FULL PROCESSLIST;
SELECT id, user, host, db, command, time, state, LEFT(info, 120) AS query
FROM information_schema.processlist
WHERE command <> 'Sleep'
ORDER BY time DESC
LIMIT 20;
-- включить журнал медленных запросов
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';
# агрегировать журнал медленных запросов
sudo mysqldumpslow -s t -t 20 /var/log/mysql/slow.log
# посмотреть план проблемного запроса
mysql -e "EXPLAIN SELECT ...\G"
Один запрос без индекса на таблице в несколько миллионов строк способен положить сервер целиком: он читает таблицу с диска, вытесняет page cache, поднимает wa, и все остальные запросы начинают ждать. Это самая частая причина «внезапной» нагрузки на сайтах, которые годами работали нормально, — таблица просто доросла до размера, при котором полный перебор перестал быть дешёвым.

Боты и парсеры или реальный трафик: разбор access.log
Прежде чем оптимизировать код, проверьте, за чей трафик вы платите. Разбор лога занимает минуту.
# топ IP по числу запросов
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# топ User-Agent
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# топ URL
awk '{print $7}' /var/log/nginx/access.log | cut -d? -f1 | sort | uniq -c | sort -rn | head -20
# что делает конкретный IP
grep '^203.0.113.45 ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
Признаки, по которым парсер отличается от людей:
- Один IP или подсеть даёт непропорционально большую долю запросов — десятки в секунду без пауз.
- Запросы идут строго последовательно по каталогу или по всем комбинациям параметров фильтра — комбинаторный взрыв на страницах со смарт-фильтром убивает сервер быстрее любого DDoS.
- Нет запросов к статике: браузер тянет CSS, шрифты и картинки, парсер — только HTML.
- Отсутствует или подделан Referer, User-Agent пустой, одинаковый на тысячи запросов или содержит имя HTTP-библиотеки.
- Заявленный Googlebot приходит с адреса, который не подтверждается обратным DNS.
# проверка «настоящий ли это поисковый робот»
host 66.249.66.1
# в ответе должен быть домен googlebot.com или google.com,
# затем прямая проверка полученного имени:
host crawl-66-249-66-1.googlebot.com
Что с этим делать. Полезных роботов не блокируют — им ограничивают глубину: закрывают в robots.txt параметрические URL фильтров и сортировок, отдают корректные канонические ссылки, убирают бесконечные календари и пагинации. Вредным ставят ограничение частоты на уровне nginx:
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=connperip:10m;
server {
location / {
limit_req zone=perip burst=20 nodelay;
limit_conn connperip 20;
limit_req_status 429;
}
}
Сначала выставляйте лимит в режиме наблюдения и смотрите, сколько живого трафика он бы задел: слишком жёсткое правило отрежет мобильных операторов с общим NAT-адресом. Подробный разбор порогов и алгоритмов — в статье про rate limiting.
Не блокируйте по User-Agent как основной мере: строка подделывается одной опцией в любой библиотеке. Ограничение частоты по адресу и по сессии работает против того, что реально стоит денег, — против количества запросов.
Симптом → быстрая проверка → вероятная причина → первое действие
| Симптом | Быстрая проверка | Вероятная причина | Первое действие |
|---|---|---|---|
| Load высокий, wa около нуля, us 90%+ | ps aux --sort=-%cpu | head | Тяжёлый процесс: PHP, крон, импорт, антивирус | Найти и остановить процесс, вынести задачу в очередь |
| Load высокий, wa высокий, us низкий | iostat -x 1, iotop -oPa | Упор в диск: бэкап, запрос без индекса, лимит IOPS | Отложить фоновую задачу, найти запрос в slow log |
| Всё тормозит рывками, отклик плавает | vmstat 1 (si/so), free -h | Свопинг из-за нехватки RAM | Снизить число воркеров, найти утечку, добавить память |
| Процессы «исчезают» без ошибок | dmesg -T | grep -i oom | OOM killer | Пересчитать max_children и memory_limit |
| «No space left on device», диск свободен | df -i | Кончились иноды | Вычистить каталог с миллионами мелких файлов |
| df занято, du не находит объём | lsof +L1 | Удалённый, но открытый файл | Перезапустить или reload держащего процесса |
| 502/504 при свободных ресурсах | ss -ltn, статус пула PHP-FPM | Кончились воркеры или backlog | Проверить upstream, увеличить воркеров в пределах RAM |
| Всплеск соединений, ресурсы свободны | ss -s, ss -tan state time-wait | wc -l | Наплыв ботов или отсутствие keep-alive | Разобрать access.log, включить rate limit |
| Медленно только на одном ядре из восьми | top, клавиша 1 | Однопоточная задача | Параллелить или профилировать код, а не добавлять ядра |
| st в vmstat устойчиво больше нуля | vmstat 1 | Steal: переподписка хоста | Обращение к провайдеру, смена тарифа или площадки |
Быстрые меры и правильные: почему апгрейд без диагностики — самый дорогой путь
Быстрые меры нужны, чтобы сайт ожил прямо сейчас. Они не решают причину и требуют возврата к задаче.
- Остановить фоновую задачу, которая совпала с пиком: импорт, бэкап, переиндексацию.
- Включить или прогреть кеш страниц, отдавать анонимным пользователям статику из кеша.
- Ограничить частоту запросов к тяжёлым URL на уровне nginx.
- Временно отключить тяжёлый модуль или плагин, если пик совпал с его включением.
- Убрать из крона всё, что не критично на ближайший час, и развести оставшееся по минутам, а не запускать «в ноль».
Правильные меры дают эффект надолго: индекс на таблицу, кеширование результата вместо повторного вычисления, вынос долгих операций в очередь, профилирование медленного кода, сжатие и отдача статики через CDN, оптимизация запросов. Практический разбор — в материалах о медленной загрузке сайта и оптимизации скорости.
Добавление ресурсов — законный вариант, но последний в списке. Если причина в однопоточном коде, ядра не помогут. Если причина в запросе без индекса, вы просто отложите проблему до следующего роста таблицы, заплатив за это ежемесячно. Апгрейд оправдан, когда диагностика показала честное исчерпание ресурса при уже оптимизированном приложении: например, устойчиво высокий await при упоре в лимит IOPS тарифа. Тогда переход на более быстрые диски или больший план у провайдера — например, у Selectel — решает задачу честно и предсказуемо.
Правило: не меняйте два параметра сразу. Одно изменение — один замер. Иначе через неделю никто в команде не сможет сказать, что именно помогло, а что просто совпало со спадом трафика.
Мониторинг, чтобы в следующий раз увидеть заранее
Разовая диагностика отвечает на вопрос «что сейчас». Она не отвечает на вопрос «когда началось» и «что изменилось». Для этого нужны исторические данные. Минимум, который окупается:
- Сбор системных метрик с хранением хотя бы 30 дней: load, CPU по типам, память и своп, место и иноды, IOPS и await, число соединений.
- Время ответа в логе веб-сервера — как показано выше. Без
$request_timeвы не отличите «сервер медленный» от «медленный один URL». - Слой приложения: медленные запросы к базе, счётчик ошибок 5xx, длина очередей.
- Алерты на симптомы, а не на пороги ресурсов: рост доли 5xx и рост времени ответа важнее, чем «CPU выше 80%». Логика — в разборе золотых сигналов мониторинга.
- Централизованные логи с ротацией — иначе разбор инцидента упрётся в то, что нужные строки уже удалены; см. практики работы с логами.

Как проверить снаружи
Диагностика изнутри показывает ресурсы, но не показывает, что видит пользователь. Внешний замер закрывает этот пробел и заодно отвечает на вопрос, действительно ли проблема на сервере.
- Замерить время ответа и TTFB. Инструмент проверки скорости сайта показывает время до первого байта и полное время загрузки. Высокий TTFB при быстрой отдаче остальной страницы — это ровно то, что вы только что искали внутри: сервер долго думает.
- Зафиксировать падения и деградацию. Мониторинг доступности даёт историю: когда началось, как долго длилось, повторяется ли по расписанию. Регулярные пики по часам — почти всегда крон или бэкап.
- Отделить свою аварию от чужой. Раздел сбоев и инцидентов помогает понять, локальная у вас проблема или у провайдера, CDN либо внешнего API, от которого вы зависите.
- Проверить, не связаны ли всплески с ошибками страниц. Массовые 404 и цепочки редиректов заставляют роботов ходить кругами и создают лишнюю нагрузку — поиск битых ссылок находит такие маршруты.
Полезно совмещать: если внешний замер показывает стабильный TTFB, а пользователи жалуются на медленную загрузку, проблема во фронтенде, а не в нагрузке на сервер.
Частые вопросы
Какая нагрузка на сервер считается нормальной?
Универсального числа нет. Ориентир: load average, делённый на число ядер, устойчиво ниже 0,7 — запас есть; около 1 — работа на пределе; выше 2 — очередь растёт, отклик деградирует. При этом высокий load при нулевом wa и низком времени ответа сайта проблемой не является: сервер занят, но справляется.
Как посмотреть нагрузку на сервер Linux одной командой?
vmstat 1 5 даёт больше всего информации за один вызов: очередь на CPU, блокированные процессы, своп, ввод-вывод и распределение процессорного времени. Если из системы доступен только top, нажмите 1 для разбивки по ядрам и смотрите строку %Cpu(s) и колонку состояния S.
Как проверить свободное место на диске в Linux?
df -h покажет место, df -i — иноды. Проверять нужно обе команды: «диск полон» при видимо свободном месте — это почти всегда кончившиеся иноды. Найти, что занимает объём, помогает du -xh --max-depth=1 / или интерактивный ncdu -x /.
Стоит ли отключать своп на веб-сервере?
Нет. Своп — не причина тормозов, а индикатор нехватки памяти. Без свопа при исчерпании RAM ядро сразу вызывает OOM killer и убивает процесс — обычно самый крупный, то есть базу данных. Разумнее оставить небольшой своп и снизить vm.swappiness до 10.
Как понять, что нагрузку создают боты, а не пользователи?
Разберите access.log: посчитайте запросы по IP и по User-Agent, посмотрите долю обращений к статике. Живой браузер запрашивает CSS, шрифты и изображения, парсер — только HTML. Последовательный обход всех комбинаций параметров фильтра и десятки запросов в секунду с одного адреса — надёжный признак автоматики.
Поможет ли добавление процессоров и памяти?
Только если диагностика показала честное исчерпание именно этого ресурса. При однопоточной задаче лишние ядра не используются, при запросе без индекса память лишь отодвигает проблему. Апгрейд без диагностики — самый дорогой способ отложить решение: он оплачивается каждый месяц.
Чеклист: 15 минут на поиск причины
uptimeиnproc— нормализовать load на число ядер, оценить тренд по трём числам.vmstat 1— определить слой: r (CPU), b и wa (диск), si/so (своп), st (гипервизор).free -h— смотреть available, а не free; проверить, растёт ли использование свопа.dmesg -T | grep -i oom— проверить, не убивал ли OOM killer процессы за последние часы.df -hи обязательноdf -i— место и иноды на всех разделах.du -xh --max-depth=1 /илиncdu -x /, при расхождении сdf—lsof +L1.iostat -x 1— await и aqu-sz;iotop -oPa— кто именно пишет и читает.ss -sиss -ltn— число соединений, переполнение очереди приёма.ps aux --sort=-%cpuи--sort=-%mem— назвать процесс по имени.- Логи: время ответа nginx, slowlog PHP-FPM,
SHOW FULL PROCESSLISTи журнал медленных запросов базы. - access.log: топ IP, топ User-Agent, доля запросов к статике — отделить ботов от людей.
- Внешний замер: скорость, история в мониторинге, контекст в сбоях.
- Записать одно изменение, замерить эффект, только потом делать следующее.