Фраза «сервер тормозит» ничего не значит, пока ты не понял, что именно ограничивает работу: процессор, память, диск или сеть. Новички обычно сразу лезут менять настройки ядра, и это почти всегда ошибка. Сначала измеряем, потом находим узкое место, и только потом, если нужно, крутим параметры. В этой статье разберём стандартный набор инструментов: top и его собратья, iostat, vmstat, perf и тюнинг через sysctl.

Примеры идут на сервере srv-linux01 (192.168.10.11) в сети 192.168.10.0/24, домен itpages.lab.local. Набор утилит ставится из пакетов sysstat, procps и linux-tools.

Шаг 1. Метод USE: с чего начинать

Есть простая рамка, называется USE: для каждого ресурса смотри Utilization (занятость), Saturation (очередь ожидания) и Errors (ошибки). Занятость процессора на 100 процентов ещё не беда, если очередь пуста. А вот очередь из двадцати процессов на четырёх ядрах уже симптом. Поэтому начинаем с общей картины за тридцать секунд:

uptime
dmesg -T | tail -20
vmstat 1 5
mpstat -P ALL 1 3
iostat -xz 1 3
free -m
sar -n DEV 1 3

Эти семь команд отвечают на главный вопрос: куда смотреть дальше. Загрузка в uptime (load average) показывает среднее число процессов, которые работают или ждут процессор, и в Linux ещё и тех, что ждут диск. Сравнивай её с числом ядер: nproc. Если на четырёх ядрах loadavg держится около 4, ресурс загружен полностью, а если 16, перед тобой очередь.

Шаг 2. Читаем top правильно

Запусти top и нажми несколько клавиш, которые превращают его из пёстрой картинки в инструмент: 1 раскрывает отдельные строки по ядрам, M сортирует по памяти, P по процессору, c показывает полную команду, H отдельные потоки.

top -b -n 1 | head -15
top -b -n 1 -o %MEM | head -12
ps -eo pid,ppid,user,%cpu,%mem,stat,etime,cmd --sort=-%cpu | head

В верхней строке с процессором обращай внимание на поля: us (пользовательские процессы), sy (ядро), wa (ожидание ввода-вывода), st (кража времени гипервизором). Высокий wa говорит о диске, высокий sy о системных вызовах или прерываниях, высокий st о том, что виртуалку обделили на хосте, и тут тюнинг внутри гостя не поможет. Для живого обзора удобен htop, а для тех, кто любит историю, atop, который пишет снимки в журнал и позволяет посмотреть, что было вчера в три ночи.

Шаг 3. Память: свободно ли на самом деле

Команда free путает новичков колонкой free. В Linux свободная память намеренно используется под кэш файлов, поэтому смотри на available:

free -h
vmstat -s | head -12
cat /proc/meminfo | grep -E 'MemAvailable|Dirty|Writeback|SwapCached'
vmstat 1 5

В выводе vmstat колонки si и so показывают обмен со свопом, и если они стабильно ненулевые, системе реально не хватает памяти. Единичные килобайты не страшны, а постоянные мегабайты в секунду означают, что приложение ползёт. Тогда ищи потребителей: ps aux --sort=-rss | head.

Шаг 4. Диск: iostat и очередь запросов

Для диска главная команда это iostat с расширенной статистикой:

iostat -xz 1 5
sudo iotop -oPa
sudo pidstat -d 1 5

Колонки, которые важны: r/s и w/s (операции в секунду), rkB/s и wkB/s (пропускная способность), await (среднее время операции в миллисекундах вместе с очередью), aqu-sz (средняя длина очереди) и %util (занятость устройства). Для обычных дисков await в районе единиц миллисекунд нормален, десятки это уже боль. Для NVMe %util около 100 процентов ещё не значит насыщения, потому что устройство обрабатывает много запросов параллельно, и ориентироваться стоит именно на await и очередь. Чтобы найти, кто мучает диск, используй iotop или pidstat -d, они показывают процессы, а не просто цифры по устройству.

Если проблема в записи, проверь планировщик и параметры монтирования:

