Привет, Хабр! Уверен, многие сталкивались с тормозами сервера, долгой загрузкой страниц. Логи молчат, нужно искать виновника. Системный мониторинг демонстрирует, что 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 растет, это прямой признак того, что памяти физически не хватает и это уже бьет по производительности.

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

Тест с одной из VPS
Тест с одной из VPS
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) на интерфейсе часто указывает на проблемы с сетевым оборудованием, его перегруженность или неверную настройку.

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

Тест с одной из VPS
Тест с одной из VPS

В пункте 4 отсутствует вывод из-за отсутствия слушающих портов, а стандартные (моё подключение по SSH) исключены из вывода в скрипте.

Заключение

Эти скрипты - очень хороший стартовый набор для быстрой диагностики. Они помогают перейти от недоумения (с чего начать...) к конкретным данным: «процесс N ест всю память» или «процесс N создает огромную нагрузку на диск». С данными, полученными от скриптов, проще искать причину.

Важное уточнение. Данные скрипты не заменят Zabbix или Prometheus, создавались они не для этих целей. Они скорее являются решением, где тяжелые системы мониторинга избыточны. Я использую их на нескольких лёгких VPS, куда громоздить мониторинг не имеет смысла.

Комментарии (2)


  1. THEOILMAN
    07.10.2025 15:40

    Довольно часто об этом говорю, и тут опять к месту: Штука для одменов, которые одменели 25 лет винду и тут нагрянуло импортозамещение)


  1. abask
    07.10.2025 15:40

    скормил первый скрипт дипсик

    говорит, что имеется критическая ошибка в анализе dirty pages.

    https://chat.deepseek.com/share/6jwd8n90y1ad5w8zyc