Привет, Хабр! Уверен, многие сталкивались с тормозами сервера, долгой загрузкой страниц. Логи молчат, нужно искать виновника. Системный мониторинг демонстрирует, что CPU вроде не загружен, память не полностью израсходована, а отклик системы оставляет желать лучшего.
В такие моменты стандартных утилит вроде top
или htop
часто недостаточно, нужен более детальный анализ. С этим мне приходится периодически сталкиваться, из-за чего и были написаны 3 bash-скрипта. Они дают сбор ключевых метрик системы для дальнейшего разбора.
Поиск аномалий памяти
Первый скрипт выходит за рамки простого показа free -m
. Его задача - найти процессы, которые ведут себя подозрительно: едят память слишком быстро, удерживают её без необходимости или создают чрезмерную нагрузку на подсистему ввода-вывода.
memory_analysis.sh
:
#!/bin/bash
echo "=== Анализ памяти и процессов-кандидатов на OOM Killer ==="
echo "Временная метка: $(date)"
echo ""
# 1. Общая картина по памяти
echo "1. Сводка по памяти:"
echo "-------------------"
free -h
echo ""
# 2. Детализация по оперативной памяти
echo "2. Детализация использования RAM и Swap:"
echo "----------------------------------------"
cat /proc/meminfo | grep -E "(MemTotal|MemAvailable|SwapTotal|SwapFree|SwapCached)"
echo ""
# 3. Поиск процессов, потребляющих много памяти
# Сортируем по резидентной памяти (RSS), которая реально находится в RAM
echo "3. Первые 10 процессов по использованию резидентной памяти (RSS):"
echo "-------------------------------------------------------------"
ps aux --sort=-%mem | awk 'NR<=11 {printf "%-8s %-6s %-4s %-8s %-8s %s\n", $2, $1, $4, $3, $6/1024" MB", $11}'
echo ""
# 4. Анализ памяти, которая ждет записи на диск
# Это может быть индикатором нагрузки на I/O
echo "4. Процессы с большим объемом ожидающей записи памяти:"
echo "------------------------------------------------------------------"
for pid in $(ps -eo pid --no-headers); do
if [ -f /proc/$pid/statm ]; then
dirty_pages=$(grep -oP '^[0-9]+\s+[0-9]+\s+\K[0-9]+' /proc/$pid/statm 2>/dev/null)
if [ -n "$dirty_pages" ] && [ "$dirty_pages" -gt 1000 ]; then
proc_name=$(cat /proc/$pid/comm 2>/dev/null)
dirty_kb=$((dirty_pages * 4)) # переводим страницы в килобайты (обычно 4KB на страницу)
echo "PID: $pid, Имя: $proc_name, Грязная память: $dirty_kb KB"
fi
fi
done | sort -k6 -nr | head -10
echo ""
# 5. Мониторинг "давления" памяти (PSI - Pressure Stall Information)
# Показывает, сколько времени процессы проводят в ожидании памяти
echo "5. Давление на память (PSI):"
echo "---------------------------"
if [ -f /proc/pressure/memory ]; then
cat /proc/pressure/memory
else
echo "Информация о ''давлении'' памяти не поддерживается в этой версии ядра."
fi
echo ""
Как это работает и на что смотреть:
Пункты 1 и 2 дают нам общую отправную точку. Важно смотреть не на
used
, а наavailable
илиfree + buffers/cache
. Еслиavailable
память иссякает, система начинает активно использовать swap.Пункт 3 - в целом, классика. Но здесь сортировка идет именно по резидентной памяти. Столбец
RSS
показывает, сколько физической RAM реально занимает процесс. Резкий рост RSS у одного из процессов - явный сигнал.Пункт 4 - это уже глубокая диагностика. Большое скопление такой памяти у одного процесса может указывать на то, что он генерирует много данных и не успевает их сбрасывать, создавая нагрузку на диск подсистему. Если этот показатель постоянно высокий, стоит проверить настройки сброса «грязных» страниц (
/proc/sys/vm/dirty_ratio
иdirty_background_ratio
).Пункт 5 (Pressure Stall Information) - современная и очень точная метрика. Он показывает, в какой степени процессы были вынуждены простаивать из-за нехватки памяти. Если значение
full
растет, это прямой признак того, что памяти физически не хватает и это уже бьет по производительности.
Пример вывода:

Pressure Stall Information (PSI)
Pressure Stall Information (PSI) - индикатор перегрузки системы.
Когда PSI регистрирует рост показателей - значит, что процессы начали скапливаться в очередях. Например, если данные о памяти показывают увеличение значений «full» - прямой сигнал о том, что системе не хватает оперативной памяти, и процессы вынуждены долго ждать, пока освободится место.
В отличие от классических метрик (таких как загрузка CPU или свободная память), PSI измеряет не объём ресурсов, а время ожидания. Это позволяет заранее заметить проблемы - ещё до того, как сервер начнет «лагать» или приложения перестанут отвечать.
Глубокий анализ дискового ввода-вывода
Второй скрипт призван найти не просто большие файлы, а именно те процессы, которые создают основную нагрузку на дисковую подсистему прямо сейчас.
io_analyzer.sh
:
#!/bin/bash
echo "=== Анализ дискового ввода-вывода и поиск файловых дескрипторов ==="
echo "Временная метка: $(date)"
echo ""
# 1. Общая статистика ввода-вывода с помощью iostat
echo "1. Общая статистика I/O (первые 3 секунды - усреднение, потом live):"
echo "------------------------------------------------------------------"
if command -v iostat &> /dev/null; then
iostat -dx 1 3
else
echo "Утилита 'iostat' не установлена. Установите пакет 'sysstat'."
fi
echo ""
# 2. Использование места на диске точками монтирования
echo "2. Использование дискового пространства:"
echo "---------------------------------------"
df -h | sort -k5 -hr
echo ""
# 3. Поиск процессов, ведущих активную дисковую деятельность
# Используем pidstat, если доступен
echo "3. Процессы с активной дисковой нагрузкой (KB/sec):"
echo "--------------------------------------------------"
if command -v pidstat &> /dev/null; then
pidstat -dl 1 1 | sort -k6 -nr | head -15
else
echo "Утилита 'pidstat' не установлена. Установите пакет 'sysstat'."
echo "Альтернатива: использование /proc//io (более сложный парсинг)."
fi
echo ""
# 4. Поиск процессов, удерживающих открытыми много файловых дескрипторов
# Это может указывать на утечку дескрипторов или на процесс, работающий с огромным количеством файлов
echo "4. Первые 10 процессов по количеству открытых файловых дескрипторов:"
echo "---------------------------------------------------------------"
for pid in $(ps -eo pid --no-headers); do
if [ -d /proc/$pid/fd ]; then
fd_count=$(ls -1 /proc/$pid/fd 2>/dev/null | wc -l)
proc_name=$(ps -p $pid -o comm= 2>/dev/null)
if [ -n "$proc_name" ]; then
echo "PID: $pid, Имя: $proc_name, FD: $fd_count"
fi
fi
done | sort -t',' -k3 -nr | head -10
echo ""
# 5. Поиск больших файлов в открытых дескрипторах
# Может помочь найти процесс, который ведет запись в огромный лог или временный файл
echo "5. Процессы с открытыми большими файлами (>100MB):"
echo "-------------------------------------------------"
for pid in $(ps -eo pid --no-headers); do
if [ -d /proc/$pid/fd ]; then
for fd in /proc/$pid/fd/*; do
file_size=$(stat -Lc%s "$fd" 2>/dev/null)
if [ -n "$file_size" ] && [ "$file_size" -gt 104857600 ]; then # 100MB в байтах
file_name=$(readlink -f "$fd" 2>/dev/null)
proc_name=$(ps -p $pid -o comm= 2>/dev/null)
file_size_mb=$((file_size / 1024 / 1024))
echo "PID: $pid, Процесс: $proc_name, Файл: $file_name, Размер: ~$file_size_mb MB"
fi
done
fi
done
Как это работает и на что смотреть:
Пункт 1 (
iostat
). Ключевые колонки:%util
(процент загрузки устройства, близкий к 100% означает, что диск - узкое место),await
(среднее время ожидания I/O-операции, в мс). Высокийawait
при высоком%util
- явный признак перегруженности диска.Пункт 3 (
pidstat -dl
) - показывает, сколько килобайт в секунду каждый процесс читает и пишет на диск. Это позволяет сразу идентифицировать самого активного виновника.Пункт 4 - наверное больше про "тихие" проблемы. Процесс, который открыл тысячи файловых дескрипторов и не закрывает их, может со временем исчерпать лимиты системы (
ulimit -n
), что приведет к ошибкам у этого и других процессов.Пункт 5. Иногда процесс (часто это СУБД или кэширующий демон) может открыть огромный файл для чтения/записи. Этот цикл может найти такой процесс и показать, с каким именно файлом он работает. Нередко это указывает на неверно настроенные логи или временные файлы, разросшиеся до нескольких ГБ.
Пример вывода:


Детектор сетевых аномалий
Третий скрипт фокусируется на сетевой активности. Он помогает найти процессы, устанавливающие подозрительно много соединений, или обнаружить неожиданно высокий сетевой трафик.
network_analysis.sh
:
#!/bin/bash
echo "=== Анализ сетевых соединений и трафика ==="
echo "Временная метка: $(date)"
echo ""
# 1. Статистика по сетевым интерфейсам
echo "1. Статистика по сетевым интерфейсам:"
echo "------------------------------------"
if command -v ip &> /dev/null; then
ip -s link
else
netstat -i
fi
echo ""
# 2. Сводка по установленным соединениям
echo "2. Количество соединений по состояниям:"
echo "--------------------------------------"
netstat -tunl | awk '/^tcp|^udp/ {state=$6} END {
for (s in state) print s, state[s]
}' | sort -rn -k2
echo ""
# 3. Процессы, имеющие много сетевых соединений
echo "3. Первые 10 процессов по количеству сетевых соединений:"
echo "---------------------------------------------------"
netstat -tunp 2>/dev/null | awk '$6=="ESTABLISHED"{print $7}' | cut -d'/' -f1 | sort | uniq -c | sort -rn | head -10 | while read count pid; do
if [ -n "$pid" ] && [ "$pid" != "-" ]; then
proc_name=$(ps -p $pid -o comm= 2>/dev/null)
echo "Соединений: $count, PID: $pid, Процесс: $proc_name"
fi
done
echo ""
# 4. Поиск процессов, слушающих нестандартные порты
echo "4. Слушающие порты (исключая стандартные 22, 80, 443, 5432 и т.д.):"
echo "------------------------------------------------------------------"
netstat -tunlp | grep LISTEN | while read proto recvq sendq local_addr foreign_addr state pid_program; do
port=$(echo $local_addr | awk -F: '{print $NF}')
# Исключаем некоторые стандартные порты для чистоты картины
if [[ $port =~ ^(22|80|443|53|25|587|993|995|5432|3306|27017|11211|6379)$ ]]; then
continue
fi
pid=$(echo $pid_program | cut -d'/' -f1)
program=$(echo $pid_program | cut -d'/' -f2-)
echo "Порт: $port, PID: $pid, Процесс: $program"
done
echo ""
# 5. Мониторинг сетевых ошибок и отброшенных пакетов
echo "5. Статистика сетевых ошибок и отбросов:"
echo "---------------------------------------"
if command -v ip &> /dev/null; then
echo "Интерфейс | Ошибки(RX/TX) | Сбросы(RX/TX) |"
echo "---------|---------------|--------------|"
ip -s link | awk 'BEGIN{ORS=" "} /^[0-9]+:/ {iface=$2; getline; getline; rx_err=$2; tx_err=$10; getline; rx_drop=$2; tx_drop=$10; print iface, rx_err"/"tx_err, rx_drop"/"tx_drop "\n"}'
else
netstat -i | awk 'NR>2 {print $1, $5"/"$9, $6"/"$10}' # errors (in/out), drops (in/out)
fi
Как это работает и на что смотреть:
Пункт 2 показывает распределение TCP-соединений по состояниям. Большое количество соединений в состоянии
TIME_WAIT
илиCLOSE_WAIT
может указывать на проблемы с сетевым стеком приложения или на его неумение корректно закрывать соединения.Пункт 3. Неожиданно высокое число соединений у одного процесса (например, у веб-сервера или базы данных) может быть как нормой, так и признаком DDoS-атаки, утечки соединений в приложении или активности бота.
Пункт 4 - безопасность и осведомленность. Скрипт находит службы, слушающие порты, которые не являются общеизвестными (например, 22 для SSH или 80 для HTTP). Это помогает быстро обнаружить неавторизованные или забытые сервисы.
Пункт 5 - диагностика проблем на сетевом ��ровне. Растущее количество ошибок (
errors
) или сброшенных пакетов (drops
) на интерфейсе часто указывает на проблемы с сетевым оборудованием, его перегруженность или неверную настройку.
Пример вывода:

В пункте 4 отсутствует вывод из-за отсутствия слушающих портов, а стандартные (моё подключение по SSH) исключены из вывода в скрипте.
Заключение
Эти скрипты - очень хороший стартовый набор для быстрой диагностики. Они помогают перейти от недоумения (с чего начать...) к конкретным данным: «процесс N ест всю память» или «процесс N создает огромную нагрузку на диск». С данными, полученными от скриптов, проще искать причину.
Важное уточнение. Данные скрипты не заменят Zabbix или Prometheus, создавались они не для этих целей. Они скорее являются решением, где тяжелые системы мониторинга избыточны. Я использую их на нескольких лёгких VPS, куда громоздить мониторинг не имеет смысла.
Комментарии (2)
abask
07.10.2025 15:40скормил первый скрипт дипсик
говорит, что имеется критическая ошибка в анализе dirty pages.
THEOILMAN
Довольно часто об этом говорю, и тут опять к месту: Штука для одменов, которые одменели 25 лет винду и тут нагрянуло импортозамещение)