В продолжении статьи: https://habr.com/ru/articles/962290
Пришла мне мысль что для такой задачи должны быть готовые зрелые решения. В частности в smartmontools и оказалось что всё уже давно придумано. Но не до конца ...
Решил поделится опытом. Причесал моё словоблудие нейронкой.
📋 Оглавление
⚙️ Основная конфигурация smartd
Файл: /etc/smartmontools/smartd.conf
DEVICESCAN -a -o on -S on \
-s (S/../../7/10|L/../../01/12) \
-m root \
-M exec /path/to/script/smartd-tg-alert.sh \
-n standby,10,q
🔍 Параметры DEVICESCAN
Параметр | Описание |
|---|---|
| Автоматическое обнаружение всех дисков: SATA, NVMe, USB (с поддержкой SMART) |
| Мониторить все SMART-атрибуты, ошибки, health-статус |
| Включить автоматический offline-сбор данных (если поддерживается) |
| Включить автосохранение атрибутов |
| Расписание тестов (см. ниже) |
| Получатель email-уведомлений (резервный канал) |
| Скрипт для кастомных уведомлений |
| Не будить диск в спящем режиме (до 10 пропусков, |
📅 Формат расписания -s
Синтаксис: T/MM/DD/d/HH
Поле | Значение | Пример |
|---|---|---|
| Тип теста: |
|
| Месяц (01–12 или |
|
| День месяца (01–31 или |
|
| День недели (1–7, где 7 = воскресенье) |
|
| Час запуска (00–23) |
|
Моя конфигурация:
(S/../../7/10|L/../../01/12)
Тест | Расписание |
|---|---|
🔹 Короткий | Каждое воскресенье в 10:00 |
🔹 Длинный | 1-го числа каждого месяца в 12:00 |
💡 Важно: Если в момент запуска диск в режиме
standbyи активна опция-n, тест будет пропущен. Для критичных систем можно убрать эту опцию или использовать-n standby,10,m(отправить письмо при пропуске).
🔔 Скрипт уведомлений в Telegram
Файл: /path/to/script/smartd-tg-alert.sh
#!/bin/bash
BOT_TOKEN="bot-token"
CHAT_ID="chat-id"
HOST=$(hostname)
MESSAGE="$SMARTD_MESSAGE"
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
-d "chat_id=$CHAT_ID" \
-d "text=🖥 *$HOST*\n\n$MESSAGE" \
-d "parse_mode=Markdown" >/dev/null 2>&1
# Всегда возвращаем 0, чтобы smartd не считал ошибку
exit 0
🔧 Настройка прав:
sudo chmod +x /path/to/script/smartd-tg-alert.sh
sudo chown root:root /path/to/script/smartd-tg-alert.sh
⚠️ Важно: Бот должен быть запущен, а чат с ним — начат пользователем. Бот не может писать первым.
🔄 Fallback-скрипт: проверка пропущенных тестов
Зачем: Если ПК был выключен в момент планового теста — этот скрипт запустит его при первой возможности.
Файл: /path/to/script/smart-fallback.sh
#!/bin/bash
# === CONFIG ===
SHORT_MAX_DAYS=8 # если short тест старше 8 дней → запускаем
LONG_MAX_DAYS=35 # если long тест старше 35 дней → запускаем
LOG_TAG="smart-fallback"
log() {
logger -t "$LOG_TAG" "$1"
}
get_power_on_hours() {
# Возвращает Power-On Hours (целое) или пусто.
# Поддержка ATA и NVMe.
local disk="$1"
local a out
out=$(smartctl -a "$disk" 2>/dev/null) || return 0
# NVMe: "Power On Hours: 1234"
a=$(echo "$out" | awk -F: '/^[[:space:]]*Power On Hours[[:space:]]*:/ {gsub(/[^0-9]/,"",$2); print $2; exit}')
if [[ -n "$a" ]]; then
printf '%s' "$a"
return 0
fi
# ATA: таблица атрибутов, строка "Power_On_Hours"
a=$(echo "$out" | awk '$2=="Power_On_Hours" {print $10; exit}')
if [[ "$a" =~ ^[0-9]+$ ]]; then
printf '%s' "$a"
return 0
fi
}
get_last_selftest_poh() {
# Возвращает Lifetime (hours) из самого свежего self-test указанного типа.
# type: "Short" или "Extended"
local output="$1"
local type="$2"
# В selftest-логе smartctl колонка обычно называется "LifeTime(hours)".
# Для строк вида:
# "# 1 Short offline Completed ... 00% 12345 -"
# или:
# "# 1 Extended offline Completed ... 00% 12345 -"
# NVMe формат другой, например:
# "0 Short Completed ... 1353 -"
#
# Берем первое (самое свежее) совпадение и вынимаем число из колонки lifetime.
echo "$output" | awk -v t="$type" '
$0 ~ t && $0 !~ /^[[:space:]]*Num[[:space:]]/ {
for (i=1; i<=NF; i++) {
if ($i ~ /^[0-9]+$/) n = $i
}
if (n ~ /^[0-9]+$/) { print n; exit }
}
'
}
check_and_run_test() {
local disk="$1"
# получаем лог тестов
local output
output=$(smartctl -l selftest "$disk" 2>/dev/null)
local poh
poh=$(get_power_on_hours "$disk")
# --- SHORT TEST ---
local last_short_poh short_age_days
last_short_poh=$(get_last_selftest_poh "$output" "Short")
short_age_days=""
if [[ -n "$poh" && -n "$last_short_poh" && "$poh" =~ ^[0-9]+$ && "$last_short_poh" =~ ^[0-9]+$ && "$poh" -ge "$last_short_poh" ]]; then
short_age_days=$(( (poh - last_short_poh) / 24 ))
fi
if [[ -z "$last_short_poh" || -z "$poh" || -z "$short_age_days" || "$short_age_days" -gt "$SHORT_MAX_DAYS" ]]; then
log "Running SHORT test on $disk (age_days: ${short_age_days:-unknown})"
smartctl -t short "$disk" >/dev/null 2>&1
fi
# --- LONG TEST ---
local last_long_poh long_age_days
last_long_poh=$(get_last_selftest_poh "$output" "Extended")
long_age_days=""
if [[ -n "$poh" && -n "$last_long_poh" && "$poh" =~ ^[0-9]+$ && "$last_long_poh" =~ ^[0-9]+$ && "$poh" -ge "$last_long_poh" ]]; then
long_age_days=$(( (poh - last_long_poh) / 24 ))
fi
if [[ -z "$last_long_poh" || -z "$poh" || -z "$long_age_days" || "$long_age_days" -gt "$LONG_MAX_DAYS" ]]; then
log "Running LONG test on $disk (age_days: ${long_age_days:-unknown})"
smartctl -t long "$disk" >/dev/null 2>&1
fi
}
# === MAIN ===
smartctl --scan-open 2>/dev/null | awk '{print $1}' | while IFS= read -r disk; do
[[ -z "$disk" ]] && continue
if smartctl -c "$disk" &>/dev/null; then
check_and_run_test "$disk"
fi
done
🔧 systemd: сервис + таймер
/etc/systemd/system/smart-fallback.service
[Unit]
Description=SMART fallback test runner
[Service]
Type=oneshot
ExecStart=/path/to/script/smart-fallback.sh
/etc/systemd/system/smart-fallback.timer
[Unit]
Description=Run SMART fallback daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
▶️ Активация:
sudo chmod +x /path/to/script/smart-fallback.sh
sudo systemctl daemon-reload
sudo systemctl enable --now smart-fallback.timer
📊 Weekly Summary: еженедельная сводка
Зачем: Не просто алерты «что-то сломалось», а регулярный обзор состояния всех дисков.
Файл: /path/to/script/smart-summary.sh
#!/bin/bash
BOT_TOKEN="bot-token"
CHAT_ID="chat-id"
HOST=$(hostname)
DATE=$(date '+%Y-%m-%d')
LOG_TAG="smart-summary"
log() {
logger -t "$LOG_TAG" -p user.info "$1"
}
# Используем <b>...</b> вместо *...* для жирного
MESSAGE=$(printf '📊 <b>SMART Summary (%s)</b>\nДата: %s' "$HOST" "$DATE")$'\n\n'
for disk in $(smartctl --scan-open | awk '{print $1}'); do
if smartctl -c "$disk" &>/dev/null; then
# Получаем health (работает и для ATA, и для NVMe)
health=$(smartctl -H "$disk" 2>/dev/null | grep -E "SMART overall-health|SMART Health Status" | sed 's/.*: //')
# Температура
temp=$(smartctl -A "$disk" 2>/dev/null | grep -i temperature | head -n1 | awk -F': ' '{print $2}' | awk '{print $1, $2}')
MESSAGE+=$(printf '💽 <b>%s</b> - %s' "$disk" "$health")$'\n\n'
if [[ -n "$temp" && "$temp" != *" "* ]]; then
# Важно: $(...) срезает завершающие переводы строк, поэтому добавляем их снаружи.
MESSAGE+=$(printf '%s' "$temp")$'\n'
fi
# Ошибки: Reallocated, Pending, Uncorrectable
errors=$(smartctl -A "$disk" 2>/dev/null | grep -E "(Reallocated_Sector|Current_Pending|Offline_Uncorrectable|Media_Error)" | awk '$10 > 0 {print " • " $2 ": " $10}')
if [[ -n "$errors" ]]; then
MESSAGE+=$(printf '❗ Ошибки:\n%s' "$errors")$'\n'
fi
# Износ SSD
wear=$(smartctl -A "$disk" 2>/dev/null | grep -E "(Wear_Leveling|Percent_Lifetime|Media_Wearout)" | head -n1 | awk -F': ' '{print $2}' | awk '{print $1}')
if [[ -n "$wear" ]]; then
MESSAGE+=$(printf '🧠 Износ: %s' "$wear")$'\n'
fi
MESSAGE+=$'\n'
fi
done
# Отправляем с parse_mode=HTML и получаем код ответа
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
--data-urlencode "chat_id=$CHAT_ID" \
--data-urlencode "text=$MESSAGE" \
--data-urlencode "parse_mode=HTML")
# Опционально: логирование ошибок
if [[ "$HTTP_CODE" != "200" ]]; then
log "[$(date)] SMART summary failed: HTTP $HTTP_CODE"
fi
exit 0
🔧 systemd: сервис + таймер
/etc/systemd/system/smart-summary.service
[Unit]
Description=SMART weekly summary
[Service]
Type=oneshot
ExecStart=/path/to/script/smart-summary.sh
/etc/systemd/system/smart-summary.timer
[Unit]
Description=Run SMART summary weekly
[Timer]
OnCalendar=Mon 12:00
Persistent=true
[Install]
WantedBy=timers.target
▶️ Активация:
sudo chmod +x /path/to/script/smart-summary.sh
sudo systemctl daemon-reload
sudo systemctl enable --now smart-summary.timer
🧩 Архитектура системы
┌─────────────────────────────────────┐
│ smartd (демон) │
│ • Мониторит диски в реальном времени│
│ • Запускает тесты по расписанию │
│ • Вызывает скрипт при событиях │
└────────────┬────────────────────────┘
│ событие (алерт)
▼
┌─────────────────────────────────────┐
│ 📢 smartd-tg-alert.sh │
│ • Мгновенное уведомление в Telegram│
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 🔄 smart-fallback.timer (daily) │
│ • Проверяет, не пропущены ли тесты│
│ • Запускает их при необходимости │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 📊 smart-summary.timer (weekly) │
│ • Формирует сводку по всем дискам │
│ • Отправляет отчёт в Telegram │
└─────────────────────────────────────┘
🎯 Принципы работы:
Компонент | Тип | Частота | Цель |
|---|---|---|---|
| Демон | Постоянно | Реактивный мониторинг |
Скрипт-хук | По событию | Мгновенные алерты | |
Скрипт + timer | Ежедневно | Надёжность (пропущенные тесты) | |
Скрипт + timer | Еженедельно | Проактивный обзор |
✅ Чеклист проверки
🔧 Конфигурация
sudo smartd -d -c /etc/smartmontools/smartd.conf— нет ошибок парсингаsudo smartctl --scan— все ожидаемые диски отображаютсяsudo systemctl enable --now smartd— служба активна
🔔 Уведомления
Скрипт
smartd-tg-alert.shимеет права+xи владельцаrootБот активен, чат начат, токен верный
Проверка отправки в телеграм напрямую:
SMARTD_MESSAGE=$'TEST from smartd\nsecond line' /path/to/script/smartd-tg-alert.shПроверка отправки через
smartd: добавить в/etc/smartmontools/smartd.confвременно строку-M test \И запуститьsudo smartd -c /etc/smartmontools/smartd.conf -q onecheck
🔄 Fallback
systemctl list-timers | grep smart-fallback— таймер активен, следующее срабатывание указаноsudo systemctl start smart-fallback.service— выполняется без ошибокВ
journalctl -t smart-fallbackпоявляются записи при запуске
📊 Summary
systemctl list-timers | grep smart-summary— таймер настроен на воскресенье 12:00sudo systemctl start smart-summary.service— сводка приходит в TelegramВ сообщении отображаются: здоровье, температура, ошибки, износ (для SSD)
📋 Полезные команды
# Статус службы smartd
sudo systemctl status smartd
# Логи в реальном времени
journalctl -u smartd -f
# Проверка расписания тестов для диска
sudo smartctl -l selftest /dev/sdb
# Ручной запуск теста
sudo smartctl -t short /dev/sdb
sudo smartctl -t long /dev/sdb
# Просмотр результатов тестов
sudo smartctl -l selftest /dev/sdb
# Проверка таймеров
systemctl list-timers --all | grep smart

























