Уведомления о высокой нагрузке и OOM
Сервер «как будто живой», SSH открывается, но всё работает в 30 раз медленнее. Часто причина — забытый процесс, который сожрал CPU/память. Если уведомление пришло в момент скачка, расследование занимает минуты, а не часы.
Что мониторим
Заголовок раздела «Что мониторим»- средняя нагрузка (
load average) > N × число CPU; - использование RAM > 90 %;
- использование swap > 50 %;
- срабатывания OOM killer в
dmesg/journalctl.
/usr/local/bin/notifly-load-check:
#!/usr/bin/env bashset -euset -a; source /etc/notifly.env; set +a
HOST=$(hostname -s)CPUS=$(nproc)LOAD1=$(awk '{print $1}' /proc/loadavg)LOAD_PCT=$(awk -v l="$LOAD1" -v c="$CPUS" 'BEGIN{printf "%d", l/c*100}')
# Память (без кеша)read -r _ MTOTAL MUSED _ <<<"$(free -m | awk '/^Mem:/{print $1, $2, $3, $4}')"MEM_PCT=$(( MUSED * 100 / MTOTAL ))
read -r _ STOTAL SUSED _ <<<"$(free -m | awk '/^Swap:/{print $1, $2, $3, $4}')"SWAP_PCT=0[ "$STOTAL" -gt 0 ] && SWAP_PCT=$(( SUSED * 100 / STOTAL ))
ALERTS=()
[ "$LOAD_PCT" -ge 200 ] && ALERTS+=("CPU load ${LOAD_PCT}% (load1=${LOAD1}, ${CPUS} CPU)")[ "$MEM_PCT" -ge 90 ] && ALERTS+=("RAM ${MEM_PCT}% занято (${MUSED}/${MTOTAL} МБ)")[ "$SWAP_PCT" -ge 50 ] && ALERTS+=("Swap ${SWAP_PCT}% (${SUSED}/${STOTAL} МБ)")
if [ "${#ALERTS[@]}" -gt 0 ]; then TOP=$(ps -eo pid,user,pcpu,pmem,comm --sort=-pcpu | head -6) /usr/local/bin/notifly-send \ "🔥 Нагрузка на $HOST" \ "$(printf '%s\n' "${ALERTS[@]}")
Топ процессов:$TOP" 8fisudo chmod +x /usr/local/bin/notifly-load-checkЗапуск каждые 5 минут
Заголовок раздела «Запуск каждые 5 минут»*/5 * * * * root /usr/local/bin/notifly-load-checkЧтобы не получать однотипные сообщения каждые 5 минут, добавьте флаг-файл:
FLAG=/tmp/notifly-load-flagNOW=$(date +%s)LAST=$(stat -c %Y "$FLAG" 2>/dev/null || echo 0)[ $((NOW - LAST)) -lt 1800 ] && exit 0touch "$FLAG"Тогда повторы будут не чаще одного раза в 30 минут.
OOM killer
Заголовок раздела «OOM killer»Когда ядро убивает процесс из-за нехватки памяти, оно пишет об этом в dmesg.
Поднимаем системный watcher:
/usr/local/bin/notifly-oom:
#!/usr/bin/env bashset -euset -a; source /etc/notifly.env; set +a
journalctl -kf -o cat --since now | \while read -r line; do if echo "$line" | grep -qiE "killed process|out of memory"; then /usr/local/bin/notifly-send \ "💀 OOM killer на $(hostname -s)" \ "$line" 10 || true fidone/etc/systemd/system/notifly-oom.service:
[Unit]Description=Notify about OOM killsAfter=network-online.target
[Service]ExecStart=/usr/local/bin/notifly-oomRestart=alwaysRestartSec=10s
[Install]WantedBy=multi-user.targetsudo chmod +x /usr/local/bin/notifly-oomsudo systemctl daemon-reloadsudo systemctl enable --now notifly-oomWindows: PowerShell + Task Scheduler
Заголовок раздела «Windows: PowerShell + Task Scheduler»Аналог для Windows-серверов: следит за загрузкой CPU/RAM и размером
file-pagefile-файла. Пользуется общая функция Send-Notifly.
. C:\scripts\Notifly.ps1
$Host = $env:COMPUTERNAME$FlagFile = "$env:TEMP\notifly-load.flag"
# CPU за последние ~3 секунды$cpu = (Get-Counter '\Processor(_Total)\% Processor Time' -SampleInterval 1 -MaxSamples 3). CounterSamples.CookedValue | Measure-Object -Average | Select-Object -Expand Average$cpu = [int]$cpu
$os = Get-CimInstance Win32_OperatingSystem$memUsedPct = [int]((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100)
$pageFile = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue$pageUsedPct = if ($pageFile -and $pageFile.AllocatedBaseSize -gt 0) { [int](($pageFile.CurrentUsage / $pageFile.AllocatedBaseSize) * 100)} else { 0 }
$alerts = @()if ($cpu -ge 90) { $alerts += "CPU: $cpu%" }if ($memUsedPct -ge 90) { $alerts += "RAM: $memUsedPct%" }if ($pageUsedPct -ge 50) { $alerts += "Page file: $pageUsedPct%" }
if ($alerts.Count -gt 0) { # Дебаунс на 30 минут if (Test-Path $FlagFile) { $age = (Get-Date) - (Get-Item $FlagFile).LastWriteTime if ($age.TotalMinutes -lt 30) { exit 0 } } New-Item -ItemType File -Path $FlagFile -Force | Out-Null
$top = Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 | Format-Table -AutoSize ProcessName, Id, @{N='CPU(s)';E={[math]::Round($_.CPU,1)}}, ` @{N='RAM(MB)';E={[int]($_.WorkingSet64/1MB)}} | Out-String
Send-Notifly -Title "🔥 Нагрузка на $Host" ` -Message ($alerts -join ", ") + "`n`nТоп процессов:`n$top" ` -Priority 8}Регистрация в Task Scheduler (каждые 5 минут):
$Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\scripts\Notifly-Load-Check.ps1"$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) ` -RepetitionInterval (New-TimeSpan -Minutes 5)$Princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel HighestRegister-ScheduledTask -TaskName "Notifly Load Check" ` -Action $Action -Trigger $Trigger -Principal $PrincWindows-аналог OOM-killer
Заголовок раздела «Windows-аналог OOM-killer»Windows не имеет OOM killer, но жёсткие сбои по памяти логируются в Application Event Log
с источником Resource-Exhaustion-Detector (Event ID 2004). Подпишемся на этот журнал:
$Trigger = New-ScheduledTaskTrigger -AtStartup$Trigger.Subscription = @"<QueryList> <Query Id="0" Path="Application"> <Select Path="Application"> *[System[Provider[@Name='Microsoft-Windows-Resource-Exhaustion-Detector'] and EventID=2004]] </Select> </Query></QueryList>"@$Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -Command `". C:\scripts\Notifly.ps1; Send-Notifly -Title '💀 Критическая нехватка памяти на $env:COMPUTERNAME' -Message 'Resource-Exhaustion-Detector зафиксировал истощение RAM' -Priority 10`""Register-ScheduledTask -TaskName "Notifly Memory Exhaustion" -Trigger $Trigger -Action $Action ` -Principal (New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest)- Видно момент скачка. Топ процессов в сообщении часто сразу указывает на виновника.
- Поймать OOM — самое ценное. Эти события быстро «съезжают» из dmesg, и без алерта о них узнают спустя дни.
- Дешёвый «датчик жизни»: если на нагруженном проде сутками тихо, можно проверить, что мониторинг сам жив.
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Использовать
pidstatилиpressure stall information(PSI) для более точной оценки реального backpressure. - Сравнивать текущий load со средним за неделю — алертить только при отклонении.
- Связать с уведомлением о падении сервисов — обычно после OOM падает что-то конкретное.