Сервер работает, но медленно. Страницы грузятся с задержкой, база данных тормозит, под нагрузкой всё падает. Прежде чем покупать новое железо или мигрировать на более дорогой тариф — убедитесь, что вы используете текущее на 100%. Эта статья о том, как профилировать, диагностировать и оптимизировать Linux-сервер под реальную нагрузку.
Методология: сначала измеряем, потом меняем
Главная ошибка при оптимизации — начинать с изменений без понимания проблемы. «Давайте увеличим swap» или «добавим RAM» — это стрельба вслепую. Правильный подход: измерить → найти узкое место → устранить → снова измерить.
Инструментарий делится на уровни детализации:
системный уровень → top, htop, vmstat, iostat, sar
процессный уровень → perf, strace, ltrace, lsof
сетевой уровень → ss, netstat, iftop, nethogs, tcpdump
дисковый уровень → iotop, blktrace, fio
профилировщики → perf, bpftrace, flame graphs
CPU: диагностика и оптимизация
Что жрёт процессор
# Базовый top с сортировкой по CPU
top -o %CPU
# htop — интерактивный, красивый
htop
# Детальная картина по ядрам
mpstat -P ALL 1 5 # 5 измерений каждую секунду
# pidstat — статистика по процессам
pidstat -u 1 10 # каждую секунду, 10 раз
Смотрите на us (userspace), sy (kernel), wa (iowait), st (steal — для виртуалок). Высокий wa — проблема с диском или сетью, а не с CPU.
Приоритеты процессов — nice и ionice
# Запустить процесс с низким приоритетом
nice -n 19 ./heavy_backup.sh
# Изменить приоритет запущенного процесса
renice -n 10 -p 1234
# Приоритет I/O (для компилятора в фоне)
ionice -c 3 -p 1234 # класс idle — работает только когда диск свободен
# Совместно
ionice -c 2 -n 7 nice -n 19 make -j$(nproc)
Привязка процессов к ядрам — CPU affinity
Для highload-сервисов критично избегать перепрыгивания между ядрами (cache miss):
# Запустить nginx worker на ядрах 0-3
taskset -c 0-3 nginx
# Изменить для запущенного процесса
taskset -pc 0-3 $(pgrep nginx | head -1)
# Посмотреть текущую привязку
taskset -p 1234
perf — профилировщик ядра
# Установка
apt install linux-tools-generic linux-tools-$(uname -r)
# Общая статистика производительности
perf stat ./your_program
# Найти горячие функции (10 секунд)
perf top -p $(pgrep php-fpm | head -1)
# Запись для flame graph
perf record -F 99 -g -p $(pgrep nginx) -- sleep 10
perf script > perf.out
# Flame graph (нужен Brendan Gregg's flamegraph)
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-perf.pl perf.out | ./FlameGraph/flamegraph.pl > flame.svg
Flame graph — это визуализация стека вызовов. Широкие «языки пламени» = много времени в этой функции.
Память: диагностика и управление
Понимаем использование памяти
# Читаем правильно — free показывает не совсем то, что думаем
free -h
# "available" — реально доступная память, не "free"
# Детальная информация
cat /proc/meminfo
# Кто сколько жрёт
ps aux --sort=-%mem | head -20
# Smem — более точные данные (учитывает shared memory)
apt install smem
smem -tk -s uss | tail -5
Virtual Memory: tuning через sysctl
/etc/sysctl.d/99-vm.conf:
# vm.swappiness — насколько агрессивно использовать swap
# 0 = почти не свопировать (но не отключить совсем)
# 10 = для серверов с достаточной RAM — хорошее значение
# 60 = дефолт, нормально для десктопа
vm.swappiness = 10
# vm.dirty_ratio — процент RAM, при котором начинается синхронный flush на диск
# Для серверов с SSD можно увеличить
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
# Размер таблицы vnodes
vm.vfs_cache_pressure = 50 # меньше = кешируем дольше
# Отключаем OOM killer для критичных процессов
# vm.overcommit_memory = 2 # осторожно, лучше знать что делаете
Huge Pages для баз данных
PostgreSQL, MySQL и Java-приложения хорошо работают с huge pages (2MB вместо 4KB):
# Проверяем текущее состояние
grep -i huge /proc/meminfo
# Transparent Huge Pages (THP) — автоматически
cat /sys/kernel/mm/transparent_hugepage/enabled
# Для баз данных THP лучше отключить!
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# Статические huge pages для PostgreSQL
# В /etc/sysctl.conf:
# vm.nr_hugepages = 1024 # 1024 * 2MB = 2GB
# В PostgreSQL
# huge_pages = on # в postgresql.conf
Диски: I/O оптимизация
Диагностика дисковой подсистемы
# Общая нагрузка на диски в реальном времени
iostat -xz 1
# Ключевые метрики:
# %util — насколько занят диск (>80% = проблема)
# await — среднее время ожидания запроса (мс)
# r/s, w/s — операции чтения/записи в секунду
# Кто именно читает/пишет
iotop -o # только активные процессы
# Детальная трассировка
blktrace -d /dev/sda -o - | blkparse -i -
Планировщик I/O
# Посмотреть текущий scheduler
cat /sys/block/sda/queue/scheduler
# Выбор scheduler:
# none/mq-deadline — для NVMe SSD (hardware очередь хороша)
# mq-deadline — для SATA SSD и HDD
# bfq — для десктопа, справедливое распределение
echo mq-deadline > /sys/block/sda/queue/scheduler
# Закрепить через udev /etc/udev/rules.d/60-scheduler.rules:
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", \
ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", \
ATTR{queue/scheduler}="mq-deadline"
Параметры очереди
# Размер очереди запросов (для NVMe можно больше)
cat /sys/block/nvme0n1/queue/nr_requests
echo 1024 > /sys/block/nvme0n1/queue/nr_requests
# Read-ahead: сколько данных читать заранее
# Для баз данных — уменьшаем, для стриминга файлов — увеличиваем
blockdev --setra 256 /dev/sda # 256 * 512 = 128KB
Тестирование производительности дисков
# fio — промышленный стандарт для тестирования I/O
apt install fio
# Тест случайного чтения 4K (как у базы данных)
fio --name=randread --ioengine=libaio --iodepth=32 \
--rw=randread --bs=4k --direct=1 --size=1G \
--numjobs=4 --runtime=60 --group_reporting
# Последовательная запись (как у лога)
fio --name=seqwrite --ioengine=libaio --iodepth=8 \
--rw=write --bs=1M --direct=1 --size=10G \
--numjobs=1 --runtime=60 --group_reporting
Сеть: тюнинг стека TCP/IP
Диагностика сетевой подсистемы
# Текущие соединения и их состояния
ss -s # краткая статистика
ss -tnp # все TCP соединения с процессами
# Ширина полосы в реальном времени
iftop -i eth0
nethogs eth0 # по процессам
# Статистика сетевого интерфейса
ip -s link show eth0
# Ошибки и дропы
ethtool -S eth0 | grep -i drop
cat /proc/net/dev
Тюнинг TCP стека
/etc/sysctl.d/99-network.conf:
# Размеры буферов сокетов
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 65536
net.core.wmem_default = 65536
# TCP буферы (min, default, max в байтах)
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
# BBR — современный алгоритм контроля перегрузки Google
# Значительно улучшает пропускную способность при потерях
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# TIME_WAIT sockets
net.ipv4.tcp_tw_reuse = 1 # переиспользуем TIME_WAIT сокеты для новых соединений
net.ipv4.tcp_fin_timeout = 15 # уменьшаем время ожидания FIN
# Очередь соединений
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Keep-alive для обнаружения мёртвых соединений
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
# Диапазон портов для исходящих соединений
net.ipv4.ip_local_port_range = 1024 65535
# Проверяем что BBR загружен
sysctl net.ipv4.tcp_congestion_control
lsmod | grep bbr
Настройка сетевого интерфейса
# Проверяем настройки карты
ethtool eth0
# Включаем offloading (перекладываем работу на NIC)
ethtool -K eth0 tso on gso on gro on # TCP/Generic segmentation offload
# Ring buffers — увеличиваем для highload
ethtool -G eth0 rx 4096 tx 4096
# IRQ affinity — привязываем прерывания к ядрам
cat /proc/interrupts | grep eth0
# Пишем скрипт привязки через /proc/irq/N/smp_affinity
Профилирование с bpftrace и eBPF
Современный инструментарий для глубокого анализа без влияния на производительность:
# Установка
apt install bpftrace
# Топ системных вызовов
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }'
# Медленные запросы к диску (>10мс)
bpftrace -e 'tracepoint:block:block_rq_complete {
if (args->nr_sector > 0) {
$latency = (nsecs - @start[args->sector]) / 1000000;
if ($latency > 10) { printf("Slow I/O: %d ms\n", $latency); }
}
}'
# Трассировка TCP retransmits
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[comm] = count(); }'
Профилирование PHP-FPM специфически
Раз уж мы работаем с PHP:
# Статус PHP-FPM (если включён status page)
curl http://localhost/fpm-status?full
# Медленные запросы через slow log
# В php-fpm pool config:
# slowlog = /var/log/php-fpm/slow.log
# request_slowlog_timeout = 5s
# Анализ slow log
sort -t$'\t' -k1 -rn /var/log/php-fpm/slow.log | head -20
# strace на php-fpm worker
strace -c -p $(pgrep php-fpm | head -1) 2>&1 | head -50
Система мониторинга производительности
sar — System Activity Reporter
apt install sysstat
systemctl enable sysstat
# Статистика за сегодня
sar -u # CPU
sar -r # память
sar -b # I/O
sar -n DEV # сеть
# За конкретное время
sar -u -s 10:00:00 -e 11:00:00
# Сохранить историю в файл
sar -A > /tmp/system_report_$(date +%Y%m%d).txt
vmstat — обзор системы одной командой
# Запускаем каждую секунду
vmstat 1
# Колонки:
# procs: r(run queue), b(blocked)
# memory: swpd, free, buff, cache
# io: bi(blocks in), bo(blocks out)
# cpu: us, sy, id, wa, st
Если r > числа CPU — CPU перегружен. Если wa > 20% — проблема с диском.
Тюнинг NUMA-систем
Для серверов с несколькими CPU-сокетами:
# Проверяем топологию NUMA
numactl --hardware
numastat
# Запуск на конкретном NUMA узле
numactl --cpunodebind=0 --membind=0 ./your_app
# Для Redis на NUMA-системе
numactl --interleave=all redis-server
# Автоматический балансировщик NUMA
echo 1 > /proc/sys/kernel/numa_balancing
Чеклист оптимизации
Порядок важен — сначала находим, потом чиним:
Измерить базовые метрики (
top,iostat,free,ss)Определить узкое место (CPU / RAM / Disk I/O / Network)
Детальное профилирование проблемной подсистемы
Применить изменение
Снова измерить — убедиться в улучшении
Применить к продакшну через конфиги (sysctl, udev, systemd)
Помните: преждевременная оптимизация — корень всех зол. Оптимизируйте то, что реально тормозит, с данными в руках.
Create an account or sign in to leave a review
There are no reviews to display.