Интеграция с Grafana

Обновлено 14.07.2026
Подключение внешней Grafana к источникам данных Proto Observability Platform: метрики (VictoriaMetrics), логи (VictoriaLogs), трейсы (ClickHouse) и OLAP-аналитика (Cube.js).

Proto Observability Platform хранит телеметрию в открытых стандартных хранилищах. Это позволяет подключить к платформе внешнюю Grafana и строить в ней собственные дашборды — например, вынести часть данных ProtoOBP в существующие управленческие дашборды.

На этой странице:


Что и откуда берётся

ДанныеХранилищеПортПротоколПлагин Grafana
МетрикиVictoriaMetrics8428PromQLVictoriaMetrics или Prometheus
ЛогиVictoriaLogs9428LogsQLVictoriaLogs
ТрейсыClickHouse (proto.spans)8123SQLClickHouse
OLAP-аналитикаCube.js4000RESTCube

Требуется Grafana 12.2.5 или новее — актуальные версии плагинов datasource не запускаются на более ранних релизах.

Способы доступа: прямой и через шлюз

Есть два варианта получения данных внешним клиентом:

  • Прямой доступ к хранилищам (описан на этой странице). Grafana подключается напрямую к портам VM/VL/ClickHouse/Cube. Подходит для развёртывания внутри доверенной сети с ограничением доступа по IP. Простая настройка, не требует токенов.
  • Доступ через шлюз платформы с RBAC-разграничением по сервисам (/promqlapi/, /logsqlapi/, /cubejs-api/ с Bearer-токеном proto-auth). Описан в разделе Справочник API. Применяйте его, когда нужен разграниченный по правам доступ или когда хранилища нельзя открывать напрямую.

Ниже описан прямой доступ. Ограничьте сетевой доступ к портам источников файрволом/allowlist.

Шаг 1. Открыть порты источников

По умолчанию хранилища доступны только во внутренней docker-сети платформы. Чтобы к ним подключилась внешняя Grafana, опубликуйте порты. Создайте рядом с основным compose-файлом платформы override и примените его:

# docker-compose.grafana.yaml
services:
  proto-metric-storage:
    ports:
      - "8428:8428"   # VictoriaMetrics
  proto-log-storage:
    ports:
      - "9428:9428"   # VictoriaLogs
  proto-database:
    ports:
      - "8123:8123"   # ClickHouse (HTTP)
  proto-data-analyzer-api:
    ports:
      - "4000:4000"   # Cube.js

Основной compose-файл платформы содержит номер версии в имени — docker-compose-<версия>.yaml (например docker-compose-201.yaml):

docker compose -f docker-compose-201.yaml -f docker-compose.grafana.yaml up -d

Рекомендуется привязать публикацию к внутреннему интерфейсу, а не ко всем сразу, например "10.0.0.5:8428:8428", и/или закрыть порты файрволом.

Проверка доступности с хоста платформы:

curl -s "http://localhost:8428/api/v1/query?query=vector(1)"                 # VM   → JSON
curl -s "http://localhost:9428/select/logsql/query?query=*&limit=1" -o /dev/null -w "VL %{http_code}\n"
curl -s "http://localhost:8123/ping"                                         # CH   → Ok.
curl -s "http://localhost:4000/readyz"                                       # Cube → ready

Шаг 2. Read-only пользователь ClickHouse

Для трейсов заведите в ClickHouse отдельного пользователя только на чтение. Профиль readonly = 2 запрещает запись и DDL, но разрешает установку session-настроек, которые отправляет плагин Grafana (readonly = 1 их отклонит).

Выполните SQL от имени административной учётной записи ClickHouse — её имя и пароль для proto-database предоставляются вендором по запросу (прикладной пользователь платформы не имеет права CREATE USER):

CREATE SETTINGS PROFILE IF NOT EXISTS grafana_readonly SETTINGS readonly = 2;

CREATE USER IF NOT EXISTS grafana_ro
  IDENTIFIED WITH sha256_password BY '<пароль>'
  SETTINGS PROFILE 'grafana_readonly';

GRANT SELECT ON proto.* TO grafana_ro;
docker exec -i proto-database clickhouse-client \
  --user <admin-user> --password '<admin-pw>' --multiquery <<'SQL'
CREATE SETTINGS PROFILE IF NOT EXISTS grafana_readonly SETTINGS readonly = 2;
CREATE USER IF NOT EXISTS grafana_ro IDENTIFIED WITH sha256_password BY '<пароль>' SETTINGS PROFILE 'grafana_readonly';
GRANT SELECT ON proto.* TO grafana_ro;
SQL

Проверка: SELECT count() FROM proto.spans под grafana_ro работает, INSERT и DROP отклоняются.

Шаг 3. Секрет Cube.js

Отдельный токен для Cube заводить не нужно — плагин Grafana сам подписывает JWT секретом Cube. Понадобится значение CUBEJS_API_SECRET контейнера proto-data-analyzer-api:

docker exec proto-data-analyzer-api printenv CUBEJS_API_SECRET

Это значение указывается в настройках Cube-датасорса (см. шаг 5).

Шаг 4. Установка плагинов Grafana

Установите в Grafana четыре плагина datasource:

  • victoriametrics-metrics-datasource
  • victoriametrics-logs-datasource
  • grafana-clickhouse-datasource
  • grafana-cube-datasource

При запуске Grafana в Docker их можно поставить переменной окружения:

environment:
  GF_INSTALL_PLUGINS: "victoriametrics-metrics-datasource,victoriametrics-logs-datasource,grafana-clickhouse-datasource,grafana-cube-datasource"

Шаг 5. Настройка датасорсов

Ниже — пример автопровижининга (provisioning/datasources/datasources.yaml). Замените <хост-ProtoOBP> на адрес хоста платформы, а креды — на заданные ранее.

apiVersion: 1
datasources:
  - name: ProtoOBP-Metrics
    uid: pobp-metrics
    type: victoriametrics-metrics-datasource
    access: proxy
    url: http://<хост-ProtoOBP>:8428
    isDefault: true
    jsonData:
      httpMethod: POST

  - name: ProtoOBP-Logs
    uid: pobp-logs
    type: victoriametrics-logs-datasource
    access: proxy
    url: http://<хост-ProtoOBP>:9428

  - name: ProtoOBP-ClickHouse
    uid: pobp-clickhouse
    type: grafana-clickhouse-datasource
    access: proxy
    jsonData:
      host: <хост-ProtoOBP>
      port: 8123
      protocol: http
      username: grafana_ro
      defaultDatabase: proto
    secureJsonData:
      password: <пароль-grafana_ro>

  - name: ProtoOBP-Cube
    uid: pobp-cube
    type: grafana-cube-datasource
    access: proxy
    url: http://<хост-ProtoOBP>:4000
    jsonData:
      deploymentType: self-hosted
    secureJsonData:
      apiSecret: <CUBEJS_API_SECRET>

После настройки проверьте каждый датасорс кнопкой Save & Test — все четыре должны быть исправны.

Примеры запросов для панелей

Метрики (PromQL, VictoriaMetrics)

Golden signals по сервисам:

sum by (service) (services_calls)          # пропускная способность (calls/min)
avg by (service) (services_callduration)   # средняя латентность, мс
max by (service) (services_errorcallsperc) # доля ошибок, %

Переменную дашборда service удобно наполнять из label_values(services_calls, service).

Логи (LogsQL, VictoriaLogs)

* | stats by (status) count()                                    # объём логов по severity (statsRange)
status:(error OR critical) | sort by (_time desc) | limit 100    # последние ошибки (панель Logs, instant)

Поля логов: _msg, status (уровень: info/warn/error/critical), service, hostname. Для графика «объём по severity» задайте тип запроса statsRange (стат-агрегация во времени с авто-бакетингом); для панели логов «последние ошибки» — instant (панель типа Logs, а не таблица).

Трейсы (SQL, ClickHouse)

Поиск рутовых спанов (таблица):

SELECT lower(hex(traceID)) AS trace_id, service, name AS operation,
       toUnixTimestamp64Milli(start) AS start_ms,
       round(duration/1e6, 2) AS duration_ms, error
FROM proto.spans
WHERE $__timeFilter(start) AND is_top_level = 1
ORDER BY start DESC LIMIT 100

Waterfall выбранного трейса (панель типа Traces, формат trace):

SELECT lower(hex(s.traceID)) AS traceID, toString(s.spanID) AS spanID,
       if(s.parentID = 0, '', toString(s.parentID)) AS parentSpanID,
       s.service AS serviceName, s.name AS operationName,
       toUnixTimestamp64Milli(s.start) AS startTime, s.duration/1e6 AS duration,
       arrayMap(k -> map('key', k, 'value', s.meta[k]), mapKeys(s.meta)) AS tags
FROM proto.spans AS s
WHERE lower(hex(s.traceID)) = '${trace_id}'
ORDER BY s.start ASC

Особенности схемы proto.spans: traceIDUInt128 (приводится к hex-строке); длительность хранится в наносекундах (делится на 1e6 для миллисекунд); теги лежат в поле meta (Map).

OLAP-аналитика (Cube.js)

Вызовы по сервисам за период (таблица или barchart):

{
  "measures": ["Calls.count"],
  "dimensions": ["Calls.service"],
  "timeDimensions": [ { "dimension": "Calls.date", "dateRange": "Last 24 hours" } ],
  "order": [ ["Calls.count", "desc"] ],
  "limit": 10
}

Cube предоставляет 60+ моделей (сервисы, вызовы, ошибки, RUM, бизнес-транзакции, алерты и др.) — полный список доступен через /cubejs-api/v1/meta.

Примеры дашбордов

Четыре готовых дашборда ниже используют uid датасорсов из шага 5. Импорт: Dashboards → New → Import → вставьте JSON → при необходимости сопоставьте датасорсы. Запросы панелей разобраны в разделе Примеры запросов.

Метрики — Golden Signals

Пропускная способность, латентность и доля ошибок по сервисам с фильтром service вверху.

Дашборд метрик (Golden Signals)

JSON для импорта
{
  "uid": "pobp-metrics-golden",
  "title": "ProtoOBP — Метрики (Golden Signals)",
  "schemaVersion": 39,
  "version": 1,
  "time": { "from": "now-3h", "to": "now" },
  "templating": {
    "list": [
      {
        "name": "service",
        "type": "query",
        "datasource": { "type": "victoriametrics-metrics-datasource", "uid": "pobp-metrics" },
        "query": { "query": "label_values(services_calls, service)", "refId": "ServiceVar" },
        "refresh": 2, "includeAll": true, "multi": true, "allValue": ".*",
        "current": { "text": "All", "value": "$__all" }, "sort": 1
      }
    ]
  },
  "panels": [
    {
      "id": 1, "type": "timeseries", "title": "Пропускная способность (calls/min) по сервисам",
      "gridPos": { "h": 8, "w": 24, "x": 0, "y": 0 },
      "datasource": { "type": "victoriametrics-metrics-datasource", "uid": "pobp-metrics" },
      "targets": [ { "refId": "A", "expr": "sum by (service) (services_calls{service=~\"$service\"})", "legendFormat": "{{service}}" } ]
    },
    {
      "id": 2, "type": "timeseries", "title": "Латентность (avg, мс) по сервисам",
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
      "datasource": { "type": "victoriametrics-metrics-datasource", "uid": "pobp-metrics" },
      "fieldConfig": { "defaults": { "unit": "ms" }, "overrides": [] },
      "targets": [ { "refId": "A", "expr": "avg by (service) (services_callduration{service=~\"$service\"})", "legendFormat": "{{service}}" } ]
    },
    {
      "id": 3, "type": "timeseries", "title": "Доля ошибок (%) по сервисам",
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
      "datasource": { "type": "victoriametrics-metrics-datasource", "uid": "pobp-metrics" },
      "fieldConfig": { "defaults": { "unit": "percent" }, "overrides": [] },
      "targets": [ { "refId": "A", "expr": "max by (service) (services_errorcallsperc{service=~\"$service\"})", "legendFormat": "{{service}}" } ]
    }
  ]
}

Логи — Explorer

Объём логов по уровням (временной ряд, запрос типа statsRange) и панель логов с последними ошибками (время + сообщение, метки раскрываются по клику).

Дашборд логов

JSON для импорта
{
  "uid": "pobp-logs-explorer",
  "title": "ProtoOBP — Логи (Explorer)",
  "schemaVersion": 39,
  "version": 1,
  "time": { "from": "now-1h", "to": "now" },
  "panels": [
    {
      "id": 1, "type": "timeseries", "title": "Объём логов по severity",
      "gridPos": { "h": 9, "w": 24, "x": 0, "y": 0 },
      "datasource": { "type": "victoriametrics-logs-datasource", "uid": "pobp-logs" },
      "targets": [ { "refId": "A", "expr": "* | stats by (status) count() logs", "queryType": "statsRange" } ]
    },
    {
      "id": 2, "type": "logs", "title": "Последние ошибки",
      "gridPos": { "h": 12, "w": 24, "x": 0, "y": 9 },
      "datasource": { "type": "victoriametrics-logs-datasource", "uid": "pobp-logs" },
      "options": { "showTime": true, "showLabels": false, "wrapLogMessage": true, "enableLogDetails": true, "sortOrder": "Descending", "dedupStrategy": "none" },
      "targets": [ { "refId": "A", "expr": "status:(error OR critical) | sort by (_time desc) | limit 100", "queryType": "instant" } ]
    }
  ]
}

Трейсы

Поиск рутовых спанов (клик по trace_id открывает трейс), waterfall выбранного трейса и таблица его спанов.

Дашборд трейсов с waterfall

JSON для импорта
{
  "uid": "pobp-traces",
  "title": "ProtoOBP — Трейсы",
  "schemaVersion": 39,
  "version": 1,
  "time": { "from": "now-1h", "to": "now" },
  "templating": {
    "list": [
      { "name": "trace_id", "type": "textbox", "label": "Trace ID (hex)", "query": "", "current": { "text": "", "value": "" } }
    ]
  },
  "panels": [
    {
      "id": 1, "type": "table", "title": "Поиск трейсов (root spans)",
      "gridPos": { "h": 10, "w": 24, "x": 0, "y": 0 },
      "datasource": { "type": "grafana-clickhouse-datasource", "uid": "pobp-clickhouse" },
      "targets": [
        {
          "refId": "A", "editorType": "sql", "queryType": "table", "format": 1, "pluginVersion": "4.18.0",
          "rawSql": "SELECT lower(hex(traceID)) AS trace_id, service, name AS operation, toUnixTimestamp64Milli(start) AS start_ms, round(duration/1e6, 2) AS duration_ms, error FROM proto.spans WHERE $__timeFilter(start) AND is_top_level = 1 ORDER BY start DESC LIMIT 100",
          "builderOptions": { "database": "proto", "table": "spans", "queryType": "table", "mode": "list", "columns": [], "filters": [], "orderBy": [], "limit": 100, "meta": {} },
          "meta": { "builderOptions": { "database": "proto", "table": "spans", "queryType": "table", "mode": "list", "columns": [], "filters": [], "orderBy": [], "limit": 100, "meta": {} } }
        }
      ],
      "fieldConfig": {
        "defaults": {},
        "overrides": [
          { "matcher": { "id": "byName", "options": "trace_id" },
            "properties": [ { "id": "links", "value": [ { "title": "Показать трейс", "url": "/d/pobp-traces?var-trace_id=${__value.raw}" } ] } ] }
        ]
      }
    },
    {
      "id": 2, "type": "traces", "title": "Waterfall трейса: $trace_id",
      "gridPos": { "h": 16, "w": 24, "x": 0, "y": 10 },
      "datasource": { "type": "grafana-clickhouse-datasource", "uid": "pobp-clickhouse" },
      "targets": [
        {
          "refId": "A", "editorType": "sql", "queryType": "traces", "format": 3, "pluginVersion": "4.18.0",
          "rawSql": "SELECT lower(hex(s.traceID)) AS traceID, toString(s.spanID) AS spanID, if(s.parentID = 0, '', toString(s.parentID)) AS parentSpanID, s.service AS serviceName, s.name AS operationName, toUnixTimestamp64Milli(s.start) AS startTime, s.duration/1e6 AS duration, arrayMap(k -> map('key', k, 'value', s.meta[k]), mapKeys(s.meta)) AS tags FROM proto.spans AS s WHERE lower(hex(s.traceID)) = '${trace_id}' ORDER BY s.start ASC",
          "builderOptions": { "database": "proto", "table": "spans", "queryType": "traces", "mode": "trace", "columns": [], "filters": [], "orderBy": [], "limit": 1000, "meta": { "isTraceIdMode": true, "traceId": "${trace_id}", "traceDurationUnit": "milliseconds" } },
          "meta": { "builderOptions": { "database": "proto", "table": "spans", "queryType": "traces", "mode": "trace", "columns": [], "filters": [], "orderBy": [], "limit": 1000, "meta": { "isTraceIdMode": true, "traceId": "${trace_id}", "traceDurationUnit": "milliseconds" } } }
        }
      ]
    },
    {
      "id": 3, "type": "table", "title": "Спаны трейса (таблица): $trace_id",
      "gridPos": { "h": 12, "w": 24, "x": 0, "y": 26 },
      "datasource": { "type": "grafana-clickhouse-datasource", "uid": "pobp-clickhouse" },
      "targets": [
        {
          "refId": "A", "editorType": "sql", "queryType": "table", "format": 1, "pluginVersion": "4.18.0",
          "rawSql": "SELECT toString(s.spanID) AS span_id, if(s.parentID = 0, '', toString(s.parentID)) AS parent_id, s.service AS service, s.name AS operation, round(s.duration/1e6, 2) AS duration_ms, s.error AS error, toUnixTimestamp64Milli(s.start) AS start_ms FROM proto.spans AS s WHERE lower(hex(s.traceID)) = '${trace_id}' ORDER BY s.start ASC",
          "builderOptions": { "database": "proto", "table": "spans", "queryType": "table", "mode": "list", "columns": [], "filters": [], "orderBy": [], "limit": 1000, "meta": {} },
          "meta": { "builderOptions": { "database": "proto", "table": "spans", "queryType": "table", "mode": "list", "columns": [], "filters": [], "orderBy": [], "limit": 1000, "meta": {} } }
        }
      ]
    }
  ]
}

Бизнес-аналитика — Cube.js

Агрегаты OLAP-слоя: вызовы по сервисам (таблица) и top-10 сервисов (barchart).

Дашборд бизнес-аналитики (Cube.js)

JSON для импорта
{
  "uid": "pobp-business-cube",
  "title": "ProtoOBP — Бизнес-аналитика (Cube.js)",
  "schemaVersion": 39,
  "version": 1,
  "time": { "from": "now-24h", "to": "now" },
  "panels": [
    {
      "id": 1, "type": "table", "title": "Вызовы по сервисам (Calls.count)",
      "gridPos": { "h": 10, "w": 24, "x": 0, "y": 0 },
      "datasource": { "type": "grafana-cube-datasource", "uid": "pobp-cube" },
      "targets": [
        {
          "refId": "A",
          "measures": ["Calls.count"], "dimensions": ["Calls.service"],
          "timeDimensions": [ { "dimension": "Calls.date", "dateRange": "Last 24 hours" } ],
          "filters": [], "order": [ ["Calls.count", "desc"] ]
        }
      ]
    },
    {
      "id": 2, "type": "barchart", "title": "Top сервисы по вызовам",
      "gridPos": { "h": 10, "w": 24, "x": 0, "y": 10 },
      "datasource": { "type": "grafana-cube-datasource", "uid": "pobp-cube" },
      "targets": [
        {
          "refId": "A",
          "measures": ["Calls.count"], "dimensions": ["Calls.service"],
          "timeDimensions": [ { "dimension": "Calls.date", "dateRange": "Last 24 hours" } ],
          "filters": [], "order": [ ["Calls.count", "desc"] ], "limit": 10
        }
      ]
    }
  ]
}

Диагностика типовых проблем

  • Панели метрик/трейсов/Cube пустые, в консоли 404 ... module.js, SystemJS Error#7, react/jsx-runtime. Версия Grafana ниже требуемой плагинами. Нужна Grafana 12.2.5+.
  • Работают только логи, остальное — нет. Тот же признак устаревшей Grafana: плагин VictoriaLogs совместим со старыми версиями, остальные — нет.
  • Cube-датасорс возвращает 404 при проверке. В URL указан полный путь /cubejs-api/v1; оставьте только базовый адрес http://host:4000.
  • Панели ClickHouse падают с Cannot read properties of undefined (reading 'meta'). В таргете панели должен присутствовать объект builderOptions (его формирует редактор запросов плагина). Постройте запрос через UI плагина и сохраните панель, либо используйте готовый экспорт дашборда.
  • Not enough privileges при создании пользователя ClickHouse. SQL нужно выполнять от администратора (default), а не от прикладного пользователя.