Files
Sanders 465b27d673 ServerMonitor: Dockerized health-check service with Telegram alerts
- Pure stdlib Python (3.12), runs on python:3.12-alpine as non-root
- Checks: ping, HTTP (expected codes), TCP ports, Netdata alarms + metrics
- Netdata v2 compatible: system.cpu (no idle dim), system.ram, disk_space.* discovery
- AlertEngine dedup: first alert, reminders, recovery (only after real alert)
- Baseline first-run (no alert storm on deploy), atomic state file, --once mode
- 20 unit tests passing; verified live against 192.168.0.5
2026-08-05 22:28:20 +03:00

134 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ServerMonitor
Docker-контейнер для мониторинга домашнего сервера (Unraid) с удалённого VPS. Периодически проверяет доступность сервисов и шлёт алерты в Telegram при падениях, напоминания, пока сервис не поднимется, и сообщения о восстановлении.
## Что это
ServerMonitor — это лёгкий Python-сервис без сторонних зависимостей. Он работает на `python:3.12-alpine`, использует только стандартную библиотеку и предназначен для развёртывания на VPS. Сервис мониторит хост `192.168.0.5` по сети: пингует его, делает HTTP-запросы к веб-приложениям, проверяет TCP-порты и забирает алармы/метрики из Netdata.
## Как это работает
```
┌─────────┐ ping / http / tcp / netdata ┌──────────────┐
│ VPS │ ───────────────────────────────→│ Unraid 192. │
│ контейнер│ │ 168.0.5 │
│ServerMonitor│ │ │
└────┬────┘ └──────────────┘
│ Telegram (alert / reminder / recovery)
┌─────────────┐
│ Telegram │
└─────────────┘
```
1. Контейнер каждые `check_interval_sec` секунд выполняет набор проверок.
2. Если проверка падает — отправляется первый алерт.
3. Пока сервис остаётся недоступным, каждые `reminder_interval_sec` секунд приходит напоминание.
4. Когда сервис восстанавливается — приходит сообщение о recovery.
5. Состояние хранится в `/data/state.json`, поэтому перезапуск контейнера не вызывает повторной волны алертов.
## Быстрый старт
```bash
cp .env.example .env
# отредактируй .env: добавь TELEGRAM_BOT_TOKEN и TELEGRAM_CHAT_ID
docker compose up -d --build
```
## Как получить токен бота и chat_id
1. Напишите [@BotFather](https://t.me/BotFather), создайте бота и скопируйте токен в `TELEGRAM_BOT_TOKEN`.
2. Напишите [@userinfobot](https://t.me/userinfobot), получите свой ID и вставьте его в `TELEGRAM_CHAT_ID`.
## Настройка чеков в config.json
| Секция | Поле | Описание |
|--------|------|----------|
| `host` | строка | IP или имя мониторимого хоста |
| `check_interval_sec` | число | Интервал между циклами проверок, сек |
| `reminder_interval_sec` | число | Интервал между напоминаниями, сек |
| `timeout_sec` | число | Таймаут сетевых операций, сек |
| `state_file` | строка | Путь к файлу состояния |
| `telegram.bot_token` | строка | Токен Telegram-бота |
| `telegram.chat_id` | строка | ID чата для алертов |
| `thresholds.cpu_percent` | число | Порог загрузки CPU, % |
| `thresholds.ram_avail_mb` | число | Минимум доступной RAM, МБ |
| `thresholds.disk_percent` | число | Порог занятости диска, % |
| `http_checks` | массив | `{name, url, expected}` |
| `tcp_checks` | массив | `{name, host, port}` |
| `netdata.base_url` | строка | URL Netdata |
`expected` — список допустимых HTTP-кодов, например `[200]` или `[200, 401]`.
## Формат алертов
**Первый падение:**
```
⚠️ <b>Server alert</b> · 192.168.0.5
🔴 <b>Gitea</b>: HTTP 502 (expected 200), 12ms
```
**Напоминание:**
```
⏰ <b>Still down</b> · 192.168.0.5
🔴 <b>Gitea</b>: HTTP 502 (expected 200), 12ms
```
**Восстановление:**
```
✅ <b>Recovered</b> · 192.168.0.5
🟢 <b>Gitea</b>: HTTP 200, 10ms
```
**Алармы Netdata:**
```
⚠️ <b>Server alert</b> · 192.168.0.5
🔴 <b>Netdata alarms</b>: CPU_USAGE [CRITICAL]: 95 — ...
```
## Лог-режим без токена
Если `TELEGRAM_BOT_TOKEN` пустой, сообщения не отправляются, а пишутся в лог `[TELEGRAM LOG-ONLY]`. Это удобно для отладки.
## Локальный запуск без Docker
```bash
# состояние пишется в ./state.json, а не в /data
STATE_FILE=./state.json python3 monitor.py --once
```
Флаг `--once` запускает один цикл проверок и завершает работу. Без `STATE_FILE` сервис попытается писать в `/data` (вне Docker это обычно недоступно — в этом случае состояние просто не сохранится, а первый запуск будет считаться baseline-прогоном).
## Запуск тестов
```bash
python3 -m unittest discover -s tests -v
```
## Troubleshooting
**Контейнер не видит хост:**
- Убедитесь, что у VPS есть маршрут к `192.168.0.5`.
- Проверьте firewall на Unraid/VPS.
- Для тестов можно запустить `docker run --rm --network host ...`.
**Ping в контейнере:**
- Внутри `python:3.12-alpine` может не быть прав на ICMP.
- `ping_check` деградирует gracefully: возвращает `ok=True` с сообщением `ping unavailable`, чтобы не ломать весь цикл.
**Состояние не сохраняется:**
- Проверьте, что volume `monitor-state` смонтирован в `/data`.
- В образе `/data` принадлежит пользователю `app` (uid 10001).