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
APP_URL=https://academic.jze9.ru APP_URL=https://academic.jze9.ru
ENVIRONMENT=development 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 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`) прогоняет два гейта, Перед деплоем CI (`.gitea/workflows/deploy.yml`, job `test`) прогоняет два гейта,

View File

@@ -14,6 +14,8 @@ volumes:
es_prod_data: es_prod_data:
faiss_index_prod: faiss_index_prod:
qdrant_prod_data: qdrant_prod_data:
prometheus_data:
grafana_data:
x-app-env: &app-env x-app-env: &app-env
env_file: .env env_file: .env
@@ -65,6 +67,36 @@ services:
- antiplagiator - antiplagiator
restart: unless-stopped 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) # RabbitMQ вынесен на отдельный сервер 192.168.20.82 (см. RABBITMQ_URL в .env)
api: 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 import Depends, FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from prometheus_fastapi_instrumentator import Instrumentator
from app.api import admin, auth, documents, reports, search, tasks from app.api import admin, auth, documents, reports, search, tasks
from app.config import settings 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(reports.router, prefix="/api")
app.include_router(admin.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 ──────────────────────────────────────────────────────────────── # ─── WebSocket ────────────────────────────────────────────────────────────────
@app.websocket("/ws/tasks/{public_id}") @app.websocket("/ws/tasks/{public_id}")

View File

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