- 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
134 lines
6.3 KiB
Markdown
134 lines
6.3 KiB
Markdown
# 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).
|