Grafana 8 + Tempo: разворачиваем distributed tracing и связываем его с метриками и логами
Grafana 8.0 GA объединила метрики, логи и трейсы в одном UI. Разбираем как поднять Tempo и получить корреляцию между Prometheus, Loki и трейсами в продакшне.
Grafana 8.0 выходит в GA с единым UI для метрик (Prometheus), логов (Loki) и трейсов (Tempo) - unified observability stack в одном интерфейсе
Grafana 8.0 вышла в GA в начале июня, и мы с тех пор аккуратно смотрели на неё в нескольких клиентских окружениях. На этой неделе наконец развернули полный стек в одном из production-проектов - с Tempo для трейсов и корреляцией между тремя источниками данных. Поделимся что там внутри и где пришлось поработать руками.
Что изменилось в Grafana 8
Главное изменение не в отдельных datasource, а в концепции: теперь в одном UI можно переходить от метрики к логам к трейсу и обратно без смены инструмента. Prometheus показывает всплеск latency - кликаешь в точку на графике, видишь LogQL-запрос с тем же timerange в Loki, оттуда Trace ID из лога открывает конкретный span в Tempo. На бумаге это звучит как маркетинг, на практике - это существенное изменение того, как выглядит troubleshooting.
Ещё из заметных изменений в 8.0:
- Unified alerting - алертинг переработан, теперь он не привязан к отдельной datasource а работает как отдельный движок поверх любого источника данных. Alertmanager интегрируется как routing layer.
- Live streaming - дашборды могут обновляться в реальном времени через WebSocket без страничного refresh, интересно для операционных экранов.
- Новый UI панелей - panel editor переписан, и сначала это сбивает с толку, потому что всё поменяло место.
Но нас интересует именно трейсинг и корреляция, об этом подробнее.
Tempo: что это и зачем
Grafana Tempo - относительно новый бэкенд для distributed tracing, первый релиз был в конце 2020-го. Концепция отличается от Jaeger и Zipkin: Tempo не индексирует span-атрибуты для поиска, хранит трейсы в объектном хранилище (S3, GCS, Azure Blob, локально), а искать трейсы предполагается по Trace ID, который пришёл из другого места - из лога или метрики. Это делает хранение дешёвым, но поиск «найди мне все медленные запросы» через UI Tempo не работает - нужен Loki или Prometheus как точка входа.
Для нашего сценария это хорошо: у нас уже есть Loki с логами сервисов, которые пишут Trace ID в structured fields. Значит, мы не теряем ничего существенного.
Как разворачивали
У клиента Kubernetes-кластер, Prometheus + Grafana 7.x уже стоит, Loki подняли несколько месяцев назад. Задача - добавить Tempo и обновить Grafana до 8.
Tempo развернули через Helm-чарт в минимальной конфигурации: один pod, локальный volume для хранения трейсов. Для production рекомендуют объектное хранилище, у нас пока S3-совместимое через MinIO - это не идеальное решение для серьёзной нагрузки, но для начала работает.
Конфигурация Tempo выглядит достаточно лаконично: указываешь бэкенд хранилища, порты для приёма трейсов (Tempo поддерживает OpenTelemetry, Jaeger, Zipkin протоколы через единый receiver), и базовые параметры compactor и querier.
Инструментирование приложений - это отдельная история. У клиента несколько Python и Go сервисов. OpenTelemetry SDK для обоих языков в целом зрелый. Основная работа - пробросить Trace ID через контекст запросов между сервисами, настроить exporter на Tempo endpoint.
Выяснилось, что часть сервисов уже использует что-то похожее на Jaeger через старые SDK - миграция потребовала замены клиентской библиотеки и изменения endpoint. Не трагедия, но несколько часов работы.
Корреляция логов и трейсов в Loki настраивается через derived fields в конфигурации datasource в Grafana. Указываешь регулярное выражение для извлечения Trace ID из лог-строки и URL для перехода в Tempo:
derivedFields:
- name: TraceID
matcherRegex: "trace_id=(\\w+)"
url: "$${__value.raw}"
datasourceUid: tempo-uid
После этого в Explore при просмотре логов появляется кликабельная ссылка на трейс. Это и есть та самая correlation, ради которой весь разговор.
Где пришлось повозиться
Grafana 8 и старые дашборды. При обновлении с 7.x часть дашбордов поломалась в плане UI - панели не пропали, данные сохранились, но panel editor изменился настолько, что некоторые кастомные настройки оказались перемещены в другие места. Особенно это коснулось overrides в таблицах. Потратили несколько часов на ревизию критичных дашбордов.
Unified alerting мы пока не переключали. В Grafana 8 есть legacy alerting mode, мы оставили его включённым - у клиента несколько десятков настроенных алертов, и одновременная миграция выглядит рискованно. Переход планируем отдельно, не совмещая с обновлением стека.
Tempo и retention. По умолчанию Tempo хранит всё. Retention настраивается через compactor, но документация на момент нашего развёртывания местами неполная - пришлось смотреть в исходники и issues GitHub. Настроили 7 дней через compactor block retention.
Sampling. Писать 100% трейсов в продакшне с нагруженным сервисом - не лучшая идея. Настроили probabilistic sampling на стороне приложения через OpenTelemetry SDK, 10% трейсов. Для нашего объёма запросов это даёт достаточно данных для анализа.
Что получили в итоге
Рабочая цепочка: всплеск на графике Prometheus - переход в Loki с тем же timerange - в логах видны ошибки с Trace ID - открываем трейс в Tempo и видим где именно в цепочке сервисов потерялось время. До этого каждый источник данных был отдельным инструментом, и correlation делалась вручную через timestamp.
Это не означает что все проблемы теперь диагностируются за секунды - трейс показывает что медленно, но не всегда объясняет почему. Но устранить одно ненужное переключение контекста между инструментами - это уже ощутимо.
Для клиентов на managed-сопровождении мы смотрим на этот стек как на стандартный набор для проектов с микросервисной архитектурой. Prometheus + Loki уже были частью базовой обвязки, Tempo и обновление до Grafana 8 логично закрывают трейсинг как третий столп observability.
Tempo молодой проект - Jaeger у него уже несколько лет форы. Но интеграция с Grafana здесь нативная, и если Prometheus + Loki уже стоят, добавление Tempo выглядит проще, чем разворачивать Jaeger с его UI отдельно.
- Kubernetes 1.22: Ingress в GA и прощание с PodSecurityPolicy · 5 августа 2021
- Elastic Stack 7.14: настраиваем ML anomaly detection для логов сетевого оборудования · 25 августа 2021