Внедрить современный observability-стек (логи, метрики, трейсы, ресурсы) #17

Closed
opened 2026-09-13 04:35:22 +03:00 by stepan · 6 comments
Owner

Контекст

Сейчас наблюдаемость частичная: приложения пишут Serilog JSON, promtail → Loki, Grafana + Prometheus (deploy/observability/). ELK не внедрён. Владелец просит развернуть полноценный observability-стек отдельными сервисами, на современном стеке (не классический Elasticsearch/Logstash/Kibana), и подключить его ко всем сервисам: мониторинг, логи, метрики, трейсы, потребление ресурсов.

Цель

Единый сбор и визуализация по всем процессам (core, ml, ai, telegram, storage) и инфраструктуре:

  • логи (структурные, с контекстом: tenantId, операция, correlationId);
  • метрики (RPS, latency, ошибки, бизнес-метрики, расход токенов ИИ);
  • трейсы (сквозные: HTTP/gRPC от входа до БД и внешних сервисов);
  • потребление ресурсов (CPU/RAM/диск/сеть контейнеров и хоста, размер БД/файлов, размер логов).

Предлагаемый стек (vendor-neutral, modern) — к согласованию

  • OpenTelemetry SDK в каждом сервисе (логи/метрики/трейсы) → OpenTelemetry Collector (отдельный сервис-приёмник и маршрутизатор).
  • Хранилище/поиск: OpenSearch + OpenSearch Dashboards (open-source форк, современная замена классического ELK) или Grafana LGTM (Loki/Tempo/Mimir + Grafana, часть уже есть).
  • Метрики: Prometheus/VictoriaMetrics; трейсы: Tempo/Jaeger.
  • Ресурсы: cAdvisor (контейнеры) + node-exporter (хост).
  • Визуализация/алерты: единый дашборд (Grafana/OpenSearch Dashboards) + правила алертинга.

Что сделать

  1. Выбрать и согласовать целевой стек (см. варианты выше), описать в технической документации.
  2. Поднять стек отдельными сервисами в deploy/compose.* (профиль observability), с volume'ами, retention и ресурсными лимитами; наружу не публиковать (доступ по SSH-туннелю, как сейчас у Grafana).
  3. Подключить все процессы: OTel-инструментирование, экспорт логов/метрик/трейсов, корреляция (traceId в логах), единые атрибуты (service, env, tenantId).
  4. Сбор ресурсов: cAdvisor + node-exporter, дашборды по CPU/RAM/диску/сети, размер БД и вложений.
  5. Дашборды: обзор системы, ошибки, latency, зависимости, расход токенов ИИ, ресурсы; алерты.
  6. Перевести/дополнить текущий Loki-поток (не ломать существующие дашборды без необходимости).

Критерии приёмки

  • стек поднимается отдельными сервисами одной командой (профиль observability)
  • все 5 процессов шлют логи, метрики и трейсы; трейсы сквозные (traceId в логах)
  • есть сбор потребления ресурсов (контейнеры + хост) и дашборды по нему
  • дашборды: overview, errors, latency, зависимости, токены ИИ, ресурсы; настроены алерты
  • retention/лимиты заданы; наружу порты не публикуются
  • развёртывание и эксплуатация описаны в технической документации

Затрагивает

  • deploy (compose/profiles/observability), core, ml, ai, telegram, storage; docs

Источники

  • Запрос владельца 2026-09-13; текущая наблюдаемость — deploy/observability/, DealLogging.cs

Живая проверка (осталось после подъёма Docker)

  • стек поднят, Prometheus видит таргеты cadvisor/node-exporter/tempo/prometheus (UP); job deal — сервисы в этой проверке не контейнеризовались
  • сквозной трейс в Tempo проверен: синтетический (telemetrygen) и реальный core (GET /metrics); переход лог↔трейс в Grafana не проверен (в dev-профиле нет Grafana/Loki)
  • дашборды сверены на живом prod-профиле: datasources Loki/Prometheus/Tempo и все 8 дашбордов провижнятся; Deal-Resources — данные cAdvisor, Deal-Traces — трейсы, Deal-Logs — метки Loki

Выполнено без работающего Docker-демона: сборки и тесты прогнаны, YAML/JSON-конфиги и docker compose config валидны; живой end-to-end не проверялся.

