Tarantool - это in-memory СУБД и сервер приложений в одном процессе. Если он начнёт упираться в память arena, копить отстающую репликацию или захлёбываться запросами, вы должны узнать об этом не из жалоб пользователей, а из графика. Цепочка наблюдаемости в чистом Tarantool выглядит так: instance отдаёт метрики -> Prometheus их собирает (scrape) и хранит как time series -> Grafana рисует дашборды и считает алерты. В этом уроке разбираем, как именно метрики рождаются внутри процесса, как они превращаются в текстовый эндпоинт и как доезжают до графика.
Откуда берутся числа: модуль metrics
В Tarantool 3.x модуль
Код: Выделить всё
metrics- Коллекторы - объекты, которые хранят значения в памяти. Семантика типов один в один как у Prometheus.
- Callback-и сбора - функции, которые перед отдачей метрик опрашивают ,
Код: Выделить всё
box.info,Код: Выделить всё
box.statи заполняют встроенные коллекторы с префиксомКод: Выделить всё
box.slab.info().Код: Выделить всё
tnt_
Код: Выделить всё
counter - монотонно растёт, сбрасывается только при рестарте (tnt_net_requests_total)
gauge - произвольное число вверх/вниз (tnt_info_memory_lua)
histogram - набор bucket-ов по границам le + _sum + _count (латентность)
summary - то же, но считает квантили на стороне инстанса
Код: Выделить всё
tnt_*Код: Выделить всё
metrics.collect()Код: Выделить всё
box.stat()Код: Выделить всё
box.info()Метрика идентифицируется не только именем, но и набором label (меток). Например
Код: Выделить всё
tnt_space_len{name="users", engine="memtx"}Код: Выделить всё
metrics.labelsКод: Выделить всё
{{ instance_name }}От памяти к HTTP: плагины и форматы
Коллекторы живут в памяти как Lua-объекты. Чтобы Prometheus их забрал, нужно отдать их по HTTP в plain-text формате экспозиции Prometheus. За это отвечают плагины модуля metrics:
Код: Выделить всё
prometheusКод: Выделить всё
# HELP/# TYPEКод: Выделить всё
имя{метки} значениеКод: Выделить всё
jsonВ чистом Tarantool 3.x правильный способ поднять эндпоинт - официальная роль
Код: Выделить всё
roles.metrics-exportКод: Выделить всё
metrics:
include: [ all ]
exclude: [ vinyl ]
labels:
alias: '{{ instance_name }}'
groups:
storages:
roles: [ roles.metrics-export ]
roles_cfg:
roles.metrics-export:
http:
- listen: 8081
endpoints:
- path: /metrics/prometheus/
format: prometheus
- path: /metrics/json/
format: json
Код: Выделить всё
curl http://host:8081/metrics/prometheus/Код: Выделить всё
tnt_info_uptime 137.5
Путь метрики: инстанс - Prometheus - Grafana
Prometheus: pull, а не push
Принципиальная вещь: Prometheus работает по модели pull. Не Tarantool шлёт метрики - Prometheus сам ходит на эндпоинты по расписанию (
Код: Выделить всё
scrape_intervalКод: Выделить всё
rate()Конфиг scrape-задания перечисляет инстансы как targets и задаёт путь:
Код: Выделить всё
global:
scrape_interval: 5s
scrape_configs:
- job_name: tarantool
metrics_path: /metrics/prometheus/
static_configs:
- targets:
- 'storage-a-001:8081'
- 'storage-a-002:8082'
- 'router-a-001:8083'
Grafana сама ничего не собирает - это слой визуализации поверх datasource (Prometheus). Панели шлют в Prometheus PromQL-запросы и рисуют ответ. Для Tarantool есть готовые официальные дашборды на grafana.com: ID 21474 для Tarantool 3 (Prometheus) и ID 21484 (InfluxDB). Импорт - по ID или по JSON. Дашборд параметризован переменной datasource и меткой alias, поэтому одна и та же панель работает для всех нод кластера через выпадающий список.
Полезные внутренние метрики, на которые стоит смотреть в первую очередь:
Код: Выделить всё
tnt_info_memory_lua - память Lua-рантайма (утечки бизнес-логики)
tnt_slab_arena_used_ratio - заполнение arena (риск ER_MEMORY_ISSUE)
tnt_net_requests_current - бэклог незавершённых запросов
tnt_replication_lag - отставание реплики, секунды
tnt_space_len{name=...} - число кортежей в спейсе
Код: Выделить всё
local metrics = require('metrics')
local ops = metrics.counter('myapp_orders_total', 'Число заказов')
-- внутри обработчика:
ops:inc(1, { status = 'ok' })
Частые заблуждения и грабли
Метрики - это pull. Если на графике пусто, в 90% случаев проблема не в Tarantool, а в том, что Prometheus не достучался до эндпоинта (порт, firewall, неверный metrics_path) или выбран не тот datasource в Grafana.
- Counter путают с rate. всегда растёт - смотреть на него напрямую бессмысленно. Нужен
Код: Выделить всё
tnt_net_requests_total. На сыром counter график выглядит как прямая линия.Код: Выделить всё
rate(tnt_net_requests_total[1m]) - Нет метки инстанса. Без /
Код: Выделить всё
aliasсерии разных нод схлопываются, иКод: Выделить всё
{{ instance_name }}на счётчике даёт скачки при рестарте чужой ноды. Метка инстанса обязательна в кластере.Код: Выделить всё
rate - Пустой дашборд при живых метриках. Официальные панели опираются на группу , в частности на
Код: Выделить всё
info. Если эта группа исключена вКод: Выделить всё
tnt_info_uptime, дашборд "слепнет".Код: Выделить всё
metrics.exclude - Взрыв кардинальности. Метка с высокой кардинальностью (user_id, trace_id) на пользовательской метрике порождает миллионы серий и кладёт Prometheus по памяти. Метки - только для значений с малым числом вариантов.
- vinyl-метрики по умолчанию урезаны. по числу кортежей выключены ради производительности; их включают отдельно. Не ждите их в дашборде "из коробки".
Код: Выделить всё
tnt_vinyl_* - Слэш в конце пути. Путь в роли и в Prometheus должны совпадать байт в байт, включая завершающий слэш.
Код: Выделить всё
metrics_path
Поднимите одиночный инстанс Tarantool 3.x с включённой ролью
Код: Выделить всё
roles.metrics-exportКод: Выделить всё
curl -s http://127.0.0.1:8081/metrics/prometheus/ | grep tnt_info_uptimeКод: Выделить всё
box.spaceКод: Выделить всё
tnt_net_requests_totalКод: Выделить всё
aliasКонтрольные вопросы
- Кто инициирует передачу метрик - Tarantool или Prometheus, и как называется эта модель?
- В какой момент реально вычисляются встроенные метрики и почему включение метрик дёшево для инстанса?
Код: Выделить всё
tnt_* - Почему на counter-метрику нужно навешивать , а не смотреть на её абсолютное значение?
Код: Выделить всё
rate() - Что произойдёт с дашбордом, если в кластере не задать метку с именем инстанса, и как её добавить в декларативном конфиге?