# LLM-Zabbix LLM-Zabbix — это связка из двух отдельных Python-проектов: - `alert-receiver` — принимает webhook из Zabbix, валидирует токен и пересылает событие дальше; - `alert-processor` — асинхронно обрабатывает события через Redis queue, делает enrichment, triage, correlation/RCA, remediation и доставку уведомлений. В текущей схеме **Ollama разворачивается отдельно** от стека LLM-Zabbix. `alert-processor` обращается к нему по адресу хоста Docker, например: ```env LLM_BASE_URL=http://host.docker.internal:11434 ``` Для Linux-хоста это работает при наличии в `docker-compose.yml` строки: ```yaml extra_hosts: - "host.docker.internal:host-gateway" ``` --- ## Архитектура Поток обработки такой: 1. Zabbix отправляет webhook в `alert-receiver`. 2. `alert-receiver` проверяет `WEBHOOK_TOKEN`, нормализует payload и пересылает его в `alert-processor`. 3. `alert-processor-ingest` кладет событие в Redis queue и сразу возвращает `202 Accepted`. 4. `alert-processor-worker` забирает событие из очереди и выполняет: - state tracking; - suppress / flap / recovery logic; - Zabbix API enrichment; - LLM triage для low severity; - deterministic correlation + LLM correlation fallback; - LLM remediation; - отправку уведомлений в Matrix / Mail; - audit logging. 5. `alert-worker-health` показывает состояние worker и очередей. --- ## Структура репозитория ```text LLM-Zabbix/ ├── docker-compose.yml ├── alert-receiver/ │ ├── .env │ ├── Dockerfile │ ├── requirements.txt │ └── app/ │ └── main.py └── alert-processor/ ├── .env ├── Dockerfile ├── requirements.txt ├── config/ │ ├── event_kind_rules.yaml │ └── root_cause_map.yaml └── app/ ├── main.py ├── worker.py └── worker_health.py ``` --- ## Требования - Docker Engine / Docker Compose plugin; - отдельный Redis в составе compose; - отдельно развернутый Ollama; - доступ `alert-processor` к: - Zabbix API; - Matrix homeserver / MAS; - SMTP relay; - Ollama API. --- ## Как работает `.env` В проекте **два отдельных `.env` файла**: - `alert-receiver/.env` - `alert-processor/.env` Они не взаимозаменяемы: каждый управляет только своим приложением. --- ## `alert-receiver/.env` ### Назначение Файл управляет входной точкой webhook из Zabbix и пересылкой события в `alert-processor`. ### Что обычно находится в этом `.env` Минимальный пример: ```env APP_NAME=alert-receiver APP_HOST=0.0.0.0 APP_PORT=8080 WEBHOOK_TOKEN=change_me_webhook_token FORWARD_ENABLED=true PROCESSOR_URL=http://alert-processor-ingest:8081/internal/events INTERNAL_API_TOKEN=change_internal_token FORWARD_TIMEOUT_SECONDS=10 ``` > Если у вас в `alert-receiver` имена переменных отличаются, ориентируйтесь на фактический `config.py` этого проекта. По смыслу переменные должны отвечать именно за эти функции. ### Назначение переменных - `APP_NAME` — имя сервиса в логах. - `APP_HOST` — адрес bind внутри контейнера. - `APP_PORT` — порт FastAPI/uvicorn внутри контейнера. - `WEBHOOK_TOKEN` — токен, который должен передать Zabbix в webhook. **Обязателеен для минимальной настройки.** - `FORWARD_ENABLED` — включена ли пересылка событий в `alert-processor`. - `PROCESSOR_URL` — endpoint ingest-сервиса `alert-processor`. - `INTERNAL_API_TOKEN` — токен, которым `alert-receiver` аутентифицируется перед `alert-processor`. **Обязателеен для минимальной настройки.** - `FORWARD_TIMEOUT_SECONDS` — timeout ожидания ответа от ingest. ### Пример для Docker Compose ```env APP_NAME=alert-receiver APP_HOST=0.0.0.0 APP_PORT=8080 WEBHOOK_TOKEN=super_webhook_token FORWARD_ENABLED=true PROCESSOR_URL=http://alert-processor-ingest:8081/internal/events INTERNAL_API_TOKEN=super_internal_token FORWARD_TIMEOUT_SECONDS=10 ``` --- ## `alert-processor/.env` ### Назначение Файл управляет всей логикой асинхронной обработки: очередь, Redis, Matrix, Mail, Zabbix enrichment, LLM triage/remediation/correlation, audit, correlation YAML. Ниже — рабочий пример. ```env APP_NAME=alert-processor APP_HOST=0.0.0.0 APP_PORT=8081 INTERNAL_API_TOKEN=super_internal_token REQUIRE_INTERNAL_API_TOKEN=true REDIS_ENABLED=true REDIS_URL=redis://redis:6379/0 REDIS_KEY_PREFIX=alert REDIS_FINGERPRINT_TTL_SECONDS=86400 REDIS_EVENT_TTL_SECONDS=604800 SUPPRESS_ENABLED=true SUPPRESS_WINDOW_SECONDS=900 SUPPRESS_APPLY_TO_AVERAGE=true FLAP_ENABLED=true FLAP_WINDOW_SECONDS=120 FLAP_THRESHOLD=4 FLAP_APPLY_TO_AVERAGE=true CORRELATION_ENABLED=true CORRELATION_WINDOW_SECONDS=180 CORRELATION_SUPPRESS_CHILDREN=true CORRELATION_KIND_RULES_PATH=/app/config/event_kind_rules.yaml CORRELATION_ROOT_CAUSE_PATH=/app/config/root_cause_map.yaml MATRIX_ENABLED=true MATRIX_HOMESERVER_URL=https://mr.example.org MATRIX_ROOM_ID=!roomid:mr.example.org MATRIX_ACCESS_TOKEN=initial_access_token MATRIX_REFRESH_TOKEN=initial_refresh_token MATRIX_OAUTH_TOKEN_ENDPOINT=https://ms.example.org/oauth2/token MATRIX_OAUTH_CLIENT_ID=LLMZABBIX01 MATRIX_OAUTH_CLIENT_SECRET= MATRIX_ACCESS_TOKEN_EXPIRES_IN_SECONDS=300 MATRIX_REFRESH_MARGIN_SECONDS=60 MATRIX_TOKEN_STATE_FILE=/app/.matrix_token_state.json MATRIX_MESSAGE_TYPE=m.notice MATRIX_REQUEST_TIMEOUT_SECONDS=10 MATRIX_VERIFY_TLS=true MAIL_ENABLED=true MAIL_SMTP_HOST=smtp.example.org MAIL_SMTP_PORT=587 MAIL_SMTP_USERNAME=monitoring@example.org MAIL_SMTP_PASSWORD=change_me MAIL_FROM=monitoring@example.org MAIL_TO=ops@example.org MAIL_USE_STARTTLS=true MAIL_USE_TLS=false MAIL_TIMEOUT_SECONDS=15 MAIL_SUBJECT_PREFIX=[LLM-Zabbix] ZABBIX_API_ENABLED=true ZABBIX_API_URL=https://zb.example.org/api_jsonrpc.php ZABBIX_API_TOKEN=change_me_zabbix_api_token ZABBIX_WEB_URL=https://zb.example.org ZABBIX_API_TIMEOUT_SECONDS=10 ZABBIX_API_VERIFY_TLS=true ZABBIX_GRAPH_PERIOD_HOURS=1 ZABBIX_GRAPH_TIMEZONE=Europe/Moscow ZABBIX_ENRICH_ONLY_NOTIFY=true LLM_ENABLED=true LLM_BASE_URL=http://host.docker.internal:11434 LLM_MODEL=qwen3.5:4b LLM_TIMEOUT_SECONDS=45 LLM_VERIFY_TLS=true LLM_TEMPERATURE=0.1 LLM_MAX_STEPS=4 LLM_MAX_COMMANDS=4 LLM_TRIAGE_ENABLED=true LLM_TRIAGE_CACHE_TTL_SECONDS=3600 LLM_CORRELATION_ENABLED=true LLM_CORRELATION_MIN_CONFIDENCE=medium QUEUE_ENABLED=true QUEUE_NAME=alert:queue:events QUEUE_PROCESSING_NAME=alert:queue:processing QUEUE_DEADLETTER_NAME=alert:queue:deadletter QUEUE_BLOCK_TIMEOUT_SECONDS=5 QUEUE_MAX_ATTEMPTS=3 QUEUE_DEDUP_TTL_SECONDS=86400 QUEUE_REQUEUE_PROCESSING_ON_STARTUP=true AUDIT_ENABLED=true AUDIT_KEY_PREFIX=alert:audit AUDIT_TTL_SECONDS=604800 AUDIT_MAX_STAGE_RECORDS=200 ``` ### Блоки переменных и из назначение #### Общие - `APP_NAME`, `APP_HOST`, `APP_PORT` — базовые параметры FastAPI ingest. - `INTERNAL_API_TOKEN` — токен для запросов от `alert-receiver`. **Обязателеен для минимальной настройки, должен совпадать с alert-receiver.** - `REQUIRE_INTERNAL_API_TOKEN` — обязательность токена. #### Redis / state - `REDIS_URL` — подключение к Redis. - `REDIS_KEY_PREFIX` — префикс ключей. - `REDIS_FINGERPRINT_TTL_SECONDS` — TTL fingerprint state. - `REDIS_EVENT_TTL_SECONDS` — TTL снапшотов событий. #### Suppress / flap - `SUPPRESS_*` — антиспам по повторяющимся событиям. - `FLAP_*` — подавление флаппинга. #### Correlation / RCA - `CORRELATION_ENABLED` — включает корреляцию. - `CORRELATION_WINDOW_SECONDS` — временное окно для поиска связанных событий. - `CORRELATION_SUPPRESS_CHILDREN` — suppress дочерних low/average событий. - `CORRELATION_KIND_RULES_PATH` — YAML классификации event kinds. - `CORRELATION_ROOT_CAUSE_PATH` — YAML root-cause mapping. #### Matrix - `MATRIX_*` — доставка уведомлений в Matrix и refresh токенов через MAS. - `MATRIX_TOKEN_STATE_FILE` — файл хранения актуальных rotated токенов. #### Mail - `MAIL_*` — SMTP-доставка email уведомлений. #### Zabbix enrichment - `ZABBIX_API_*` — подключение к Zabbix API. - `ZABBIX_WEB_URL` — базовый URL Zabbix Web UI для ссылок. - `ZABBIX_GRAPH_*` — генерация графиков. - `ZABBIX_ENRICH_ONLY_NOTIFY` — enrich только для реально отправляемых событий. #### LLM - `LLM_BASE_URL` — адрес Ollama. В текущей схеме **Ollama развернут отдельно** и доступен через `host.docker.internal`. - `LLM_MODEL` — имя локальной модели. - `LLM_TIMEOUT_SECONDS` — timeout запросов к LLM. - `LLM_TEMPERATURE` — temperature для triage/remediation/correlation. - `LLM_MAX_STEPS`, `LLM_MAX_COMMANDS` — ограничение размера remediation. #### LLM triage - `LLM_TRIAGE_ENABLED` — включает triage low-severity событий. - `LLM_TRIAGE_CACHE_TTL_SECONDS` — TTL кэша triage verdicts. #### LLM correlation fallback - `LLM_CORRELATION_ENABLED` — включает fallback-корреляцию через LLM, если deterministic correlation не дал результата. - `LLM_CORRELATION_MIN_CONFIDENCE` — минимальная уверенность LLM (`low`, `medium`, `high`). #### Queue - `QUEUE_*` — параметры Redis queue и deadletter. #### Audit - `AUDIT_*` — аудит обработки и хранение event journal. --- ## YAML для корреляции Файлы лежат в `alert-processor/config/`. ### `event_kind_rules.yaml` Определяет, как событие классифицируется в `kind`. Пример: ```yaml event_kind_rules: - kind: postgresql_unavailable trigger_patterns: - "postgresql.+unavailable" - "postgres.+unavailable" - kind: mssql_unavailable trigger_patterns: - "mssql.+unavailable" - "sql server.+unavailable" scope_in: - "host_os" ``` ### `root_cause_map.yaml` Определяет, какие `kind` могут объяснять другие `kind`. Пример: ```yaml root_cause_map: postgresql_unavailable: explains: - service_unavailable - db_connection_error mssql_unavailable: explains: - service_unavailable - db_connection_error ``` После изменения YAML нужно **перезапустить worker**. --- ## Отдельный Ollama Ollama в текущей схеме **не входит в compose LLM-Zabbix**. Он разворачивается отдельно и должен быть доступен с Docker host на `11434`. Пример compose для Ollama: ```yaml services: ollama: image: ollama/ollama:latest container_name: ollama restart: unless-stopped ports: - "11434:11434" volumes: - ./ollama-data:/root/.ollama ``` Затем внутри Ollama нужно загрузить модель: ```bash docker exec -it ollama ollama pull qwen3.5:4b ``` Проверка: ```bash curl http://127.0.0.1:11434/api/tags ``` --- ## Подготовка каталогов Перед первым запуском из корня репозитория: ```bash mkdir -p alert-processor/config mkdir -p alert-processor/.graph_images mkdir -p redis-data touch alert-processor/.matrix_token_state.json ``` Если YAML уже лежат в репозитории, просто убедитесь, что файлы на месте: - `alert-processor/config/event_kind_rules.yaml` - `alert-processor/config/root_cause_map.yaml` --- ## Запуск через Docker Compose Из корня репозитория: ```bash docker compose up -d --build ``` Проверить контейнеры: ```bash docker compose ps ``` Посмотреть логи worker: ```bash docker compose logs -f alert-processor-worker ``` --- ## Health-check и полезные endpoints ### Receiver ```bash curl http://127.0.0.1:8080/health ``` ### Processor ingest ```bash curl http://127.0.0.1:8081/health ``` ### Worker health ```bash curl http://127.0.0.1:8082/health ``` ### Audit recent ```bash curl http://127.0.0.1:8081/audit/recent?limit=5 ``` ### Audit by correlation_id ```bash curl http://127.0.0.1:8081/audit/events/ ``` ### Audit by event_id ```bash curl http://127.0.0.1:8081/audit/by-event/ ``` --- ## Как подключить Zabbix В Zabbix Media Type / webhook используйте URL `alert-receiver`, а не `alert-processor`. Пример макросов: ```text {$ALERT_RECEIVER_URL} = http://:8080/webhook/zabbix {$ALERT_RECEIVER_TOKEN} = ``` То есть Zabbix отправляет только в: ```text http://:8080/webhook/zabbix ``` Дальше маршрутизация происходит внутри LLM-Zabbix автоматически. --- ## Пример безопасной последовательности запуска 1. Поднять отдельно Ollama с загруженной языковой моделью. 2. Проверить, что хост отвечает на `http://127.0.0.1:11434/api/tags`. 3. Заполнить `alert-receiver/.env`. 4. Заполнить `alert-processor/.env`. 5. Убедиться, что: - `LLM_BASE_URL=http://host.docker.internal:11434` - в compose есть `extra_hosts: ["host.docker.internal:host-gateway"]` 6. Запустить: ```bash docker compose up -d --build ``` 7. Проверить `8080/health`, `8081/health`, `8082/health`. 8. Отправить тестовый alert. 9. Проверить `audit/recent`. 10. Только потом переключать production webhook Zabbix. --- ## Что хранить в backup Минимально: - `alert-receiver/.env` - `alert-processor/.env` - `alert-processor/config/*.yaml` - `alert-processor/.matrix_token_state.json` - `redis-data/` - `.graph_images/` — по желанию --- ## Примечания - `alert-processor-ingest` быстро отвечает `202`, вся тяжелая логика выполняется в worker. - Если `LLM correlation` в audit имеет статус `skipped`, это значит, что fallback вызвался, но его результат не был применен. - Если `correlation_source=deterministic`, значит LLM fallback не понадобился. - Для Linux доступ к отдельно развернутому Ollama из контейнера реализован через: - `LLM_BASE_URL=http://host.docker.internal:11434` - `extra_hosts: ["host.docker.internal:host-gateway"]` - При недоступности языковой модели алерты обрабатываются базовым функционалом, но теряется remidiation