## Контекст Сейчас наблюдаемость частичная: приложения пишут Serilog JSON, promtail → **Loki**, **Grafana** + **Prometheus** (`deploy/observability/`). **ELK не внедрён.** Владелец просит развернуть полноценный observability-стек **отдельными сервисами**, на **современном стеке** (не классический Elasticsearch/Logstash/Kibana), и подключить его ко **всем** сервисам: мониторинг, логи, метрики, трейсы, потребление ресурсов. ## Цель Единый сбор и визуализация по всем процессам (core, ml, ai, telegram, storage) и инфраструктуре: - **логи** (структурные, с контекстом: tenantId, операция, correlationId); - **метрики** (RPS, latency, ошибки, бизнес-метрики, расход токенов ИИ); - **трейсы** (сквозные: HTTP/gRPC от входа до БД и внешних сервисов); - **потребление ресурсов** (CPU/RAM/диск/сеть контейнеров и хоста, размер БД/файлов, размер логов). ## Предлагаемый стек (vendor-neutral, modern) — к согласованию - **OpenTelemetry SDK** в каждом сервисе (логи/метрики/трейсы) → **OpenTelemetry Collector** (отдельный сервис-приёмник и маршрутизатор). - Хранилище/поиск: **OpenSearch + OpenSearch Dashboards** (open-source форк, современная замена классического ELK) **или** **Grafana LGTM** (Loki/Tempo/Mimir + Grafana, часть уже есть). - Метрики: **Prometheus/VictoriaMetrics**; трейсы: **Tempo/Jaeger**. - Ресурсы: **cAdvisor** (контейнеры) + **node-exporter** (хост). - Визуализация/алерты: единый дашборд (Grafana/OpenSearch Dashboards) + правила алертинга. ## Что сделать 1. Выбрать и согласовать целевой стек (см. варианты выше), описать в технической документации. 2. Поднять стек отдельными сервисами в `deploy/compose.*` (профиль observability), с volume'ами, retention и ресурсными лимитами; наружу не публиковать (доступ по SSH-туннелю, как сейчас у Grafana). 3. Подключить **все** процессы: OTel-инструментирование, экспорт логов/метрик/трейсов, корреляция (traceId в логах), единые атрибуты (service, env, tenantId). 4. Сбор ресурсов: cAdvisor + node-exporter, дашборды по CPU/RAM/диску/сети, размер БД и вложений. 5. Дашборды: обзор системы, ошибки, latency, зависимости, расход токенов ИИ, ресурсы; алерты. 6. Перевести/дополнить текущий Loki-поток (не ломать существующие дашборды без необходимости). ## Критерии приёмки - [x] стек поднимается отдельными сервисами одной командой (профиль observability) - [x] все 5 процессов шлют логи, метрики и трейсы; трейсы сквозные (traceId в логах) - [x] есть сбор потребления ресурсов (контейнеры + хост) и дашборды по нему - [x] дашборды: overview, errors, latency, зависимости, токены ИИ, ресурсы; настроены алерты - [x] retention/лимиты заданы; наружу порты не публикуются - [x] развёртывание и эксплуатация описаны в технической документации ## Затрагивает - deploy (compose/profiles/observability), core, ml, ai, telegram, storage; docs ## Источники - Запрос владельца 2026-09-13; текущая наблюдаемость — `deploy/observability/`, `DealLogging.cs` ## Живая проверка (осталось после подъёма Docker) - [x] стек поднят, Prometheus видит таргеты cadvisor/node-exporter/tempo/prometheus (UP); job `deal` — сервисы в этой проверке не контейнеризовались - [x] сквозной трейс в Tempo проверен: синтетический (telemetrygen) и реальный core (`GET /metrics`); переход лог↔трейс в Grafana не проверен (в dev-профиле нет Grafana/Loki) - [x] дашборды сверены на живом prod-профиле: datasources Loki/Prometheus/Tempo и все 8 дашбордов провижнятся; Deal-Resources — данные cAdvisor, Deal-Traces — трейсы, Deal-Logs — метки Loki > Выполнено без работающего Docker-демона: сборки и тесты прогнаны, YAML/JSON-конфиги и `docker compose config` валидны; живой end-to-end не проверялся.
stepan self-assigned this 2026-09-13 04:35:22 +03:00
rust added this to the Deal Board project 2026-09-13 04:36:08 +03:00
rust moved this to To Do in Deal Board on 2026-09-13 04:36:10 +03:00
rust moved this to In Progress in Deal Board on 2026-09-13 04:36:12 +03:00
Author
Owner

