MHOSTMHOST

Высокая нагрузка на сервер

Если VPS стал отвечать медленно, сайты подвисают, а SSH еле реагирует на ввод — вероятная причина в высокой нагрузке. В этой статье — как измерить нагрузку и понять, много это или мало именно для вашего сервера, как найти конкретный процесс или причину, которая её создаёт (CPU, память или диск), и какое решение подходит: ограничить или остановить процесс, добавить подкачку, оптимизировать конфигурацию — или перейти на тариф с большими ресурсами.

Что понадобится

  • Доступ по SSH под root или пользователем с sudo — все команды ниже выполняются в терминале.
  • Желательно (необязательно) — доступ к панели VMmanager 6: пригодится, чтобы посмотреть графики CPU/RAM/диска за долгий период и, если понадобится, увеличить ресурсы сервера. Если ещё не заходили — см. статью «Как войти в VMmanager 6».
Совет: идите по шагам по порядку — от общей картины (load average) к конкретному процессу, затем к памяти, OOM-убийце и диску. Каждый следующий шаг сужает круг подозреваемых. Если уже понятно, куда копать (например, dmesg сразу показывает срабатывание OOM killer), смело переходите сразу к нужному шагу.

Шаг 1. Оцените load average и сравните его с числом ядер

Быстрее всего понять, насколько сервер загружен, — команда uptime:

bash
uptime

Пример вывода:

14:32:07 up 21 days, 3:12, 2 users, load average: 0.52, 0.58, 0.61

Три числа после load average — это средняя длина очереди процессов, готовых выполняться (состояние R) или ожидающих дисковый ввод-вывод (состояние D), усреднённая за последние 1, 5 и 15 минут. Это не проценты загрузки CPU — это количество процессов, которым в среднем не хватало свободного ядра или диска прямо сейчас.

Сами по себе эти числа ничего не значат без числа ядер CPU на сервере:

bash
nproc

Разделите load average на результат nproc — получится средняя занятость в пересчёте на одно ядро:

  • Все три числа около 1.0 на ядро или ниже — сервер справляется с нагрузкой.
  • 1-минутное значение заметно выше 5- и 15-минутного — короткий всплеск (отработал cron, разовый наплыв запросов), обычно не повод для беспокойства.
  • 5-минутное и особенно 15-минутное значение устойчиво выше 1.0 на ядро — нагрузка не разовая, процессы реально простаивают в очереди за CPU или диском.

Те же три числа видно и в шапке top — отдельно uptime нужен, только если хочется глянуть быстро, не открывая интерактивный интерфейс:

bash
top

По умолчанию top показывает загрузку CPU одной суммарной строкой %Cpu(s). Нажмите 1, чтобы переключиться на отдельную строку по каждому ядру — так сразу видно, размазана нагрузка равномерно или упирается в одно-единственное ядро (типичный симптом однопоточного узкого места, когда общий load average в пересчёте на все ядра выглядит невысоким, а реально всё упирается в одно ядро).

Совет: поскольку в load average считаются и процессы в состоянии D (ожидание диска), высокий load average не всегда означает, что занят именно CPU — причиной может быть и диск (Шаг 5), и нехватка памяти, из-за которой система активно уходит в своп (Шаг 3).

Шаг 2. Найдите процесс, который нагружает сервер

top по умолчанию сортирует процессы по %CPU — самый нагруженный процесс сразу окажется наверху:

bash
top

Для снимка без интерактивного режима — удобно скопировать вывод или сохранить в лог:

bash
top -bn1 | head -20

Если предпочитаете более наглядный интерфейс — поставьте htop:

Ubuntu / Debian:

bash
sudo apt install htop

AlmaLinux / Rocky (htop не входит в стандартные репозитории — сначала подключите EPEL):

bash
sudo dnf install -y epel-release
sudo dnf install -y htop
bash
htop

По умолчанию htop тоже сортирует по %CPU и сразу показывает отдельную шкалу загрузки для каждого ядра вверху экрана. Клавиша F6 открывает выбор поля сортировки (например, по памяти), F9 — отправить процессу сигнал (в том числе завершить) прямо из списка, не выходя в отдельный терминал.

Без интерактивного режима, для сортировки по CPU или по памяти отдельно:

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