cat /sys/block/sda/queue/scheduler
findmnt -o TARGET,SOURCE,OPTIONS /var
mount | grep noatime

Опция noatime в fstab отключает запись времени последнего чтения и заметно разгружает диск на серверах с множеством мелких файлов.

Шаг 5. Процессор: perf и поиск горячих точек

Когда понятно, что упёрлись в процессор, perf покажет, на что тратятся циклы. Ставится так: sudo apt install -y linux-tools-common linux-tools-$(uname -r).

sudo perf top
sudo perf stat -p $(pidof nginx | cut -d' ' -f1) sleep 10
sudo perf record -F 99 -g -p $(pidof nginx | cut -d' ' -f1) -- sleep 30
sudo perf report --stdio | head -40

perf top в реальном времени показывает функции, которые больше всего грузят процессор. Запись с флагом -g собирает стеки вызовов, а perf report разбирает их по долям. Частота 99 герц выбрана намеренно: она не совпадает с круглыми частотами таймеров и даёт неискажённую выборку. Если вместо имён функций видишь шестнадцатеричные адреса, не хватает символов отладки приложения, поставь пакет с суффиксом dbgsym или dbg.

Для быстрой оценки без perf есть и трассировка системных вызовов: sudo strace -c -p PID -f за пару секунд покажет, на каких вызовах процесс проводит время. Помни, что strace заметно тормозит цель, так что на боевом сервисе применяй коротко.

Шаг 6. Сеть: быстрая проверка

Сетевое узкое место тоже встречается, особенно за балансировщиком. Смотри на ошибки и переполнения очередей:

ss -s
ss -tin state established | head
sar -n DEV,EDEV 1 3
nstat -az | grep -E 'Retrans|ListenOverflows|ListenDrops'
ip -s link show eth0

Рост ListenOverflows означает, что очередь входящих соединений приложения переполнена, и пора увеличить backlog вместе с somaxconn. Много ретрансмитов говорит о потерях на пути, а не о перегрузке самого сервера.

Шаг 7. Тюнинг через sysctl

Только теперь, имея данные, переходим к параметрам ядра. Менять их надо по одному и через отдельный файл, чтобы всегда можно было откатиться:

sudo tee /etc/sysctl.d/90-itpages-tuning.conf <<'EOF'
# своп используем только в крайнем случае
vm.swappiness = 10
# раньше начинать сброс грязных страниц, чтобы не было пиков записи
vm.dirty_background_ratio = 5
vm.dirty_ratio = 15
# очередь входящих соединений
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
# диапазон локальных портов для исходящих соединений
net.ipv4.ip_local_port_range = 10240 65000
# лимит файловых дескрипторов
fs.file-max = 1048576
EOF
sudo sysctl --system
sysctl vm.swappiness net.core.somaxconn

Пара комментариев. Swappiness 10 не отключает своп, а лишь просит ядро реже выгружать память приложений в пользу кэша, и для серверов баз данных это стандартная рекомендация. Параметры dirty регулируют, когда ядро начинает писать накопленные в памяти изменения на диск: слишком большие значения приводят к тому, что вся запись накапливается и потом выливается разом с зависанием на секунды. Для сети мы увеличили очереди, но само приложение (nginx, haproxy) тоже должно просить большой backlog в своей конфигурации, иначе параметр ядра ничего не изменит.

Лимиты процессов отдельно задаются через systemd. Для службы это выглядит так:

sudo systemctl edit nginx
# в открывшемся файле:
[Service]
LimitNOFILE=65536
sudo systemctl restart nginx
cat /proc/$(pidof -s nginx)/limits | grep 'open files'

Шаг 8. Сравниваем до и после

Любой тюнинг без измерений это гадание. Перед правкой сохрани базовую картину и повторяй тот же тест после:

mkdir -p ~/perf-baseline
vmstat 1 30 > ~/perf-baseline/vmstat-before.txt
iostat -xz 1 30 > ~/perf-baseline/iostat-before.txt
ab -n 10000 -c 100 http://192.168.10.11/ | grep -E 'Requests per second|Time per request'

