Интеграция с Grafana
Proto Observability Platform хранит телеметрию в открытых стандартных хранилищах. Это позволяет подключить к платформе внешнюю Grafana и строить в ней собственные дашборды — например, вынести часть данных ProtoOBP в существующие управленческие дашборды.
На этой странице:
- Что и откуда берётся
- Способы доступа: прямой и через шлюз
- Шаг 1. Открыть порты источников
- Шаг 2. Read-only пользователь ClickHouse
- Шаг 3. Секрет Cube.js
- Шаг 4. Установка плагинов Grafana
- Шаг 5. Настройка датасорсов
- Примеры запросов для панелей
- Примеры дашбордов
- Диагностика типовых проблем
Что и откуда берётся
| Данные | Хранилище | Порт | Протокол | Плагин Grafana |
|---|---|---|---|---|
| Метрики | VictoriaMetrics | 8428 | PromQL | VictoriaMetrics или Prometheus |
| Логи | VictoriaLogs | 9428 | LogsQL | VictoriaLogs |
| Трейсы | ClickHouse (proto.spans) | 8123 | SQL | ClickHouse |
| OLAP-аналитика | Cube.js | 4000 | REST | Cube |
Требуется 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-datasourcevictoriametrics-logs-datasourcegrafana-clickhouse-datasourcegrafana-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>
Cube.js: адрес
В поле URL Cube-датасорса указывается базовый адресhttp://host:4000 —
плагин сам добавляет путь /cubejs-api/v1. Полный путь в URL приведёт к ошибке
404 при проверке подключения.После настройки проверьте каждый датасорс кнопкой 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: traceID — UInt128 (приводится к hex-строке);
длительность хранится в наносекундах (делится на 1e6 для миллисекунд); теги
лежат в поле meta (Map).
ClickHouse: алиасы колонок
В запросе waterfall колонки квалифицированы через алиас таблицы (s.traceID),
поскольку выходная колонка называется так же (AS traceID). Без квалификации
ClickHouse в WHERE подставит алиас вместо исходной колонки, и результат будет
пустым.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 вверху.

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 выбранного
трейса и таблица его спанов.

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).

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), а не от прикладного пользователя.