feat(backend): метрика vidconf_host_info для плашки характеристик сервера
Info-метрика (значение всегда 1, лейблы cpus/ram_mb/gpu_name/vram_mb) — источник GPU для новой панели «Характеристики сервера» в дашборде «Хост и контейнеры» (живые CPU/RAM/диск там же берутся из node-exporter, GPU node-exporter не знает). Данные — уже читаемые Settings.hw_* из .env, которые install.sh пишет по ADR-004. «—» вместо None/пустой строки — однозначный прочерк на панели вместо пустого текста.
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
"""Метрики Prometheus: латентность HTTP + gauge'и пайплайна и очередей.
|
||||
"""Метрики Prometheus: латентность HTTP + gauge'и пайплайна, очередей и железа.
|
||||
|
||||
`GET /metrics` — без авторизации (снаружи закрывается на уровне nginx, вне
|
||||
периметра backend, см. `docs/deploy/scaling.md`/monitoring-часть devops):
|
||||
Prometheus-серверы традиционно ходят напрямую в контейнер по внутренней
|
||||
сети, а не через публичный `/api/`-гейтвей.
|
||||
|
||||
Gauge'и `vidconf_pipeline_sessions`/`vidconf_celery_queue_depth` намеренно
|
||||
НЕ обновляются фоновой задачей — значения пересчитываются прямо в обработчике
|
||||
запроса при каждом scrape (см. докстринг `metrics_endpoint`), поэтому их
|
||||
асинхронные источники (БД, Redis) можно опросить обычным `await` вместо
|
||||
реализации синхронного `prometheus_client.registry.Collector`.
|
||||
Gauge'и `vidconf_pipeline_sessions`/`vidconf_celery_queue_depth`/
|
||||
`vidconf_host_info` намеренно НЕ обновляются фоновой задачей — значения
|
||||
пересчитываются прямо в обработчике запроса при каждом scrape (см.
|
||||
докстринг `metrics_endpoint`), поэтому их асинхронные источники (БД,
|
||||
Redis) можно опросить обычным `await` вместо реализации синхронного
|
||||
`prometheus_client.registry.Collector` (у `vidconf_host_info` источник
|
||||
и вовсе синхронный — настройки уже в памяти процесса).
|
||||
"""
|
||||
|
||||
import time
|
||||
@@ -20,6 +22,7 @@ from prometheus_client import CONTENT_TYPE_LATEST, Gauge, Histogram, generate_la
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.routing import Match
|
||||
|
||||
from core.config import get_settings
|
||||
from core.db import get_session
|
||||
from core.redis import redis_client
|
||||
from models.session import PIPELINE_STATUSES
|
||||
@@ -110,6 +113,42 @@ async def _refresh_celery_queue_depth_gauge() -> None:
|
||||
CELERY_QUEUE_DEPTH.labels(queue=queue).set(depth)
|
||||
|
||||
|
||||
# --- Info-метрика обнаруженного железа (install.sh, ADR-004) ---------------
|
||||
|
||||
HOST_INFO = Gauge(
|
||||
"vidconf_host_info",
|
||||
"Обнаруженное установщиком железо (info-метрика, значение всегда 1, данные в лейблах)",
|
||||
labelnames=("cpus", "ram_mb", "gpu_name", "vram_mb"),
|
||||
)
|
||||
|
||||
|
||||
def _hw_label(value: int | None) -> str:
|
||||
"""`None` (install.sh не запускался либо GPU не обнаружен) → «—», не пустая строка.
|
||||
|
||||
Пустой лейбл Grafana отрисовала бы как пустой текст на панели —
|
||||
прочерк однозначно читается как «не определено».
|
||||
"""
|
||||
return str(value) if value is not None else "—"
|
||||
|
||||
|
||||
def _refresh_host_info_gauge() -> None:
|
||||
"""Пересчитать `vidconf_host_info` по текущим `HW_*` настройкам (`core/config.py`).
|
||||
|
||||
Источник — `install.sh`, который пишет `HW_CPUS`/`HW_RAM_MB`/
|
||||
`HW_GPU_NAME`/`HW_VRAM_MB` в `.env` при установке (ADR-004). Живые
|
||||
CPU/RAM/диск хоста в дашборде «Хост и контейнеры» берутся из
|
||||
node-exporter напрямую — здесь только то, чего node-exporter не
|
||||
знает (GPU), плюс дублирование CPU/RAM install.sh для сверки.
|
||||
"""
|
||||
settings = get_settings()
|
||||
HOST_INFO.labels(
|
||||
cpus=_hw_label(settings.hw_cpus),
|
||||
ram_mb=_hw_label(settings.hw_ram_mb),
|
||||
gpu_name=settings.hw_gpu_name or "—",
|
||||
vram_mb=_hw_label(settings.hw_vram_mb),
|
||||
).set(1)
|
||||
|
||||
|
||||
@router.get("/metrics")
|
||||
async def metrics_endpoint(session: AsyncSession = Depends(get_session)) -> Response:
|
||||
"""Отдать метрики Prometheus в формате text exposition.
|
||||
@@ -122,4 +161,5 @@ async def metrics_endpoint(session: AsyncSession = Depends(get_session)) -> Resp
|
||||
"""
|
||||
await _refresh_pipeline_sessions_gauge(session)
|
||||
await _refresh_celery_queue_depth_gauge()
|
||||
_refresh_host_info_gauge()
|
||||
return Response(content=generate_latest(), media_type=CONTENT_TYPE_LATEST)
|
||||
|
||||
@@ -20,6 +20,7 @@ from prometheus_client.samples import Sample
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from api import metrics as metrics_module
|
||||
from core.config import get_settings
|
||||
from core.redis import redis_client
|
||||
from models.conference import Conference
|
||||
from models.session import ConferenceSession
|
||||
@@ -57,6 +58,31 @@ async def test_metrics_endpoint_returns_prometheus_exposition_format(
|
||||
assert "vidconf_http_request_duration_seconds" in families
|
||||
assert "vidconf_pipeline_sessions" in families
|
||||
assert "vidconf_celery_queue_depth" in families
|
||||
assert "vidconf_host_info" in families
|
||||
|
||||
|
||||
async def test_metrics_host_info_gauge_reflects_settings(
|
||||
client: httpx.AsyncClient, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""`vidconf_host_info` отдаёт `HW_*` из настроек как лейблы, значение всегда 1."""
|
||||
settings = get_settings()
|
||||
monkeypatch.setattr(settings, "hw_cpus", 4)
|
||||
monkeypatch.setattr(settings, "hw_ram_mb", 7937)
|
||||
monkeypatch.setattr(settings, "hw_gpu_name", None)
|
||||
monkeypatch.setattr(settings, "hw_vram_mb", None)
|
||||
|
||||
response = await client.get("/metrics")
|
||||
|
||||
samples = _samples(response.text, "vidconf_host_info")
|
||||
value = _sample_value(
|
||||
samples,
|
||||
suffix="vidconf_host_info",
|
||||
cpus="4",
|
||||
ram_mb="7937",
|
||||
gpu_name="—",
|
||||
vram_mb="—",
|
||||
)
|
||||
assert value == 1
|
||||
|
||||
|
||||
async def test_metrics_records_http_latency_by_route(client: httpx.AsyncClient) -> None:
|
||||
|
||||
Reference in New Issue
Block a user