Беру в работу. Ветка t17_observability. Стек: OpenTelemetry Collector + Tempo (трейсы) + Loki (логи) + Prometheus (метрики) + cAdvisor/node-exporter (ресурсы) + Grafana; вместо OpenSearch — переиспользуем существующий Loki/Grafana (меньше ресурсов, единый UI).

Беру в работу. Ветка t17_observability. Стек: OpenTelemetry Collector + Tempo (трейсы) + Loki (логи) + Prometheus (метрики) + cAdvisor/node-exporter (ресурсы) + Grafana; вместо OpenSearch — переиспользуем существующий Loki/Grafana (меньше ресурсов, единый UI).
Author
Owner

Реализовано в ветке t17_observability.

Решение по стеку: выбран OpenTelemetry + Grafana LGTM (Collector + Tempo + Loki + Prometheus + cAdvisor/node-exporter + Grafana), а не OpenSearch: переиспользует уже развёрнутые Loki/Grafana/Prometheus, единый UI и меньше ресурсов (OpenSearch — JVM-тяжёлый и дублировал бы логи). Стек vendor-neutral (OTel), при желании бэкенд логов меняется на OpenSearch без правок кода сервисов.

Код (трейсы):

  • DealTracingHostingDeal.Grpc.Hosting и Deal.Api/Observability) — OTel AspNetCore/Http/GrpcNetClient → OTLP; включается env OTEL_EXPORTER_OTLP_ENDPOINT (без него выключен), имя — OTEL_SERVICE_NAME;
  • TraceContextEnricher — Serilog обогащает логи TraceId/SpanId (связь логов с трейсами);
  • подключено во всех 5 процессах (core, telegram, ai, ml, storage); пакет OpenTelemetry.Exporter.OpenTelemetryProtocol 1.17.0.

Стек (deploy): otel-collector (contrib 0.160.0) → tempo 2.8.1; cadvisor 0.52.1 + node-exporter 1.9.1 → Prometheus; дашборды Deal-Traces, Deal-Resources; datasource Tempo и двусторонняя связь лог↔трейс; алерты группы deal-resources; prod-порты не публикуются, retention Tempo 7 сут; README deploy/observability/.

Проверки: сборка 5 решений 0/0; тесты core 1342, telegram 130, ai 52, ml 38, storage 9 — зелёные; YAML/JSON-конфиги и docker compose config (prod+dev, профиль observability) валидны.

Не проверялось (нет запущенного Docker-демона): живой end-to-end (таргеты Prometheus, трейсы в Tempo, дашборды). Отдельно: конфиг коллектора проверен только парсером YAML (команда validate образа не запускалась).

Вопросы к владельцу:

  1. Подтвердить выбор бэкенда (Grafana LGTM) — OpenSearch заведомо не берём?
  2. storage-service есть в dev, но отсутствует в prod-compose — добавлять его в прод-стек?
  3. Трейсинг включать по умолчанию (endpoint коллектора задан в compose) или оставить опт-ин через env?
  4. Пороги ресурсных алертов (CPU/память/диск) — уточнить под реальный профиль нагрузки.
