Обновить

Комментарии 26

smartd не проще настроить, чем костыли выдумывать?

smartd шлёт на почту отчеты, хорошо бы научить его слать ещё в телеграм. Хотя тут можно на уровне почты передавать письма скрипту и делать дальше с ними что угодно.

НЛО прилетело и опубликовало эту надпись здесь

-M exec ...

exec PATH - run the executable PATH instead of the default mail
command, when smartd needs to send email. PATH must point to an
executable binary file or script

На почту много кто умеет слать, например, виндовый CrystalDiskInfo. Кстати, он опенсорсный, так что технически можно и его научить отправлять в TG.

чтобы

"Reallocated > 0" и разве этого недостаточно? Надо поглумиться ради хелловина что-ли? Положите его в плов и выкиньте вместе с луком...

Смотря для чего, для помойки нормально. Для критических применений в утиль от греха подальше. По идее важна вообще динамика появился 1 пендинг>1 релокейт и 5 лет так висит - ну ладно. Ну и в статье мешанина из дисков и твердотельных, которые немножко по разному работают и оцениваются. И восстановить что то викторией итп. никогда ничего было нельзя - можно только до упора писать в не читаемый сектор с надежной что контроллер сам его переназначит.

"Reallocated > 0" и разве этого недостаточно?

А есть разница между собственно Reallocated и попыток Reallocated?

А негде посмотреть попытки переназначения, это нутрянка диска и доступа к ней у простых смертных нет. Есть только уже релокейты/переназначенные сектора и пендинги/кандидаты. Кандидаты - это не обязательно битые сектора, может кривая запись и не может прочитаться и контроллер пометил сектор, а если перезаписать сектор все будет нормально. Это собственно "ремонт" Викторией в режиме Эрейз, Ремап тоже самое только ПО делает очень много попыток записи в сектор.

Это собственно "ремонт" Викторией в режиме Эрейз, Ремап тоже самое

т.е. по факту это никакое не лечение? И получается что диск с одним таким сектором может как и долго-долго жить нормально с постоянными перезаписями , так и внезапно окончательно отказать?

Инфа из смарта не даёт никакой информации по одиночному дефекту в конкретный момент времени. Те дает, но только в 1 варианте: критичная инфа - списываем. Не критичный - одиночный дефект игнорируем и смотрим что будет дальше.

Более подробной инфы, не у производителя, уже не будет - некому собрать статистику - в бытовухе диски массово уже не используются, в датацентрах их(надеюсь) сразу списывают... Есть те, кто занимаются датарекавери, но к ним диск попадает уже на другом этапе...

Если вы за reallocated раз в неделю следите, то у меня для вас плохи новости. Ибо если диск сыпется всерьёз, то за неделю он рассыпется окончательно.

Так что следить за смартом надо в реальном времени. Ну и диски с reallocated переводить в разряд дискеток или мусорных помоек. А за такими можно и не следить.

А самое забавное, smart ничего не гарантирует. Попалась мне на одном из серверов парочка дисков с идеальным смартом, при этом на них живого места не было, это уже даже не зомби, а скелеты были (куча секторов со временем чтения 0.3-0.5 секунд, то есть попытки с сотой). Правда, они оба сидели в RAID1 и поэтому OS с них таки грузилась, да, за полчаса, но ведь грузилась :)

Поддерживаю. недавно было - диски в RAID1, внезапно в журнале системы пошли ошибки по записи на этот рейд. При этом контроллер говорит что с дисками все в порядке, смарт показывает что ошибок нет. А данные пришлось из ночного бэкапа вытаскивать, так как в базе сплошные ошибки.

Хороший смарт не гарантирует. Но вот плохой смарт обычно всё же гарантирует проблемы.

... столько воды ради неопределенных целей! ... толи хдд, толи ссд м.2 ... а как настраивал новый на первый запуск ни слова ... не туда ты копаешь, что очевидно не всем ... 🤦🏾‍♂️🥱

Для того чтобы что-то мониторить - неплохо понимать как все работает.

Если вы делали скан диска, после этого провели стирание и состояние ухудшилось, то диск ваш либо уроненый либо головке кирдык.

Мониторинг смарта в этом случае ничего не покажет, сектора читаются с задержкой, но читаются.

