feat(obs): наблюдаемость Prometheus + Grafana под профилем observability

Раньше видимости в прод не было — только email-монитор (костыль). Добавлено
без нагрузки по умолчанию (профиль не поднимается, пока не попросят):

- API: /metrics через prometheus-fastapi-instrumentator (кол-во/латентность
  запросов по хендлерам);
- Prometheus (infra/prometheus/prometheus.yml) скрейпит API и Flower — из Flower
  приходят метрики Celery (задачи, время, воркеры) вообще без доп. кода;
- Grafana с автопровижном источника Prometheus (infra/grafana/provisioning),
  пароль через GRAFANA_ADMIN_PASSWORD;
- docker-compose.prod.yml: сервисы prometheus/grafana под profiles:[observability]
  + volumes; .env.example и README пополнены.

Запуск: docker compose --profile observability up -d prometheus grafana.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-08-11 20:37:47 +05:00
parent 012b17304f
commit d2d99d8231
7 changed files with 91 additions and 0 deletions

View File

@@ -39,3 +39,11 @@ SMTP_TLS_VERIFY=true
# App
APP_URL=https://academic.jze9.ru
ENVIRONMENT=development
# Векторный бэкенд (опционально): faiss (по умолчанию) или qdrant. См. README.
VECTOR_BACKEND=faiss
QDRANT_URL=http://qdrant:6333
QDRANT_COLLECTION=documents
# Наблюдаемость (профиль observability, опционально)
GRAFANA_ADMIN_PASSWORD=admin

View File

@@ -184,6 +184,20 @@ docker compose -f docker-compose.prod.yml exec worker-gpu python -m app.migrate_
docker compose -f docker-compose.prod.yml up -d worker-gpu
```
## Наблюдаемость (Prometheus + Grafana)
Опционально (профиль `observability`, по умолчанию не поднимается):
```bash
docker compose -f docker-compose.prod.yml --profile observability up -d prometheus grafana
```
- API отдаёт HTTP-метрики на `/metrics` (кол-во и латентность запросов по хендлерам).
- Prometheus (`infra/prometheus/prometheus.yml`) скрейпит API и **Flower** — из
Flower приходят метрики Celery (задачи, время выполнения, воркеры) без доп. кода.
- Grafana с автоподключённым источником Prometheus (`infra/grafana/provisioning/`);
пароль admin — через `GRAFANA_ADMIN_PASSWORD` в `.env` (по умолчанию `admin`).
## Тестирование и качество кода
Перед деплоем CI (`.gitea/workflows/deploy.yml`, job `test`) прогоняет два гейта,

View File

@@ -14,6 +14,8 @@ volumes:
es_prod_data:
faiss_index_prod:
qdrant_prod_data:
prometheus_data:
grafana_data:
x-app-env: &app-env
env_file: .env
@@ -65,6 +67,36 @@ services:
- antiplagiator
restart: unless-stopped
# ─── Наблюдаемость (опционально; профиль observability) ────────────────────
# docker compose -f docker-compose.prod.yml --profile observability up -d prometheus grafana
# Prometheus скрейпит api:/metrics и flower:/metrics (метрики Celery).
prometheus:
image: prom/prometheus:v2.54.1
container_name: antiplagiator-prometheus
profiles: ["observability"]
volumes:
- ./infra/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
networks:
- antiplagiator
restart: unless-stopped
grafana:
image: grafana/grafana:11.2.0
container_name: antiplagiator-grafana
profiles: ["observability"]
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- ./infra/grafana/provisioning:/etc/grafana/provisioning:ro
- grafana_data:/var/lib/grafana
networks:
- antiplagiator
restart: unless-stopped
depends_on:
- prometheus
# ─── Приложение ────────────────────────────────────────────────────────────
# RabbitMQ вынесен на отдельный сервер 192.168.20.82 (см. RABBITMQ_URL в .env)
api:

View File

@@ -0,0 +1,10 @@
# Автопровижн источника данных Grafana → Prometheus (профиль observability).
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false

View File

@@ -0,0 +1,20 @@
# Prometheus scrape-конфиг (профиль observability в docker-compose.prod.yml).
# Скрейпит HTTP-метрики API и Celery-метрики из Flower (Flower 2.0 отдаёт их сам).
global:
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]
- job_name: api
metrics_path: /metrics
static_configs:
- targets: ["api:8000"]
- job_name: flower # метрики Celery: задачи, время выполнения, воркеры
metrics_path: /metrics
static_configs:
- targets: ["flower:5555"]

View File

@@ -7,6 +7,7 @@ from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from prometheus_fastapi_instrumentator import Instrumentator
from app.api import admin, auth, documents, reports, search, tasks
from app.config import settings
@@ -80,6 +81,11 @@ app.include_router(documents.router, prefix="/api")
app.include_router(reports.router, prefix="/api")
app.include_router(admin.router, prefix="/api")
# ─── Метрики Prometheus ───────────────────────────────────────────────────────
# HTTP-метрики (кол-во/латентность запросов по хендлерам) на /metrics.
# Prometheus скрейпит их (см. infra/prometheus/prometheus.yml, профиль observability).
Instrumentator().instrument(app).expose(app, endpoint="/metrics", include_in_schema=False)
# ─── WebSocket ────────────────────────────────────────────────────────────────
@app.websocket("/ws/tasks/{public_id}")

View File

@@ -16,3 +16,4 @@ minio==7.2.7
httpx==0.27.0
aiofiles==23.2.1
websockets==12.0
prometheus-fastapi-instrumentator==7.0.0 # /metrics для Prometheus (профиль observability)