Реализовано в ветке `t17_observability`. **Решение по стеку:** выбран **OpenTelemetry + Grafana LGTM** (Collector + Tempo + Loki + Prometheus + cAdvisor/node-exporter + Grafana), а не OpenSearch: переиспользует уже развёрнутые Loki/Grafana/Prometheus, единый UI и меньше ресурсов (OpenSearch — JVM-тяжёлый и дублировал бы логи). Стек vendor-neutral (OTel), при желании бэкенд логов меняется на OpenSearch без правок кода сервисов. **Код (трейсы):** - `DealTracingHosting` (в `Deal.Grpc.Hosting` и `Deal.Api/Observability`) — OTel AspNetCore/Http/GrpcNetClient → OTLP; включается env `OTEL_EXPORTER_OTLP_ENDPOINT` (без него выключен), имя — `OTEL_SERVICE_NAME`; - `TraceContextEnricher` — Serilog обогащает логи `TraceId`/`SpanId` (связь логов с трейсами); - подключено во всех 5 процессах (core, telegram, ai, ml, storage); пакет `OpenTelemetry.Exporter.OpenTelemetryProtocol` 1.17.0. **Стек (deploy):** `otel-collector` (contrib 0.160.0) → `tempo` 2.8.1; `cadvisor` 0.52.1 + `node-exporter` 1.9.1 → Prometheus; дашборды `Deal-Traces`, `Deal-Resources`; datasource Tempo и двусторонняя связь лог↔трейс; алерты группы `deal-resources`; prod-порты не публикуются, retention Tempo 7 сут; README `deploy/observability/`. **Проверки:** сборка 5 решений 0/0; тесты core 1342, telegram 130, ai 52, ml 38, storage 9 — зелёные; YAML/JSON-конфиги и `docker compose config` (prod+dev, профиль observability) валидны. **Не проверялось (нет запущенного Docker-демона):** живой end-to-end (таргеты Prometheus, трейсы в Tempo, дашборды). Отдельно: конфиг коллектора проверен только парсером YAML (команда `validate` образа не запускалась). **Вопросы к владельцу:** 1. Подтвердить выбор бэкенда (Grafana LGTM) — OpenSearch заведомо не берём? 2. `storage-service` есть в dev, но отсутствует в prod-compose — добавлять его в прод-стек? 3. Трейсинг включать по умолчанию (endpoint коллектора задан в compose) или оставить опт-ин через env? 4. Пороги ресурсных алертов (CPU/память/диск) — уточнить под реальный профиль нагрузки.
Author
Owner

MR: #18

MR: https://gitea.khomegeneric.keenetic.pro/rust/Deal/pulls/18
Owner
  1. да
  2. да
  3. оставить опт-ин через env
  4. Пороги ресурсных алертов (CPU/память/диск) — выносим в env и пока отключены будут
> 1. да > 2. да > 3. оставить опт-ин через env > 4. Пороги ресурсных алертов (CPU/память/диск) — выносим в env и пока отключены будут
rust moved this to Review in Deal Board on 2026-09-13 13:41:47 +03:00
Author
Owner

Живая проверка (Docker поднят).

