# 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]`.
## Формат алертов
**Первый падение:**
```
⚠️ Server alert · 192.168.0.5
🔴 Gitea: HTTP 502 (expected 200), 12ms
```
**Напоминание:**
```
⏰ Still down · 192.168.0.5
🔴 Gitea: HTTP 502 (expected 200), 12ms
```
**Восстановление:**
```
✅ Recovered · 192.168.0.5
🟢 Gitea: HTTP 200, 10ms
```
**Алармы Netdata:**
```
⚠️ Server alert · 192.168.0.5
🔴 Netdata alarms: 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).