Если после изменения показатели не улучшились, откатывай его без сожаления: удали файл из /etc/sysctl.d и выполни sudo sysctl --system, а для немедленного отката значение можно вернуть командой sudo sysctl -w со старой величиной.

Шаг 9. Профилирование приложений и ограничение ресурсов

Иногда сервер здоров, а проблему создаёт один сервис, который забирает всё. Вместо борьбы за ресурсы глобально выдели каждой службе её долю через cgroups, которыми управляет systemd. Это проще, чем кажется:

sudo systemctl set-property --runtime app-worker.service CPUQuota=200% MemoryMax=2G IOWeight=50
systemd-cgtop -d 2
systemctl show app-worker.service -p CPUQuotaPerSecUSec -p MemoryMax

CPUQuota=200% разрешает службе занимать не более двух ядер, а IOWeight делит дисковую ёмкость пропорционально между службами. Команда systemd-cgtop работает как top, но показывает потребление по группам, и сразу видно, какая служба «ест» ресурсы. Без ключа —runtime настройки сохраняются в drop-in файле и переживают перезагрузку.

Для приложений с интерпретатором полезны и штатные профилировщики: py-spy для Python (sudo py-spy top --pid PID), jstack и async-profiler для Java, pprof для Go. Они показывают, какая именно функция сжигает процессорное время, и часто дают больше, чем любые настройки ядра. Если код менять нельзя, помогает кэширование на уровне nginx, увеличение пула соединений к базе или перенос тяжёлой работы в очередь.

Шаг 10. Диск и файловая система: тонкая настройка

Для дисков, кроме планировщика и noatime, важны опции файловой системы и размеры очередей. Проверь выравнивание и размер блока командой sudo tune2fs -l /dev/sda1 | grep -E 'Block size|Features' для ext4 или xfs_info /srv для XFS. Для баз данных обычно рекомендуют XFS или ext4 с отдельным разделом под данные, чтобы журналы приложения не конкурировали за те же операции.

cat /sys/block/nvme0n1/queue/nr_requests
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
sudo blockdev --getra /dev/sda
sudo blockdev --setra 4096 /dev/sda

Для NVMe разумно выставить планировщик none, потому что у самого устройства уже своя очередь. Для вращающихся дисков под последовательное чтение полезно увеличить read-ahead, но для случайной нагрузки базы данных это лишь забивает кэш. Чтобы настройка пережила перезагрузку, оформи её правилом udev в /etc/udev/rules.d, а не вызовом в rc.local.

Шаг 11. Прозрачные большие страницы и NUMA

Две настройки, о которых часто говорят в связи с базами данных. Прозрачные огромные страницы (THP) иногда дают рывки задержки у Redis и MongoDB, и их рекомендуют отключать: echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled. На многопроцессорных серверах проверь NUMA командой numactl --hardware и привязывай тяжёлые процессы к узлу с их памятью. Но и здесь действует главное правило статьи: сначала измерь задержку и покажи, что проблема именно в этом, и только потом меняй настройки на боевых машинах.

Частые ошибки и как их избежать

  • Тюнинг без измерений. Копирование «оптимальных» sysctl из интернета нередко ухудшает ситуацию: чужие параметры написаны под другое железо и нагрузку. Сначала зафиксируй базовые показатели, меняй один параметр за раз и сравнивай результат.
  • Неверное чтение load average и free. Загрузка 8 на 16 ядрах не проблема, а 8 на двух ядрах проблема, и лишь только available в free говорит о настоящей свободной памяти. Не паникуй от маленького free и не игнорируй стабильный своп, смотри на si и so в vmstat.
  • Решение не на том уровне. Высокий st означает проблему гипервизора, высокий wa диск или хранилище, а высокий us код приложения, и никакие параметры ядра не заменят оптимизацию запроса или индекса. Определи слой по top и iostat, а затем идёшь к тому, кто им владеет.

Что дальше

Администрирование Linux — это набор связанных навыков, вот соседние темы серии: