Уведомление о деградации RAID-массива
RAID придуман, чтобы пережить смерть диска без простоя — но только если вы вовремя замените выпавший диск. Проблема в том, что деградированный массив работает почти незаметно: сервис жив, приложение отвечает, а на самом деле у вас уже нет избыточности, и смерть второго диска означает потерю данных. Узнать о выпавшем диске нужно в тот же час, а не через неделю на post-mortem.
Что мы будем делать
Заголовок раздела «Что мы будем делать»Скрипт раз в 15 минут проверяет здоровье хранилища и присылает уведомление, когда:
- массив деградировал или пересобирается (
/proc/mdstat+mdadm --detail) — высокий приоритет; - SMART сообщает о плохом здоровье диска (
smartctl -H) — высокий приоритет; - всё снова clean — сообщение о восстановлении.
Как и в рецепте про место на диске, мы храним
трёхуровневое состояние (ok/warn/crit) в /var/lib/notifly-raid/, чтобы не
получать одно и то же сообщение каждые 15 минут.
Скрипту нужны пакеты mdadm и smartmontools (smartctl). Сохраните как
/usr/local/bin/notifly-raid-check:
#!/usr/bin/env bashset -euset -a; source /etc/notifly.env; set +a
STATE_DIR=/var/lib/notifly-raidmkdir -p "$STATE_DIR"HOST=$(hostname -s)
# --- Проверяем каждый программный RAID-массив по /proc/mdstat ---for MD in /sys/block/md*; do [ -e "$MD" ] || continue DEV=$(basename "$MD") NODE="/dev/$DEV"
DETAIL=$(mdadm --detail "$NODE" 2>/dev/null || true) STATE=$(echo "$DETAIL" | awk -F: '/State :/ {sub(/^[ \t]+/, "", $2); print $2; exit}') LEVEL_RAID=$(echo "$DETAIL" | awk -F: '/Raid Level :/ {gsub(/[ \t]/, "", $2); print $2; exit}')
STATE_FILE="$STATE_DIR/$DEV" LAST=$(cat "$STATE_FILE" 2>/dev/null || echo ok)
LEVEL=ok # active/clean без флагов — норма; всё остальное считаем проблемой case "$STATE" in *degraded*|*FAILED*|*failed*) LEVEL=crit ;; *recovering*|*resyncing*|*rebuild*|*resync*) LEVEL=warn ;; esac
if [ "$LEVEL" != "$LAST" ]; then case "$LEVEL" in warn) /usr/local/bin/notifly-send \ "⚠️ RAID $DEV пересобирается — $HOST" \ "$LEVEL_RAID $DEV: состояние «$STATE». Идёт восстановление, дождитесь окончания и проверьте диск." 5 ;; crit) /usr/local/bin/notifly-send \ "🔥 RAID $DEV ДЕГРАДИРОВАЛ — $HOST" \ "$LEVEL_RAID $DEV: состояние «$STATE». Массив без избыточности! Срочно замените выпавший диск." 10 ;; ok) /usr/local/bin/notifly-send \ "✅ RAID $DEV снова в норме — $HOST" \ "$LEVEL_RAID $DEV: состояние «$STATE» (clean)." 4 ;; esac echo "$LEVEL" > "$STATE_FILE" fidone
# --- Проверяем SMART-здоровье физических дисков ---for DISK in /dev/sd?; do [ -e "$DISK" ] || continue NAME=$(basename "$DISK")
HEALTH=$(smartctl -H "$DISK" 2>/dev/null \ | awk -F: '/(overall-health|SMART Health Status)/ {gsub(/[ \t]/, "", $2); print $2; exit}') [ -n "$HEALTH" ] || continue
STATE_FILE="$STATE_DIR/smart-$NAME" LAST=$(cat "$STATE_FILE" 2>/dev/null || echo ok)
# PASSED/OK — норма, всё остальное (FAILED!) — критично LEVEL=ok case "$HEALTH" in PASSED|OK) LEVEL=ok ;; *) LEVEL=crit ;; esac
if [ "$LEVEL" != "$LAST" ]; then case "$LEVEL" in crit) /usr/local/bin/notifly-send \ "🔥 SMART: диск $NAME умирает — $HOST" \ "smartctl -H $DISK вернул «$HEALTH». Замените диск, пока массив ещё цел." 9 ;; ok) /usr/local/bin/notifly-send \ "✅ SMART диска $NAME в норме — $HOST" \ "smartctl -H $DISK снова «$HEALTH»." 4 ;; esac echo "$LEVEL" > "$STATE_FILE" fidoneСделайте исполняемым:
sudo chmod +x /usr/local/bin/notifly-raid-checkЗапуск по расписанию
Заголовок раздела «Запуск по расписанию»Через cron
Заголовок раздела «Через cron»sudo crontab -eДобавьте строку:
*/15 * * * * /usr/local/bin/notifly-raid-check >/dev/null 2>&1Через systemd timer (рекомендуется)
Заголовок раздела «Через systemd timer (рекомендуется)»/etc/systemd/system/notifly-raid.service:
[Unit]Description=Notifly RAID health check
[Service]Type=oneshotExecStart=/usr/local/bin/notifly-raid-check/etc/systemd/system/notifly-raid.timer:
[Unit]Description=Run notifly-raid every 15 minutes
[Timer]OnBootSec=5minOnUnitActiveSec=15min
[Install]WantedBy=timers.targetАктивируем:
sudo systemctl daemon-reloadsudo systemctl enable --now notifly-raid.timersudo systemctl list-timers notifly-raid.timerWindows: PowerShell + Task Scheduler
Заголовок раздела «Windows: PowerShell + Task Scheduler»На Windows аналог /proc/mdstat — это Get-PhysicalDisk (HealthStatus каждого
диска) и Storage Spaces: виртуальные диски проверяются через Get-VirtualDisk.
Скрипт шлёт тревогу, если хоть один диск или виртуальный том не Healthy.
Используется общая функция Send-Notifly из
шаблона sysadmin/index.
. C:\scripts\Notifly.ps1
$StateDir = "C:\ProgramData\Notifly\raid-state"New-Item -ItemType Directory -Path $StateDir -Force | Out-Null$Host = $env:COMPUTERNAME
function Get-Level([string]$health) { switch ($health) { "Healthy" { "ok" } "Warning" { "warn" } default { "crit" } # Unhealthy и всё неизвестное }}
# --- Здоровье физических дисков ---Get-PhysicalDisk | ForEach-Object { $name = "phys-$($_.DeviceId)" $level = Get-Level $_.HealthStatus
$stateFile = Join-Path $StateDir "$name.txt" $last = if (Test-Path $stateFile) { (Get-Content $stateFile -Raw).Trim() } else { "ok" } if ($level -eq $last) { return }
switch ($level) { "warn" { Send-Notifly -Title "⚠️ Диск $($_.FriendlyName) — предупреждение — $Host" ` -Message "HealthStatus: $($_.HealthStatus). Проверьте SMART и запланируйте замену." -Priority 5 } "crit" { Send-Notifly -Title "🔥 Диск $($_.FriendlyName) УМИРАЕТ — $Host" ` -Message "HealthStatus: $($_.HealthStatus). Срочно замените диск, пока пул цел!" -Priority 9 } "ok" { Send-Notifly -Title "✅ Диск $($_.FriendlyName) в норме — $Host" ` -Message "HealthStatus: $($_.HealthStatus)." -Priority 4 } } $level | Set-Content $stateFile}
# --- Здоровье виртуальных дисков Storage Spaces ---Get-VirtualDisk -ErrorAction SilentlyContinue | ForEach-Object { $name = "vdisk-$($_.FriendlyName)" $level = Get-Level $_.HealthStatus
$stateFile = Join-Path $StateDir "$($name -replace '[\\/:]','_').txt" $last = if (Test-Path $stateFile) { (Get-Content $stateFile -Raw).Trim() } else { "ok" } if ($level -eq $last) { return }
switch ($level) { "warn" { Send-Notifly -Title "⚠️ Том $($_.FriendlyName) деградирует — $Host" ` -Message "HealthStatus: $($_.HealthStatus), OperationalStatus: $($_.OperationalStatus)." -Priority 5 } "crit" { Send-Notifly -Title "🔥 Том $($_.FriendlyName) ДЕГРАДИРОВАЛ — $Host" ` -Message "HealthStatus: $($_.HealthStatus). Пул без избыточности — срочно замените диск!" -Priority 10 } "ok" { Send-Notifly -Title "✅ Том $($_.FriendlyName) в норме — $Host" ` -Message "HealthStatus: $($_.HealthStatus) (Healthy)." -Priority 4 } } $level | Set-Content $stateFile}Регистрация в Task Scheduler (от администратора, каждые 15 минут от SYSTEM):
$Action = New-ScheduledTaskAction ` -Execute "powershell.exe" ` -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\scripts\Notifly-Raid-Check.ps1"$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) ` -RepetitionInterval (New-TimeSpan -Minutes 15)$Princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel HighestRegister-ScheduledTask -TaskName "Notifly RAID Check" ` -Action $Action -Trigger $Trigger -Principal $Princ -Description "Notifly: проверка RAID и SMART"- Выпавший диск не остаётся незамеченным. Деградированный массив работает «как обычно», и без уведомления вы узнаёте о проблеме, только когда умирает второй диск — то есть когда уже поздно.
- SMART предупреждает заранее. Многие диски сообщают о плохом здоровье ещё до полного отказа — этого времени хватает, чтобы спокойно заказать замену.
- Трёхуровневая логика (
ok→warn→crit) исключает спам: пока состояние не меняется — уведомлений нет, а пересборку и восстановление вы видите отдельно.
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Прикладывать полный вывод
cat /proc/mdstatиmdadm --detail /dev/mdXчерез полеextras, чтобы видеть, какой именно диск выпал, прямо в приложении. - Добавить heartbeat-проверку: если сам скрипт перестанет
запускаться (например,
smartctlпропал), молчание тоже станет заметным. - Мониторить скорость пересборки (
speed/finishиз/proc/mdstat) и слать отдельное сообщение по её завершении — см. также мониторинг сервисов.