commit 3ae867184f5d5ed6db8c71bc9994bdec58487b26 Author: Alexander Zubarev Date: Thu Aug 6 18:28:10 2026 +0300 Загрузить файлы в «/» diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..231c217 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.12-slim + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +RUN mkdir -p /app/config /app/.graph_images + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8081"] \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..1e11740 --- /dev/null +++ b/Readme.md @@ -0,0 +1,534 @@ +# 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 + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3a5738b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,89 @@ +services: + redis: + image: redis:8.8-trixie + container_name: llm-zabbix-redis + command: ["redis-server", "--appendonly", "yes"] + restart: unless-stopped + volumes: + - ./redis-data:/data + networks: + - llm-zabbix + + alert-receiver: + build: + context: ./alert-receiver + container_name: alert-receiver + env_file: + - ./alert-receiver/.env + restart: unless-stopped + depends_on: + - alert-processor-ingest + ports: + - "8080:8080" + networks: + - llm-zabbix + + alert-processor-ingest: + build: + context: ./alert-processor + container_name: alert-processor-ingest + command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8081"] + env_file: + - ./alert-processor/.env + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./alert-processor/config:/app/config:ro + - ./alert-processor/.matrix_token_state.json:/app/.matrix_token_state.json + - ./alert-processor/.graph_images:/app/.graph_images + depends_on: + - redis + restart: unless-stopped + ports: + - "8081:8081" + networks: + - llm-zabbix + + alert-processor-worker: + build: + context: ./alert-processor + container_name: alert-processor-worker + command: ["python", "-m", "app.worker"] + env_file: + - ./alert-processor/.env + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./alert-processor/config:/app/config:ro + - ./alert-processor/.matrix_token_state.json:/app/.matrix_token_state.json + - ./alert-processor/.graph_images:/app/.graph_images + depends_on: + - redis + restart: unless-stopped + networks: + - llm-zabbix + + alert-worker-health: + build: + context: ./alert-processor + container_name: alert-worker-health + command: ["uvicorn", "app.worker_health:app", "--host", "0.0.0.0", "--port", "8082"] + env_file: + - ./alert-processor/.env + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./alert-processor/config:/app/config:ro + - ./alert-processor/.matrix_token_state.json:/app/.matrix_token_state.json + - ./alert-processor/.graph_images:/app/.graph_images + depends_on: + - redis + restart: unless-stopped + ports: + - "8082:8082" + networks: + - llm-zabbix + +networks: + llm-zabbix: + driver: bridge