Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54fd1a26d7 | |||
| 456cc58b25 | |||
| dccc369d0b | |||
| 77ee26014d |
@@ -98,7 +98,7 @@ SMTP_TIMEOUT_S=30
|
|||||||
# --- Версия инстанса (релиз v0.0.1) ---
|
# --- Версия инстанса (релиз v0.0.1) ---
|
||||||
# install.sh копирует значение из корневого файла VERSION при каждой
|
# install.sh копирует значение из корневого файла VERSION при каждой
|
||||||
# установке/обновлении — руками менять не нужно.
|
# установке/обновлении — руками менять не нужно.
|
||||||
VIDCONF_VERSION=0.0.8
|
VIDCONF_VERSION=0.0.9
|
||||||
|
|
||||||
# --- Профили compose. Дефолт ниже (`media,monitoring`) — только для ручного
|
# --- Профили compose. Дефолт ниже (`media,monitoring`) — только для ручного
|
||||||
# `docker compose up` БЕЗ install.sh: медиа (LiveKit+coturn) + мониторинг,
|
# `docker compose up` БЕЗ install.sh: медиа (LiveKit+coturn) + мониторинг,
|
||||||
|
|||||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,6 +3,24 @@
|
|||||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
|
||||||
проект придерживается [семантического версионирования](https://semver.org/lang/ru/).
|
проект придерживается [семантического версионирования](https://semver.org/lang/ru/).
|
||||||
|
|
||||||
|
## [0.0.9] — 2026-07-28
|
||||||
|
|
||||||
|
Метрики CPU/RAM/диска хоста и контейнеров в Grafana; дефолтные подвкладки
|
||||||
|
конференций и пользователей в админке.
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
- Мониторинг железа: `node-exporter` (CPU/RAM/диск/сеть/load average хоста)
|
||||||
|
и `cAdvisor` (метрики по каждому контейнеру) — профиль compose
|
||||||
|
`monitoring`, без публикации портов наружу. Новый дашборд Grafana «Хост и
|
||||||
|
контейнеры». Три новых алерта Prometheus: `HostMemoryLow`, `HostDiskLow`,
|
||||||
|
`HostCpuHigh` (пороги — под сервер `1gb`: 8 ГБ RAM, 4 CPU, 50 ГБ диска).
|
||||||
|
|
||||||
|
### Изменено
|
||||||
|
- Админка, вкладка «Конференции»: вкладка «Все» перенесена в конец списка
|
||||||
|
фильтров (после «Завершённые»), по умолчанию открываются «Запланированные».
|
||||||
|
- Админка, вкладка «Пользователи»: по умолчанию открываются «Активные»
|
||||||
|
(было «Все»).
|
||||||
|
|
||||||
## [0.0.8] — 2026-07-27
|
## [0.0.8] — 2026-07-27
|
||||||
|
|
||||||
Админка: несколько mail-доменов для регистрации, фильтр и поиск по команде в
|
Админка: несколько mail-доменов для регистрации, фильтр и поиск по команде в
|
||||||
@@ -206,6 +224,7 @@
|
|||||||
|
|
||||||
Первоначальная версия VidConf.
|
Первоначальная версия VidConf.
|
||||||
|
|
||||||
|
[0.0.9]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.9
|
||||||
[0.0.8]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.8
|
[0.0.8]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.8
|
||||||
[0.0.7]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.7
|
[0.0.7]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.7
|
||||||
[0.0.6]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.6
|
[0.0.6]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.6
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ services:
|
|||||||
MEDIA_ROOT: ${MEDIA_ROOT:-/app/media}
|
MEDIA_ROOT: ${MEDIA_ROOT:-/app/media}
|
||||||
# Версия инстанса (релиз v0.0.1) — install.sh копирует значение
|
# Версия инстанса (релиз v0.0.1) — install.sh копирует значение
|
||||||
# из файла VERSION (корень репозитория) в .env; отдаётся в GET /api/health.
|
# из файла VERSION (корень репозитория) в .env; отдаётся в GET /api/health.
|
||||||
VIDCONF_VERSION: ${VIDCONF_VERSION:-0.0.8}
|
VIDCONF_VERSION: ${VIDCONF_VERSION:-0.0.9}
|
||||||
# config/ лежит в корне репозитория и не попадает в образ (контекст сборки —
|
# config/ лежит в корне репозитория и не попадает в образ (контекст сборки —
|
||||||
# только backend/), поэтому plugins.yaml монтируется отдельно.
|
# только backend/), поэтому plugins.yaml монтируется отдельно.
|
||||||
volumes:
|
volumes:
|
||||||
@@ -704,6 +704,73 @@ services:
|
|||||||
profiles: ["monitoring"]
|
profiles: ["monitoring"]
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
# Метрики железа хоста (CPU, память, диск, сеть, load average) — то,
|
||||||
|
# чего нет ни в одном из приложенческих экспортеров выше. Без
|
||||||
|
# `network_mode: host` (не нужен: читаем /proc,/sys,/ хоста через
|
||||||
|
# bind-mount, а Prometheus достаёт их по имени сервиса во внутренней
|
||||||
|
# сети compose — так безопаснее, не расширяет сетевой доступ контейнера).
|
||||||
|
image: prom/node-exporter:v1.8.2
|
||||||
|
restart: unless-stopped
|
||||||
|
pid: host
|
||||||
|
volumes:
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
- /:/rootfs:ro
|
||||||
|
command:
|
||||||
|
- '--path.procfs=/host/proc'
|
||||||
|
- '--path.sysfs=/host/sys'
|
||||||
|
- '--path.rootfs=/rootfs'
|
||||||
|
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
|
||||||
|
# Не публикуем порт наружу вообще (не 127.0.0.1:9100, а совсем без
|
||||||
|
# ports) — Prometheus ходит к нему по внутренней сети compose
|
||||||
|
# (`node-exporter:9100`), публикация на хост для этого не нужна.
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9100/metrics >/dev/null || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
profiles: ["monitoring"]
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
cadvisor:
|
||||||
|
# Метрики по контейнерам (CPU/память каждого) — отвечает на вопрос
|
||||||
|
# «какой из контейнеров ест ресурсы», в отличие от node-exporter
|
||||||
|
# (только хост целиком). Дороже node-exporter по CPU/RAM (собственные
|
||||||
|
# ~60-120 МБ RSS + периодический обход cgroups) — на сервере 8 ГБ/4 CPU
|
||||||
|
# с ~13 контейнерами это приемлемо, но не бесплатно; `--docker_only`
|
||||||
|
# и `--disable_metrics` ниже отключают часть коллекторов сверх дефолта
|
||||||
|
# cAdvisor (который уже отключает cpu_topology/hugetlb/memory_numa/
|
||||||
|
# process/sched/tcp/udp и т.п.) — здесь дополнительно per-core CPU,
|
||||||
|
# perf_event и дисковые метрики (диск и так покрыт node-exporter'ом,
|
||||||
|
# дублировать через cAdvisor незачем). Список допустимых значений —
|
||||||
|
# `cadvisor -help` (в версии образа меняется, не все имена стабильны
|
||||||
|
# между релизами — при апгрейде образа сверяться с `-help`).
|
||||||
|
image: gcr.io/cadvisor/cadvisor:v0.49.2
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker/:/var/lib/docker:ro
|
||||||
|
# Только чтение — cAdvisor не должен иметь возможность управлять
|
||||||
|
# контейнерами, ему нужен только список и статистика.
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
command:
|
||||||
|
- '--docker_only=true'
|
||||||
|
- '--disable_metrics=percpu,perf_event,diskIO,disk,app'
|
||||||
|
# Без ports вообще — тот же принцип, что и у node-exporter: Prometheus
|
||||||
|
# достаёт метрики по имени сервиса (`cadvisor:8080`) внутри сети compose.
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8080/healthz >/dev/null || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 15s
|
||||||
|
profiles: ["monitoring"]
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana:13.1.0
|
image: grafana/grafana:13.1.0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -60,3 +60,58 @@ groups:
|
|||||||
# суммаризация встанет (задачи будут копиться в очереди summarize,
|
# суммаризация встанет (задачи будут копиться в очереди summarize,
|
||||||
# см. также алерт QueueGrowing). Проверить
|
# см. также алерт QueueGrowing). Проверить
|
||||||
# `docker compose ps llm` / `llm-gpu` и `docker compose logs llm`.
|
# `docker compose ps llm` / `llm-gpu` и `docker compose logs llm`.
|
||||||
|
|
||||||
|
# Железо хоста (job `node` — node-exporter). Пороги подобраны под
|
||||||
|
# конкретный сервер 1gb: 8 ГБ RAM, 4 CPU, 50 ГБ диска — если сервер
|
||||||
|
# сменится, пересчитать.
|
||||||
|
- name: vidconf-host
|
||||||
|
rules:
|
||||||
|
# MemAvailable — уже честная оценка Linux с учётом того, что легко
|
||||||
|
# освобождаемый page cache/buffers не в счёт (в отличие от naive
|
||||||
|
# used = total - free). 10% от 8 ГБ ≈ 800 МБ — `for: 10m`, чтобы не
|
||||||
|
# дёргать на кратковременный всплеск (например, разовый всплеск
|
||||||
|
# transcribe/summarize), но успеть среагировать до OOM killer.
|
||||||
|
- alert: HostMemoryLow
|
||||||
|
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
|
||||||
|
for: 10m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Мало свободной памяти на хосте"
|
||||||
|
description: >-
|
||||||
|
Свободно {{ $value | printf "%.1f" }}% RAM дольше 10 минут
|
||||||
|
(порог 10% ≈ 800 МБ из 8 ГБ). Смотреть, какой контейнер ест
|
||||||
|
память — дашборд «Хост и контейнеры», топ по памяти (cAdvisor).
|
||||||
|
|
||||||
|
# 50 ГБ диска — 10% ≈ 5 ГБ. `for: 15m` (не мгновенно): диск не растёт
|
||||||
|
# так же резко, как память, ложные срабатывания на всплеск не грозят,
|
||||||
|
# но и разовая проверка на границе некритична — 15 минут отсекает шум.
|
||||||
|
- alert: HostDiskLow
|
||||||
|
expr: (node_filesystem_avail_bytes{mountpoint="/",fstype!="tmpfs"} / node_filesystem_size_bytes{mountpoint="/",fstype!="tmpfs"}) * 100 < 10
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Мало места на диске хоста"
|
||||||
|
description: >-
|
||||||
|
Свободно {{ $value | printf "%.1f" }}% диска дольше 15 минут
|
||||||
|
(порог 10% ≈ 5 ГБ из 50 ГБ). Частые причины на этом проекте —
|
||||||
|
записи транскрибации (`recordings`), логи docker, образы/слои
|
||||||
|
после пересборки — проверить `docker system df`.
|
||||||
|
|
||||||
|
# 4 CPU. Порог 90% и `for: 15m` — сознательно строже по времени, чем
|
||||||
|
# у памяти/диска: кратковременные пики от пайплайна пост-обработки
|
||||||
|
# (транскрибация/суммаризация) — это ожидаемая, не аварийная нагрузка,
|
||||||
|
# алерт должен ловить именно устойчивую перегрузку, а не обычный всплеск.
|
||||||
|
- alert: HostCpuHigh
|
||||||
|
expr: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Устойчиво высокая загрузка CPU хоста"
|
||||||
|
description: >-
|
||||||
|
Загрузка CPU {{ $value | printf "%.1f" }}% дольше 15 минут
|
||||||
|
подряд (порог 90% из 4 ядер). Смотреть топ контейнеров по CPU
|
||||||
|
(дашборд «Хост и контейнеры», cAdvisor) и латентность API —
|
||||||
|
возможно, не хватает уровня AI/ресурсов под нагрузку.
|
||||||
|
|||||||
245
deploy/monitoring/grafana/dashboards/host.json
Normal file
245
deploy/monitoring/grafana/dashboards/host.json
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
{
|
||||||
|
"title": "Хост и контейнеры",
|
||||||
|
"description": "CPU/RAM/диск/сеть хоста (node-exporter) и разбивка по контейнерам (cAdvisor) — профиль compose monitoring. Пороги на панелях согласованы с deploy/monitoring/alerts.yml (группа vidconf-host).",
|
||||||
|
"uid": "vidconf-host",
|
||||||
|
"editable": false,
|
||||||
|
"timezone": "browser",
|
||||||
|
"schemaVersion": 39,
|
||||||
|
"version": 1,
|
||||||
|
"time": { "from": "now-6h", "to": "now" },
|
||||||
|
"refresh": "30s",
|
||||||
|
"tags": ["vidconf", "host"],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Загрузка CPU",
|
||||||
|
"description": "100 - idle, усреднено по всем ядрам. Порог алерта HostCpuHigh — 90% дольше 15 минут, см. alerts.yml.",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "percent", "max": 100, "min": 0, "custom": { "drawStyle": "line", "fillOpacity": 10 } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
|
||||||
|
"legendFormat": "CPU busy",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Load average",
|
||||||
|
"description": "node_load1/5/15 — на 4-ядерном сервере устойчивое значение выше ~4 означает очередь на CPU.",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "custom": { "drawStyle": "line", "fillOpacity": 5 } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{ "datasource": { "type": "prometheus", "uid": "prometheus" }, "expr": "node_load1", "legendFormat": "1 мин", "refId": "A" },
|
||||||
|
{ "datasource": { "type": "prometheus", "uid": "prometheus" }, "expr": "node_load5", "legendFormat": "5 мин", "refId": "B" },
|
||||||
|
{ "datasource": { "type": "prometheus", "uid": "prometheus" }, "expr": "node_load15", "legendFormat": "15 мин", "refId": "C" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "Использование RAM",
|
||||||
|
"description": "(1 - MemAvailable/MemTotal) — MemAvailable уже учитывает легко освобождаемый page cache/buffers. Порог алерта HostMemoryLow — доступно <10% дольше 10 минут, см. alerts.yml.",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "percent", "max": 100, "min": 0, "custom": { "drawStyle": "line", "fillOpacity": 10 } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100",
|
||||||
|
"legendFormat": "RAM used",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "Своп (использовано)",
|
||||||
|
"description": "SwapTotal - SwapFree. На этом сервере swap не заведён под мониторинг отдельным алертом — рост от нуля сам по себе сигнал, что памяти уже не хватает (см. HostMemoryLow).",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "decbytes", "custom": { "drawStyle": "line", "fillOpacity": 10 } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes",
|
||||||
|
"legendFormat": "swap used",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "Свободно на диске (/)",
|
||||||
|
"description": "node_filesystem_avail_bytes для корня. Порог алерта HostDiskLow — <10% (≈5 ГБ из 50 ГБ) дольше 15 минут.",
|
||||||
|
"type": "stat",
|
||||||
|
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 16 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "decbytes",
|
||||||
|
"thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 5368709120 }] }
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "node_filesystem_avail_bytes{mountpoint=\"/\",fstype!=\"tmpfs\"}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"title": "RAM сейчас, %",
|
||||||
|
"description": "Текущий снимок панели «Использование RAM». Порог совпадает с HostMemoryLow (used > 90%).",
|
||||||
|
"type": "stat",
|
||||||
|
"gridPos": { "h": 4, "w": 6, "x": 6, "y": 16 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "percent",
|
||||||
|
"thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "red", "value": 90 }] }
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"title": "Диск сейчас, %",
|
||||||
|
"description": "Текущий снимок использования корневого раздела. Порог совпадает с HostDiskLow (used > 90%).",
|
||||||
|
"type": "stat",
|
||||||
|
"gridPos": { "h": 4, "w": 6, "x": 12, "y": 16 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "percent",
|
||||||
|
"thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "red", "value": 90 }] }
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "(1 - node_filesystem_avail_bytes{mountpoint=\"/\",fstype!=\"tmpfs\"} / node_filesystem_size_bytes{mountpoint=\"/\",fstype!=\"tmpfs\"}) * 100",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"title": "CPU сейчас, %",
|
||||||
|
"description": "Текущий снимок загрузки CPU. Порог совпадает с HostCpuHigh (>90%).",
|
||||||
|
"type": "stat",
|
||||||
|
"gridPos": { "h": 4, "w": 6, "x": 18, "y": 16 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "percent",
|
||||||
|
"thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "red", "value": 90 }] }
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"title": "Сетевой трафик",
|
||||||
|
"description": "rx/tx по физическим интерфейсам хоста (исключены lo/veth/docker/br — это внутренние интерфейсы контейнеров, а не реальный сетевой трафик сервера).",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "Bps", "custom": { "drawStyle": "line", "fillOpacity": 5 } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "rate(node_network_receive_bytes_total{device!~\"lo|veth.*|docker.*|br-.*\"}[5m])",
|
||||||
|
"legendFormat": "rx {{device}}",
|
||||||
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "rate(node_network_transmit_bytes_total{device!~\"lo|veth.*|docker.*|br-.*\"}[5m])",
|
||||||
|
"legendFormat": "tx {{device}}",
|
||||||
|
"refId": "B"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"title": "Топ контейнеров по CPU",
|
||||||
|
"description": "Источник — cAdvisor (job cadvisor). Отвечает на вопрос «кто из контейнеров грузит CPU» при срабатывании HostCpuHigh.",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "percentunit", "custom": { "drawStyle": "line", "fillOpacity": 10, "stacking": { "mode": "normal" } } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "topk(5, sum by (name) (rate(container_cpu_usage_seconds_total{name!=\"\"}[5m])))",
|
||||||
|
"legendFormat": "{{name}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"title": "Топ контейнеров по памяти",
|
||||||
|
"description": "container_memory_working_set_bytes — это то, что реально учитывает cgroup OOM killer (в отличие от RSS), источник — cAdvisor.",
|
||||||
|
"type": "timeseries",
|
||||||
|
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 28 },
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": { "unit": "decbytes", "custom": { "drawStyle": "line", "fillOpacity": 10, "stacking": { "mode": "normal" } } },
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||||
|
"expr": "topk(5, container_memory_working_set_bytes{name!=\"\"})",
|
||||||
|
"legendFormat": "{{name}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# Конфигурация Prometheus (devops) — сбор метрик backend,
|
# Конфигурация Prometheus (devops) — сбор метрик backend,
|
||||||
# PostgreSQL, Redis и локального LLM-сервера. Поднимается compose-профилем
|
# PostgreSQL, Redis, железа хоста, контейнеров и локального LLM-сервера.
|
||||||
# `monitoring` (deploy/docker-compose.yml, сервис `prometheus`).
|
# Поднимается compose-профилем `monitoring` (deploy/docker-compose.yml,
|
||||||
|
# сервис `prometheus`).
|
||||||
#
|
#
|
||||||
# Имена метрик backend (`vidconf_http_request_duration_seconds`,
|
# Имена метрик backend (`vidconf_http_request_duration_seconds`,
|
||||||
# `vidconf_pipeline_sessions`, `vidconf_celery_queue_depth`) — КОНТРАКТ с
|
# `vidconf_pipeline_sessions`, `vidconf_celery_queue_depth`) — КОНТРАКТ с
|
||||||
@@ -33,6 +34,22 @@ scrape_configs:
|
|||||||
static_configs:
|
static_configs:
|
||||||
- targets: ["redis-exporter:9121"]
|
- targets: ["redis-exporter:9121"]
|
||||||
|
|
||||||
|
# Хост целиком: CPU, память, диск, сеть, load average (профиль monitoring
|
||||||
|
# — сервис node-exporter). Единственный источник, который покажет
|
||||||
|
# нехватку памяти/CPU на сервере, если она не проявится как рост
|
||||||
|
# латентности API (см. дашборд host.json).
|
||||||
|
- job_name: node
|
||||||
|
static_configs:
|
||||||
|
- targets: ["node-exporter:9100"]
|
||||||
|
|
||||||
|
# Метрики по каждому контейнеру (CPU/память отдельно у backend, worker,
|
||||||
|
# postgres и т.д. — профиль monitoring, сервис cadvisor). Отвечает на
|
||||||
|
# вопрос «какой из контейнеров ест ресурсы», в отличие от job `node`
|
||||||
|
# (только хост целиком).
|
||||||
|
- job_name: cadvisor
|
||||||
|
static_configs:
|
||||||
|
- targets: ["cadvisor:8080"]
|
||||||
|
|
||||||
# Локальный LLM-сервер (llama.cpp, LLAMA_ARG_ENDPOINT_METRICS=1). Адрес
|
# Локальный LLM-сервер (llama.cpp, LLAMA_ARG_ENDPOINT_METRICS=1). Адрес
|
||||||
# `llm:8080` разрешается ОДНИМ из двух compose-сервисов в зависимости от
|
# `llm:8080` разрешается ОДНИМ из двух compose-сервисов в зависимости от
|
||||||
# выбранного при установке пресета — `llm` (CPU, профиль `llm`, уровни
|
# выбранного при установке пресета — `llm` (CPU, профиль `llm`, уровни
|
||||||
|
|||||||
@@ -10,11 +10,25 @@
|
|||||||
| `prometheus` | `prom/prometheus:v3.13.1` | `9090` | сбор и хранение метрик, оценка правил алертинга |
|
| `prometheus` | `prom/prometheus:v3.13.1` | `9090` | сбор и хранение метрик, оценка правил алертинга |
|
||||||
| `postgres-exporter` | `quay.io/prometheuscommunity/postgres-exporter:v0.20.1` | — (внутренний) | метрики PostgreSQL |
|
| `postgres-exporter` | `quay.io/prometheuscommunity/postgres-exporter:v0.20.1` | — (внутренний) | метрики PostgreSQL |
|
||||||
| `redis-exporter` | `oliver006/redis_exporter:v1.87.0-alpine` | — (внутренний) | метрики Redis |
|
| `redis-exporter` | `oliver006/redis_exporter:v1.87.0-alpine` | — (внутренний) | метрики Redis |
|
||||||
| `grafana` | `grafana/grafana:13.1.0` | `3001` (внутри контейнера `3000`) | дашборд «Пайплайны пост-обработки» |
|
| `node-exporter` | `prom/node-exporter:v1.8.2` | — (внутренний) | метрики хоста: CPU, память, диск, сеть, load average |
|
||||||
|
| `cadvisor` | `gcr.io/cadvisor/cadvisor:v0.49.2` | — (внутренний) | метрики по каждому контейнеру (CPU/память) |
|
||||||
|
| `grafana` | `grafana/grafana:13.1.0` | `3001` (внутри контейнера `3000`) | дашборды «Пайплайны пост-обработки» и «Хост и контейнеры» |
|
||||||
|
|
||||||
|
`node-exporter`/`cadvisor` не публикуют порт на хост вообще (не только
|
||||||
|
127.0.0.1) — Prometheus ходит к ним по имени сервиса во внутренней сети
|
||||||
|
compose, публикация на хост для этого не нужна. `cadvisor` смонтирован
|
||||||
|
к `/var/run/docker.sock` только на чтение (`:ro`) и запущен с
|
||||||
|
`--docker_only` + урезанным набором коллекторов (`--disable_metrics`) —
|
||||||
|
он заметно дороже `node-exporter` по CPU/RAM, урезание снижает накладные
|
||||||
|
расходы. Если на конкретном сервере это всё равно избыточно —
|
||||||
|
`cadvisor` можно убрать из `deploy/docker-compose.yml`, оставив только
|
||||||
|
`node-exporter` (метрики хоста при этом не пострадают, пропадёт только
|
||||||
|
разбивка по контейнерам).
|
||||||
|
|
||||||
Файлы: `deploy/monitoring/prometheus.yml`, `deploy/monitoring/alerts.yml`,
|
Файлы: `deploy/monitoring/prometheus.yml`, `deploy/monitoring/alerts.yml`,
|
||||||
`deploy/monitoring/grafana/provisioning/` (datasource + провайдер
|
`deploy/monitoring/grafana/provisioning/` (datasource + провайдер
|
||||||
дашбордов), `deploy/monitoring/grafana/dashboards/pipelines.json`.
|
дашбордов), `deploy/monitoring/grafana/dashboards/pipelines.json`,
|
||||||
|
`deploy/monitoring/grafana/dashboards/host.json`.
|
||||||
|
|
||||||
## 2. Запуск
|
## 2. Запуск
|
||||||
|
|
||||||
@@ -37,9 +51,9 @@ ssh -L 9090:127.0.0.1:9090 -L 3001:127.0.0.1:3001 <user>@<host>
|
|||||||
```
|
```
|
||||||
и открывайте `http://localhost:9090` / `http://localhost:3001` у себя.
|
и открывайте `http://localhost:9090` / `http://localhost:3001` у себя.
|
||||||
|
|
||||||
Дашборд «Пайплайны пост-обработки» (папка VidConf в Grafana) появляется
|
Дашборды «Пайплайны пост-обработки» и «Хост и контейнеры» (папка VidConf
|
||||||
сразу — источник данных и дашборд провижинятся из файлов, без ручной
|
в Grafana) появляются сразу — источник данных и дашборды провижинятся из
|
||||||
настройки.
|
файлов, без ручной настройки.
|
||||||
|
|
||||||
## 3. Метрики backend
|
## 3. Метрики backend
|
||||||
|
|
||||||
@@ -78,6 +92,13 @@ Job `llm` в `prometheus.yml` скрейпит `llm:8080/metrics`
|
|||||||
| `PipelineFailed` | рост числа сеансов в статусе `failed` за 15 минут | critical |
|
| `PipelineFailed` | рост числа сеансов в статусе `failed` за 15 минут | critical |
|
||||||
| `QueueGrowing` | глубина очереди растёт 15 минут подряд и превышает 10 задач | warning |
|
| `QueueGrowing` | глубина очереди растёт 15 минут подряд и превышает 10 задач | warning |
|
||||||
| `LlmDown` | `up{job="llm"} == 0` дольше 2 минут | critical (закомментирован по умолчанию) |
|
| `LlmDown` | `up{job="llm"} == 0` дольше 2 минут | critical (закомментирован по умолчанию) |
|
||||||
|
| `HostMemoryLow` | свободно <10% RAM (≈800 МБ из 8 ГБ) дольше 10 минут | warning |
|
||||||
|
| `HostDiskLow` | свободно <10% диска (≈5 ГБ из 50 ГБ) дольше 15 минут | warning |
|
||||||
|
| `HostCpuHigh` | загрузка CPU >90% дольше 15 минут подряд | warning |
|
||||||
|
|
||||||
|
Пороги трёх алертов по железу подобраны под конкретный сервер `1gb`
|
||||||
|
(8 ГБ RAM, 4 CPU, 50 ГБ диска) — при смене сервера пересчитать
|
||||||
|
(`deploy/monitoring/alerts.yml`, группа `vidconf-host`).
|
||||||
|
|
||||||
`LlmDown` актуален только на инсталляциях с профилем `llm`/`llm-gpu`
|
`LlmDown` актуален только на инсталляциях с профилем `llm`/`llm-gpu`
|
||||||
(пресеты 3–5) — по умолчанию (профили `media,monitoring`, без AI) правило
|
(пресеты 3–5) — по умолчанию (профили `media,monitoring`, без AI) правило
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import { formatRecurrenceSummary } from '@/lib/recurrenceFormat'
|
|||||||
const PAGE_SIZE = 10
|
const PAGE_SIZE = 10
|
||||||
|
|
||||||
const STATUS_FILTERS: { value: ConferenceStatus | 'all'; label: string }[] = [
|
const STATUS_FILTERS: { value: ConferenceStatus | 'all'; label: string }[] = [
|
||||||
{ value: 'all', label: 'Все' },
|
|
||||||
{ value: 'scheduled', label: 'Запланированные' },
|
{ value: 'scheduled', label: 'Запланированные' },
|
||||||
{ value: 'active', label: 'Активные' },
|
{ value: 'active', label: 'Активные' },
|
||||||
{ value: 'ended', label: 'Завершённые' },
|
{ value: 'ended', label: 'Завершённые' },
|
||||||
|
{ value: 'all', label: 'Все' },
|
||||||
]
|
]
|
||||||
|
|
||||||
function StatusBadge({ conference }: { conference: AdminConferenceOut }) {
|
function StatusBadge({ conference }: { conference: AdminConferenceOut }) {
|
||||||
@@ -287,7 +287,7 @@ function InviteModal({ conference, onClose }: { conference: AdminConferenceOut;
|
|||||||
* блок «table-card», адаптированный под сущность «конференция»).
|
* блок «table-card», адаптированный под сущность «конференция»).
|
||||||
*/
|
*/
|
||||||
export function AdminConferencesTab() {
|
export function AdminConferencesTab() {
|
||||||
const [statusFilter, setStatusFilter] = useState<ConferenceStatus | 'all'>('all')
|
const [statusFilter, setStatusFilter] = useState<ConferenceStatus | 'all'>('scheduled')
|
||||||
const [searchInput, setSearchInput] = useState('')
|
const [searchInput, setSearchInput] = useState('')
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [offset, setOffset] = useState(0)
|
const [offset, setOffset] = useState(0)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const STATUS_FILTERS: { value: 'active' | 'blocked' | 'all'; label: string }[] =
|
|||||||
*/
|
*/
|
||||||
export function AdminUsersTab() {
|
export function AdminUsersTab() {
|
||||||
const { user: currentUser } = useAuth()
|
const { user: currentUser } = useAuth()
|
||||||
const [statusFilter, setStatusFilter] = useState<'active' | 'blocked' | 'all'>('all')
|
const [statusFilter, setStatusFilter] = useState<'active' | 'blocked' | 'all'>('active')
|
||||||
const [searchInput, setSearchInput] = useState('')
|
const [searchInput, setSearchInput] = useState('')
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [offset, setOffset] = useState(0)
|
const [offset, setOffset] = useState(0)
|
||||||
|
|||||||
Reference in New Issue
Block a user