Из всех столбцов вывода в первую очередь важны PID, %CPU, %MEM и COMMAND. Столбец STAT использует те же обозначения состояния, что и S в top: R — выполняется, D — ожидает дисковый ввод-вывод (если видите здесь D у процесса из верхних строк — вероятная причина не CPU, а диск, см. Шаг 5), S — спит, Z — зомби.

По тому, как выглядит список, можно сразу понять направление:

  • Один процесс явно выделяется по %CPU или %MEM на фоне остальных — «сбежавший» процесс, см. Шаг 6, «Один процесс вышел из-под контроля».
  • Нагрузка размазана по десяткам однотипных процессов (много php-fpm, node, воркеров вашего веб-сервера или приложения) — это не поломка одного процесса, а реальная нагрузка от трафика или не подстроенная под сервер конфигурация приложения, см. Шаг 6, «Легитимная нагрузка или плохо настроенное ПО».
Готово: как только определили PID и имя процесса-виновника (если он один) — держите его в уме, он понадобится в Шаге 6.

Шаг 3. Проверьте память и swap

Даже если с CPU всё в порядке, нехватка оперативной памяти тормозит весь сервер целиком — через активный своп.

bash
free -h

Пример вывода:

               total        used        free      shared  buff/cache   available
Mem:            3.8Gi       2.9Gi       120Mi        45Mi       900Mi       650Mi
Swap:           2.0Gi       1.6Gi       412Mi

Смотрите на колонку available в строке Mem — это не «свободная» память в буквальном смысле, а оценка того, сколько реально можно выделить новым процессам без ухода в своп (с учётом кеша страниц, который ядро может вытеснить). Если available мало, а в строке Swap значение used велико и продолжает расти — это давление на память.

Проверьте, вообще настроен ли своп на сервере:

bash
swapon --show

Пустой вывод означает, что подкачки нет вообще — тогда при нехватке RAM у ядра остаётся только один выход, описанный в Шаге 4.

Активность свопа прямо сейчас показывает vmstat:

bash
vmstat 1 5

Колонки si и so — сколько килобайт в секунду читается из подкачки и пишется в неё. Устойчиво ненулевые значения означают, что сервер прямо сейчас активно свопит: доступ к «памяти» на деле идёт через диск, который на порядки медленнее RAM, — отсюда и общее ощущение подвисания, даже если %CPU отдельных процессов выглядит не так уж высоко.

Готово: если своп активно используется и si/so не в нуле — переходите к Шагу 4, чтобы проверить, не срабатывал ли уже OOM killer, а затем к Шагу 6, «Не хватает оперативной памяти».

Шаг 4. Проверьте, не сработал ли OOM killer

Когда оперативная память и своп заканчиваются полностью, ядро Linux не просто тормозит — оно принудительно завершает один из процессов, чтобы система вообще осталась жива (OOM killer, Out-Of-Memory killer). Это объясняет внезапные падения именно самого «тяжёлого» процесса (часто СУБД или другого сервиса с большим потреблением памяти) без единой ошибки в логах самого приложения.

bash
sudo dmesg -T | grep -Ei 'out of memory|killed process'

Пример строки в выводе:

[Thu Jul  9 11:47:02 2026] Out of memory: Killed process 18342 (mysqld) total-vm:2145392kB, anon-rss:1583020kB, file-rss:0kB, shmem-rss:0kB, UID:112 pgtables:3456kB oom_score_adj:0
Проверьте: конкретный набор полей в строке (total-vm, anon-rss, UID, pgtables и т.д.) немного отличается в зависимости от версии ядра, но сама фраза Out of memory: Killed process PID (имя) стабильна на всех современных ядрах и однозначно указывает на срабатывание OOM killer.

Кольцевой буфер dmesg очищается при перезагрузке сервера. Чтобы поискать такие события за более долгий период, используйте системный журнал — те же сообщения ядра, но хранятся между перезагрузками:

bash
sudo journalctl -k --no-pager | grep -Ei 'out of memory|killed process'

Если такие записи нашлись — это подтверждает, что в этот момент именно память (а не CPU или диск) была узким местом. Переходите к Шагу 6, «Не хватает оперативной памяти», а при повторяющихся случаях — к «Ресурсов объективно не хватает — апгрейд тарифа».