Вырастет raw read error rate, но там обычно и так все непросто.

Никто не писал бы mhdd и её наследников если бы достаточно было смарта для диагностики.

Это не мониторинг и алертинг к сожалению, а просто периодические отчёты в тг. Реализовать exporter для Prometheus на go иди python не так сложно, тем более что логика уже вся описана в скриптах. Хотя, если погуглить, то всё уже давно есть. А про этот экспортёр даже статья на хабре имеется.

Первые 3 абзаца - просто "чаааавооооо"?

Кого изменилось? Когда изменилось? В рот мне ногу, руку и бэдсектора. Из какого криогенного сна автора достали? Времен перфокарт? Или анга-банга-телечанга в пещере?

Никогда, никогда, никогда в жизни нельзя было ЗАСТАВИТЬ сектор добавиться в G-лист. Только если прошивка диска сочла это нужным. Про P-лист в целом промолчу. Ни MHDD, ни Виктория никогда ничего не добавляла сама.

Максимум этого можно добиться с PC3000, WDmarvel и подобными тулзами. И то в основном лишь отчистить G-лист.

И эта логика уже на протяжении лет 20 еще со времен IDE дисков сколько я себя помню.

Размер дефект листа НИКОГДА не был особо публичным. И для каждого диска он всегда был разным.

Тот же P-лист можно было пересоздать только бурн комплектом, которых в паблике обычно днем с огнем не сыщешь.

И ни G, ни P лист не спасал никогда от ситуаций, когда головка размазывает мусор и царапины дальше по поляне. Максимум что можно было - отключить одну голову.

Заберите домохозяек с хабра. Пусть не изобретают велик, curl-ом вызовут GET запрос вебхука в телегу из любого уже существующего софта и будут довольны.

Как человек, у которого диски спокойно доживают до 80-100к часов аптайма и работают без каких либо проблем, если их не греть и не играть в футбол (статистический брак все еще 3-5%), отключить угребищную парковку принудительную (во всякой синей, зеленой серии есть. выставить на уровне софта неактивность 15-30 минут, если так хочется), могу сказать, что все выше действие ради действия.

Коллеги, а может кто подскажет, как можно мониторить под Windows smart ssd дискa или дисков на контроллере perc h710p отданных под cachecade?

Для домашнего сервера небольшой самописный скрипт раз в сутки отправляет в тг информацию по всем основным параметрам, включая smart дисков и состояние raid. Если что не так - светит красным.

Можно скриптик в студию, пожалуйста

import os
import subprocess
import datetime
import psutil
import requests
import shutil
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter  # будем задавать ширину вручную
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

# Регистрируем шрифты (убедись, что файлы шрифтов существуют!)
pdfmetrics.registerFont(TTFont('DejaVuSans', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSans-Bold', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSansMono', '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf'))

# Параметры для Telegram 
BOT_TOKEN = 'token'
CHAT_ID = 'ID_chat'

# Пути для хранения отчётов
SCRIPT_DIR = '/home/user/script/report'
PDF_DIR = os.path.join(SCRIPT_DIR, 'pdf')
os.makedirs(PDF_DIR, exist_ok=True)

# Параметры отрисовки
LINE_HEIGHT = 14
TOP_MARGIN = 50
BOTTOM_MARGIN = 50
PAGE_WIDTH = 595  # фиксированная ширина (A4)

def get_uptime():
    try:
        return subprocess.check_output(['uptime', '-p'], universal_newlines=True).strip()
    except Exception as e:
        return f"Ошибка получения аптайма: {e}"

def get_temperatures():
    try:
        out = subprocess.check_output(['sensors'], universal_newlines=True)
        temps = [line.strip() for line in out.splitlines() if '°C' in line]
        return "\n".join(temps) if temps else "Нет данных о температуре"
    except Exception as e:
        return f"Ошибка: {e}"

def get_critical_errors():
    try:
        cmd = ['journalctl', '-p', 'crit', '--since', '24 hours ago', '--no-pager']
        return subprocess.check_output(cmd, universal_newlines=True)
    except Exception as e:
        return f"Ошибка получения критических ошибок: {e}"