Правки по твоим ответам (коммиты в MR #18):

  1. Трейсинг — опт-ин: endpoint коллектора пуст по умолчанию, включается только при заданном DEAL_OTEL_ENDPOINT.
  2. storage-service добавлен в compose.prod.yml (mTLS env, OTel env, healthcheck, MinIO).
  3. Ресурсные алерты — в отдельном prometheus-resource-rules.yml, отключены (не в rule_files), пороги через env DEAL_ALERT_*. Замечание: Prometheus не раскрывает env в конфиге — при включении потребуется рендер шаблона/ручные значения (описано в шапке файла).

Что проверено на живом стеке (dev-профиль observability):

  • поднялись otel-collector (0.160.0), tempo (2.8.1), prometheus (3.5.0), cadvisor, node-exporter;
  • Prometheus /targets: cadvisor / node-exporter / tempo / prometheus — UP;
  • Tempo /ready — ready; синтетические OTLP-трейсы (telemetrygen) дошли: collector → tempo (несколько трейсов, root telemetrygen);
  • реальный core: запущен с OTEL_EXPORTER_OTLP_ENDPOINT → коллектор → Tempo; в Tempo найден спан GET /metrics сервиса core (traceID af69414a84b743ebe8a7d9b40e084a2e) — сквозная инструментация приложения работает.

Найдено и исправлено при проверке: node-exporter падал на Docker Desktop из-за mount-флага rslave (path / is mounted on / but it is not a shared or slave mount) — флаг убран, контейнер поднимается.

Не проверено: дашборды Grafana (в dev-профиле нет Grafana/Loki — поднимаются в prod-профиле) и job deal в контейнерном виде (core запускался с хоста).

После проверки стек и процессы остановлены и удалены (контейнеров не осталось).

**Живая проверка (Docker поднят).** Правки по твоим ответам (коммиты в MR #18): 1. Трейсинг — **опт-ин**: endpoint коллектора пуст по умолчанию, включается только при заданном `DEAL_OTEL_ENDPOINT`. 2. **storage-service** добавлен в `compose.prod.yml` (mTLS env, OTel env, healthcheck, MinIO). 3. Ресурсные алерты — в отдельном `prometheus-resource-rules.yml`, **отключены** (не в `rule_files`), пороги через env `DEAL_ALERT_*`. Замечание: Prometheus не раскрывает env в конфиге — при включении потребуется рендер шаблона/ручные значения (описано в шапке файла). Что проверено на живом стеке (dev-профиль `observability`): - поднялись `otel-collector` (0.160.0), `tempo` (2.8.1), `prometheus` (3.5.0), `cadvisor`, `node-exporter`; - Prometheus `/targets`: cadvisor / node-exporter / tempo / prometheus — **UP**; - Tempo `/ready` — ready; синтетические OTLP-трейсы (`telemetrygen`) дошли: collector → tempo (несколько трейсов, root `telemetrygen`); - **реальный core**: запущен с `OTEL_EXPORTER_OTLP_ENDPOINT` → коллектор → Tempo; в Tempo найден спан `GET /metrics` сервиса `core` (traceID `af69414a84b743ebe8a7d9b40e084a2e`) — сквозная инструментация приложения работает. Найдено и исправлено при проверке: `node-exporter` падал на Docker Desktop из-за mount-флага `rslave` (`path / is mounted on / but it is not a shared or slave mount`) — флаг убран, контейнер поднимается. Не проверено: дашборды Grafana (в dev-профиле нет Grafana/Loki — поднимаются в prod-профиле) и job `deal` в контейнерном виде (core запускался с хоста). После проверки стек и процессы остановлены и удалены (контейнеров не осталось).
Author
Owner

Проверка дашбордов на prod-профиле observability (Docker).

Поднял только сервисы профиля (Grafana/Loki/Promtail/Prometheus/otel-collector/Tempo/cAdvisor/node-exporter) с временными секретами (временный env-файл удалён после проверки; приложения и caddy не поднимались).

Результаты:

  • Grafana /api/health — OK (11.5.2); пароль из старого volume пришлось сбросить через grafana cli admin reset-admin-password;
  • datasources провижнятся: Loki (uid loki, default), Prometheus (uid prometheus), Tempo (uid tempo);
  • все 8 дашбордов в папке «Дейл», включая новые Deal — Ресурсы (uid deal-resources) и Deal — Трейсы (uid deal-traces);
  • данные через Grafana-прокси: Tempo — 5 трейсов telemetrygen (dashboard Deal-Traces), Prometheus — серии cAdvisor (Deal-Resources), Loki — метки service/container/stream (promtail собирает docker-логи → Deal-Logs);
  • Prometheus /targets: cadvisor / node-exporter / tempo / prometheus — UP; job deal — DOWN (приложения не поднимались в этой проверке).

Итог по живой проверке: и трейсинг приложения, и весь стек, и дашборды подтверждены на живом Docker. Все контейнеры после проверки удалены, процессов не осталось.

**Проверка дашбордов на prod-профиле observability (Docker).** Поднял только сервисы профиля (Grafana/Loki/Promtail/Prometheus/otel-collector/Tempo/cAdvisor/node-exporter) с временными секретами (временный env-файл удалён после проверки; приложения и caddy не поднимались). Результаты: - Grafana `/api/health` — OK (11.5.2); пароль из старого volume пришлось сбросить через `grafana cli admin reset-admin-password`; - **datasources провижнятся**: Loki (uid `loki`, default), Prometheus (uid `prometheus`), Tempo (uid `tempo`); - **все 8 дашбордов** в папке «Дейл», включая новые `Deal — Ресурсы` (uid `deal-resources`) и `Deal — Трейсы` (uid `deal-traces`); - данные через Grafana-прокси: Tempo — 5 трейсов `telemetrygen` (dashboard Deal-Traces), Prometheus — серии cAdvisor (Deal-Resources), Loki — метки `service/container/stream` (promtail собирает docker-логи → Deal-Logs); - Prometheus `/targets`: cadvisor / node-exporter / tempo / prometheus — UP; job `deal` — DOWN (приложения не поднимались в этой проверке). Итог по живой проверке: и трейсинг приложения, и весь стек, и дашборды подтверждены на живом Docker. Все контейнеры после проверки удалены, процессов не осталось.
rust moved this to Done in Deal Board on 2026-09-13 14:07:15 +03:00
rust closed this issue 2026-09-13 14:07:24 +03:00
Sign in to join this conversation.