Шаг 5. Проверьте дисковую нагрузку (I/O)

Высокая нагрузка иногда упирается не в CPU и не в память, а в сам диск — процессы простаивают в очереди в состоянии D, ожидая, пока диск обработает чтение или запись.

Утилита iostat идёт в составе пакета sysstat — он есть в стандартных репозиториях всех целевых дистрибутивов, EPEL не требуется:

Ubuntu / Debian:

bash
sudo apt install sysstat

AlmaLinux / Rocky:

bash
sudo dnf install -y sysstat

Запустите с расширенной статистикой:

bash
iostat -xz 1 3

-x — расширенная статистика (в том числе %util и await), -z — не показывать диски без активности за интервал, 1 3 — три отчёта с интервалом в 1 секунду. Первый отчёт iostat всегда показывает средние значения с момента загрузки сервера — ориентируйтесь на второй и последующие отчёты, которые отражают именно текущий интервал.

Пример (сокращённо — показаны не все столбцы реального вывода):

Device            r/s     w/s   %util   await
vda              12.40   58.20   97.50   42.30
  • %util — доля времени, в течение которого к диску были обращения. Для одиночного диска значение, близкое к 100%, почти всегда значит, что диск — узкое место. Для RAID-массива из нескольких дисков этот показатель не так однозначен: устройство может обслуживать несколько запросов параллельно.
  • await — среднее время выполнения запроса в миллисекундах, включая и ожидание в очереди, и само обслуживание. На SSD/NVMe в норме это единицы миллисекунд; устойчивые десятки-сотни миллисекунд — явный признак, что диск не справляется.
Готово: если %util стабильно близко к 100%, а await заметно вырос — узкое место диск, а не CPU. Смотрите Шаг 6, «Легитимная нагрузка или плохо настроенное ПО» (например, СУБД без индексов или бэкап, попавший в пик нагрузки) либо «Ресурсов объективно не хватает — апгрейд тарифа».

Шаг 6. Частые причины и что делать

Один процесс вышел из-под контроля

Если в Шаге 2 нашёлся конкретный процесс-виновник — сначала попробуйте завершить его штатно:

bash
kill -TERM 18342

Если через несколько секунд процесс всё ещё жив — завершите принудительно:

bash
kill -9 18342

Если это управляемая через systemd служба (веб-сервер, СУБД и подобное), правильнее не убивать процесс напрямую, а перезапустить саму службу — так она поднимется в чистом состоянии:

bash
sudo systemctl restart mysql

Имя службы у вас может быть другим — например, если это СУБД, см. статьи «MySQL / MariaDB: установка и настройка» или «Redis: установка и настройка».

Если убивать процесс не хочется, а хочется просто сделать его менее жадным, понизьте ему приоритет CPU:

bash
sudo renice -n 15 -p 18342

Диапазон значений — от -20 (наивысший приоритет) до 19 (низший). Процесс не завершается, но при конкуренции за CPU уступает остальным.

Чтобы жёстко ограничить долю CPU (например, не больше 50% одного ядра), не трогая приоритет, используйте cpulimit:

Ubuntu / Debian:

bash
sudo apt install cpulimit

AlmaLinux / Rocky (нужен EPEL — если уже подключали его для htop, повторно подключать не требуется):

bash
sudo dnf install -y epel-release
sudo dnf install -y cpulimit
bash
cpulimit -p 18342 -l 50

cpulimit работает в реальном времени на переднем плане. Чтобы ограничение продолжало действовать и после закрытия SSH-сессии, запустите его в фоне: cpulimit -p 18342 -l 50 &.

Для systemd-службы такое ограничение надёжнее задать самому systemd — оно переживёт перезапуск процесса и не требует отдельно висящей утилиты:

bash
sudo systemctl set-property mysql.service CPUQuota=50% MemoryMax=1G

Применяется сразу, без перезапуска службы, и сохраняется после перезагрузки сервера.

Не хватает оперативной памяти

Важно: перед созданием файла подкачки убедитесь, что на диске достаточно места:
bash
df -h /

Если места впритык или не хватает — сначала разберитесь со статьёй «Диск заполнен».