def get_services_status(services):
    statuses = {}
    for svc in services:
        try:
            stat = subprocess.check_output(['systemctl', 'is-active', svc], universal_newlines=True).strip()
        except subprocess.CalledProcessError:
            stat = "inactive"
        statuses[svc] = stat
    return statuses

def get_disk_usage():
    return psutil.disk_usage('/')

def get_smart_status(disk):
    if not shutil.which("smartctl"):
        return "smartctl не установлен"
    try:
        return subprocess.check_output(['smartctl', '-a', disk], universal_newlines=True)
    except Exception as e:
        return f"Ошибка: {e}"

def get_smart_overall(disk):
    """Возвращаем общий статус SMART (например, PASSED) для диска."""
    if not shutil.which("smartctl"):
        return "smartctl не установлен"
    try:
        overall_output = subprocess.check_output(['smartctl', '-H', disk], universal_newlines=True)
        for line in overall_output.splitlines():
            if "SMART overall-health self-assessment test result:" in line:
                return line.split(":", 1)[1].strip()
        return overall_output.strip()
    except Exception as e:
        return f"Ошибка: {e}"

def parse_smart_full(disk):
    full_output = get_smart_status(disk)
    lines = full_output.splitlines()
    start_idx = None
    end_idx = None
    for i, line in enumerate(lines):
        if "SMART Attributes Data Structure" in line:
            start_idx = i
            break
    if start_idx is None:
        return full_output
    for j in range(start_idx, len(lines)):
        if lines[j].startswith("SMART Error Log"):
            end_idx = j
            break
    block = lines[start_idx:end_idx] if end_idx else lines[start_idx:]
    return "\n".join(block)

def get_raid_status():
    try:
        with open('/proc/mdstat', 'r') as f:
            return f.read()
    except Exception as e:
        return f"Ошибка получения RAID статуса: {e}"

def get_fail2ban_stats():
    jails = ['sshd', 'postgresql', 'vsftpd']
    stats = {}
    for jail in jails:
        try:
            out = subprocess.check_output(['fail2ban-client', 'status', jail], universal_newlines=True)
            banned = "0"
            for line in out.splitlines():
                if "Currently banned" in line:
                    banned = line.split(":")[1].strip()
                    break
            stats[jail] = banned
        except Exception as e:
            stats[jail] = f"Ошибка: {e}"
    return stats

def build_report_lines(report_data):
    lines = []
    def add_line(text, font="DejaVuSans", size=10, color=colors.black):
        lines.append((text, font, size, color))
    def add_separator():
        add_line("____________________", font="DejaVuSans", size=10, color=colors.black)
    
    # Заголовок
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    add_line(f"Ежедневный отчёт о состоянии системы - {timestamp}", font="DejaVuSans-Bold", size=14)
    add_separator()
    
    # 1. Аптайм
    add_line("Аптайм системы:", font="DejaVuSans-Bold", size=12)
    for line in report_data['uptime'].splitlines():
        add_line(line)
    add_separator()
    
    # 2. Температуры
    add_line("Температуры (по всем датчикам):", font="DejaVuSans-Bold", size=12)
    for line in report_data['temperatures'].splitlines():
        add_line(line)
    add_separator()
    
    # 3. Критические ошибки
    add_line("Критические ошибки за последние 24 часа:", font="DejaVuSans-Bold", size=12, color=colors.red)
    crit = report_data['critical_errors'].strip()
    if crit:
        for line in crit.splitlines():
            add_line(line, color=colors.red)
    else:
        add_line("Нет критических ошибок", color=colors.green)
    add_separator()
    
    # 4. Состояние критических служб
    add_line("Состояние критических служб:", font="DejaVuSans-Bold", size=12)
    for svc, stat in report_data['services_status'].items():
        col = colors.green if stat == "active" else colors.red
        add_line(f"{svc}: {stat}", color=col)
    add_separator()
    
    # 5. SMART статус дисков
    add_line("SMART статус дисков:", font="DejaVuSans-Bold", size=12)
    for disk in report_data['smart']:
        overall = get_smart_overall(disk)
        header_color = colors.green if "PASSED" in overall.upper() else colors.red
        add_line(f"{disk} - {overall}", font="DejaVuSans-Bold", size=10, color=header_color)
        smart_text = parse_smart_full(disk)
        for line in smart_text.splitlines():
            # Выводим SMART-атрибуты обычным (чёрным) шрифтом
            add_line(line, font="DejaVuSansMono", size=7, color=colors.black)
        add_separator()
    
    # 6. RAID статус
    add_line("RAID статус:", font="DejaVuSans-Bold", size=12)
    raid_lines = report_data['raid_status'].splitlines()
    raid_color = colors.green if ("degraded" not in report_data['raid_status'].lower() and "inactive" not in report_data['raid_status'].lower()) else colors.red
    for line in raid_lines:
        add_line(line, color=raid_color)
    add_separator()
    
    # 7. Использование дискового пространства
    add_line("Использование дискового пространства:", font="DejaVuSans-Bold", size=12)
    du = report_data['disk_usage']
    add_line(f"Общий объём: {du.total / (1024**3):.2f} GB")
    add_line(f"Использовано: {du.used / (1024**3):.2f} GB")
    add_line(f"Свободно: {du.free / (1024**3):.2f} GB")
    add_separator()
    
    # 8. Статистика fail2ban
    add_line("Статистика fail2ban (количество забаненных адресов):", font="DejaVuSans-Bold", size=12)
    for jail, banned in report_data['fail2ban'].items():
        col = colors.green if banned == "0" else colors.red
        add_line(f"{jail}: {banned}", color=col)
    add_separator()
    
    # 9. Дополнительная информация
    add_line("Дополнительная информация:", font="DejaVuSans-Bold", size=12)
    load = os.getloadavg()
    add_line(f"Load average (1, 5, 15 мин): {load}")
    mem = psutil.virtual_memory()
    add_line(f"Использование памяти: {mem.percent}%")
    add_separator()
    
    return lines

