- Add __main__ guard to cli.py for pyinstaller packaging - Document clean-venv build producing ~11 MB binary - Add *.spec to .gitignore - Exclude heavy stdlib modules in build command
473 lines
20 KiB
Markdown
473 lines
20 KiB
Markdown
# SES Monitor
|
||
|
||
Система непрерывного мониторинга диалоговой сценарной машины **SES** (Service Engine Server).
|
||
|
||
## Что делает
|
||
|
||
Watchdog-демон, который:
|
||
- **Проверяет доступность** SES API каждые 30 секунд
|
||
- **Имитирует диалоги пользователей** по сценариям, описанным в YAML-файлах
|
||
- **Отправляет метрики в Zabbix** через zabbix_sender или UserParameter
|
||
|
||
SES — универсальная сценарная машина, поэтому мониторинг не привязан к конкретному проекту. Один и тот же демон может обслуживать любой сценарий: голосового ассистента, чат-бота, IVR, диалогов поддержки и любых других.
|
||
|
||
## Архитектура
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ ses-monitor (watchdog-демон) │
|
||
│ │
|
||
│ ┌──────────────┐ ┌────────────────┐ │
|
||
│ │ LivenessCheck │ │ ScenarioRunner │ │
|
||
│ │ (30 сек) │ │ (по YAML) │ │
|
||
│ │ │ │ │ │
|
||
│ │ GET /hostname│ │ POST /ses/ask │ │
|
||
│ │ GET /license │ │ шаги+expect │ │
|
||
│ └──────┬───────┘ └───────┬─────────┘ │
|
||
│ │ │ │
|
||
│ ┌──────▼──────────────────▼──────┐ │
|
||
│ │ Zabbix (sender / UP) │ │
|
||
│ └──────────────┬─────────────────┘ │
|
||
└─────────────────┼────────────────────────────────────┘
|
||
│ TCP 10051 / JSON file
|
||
┌───────────▼───────────┐
|
||
│ Zabbix Server │
|
||
│ (триггеры, алерты) │
|
||
└───────────────────────┘
|
||
```
|
||
|
||
|
||
## Режимы доставки в Zabbix
|
||
|
||
Монитор поддерживает два режима отправки метрик:
|
||
|
||
### sender (по умолчанию) — zabbix_sender (активный push)
|
||
Монитор запускает утилиту `zabbix_sender` и передаёт метрики через stdin.
|
||
Данные отправляются на Zabbix сервер или прокси (порт 10051, trapper).
|
||
В Zabbix элементы данных имеют тип "Zabbix траппер".
|
||
Требуется: установленный zabbix_sender на сервере с монитором.
|
||
|
||
### userparameter — Zabbix агент (пассивный опрос)
|
||
Монитор пишет JSON со всеми метриками в файл (`/var/tmp/ses_monitor/metrics.json`).
|
||
Zabbix агент по запросу сервера запускает скрипт `scripts/zbx_get.py`,
|
||
который возвращает содержимое JSON-файла.
|
||
В Zabbix используется master-элемент `ses.metrics` (тип "Zabbix агент", текст)
|
||
и зависимые элементы с препроцессингом JSONPath.
|
||
|
||
Настройка:
|
||
1. Импортировать шаблон в Zabbix:
|
||
- Zabbix 6.4: `zabbix/zbx_ses_template_up.xml`
|
||
- Zabbix 5.0: `zabbix/zbx_ses_template_5.0_up.xml`
|
||
2. Привязать шаблон к хосту (Configuration → Hosts → выберите хост → Templates)
|
||
3. В конфиге монитора `config/config.yaml` установить `mode: "userparameter"`
|
||
4. При старте демон автоматически создаёт конфиг агента:
|
||
`/opt/ses-monitor/config/zbx_agentd_ses.conf`
|
||
5. В `/etc/zabbix/zabbix_agentd.conf` добавить строку (или скопировать конфиг в `/etc/zabbix/zabbix_agentd.d/`):
|
||
```
|
||
Include=/opt/ses-monitor/config/zbx_agentd_ses.conf
|
||
```
|
||
6. Перезапустить Zabbix агента: `sudo systemctl restart zabbix-agent`
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
/opt/ses-monitor/
|
||
├── src/
|
||
│ └── ses_monitor/ # Python-пакет (src-layout)
|
||
│ ├── __init__.py # Версия, метаданные
|
||
│ ├── __main__.py # Точка входа: python -m ses_monitor
|
||
│ ├── main.py # Главный asyncio watchdog-цикл
|
||
│ ├── config.py # Загрузка YAML-конфигурации
|
||
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
|
||
│ ├── session_client.py # HTTP-клиент для SES API
|
||
│ ├── scenario_runner.py # Движок выполнения сценариев
|
||
│ ├── log_client.py # Чтение логов SES (задел на будущее)
|
||
│ ├── zabbix.py # Интеграция с Zabbix (sender / UserParameter)
|
||
│ ├── cli.py # CLI ses-test: тестирование YAML-сценариев
|
||
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
|
||
├── config/
|
||
│ ├── config.yaml.example # Шаблон конфигурации (скопировать в config.yaml)
|
||
│ └── scenarios/
|
||
│ ├── example_liveness.yaml # Пример: быстрая проверка
|
||
│ └── example_dialog.yaml # Пример: многошаговый диалог
|
||
├── zabbix/
|
||
│ ├── zbx_ses_template.xml # Шаблон Zabbix 6.4 для sender (trapper)
|
||
│ ├── zbx_ses_template_up.xml # Шаблон Zabbix 6.4 для UserParameter
|
||
│ ├── zbx_ses_template_5.0.xml # Шаблон Zabbix 5.0 для sender (trapper)
|
||
│ ├── zbx_ses_template_5.0_up.xml # Шаблон Zabbix 5.0 для UserParameter
|
||
│ └── zbx_ses_monitor.conf # Резервный конфиг UserParameter
|
||
├── scripts/
|
||
│ └── zbx_get.py # Скрипт чтения JSON для UserParameter
|
||
├── systemd/
|
||
│ └── ses-monitor.service # Systemd unit
|
||
├── tests/
|
||
│ ├── __init__.py
|
||
│ └── test_scenario_runner.py # 48 тестов
|
||
├── cli/
|
||
│ └── pyproject.toml # Отдельная сборка: только ses-test
|
||
├── pyproject.toml # Зависимости и метаданные пакета
|
||
└── README.md
|
||
```
|
||
|
||
## Установка на сервере
|
||
|
||
### 1. Требования
|
||
|
||
- **Python 3.9+**
|
||
- **Git** — для клонирования репозитория
|
||
- **Zabbix Agent** с утилитой `zabbix_sender`
|
||
- Доступ к SES API (`http://<ses-host>:6190`)
|
||
- Доступ к Zabbix Server по порту 10051
|
||
|
||
### 2. Установка пакета
|
||
|
||
```bash
|
||
# Клонируем репозиторий на сервер
|
||
git clone https://git.totmin.ru/en2zmax/ses-monitor.git /opt/ses-monitor
|
||
|
||
# Переходим в каталог проекта
|
||
cd /opt/ses-monitor
|
||
|
||
# Создаём виртуальное окружение и устанавливаем пакет
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -e .
|
||
|
||
# Создаём каталог для логов
|
||
mkdir -p /var/log/ses-monitor
|
||
```
|
||
|
||
### 3. Настройка конфигурации
|
||
|
||
Скопируйте шаблон и отредактируйте:
|
||
|
||
```bash
|
||
cp config/config.yaml.example config/config.yaml
|
||
nano config/config.yaml
|
||
```
|
||
|
||
Основные поля:
|
||
|
||
```yaml
|
||
ses:
|
||
base_url: "http://YOUR_SES_HOST:6190" # URL вашего SES API
|
||
channel: "monitoring" # Канал для мониторинговых сессий
|
||
request_timeout: 30
|
||
|
||
zabbix:
|
||
enabled: true
|
||
mode: "userparameter" # или "sender"
|
||
host: "my-server-name" # Имя хоста в Zabbix
|
||
server: "127.0.0.1" # IP Zabbix сервера (только sender)
|
||
port: 10051
|
||
sender_path: "/usr/bin/zabbix_sender"
|
||
metrics_file: "/var/tmp/ses_monitor/metrics.json" # (только userparameter)
|
||
```
|
||
|
||
### 4. Создание сценария
|
||
|
||
Скопируйте пример и адаптируйте под ваш проект:
|
||
|
||
```bash
|
||
cp config/scenarios/example_liveness.yaml config/scenarios/my_project.yaml
|
||
```
|
||
|
||
### 5. Запуск
|
||
|
||
```bash
|
||
# Проверка вручную:
|
||
cd /opt/ses-monitor
|
||
.venv/bin/python -m ses_monitor
|
||
|
||
# Установка как systemd-сервиса:
|
||
cp systemd/ses-monitor.service /etc/systemd/system/
|
||
systemctl daemon-reload
|
||
systemctl enable --now ses-monitor
|
||
|
||
# Просмотр логов:
|
||
journalctl -u ses-monitor -f
|
||
```
|
||
|
||
### 6. Импорт шаблона Zabbix
|
||
|
||
В зависимости от версии Zabbix и режима доставки:
|
||
|
||
| Zabbix | sender (trapper) | userparameter |
|
||
|---|---|---|
|
||
| 6.4 | `zabbix/zbx_ses_template.xml` | `zabbix/zbx_ses_template_up.xml` |
|
||
| 5.0 | `zabbix/zbx_ses_template_5.0.xml` | `zabbix/zbx_ses_template_5.0_up.xml` |
|
||
|
||
1. Zabbix Frontend → **Configuration → Templates → Import**
|
||
2. Выбрать файл шаблона
|
||
3. Нажать Import
|
||
4. Привязать шаблон "SES Monitor" к нужному хосту
|
||
|
||
## Установка ses-test
|
||
|
||
Если нужна только утилита `ses-test` для отладки YAML-сценариев (без демона и Zabbix):
|
||
|
||
```bash
|
||
pip install "ses-test @ git+https://git.totmin.ru/en2zmax/ses-monitor.git#subdirectory=cli"
|
||
```
|
||
|
||
После установки доступна одна команда — `ses-test`:
|
||
|
||
```bash
|
||
ses-test --help
|
||
ses-test my_scenario.yaml --ses-url http://ses-host:6190
|
||
ses-test my_scenario.yaml --ses-url http://ses-host:6190 --dry-run
|
||
```
|
||
|
||
Подробнее — см. раздел [«Как адаптировать под ваш проект»](#как-адаптировать-под-ваш-проект).
|
||
|
||
### Сборка исполняемого файла (без Python)
|
||
|
||
`ses-test` можно собрать в один бинарник (~11 MB), не требующий Python на целевой машине.
|
||
|
||
Сборка производится в чистом виртуальном окружении — чтобы в бинарник не попали
|
||
лишние системные пакеты (numpy, pillow и др.):
|
||
|
||
```bash
|
||
# 1. Клонируем репозиторий
|
||
git clone https://git.totmin.ru/en2zmax/ses-monitor.git
|
||
cd ses-monitor
|
||
|
||
# 2. Чистое окружение
|
||
python -m venv /tmp/build
|
||
/tmp/build/bin/pip install ./cli pyinstaller
|
||
|
||
# 3. Сборка
|
||
/tmp/build/bin/pyinstaller --onefile --name ses-test \
|
||
--paths src/ \
|
||
--hidden-import ses_monitor.utils \
|
||
--strip \
|
||
--exclude-module tkinter \
|
||
--exclude-module turtle \
|
||
--exclude-module sqlite3 \
|
||
--exclude-module curses \
|
||
--exclude-module idlelib \
|
||
--exclude-module pydoc \
|
||
--exclude-module venv \
|
||
--exclude-module ensurepip \
|
||
--exclude-module unittest \
|
||
--exclude-module test \
|
||
--exclude-module multiprocessing \
|
||
src/ses_monitor/cli.py
|
||
```
|
||
|
||
Готовый бинарник: `dist/ses-test` (~11 MB, Linux) или `dist/ses-test.exe` (Windows —
|
||
запустить те же команды на Windows).
|
||
|
||
Для сборки под другую ОС нужна эта ОС — PyInstaller не кросс-компилирует.
|
||
|
||
## Формат сценариев
|
||
|
||
Сценарии описываются в YAML-файлах в каталоге `config/scenarios/`. Это гибкий DSL, который не зависит от конкретного проекта SES.
|
||
|
||
### Полный формат
|
||
|
||
```yaml
|
||
# Имя сценария (используется в логах)
|
||
name: "example: проверка диалога"
|
||
|
||
# Описание для документации
|
||
description: "Проверяет что бот корректно отвечает на вопросы"
|
||
|
||
# Включён ли сценарий (false — отключён)
|
||
enabled: true
|
||
|
||
# Интервал запуска в секундах
|
||
interval: 300
|
||
|
||
# Таймаут HTTP-запроса в секундах
|
||
timeout: 30
|
||
|
||
# ID робота (обязательно). Узнать: GET /ses/robot/listall
|
||
robot_id: "uuid-вашего-робота"
|
||
|
||
# Канал (если не указан — берётся из config.yaml)
|
||
channel: "monitoring"
|
||
|
||
# Не переоткрывать закрытые сессии (false — создаём новую сессию каждый раз)
|
||
restart_closed: false
|
||
|
||
# Шаги сценария
|
||
steps:
|
||
- send: "Привет"
|
||
expect:
|
||
# Каждое ожидание проверяет значение в JSON-ответе
|
||
- path: "error"
|
||
equals: 0
|
||
|
||
- path: "answer[0].messages[0]"
|
||
contains: "Здрав"
|
||
|
||
- send: "Как дела?"
|
||
expect:
|
||
- path: "error"
|
||
equals: 0
|
||
- path: "answer"
|
||
not_empty: true
|
||
```
|
||
|
||
### Условия проверки (expect)
|
||
|
||
| Условие | Описание | Пример |
|
||
|---|---|---|
|
||
| `equals` | Точное совпадение | `equals: 0` |
|
||
| `contains` | Подстрока содержится (без учёта регистра) | `contains: "здравствуйте"` |
|
||
| `not_contains` | Подстрока отсутствует | `not_contains: "ошибка"` |
|
||
| `not_empty` | Поле не пустое | `not_empty: true` |
|
||
| `regex` | Регулярное выражение | `regex: "\\d{10,}"` |
|
||
| `exists` | Поле существует и не None | `exists` (без значения) |
|
||
|
||
### Пути JSON (path)
|
||
|
||
Формат: `ключ.подключ[индекс].поле`
|
||
|
||
```yaml
|
||
# Корневое поле
|
||
path: "error"
|
||
|
||
# Массив → первый элемент → поле
|
||
path: "answer[0].messages[0]"
|
||
|
||
# Глубокая вложенность
|
||
path: "request.data[0].reply.confidence"
|
||
```
|
||
|
||
## Как адаптировать под ваш проект
|
||
|
||
1. **Узнайте robot_id** вашего сценария:
|
||
```bash
|
||
curl -s http://<ses-host>:6190/ses/robot/listall
|
||
```
|
||
|
||
2. **Пропишите robot_id** в файле сценария (поле `robot_id` в YAML)
|
||
|
||
3. **Создайте файл сценария**, описав шаги диалога:
|
||
```bash
|
||
cp config/scenarios/example_dialog.yaml config/scenarios/my_project.yaml
|
||
vim config/scenarios/my_project.yaml
|
||
```
|
||
|
||
4. **Протестируйте сценарий** утилитой `ses-test`:
|
||
```bash
|
||
# Базовый прогон
|
||
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190
|
||
|
||
# С детальным выводом JSON-ответов (режим отладки)
|
||
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 -v
|
||
|
||
# Только валидация YAML без запросов к SES
|
||
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --dry-run
|
||
|
||
# С переопределением robot_id (если не указан в YAML)
|
||
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --robot-id abc-123
|
||
|
||
# С фиксированной сессией для повторных прогонов
|
||
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --session debug-1
|
||
```
|
||
|
||
**Примеры вывода:**
|
||
|
||
Успешный прогон (без `-v`):
|
||
```
|
||
=== Сценарий: 'test: МФЦ диалог' ===
|
||
SES: http://ses-host:6190
|
||
Robot ID: abc-123-def-456
|
||
Канал: monitoring
|
||
Шагов: 2
|
||
Сессия: monitoring_test_1782271424
|
||
|
||
── Шаг 1/2 ────────────────────────────────────────
|
||
→ Привет
|
||
← Время ответа: 176ms
|
||
[PASS] path=error equals=0
|
||
[PASS] path=answer[0].messages[0] not_empty
|
||
|
||
── Шаг 2/2 ────────────────────────────────────────
|
||
→ Записаться на приём
|
||
← Время ответа: 382ms
|
||
[PASS] path=error equals=0
|
||
[PASS] path=answer[0].messages[0] not_empty
|
||
|
||
──────────────────────────────────────────────────
|
||
РЕЗУЛЬТАТ: УСПЕШНО (2/2 шагов)
|
||
```
|
||
|
||
Проваленный шаг:
|
||
```
|
||
── Шаг 1/1 ────────────────────────────────────────
|
||
→ Привет
|
||
← Время ответа: 177ms
|
||
[PASS] path=error equals=0
|
||
[FAIL] Поле 'answer[0].messages[0]' = 'пожалуйста выберите...'
|
||
не содержит 'несуществующая_фраза_xyz_999'
|
||
──────────────────────────────────────────────────
|
||
РЕЗУЛЬТАТ: ПРОВАЛЕН (шаг 1)
|
||
```
|
||
|
||
Режим отладки (`-v`):
|
||
```
|
||
── Шаг 1/2 ────────────────────────────────────────
|
||
→ Привет
|
||
← Время ответа: 176ms
|
||
← RAW JSON:
|
||
{
|
||
"error": 0,
|
||
"question": "Привет",
|
||
"answer": [{
|
||
"messages": [
|
||
"Пожалуйста выберите действие или задайте вопрос.\n\n{button:...}\n"
|
||
],
|
||
"voice": "Коля",
|
||
"interruptible": false
|
||
}],
|
||
"session": "monitoring_test_1782271424",
|
||
"uuid": "132e530c-..."
|
||
}
|
||
|
||
[PASS] path=error equals=0
|
||
[PASS] path=answer[0].messages[0] not_empty
|
||
```
|
||
|
||
**Exit code:** `0` — все шаги пройдены, `1` — есть ошибки, `2` — ошибка аргументов.
|
||
|
||
5. **Запустите демон** — метрики агрегируются автоматически (шаблон Zabbix универсален)
|
||
|
||
## Расширение системы
|
||
|
||
### Добавление нового сценария
|
||
|
||
1. Создать `config/scenarios/my_scenario.yaml`
|
||
2. Скопировать структуру из примеров
|
||
3. Указать `robot_id` вашего робота (узнать: `GET /ses/robot/listall`)
|
||
4. Описать шаги и ожидания
|
||
5. Сценарий автоматически подхватится через hot-reload (каждые 60 секунд)
|
||
|
||
### Добавление новой метрики
|
||
|
||
1. В нужном модуле добавить поле в датакласс-результат
|
||
2. В `main.py` вызвать `self._zabbix.add(key, value)`
|
||
3. Добавить item в Zabbix шаблон
|
||
|
||
## Мониторинг самого монитора
|
||
|
||
- **systemd**: `systemctl is-active ses-monitor`
|
||
- **Zabbix Active Agent**: `proc.num[python,root,ses_monitor]`
|
||
- **Логи**: `journalctl -u ses-monitor --since "10 min ago" -o cat | grep ERROR`
|
||
|
||
## Отказоустойчивость
|
||
|
||
- **Автоперезапуск**: systemd Restart=always, RestartSec=10
|
||
- **Graceful shutdown**: по SIGTERM отправляет последний батч метрик
|
||
- **Таймауты**: все HTTP-запросы с ограничением по времени (по умолчанию 30с)
|
||
- **Изоляция сценариев**: ошибка в одном сценарии не влияет на другие
|
||
- **Hot-reload**: сценарии перечитываются без перезапуска демона
|
||
|
||
## Лицензия
|
||
|
||
MIT
|
||
|