Создайте файл подкачки (размер 2G — пример, подберите под объём памяти и свободное место на вашем сервере):

bash
sudo fallocate -l 2G /swapfile
Совет: если fallocate вернул ошибку (бывает на некоторых файловых системах), используйте dd — он медленнее, но работает всегда:
bash
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress

Ограничьте права на файл, инициализируйте его как своп и включите:

bash
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Чтобы своп подключался автоматически после перезагрузки, добавьте запись в /etc/fstab:

bash
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Проверьте результат:

bash
sudo swapon --show
free -h

Дополнительно можно снизить vm.swappiness — параметр ядра (по умолчанию 60, диапазон 0200), который определяет, насколько охотно ядро уходит в своп вместо того, чтобы вытеснять кеш страниц:

bash
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
Важно: своп — не замена нехватающей оперативной памяти, а подстраховка от аварийного завершения процессов OOM killer'ом. Он на порядок медленнее RAM, так что устойчиво активный своп почти всегда означает «пора оптимизировать или переходить на тариф с большим объёмом RAM», а не нормальное рабочее состояние сервера.

Легитимная нагрузка или плохо настроенное ПО

bash
crontab -l
bash
systemctl list-timers

Если совпало — перенесите тяжёлые задачи на менее загруженное время суток: «Автобэкапы по расписанию (cron)», «Инкрементальные бэкапы с restic», «Резервное копирование с rsync».

  • Запросы к базе без индексов, N+1-запросы, отсутствие кеширования — самая частая причина ситуации «трафик вроде бы не вырос, а нагрузка выросла». Это уже вопрос кода и конфигурации приложения, а не самого VPS.

Ресурсов объективно не хватает — апгрейд тарифа

Если после отключения/ограничения лишних процессов и оптимизации сервер всё равно стабильно упирается в потолок под реально необходимой нагрузкой — вероятно, пора расти вместе с проектом, а не бороться с симптомами.

  • Посмотрите графики CPU/RAM/диска за последние дни или недели в карточке сервера в VMmanager 6 — если нагрузка стабильно высокая, а не разовый всплеск, это и есть сигнал, что текущему тарифу не хватает ресурсов на ваш проект.
📷 [скриншот: график загрузки CPU/RAM сервера в карточке VPS в VMmanager 6]
  • Апгрейд vCPU, RAM или диска оформляется в личном кабинете my.mhost.ee (либо через поддержку MHost).

Если сервер похоже взломан

Иногда то, что выглядит как «нагрузка от трафика», на самом деле не имеет отношения ни к вашему приложению, ни к легитимным посетителям — сервер может быть взломан и использоваться для майнинга криптовалюты, рассылки спама или участия в DDoS-атаках. Признаки, что дело не в конфигурации, а во взломе:

  • В списке процессов (Шаг 2) — незнакомое имя, которое вы не устанавливали, особенно запущенное из /tmp, /var/tmp или /dev/shm.
  • Процесс завершаете (kill -9), а через несколько секунд он появляется снова — с тем же или другим PID.
  • Резко выросла исходящая сетевая активность без видимой причины:
bash
sudo ss -tnp
  • В crontab -l или файлах /etc/cron.d/ есть задания, которые вы точно не добавляли.
Важно: если увидели что-то из перечисленного — не тратьте время на тонкую настройку производительности, это уже вопрос не оптимизации, а безопасности. Пройдите статью «Чеклист базовой защиты VPS». В по-настоящему запущенных случаях самый надёжный вариант — не «вычищать» скомпрометированную систему вручную, а переустановить ОС с нуля через VMmanager (см. «Переустановка ОС и выбор шаблона») и восстановить из бэкапа только данные, а не системное окружение целиком.

Что дальше

Если после всех шагов сервер всё равно периодически «проседает» именно под пиковой, но реально необходимой нагрузкой — скорее всего, дело не в разовой поломке, а в том, что текущему тарифу пора расти вместе с проектом (см. Шаг 6 выше). Смежные темы: если из-за нагрузки сайт не просто тормозит, а не открывается совсем — статья «Сайт не открывается»; если не запускается сама служба, а не просто потребляет много ресурсов, — статья «Служба не запускается»; если больше похоже на нехватку места на диске, а не CPU/RAM, — статья «Диск заполнен».