def draw_report(lines, pdf_file_path, page_width, page_height):
    c = canvas.Canvas(pdf_file_path, pagesize=(page_width, page_height))
    y = page_height - TOP_MARGIN
    for text, font, size, color in lines:
        c.setFont(font, size)
        c.setFillColor(color)
        c.drawString(50, y, text)
        y -= LINE_HEIGHT
    c.save()

def main():
    # Собираем данные отчёта
    report_data = {}
    report_data['uptime'] = get_uptime()
    report_data['temperatures'] = get_temperatures()
    report_data['critical_errors'] = get_critical_errors()
    report_data['services_status'] = get_services_status(['fail2ban', 'ssh', 'postgresql'])
    report_data['disk_usage'] = get_disk_usage()
    # Задаём список дисков для SMART 
    report_data['smart'] = ['/dev/sda', '/dev/sdb']
    report_data['raid_status'] = get_raid_status()
    report_data['fail2ban'] = get_fail2ban_stats()

    # Собираем список строк отчёта
    lines = build_report_lines(report_data)
    total_lines = len(lines)
    required_height = TOP_MARGIN + (total_lines * LINE_HEIGHT) + BOTTOM_MARGIN
    print(f"Всего строк: {total_lines}, требуемая высота: {required_height}")

    now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
    pdf_file = os.path.join(PDF_DIR, f'report_{now}.pdf')
    draw_report(lines, pdf_file, PAGE_WIDTH, required_height)
    print(f"PDF отчёт сохранён: {pdf_file}")

    # Отправляем PDF через Telegram
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendDocument"
    with open(pdf_file, 'rb') as f:
        files = {'document': f}
        data = {'chat_id': CHAT_ID, 'caption': 'Ежедневный отчёт сервера'}
        response = requests.post(url, files=files, data=data)
    print("Ответ Telegram:", response.text)

if __name__ == '__main__':
    main()

Пожалуйста. Бот собирает и отправляет в чат один раз в сутки (через crontab) pdf с параметрами. Здесь у меня из параметров:
- аптайм
- температура
- критические ошибки
- состояние критических служб
- smart дисков
- статус RAID
- использование дискового пространства
- статистика fail2ban
- средняя нагрузка
- использование памяти

Если в виктории пошли красные сектора, ты уже насилуешь труп.

У этого диска явно кончилась область с резервными секторами и это никак не связано умный он или нет, а кончилась она потому что в диск попала пыль, мусор, который в процессе работы задирает поверхность, вот тебе и красные сектора, ты борешься со следствием, а не причиной.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации