Загрузить файлы в «/»

This commit is contained in:
2026-08-06 18:28:10 +03:00
commit 3ae867184f
3 changed files with 643 additions and 0 deletions
+20
View File
@@ -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"]
+534
View File
@@ -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/<correlation_id>
```
### Audit by event_id
```bash
curl http://127.0.0.1:8081/audit/by-event/<event_id>
```
---
## Как подключить Zabbix
В Zabbix Media Type / webhook используйте URL `alert-receiver`, а не `alert-processor`.
Пример макросов:
```text
{$ALERT_RECEIVER_URL} = http://<ip-or-dns>:8080/webhook/zabbix
{$ALERT_RECEIVER_TOKEN} = <WEBHOOK_TOKEN>
```
То есть Zabbix отправляет только в:
```text
http://<host>: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
+89
View File
@@ -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