Привет, Хабр! Уверен, многие сталкивались с тормозами сервера, долгой загрузкой страниц. Логи молчат, нужно искать виновника. Системный мониторинг демонстрирует, что 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 -i "Private_Dirty:" /proc/$pid/smaps 2>/dev/null | awk '{sum += $2} END {print sum}') 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 -tun | awk '/^tcp/ {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 line; do port=$(echo $line | awk '{print $4}' | awk -F: '{print $NF}') pid_program=$(echo $line | awk '{print $7}') # Исключаем некоторые стандартные порты 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 show | awk ' /^[0-9]+:/ {iface=$2; getline} /RX.*bytes/ {getline; rx_err=$2; tx_err=$10; getline; rx_drop=$2; tx_drop=$10; printf "%-12s | %-13s | %-12s\n", iface, rx_err"/"tx_err, rx_drop"/"tx_drop}' else netstat -i | awk 'NR>2 {print $1, $5"/"$9, $6"/"$10}' fi
Как это работает и на что смотреть:
Пункт 2 показывает распределение TCP-соединений по состояниям. Большое количество соединений в состоянии
TIME_WAITилиCLOSE_WAITможет указывать на проблемы с сетевым стеком приложения или на его неумение корректно закрывать соединения.Пункт 3. Неожиданно высокое число соединений у одного процесса (например, у веб-сервера или базы данных) может быть как нормой, так и признаком DDoS-атаки, утечки соединений в приложении или активности бота.
Пункт 4 - безопасность и осведомленность. Скрипт находит службы, слушающие порты, которые не являются общеизвестными (например, 22 для SSH или 80 для HTTP). Это помогает быстро обнаружить неавторизованные или забытые сервисы.
Пункт 5 - диагностика проблем на сетевом уровне. Растущее количество ошибок (
errors) или сброшенных пакетов (drops) на интерфейсе часто указывает на проблемы с сетевым оборудованием, его перегруженность или неверную настройку.
Пример вывода:

В пункте 4 отсутствует вывод из-за отсутствия слушающих портов, а стандартные (моё подключение по SSH) исключены из вывода в скрипте.
Заключение
Эти скрипты - очень хороший стартовый набор для быстрой диагностики. Они помогают перейти от недоумения (с чего начать...) к конкретным данным: «процесс N ест всю память» или «процесс N создает огромную нагрузку на диск». С данными, полученными от скриптов, проще искать причину.
Важное уточнение. Данные скрипты не заменят Zabbix или Prometheus, создавались они не для этих целей. Они скорее являются решением, где тяжелые системы мониторинга избыточны. Я использую их на нескольких лёгких VPS, куда громоздить мониторинг не имеет смысла.
P. S. В моей группе в Телеграмм разбираем практические кейсы: скрипты (Python/Bash/PowerShell), тонкости ОС и инструменты для эффективной работы.
