В распределенной системе понять, что именно произошло во время выполнения запроса, бывает сложнее, чем исправить саму ошибку. Логи показывают события по отдельности, метрики — общую динамику, но без связки между ними картина остается фрагментарной. Мы решили выстроить наблюдаемость на базе OpenTelemetry и использовать Sentry для анализа трейсов.
Микросервисная архитектура решает проблему масштабируемости, но почти всегда создает новую — потерю прозрачности. Один пользовательский запрос может пройти через десяток сервисов, очереди, сторонние API и фоновые процессы. Если где-то возникает ошибка, найти ее источник без полноценной трассировки — задача на часы, а иногда и на дни.
Чтобы избежать этого, мы выстраиваем связку OpenTelemetry + Sentry. OpenTelemetry позволяет стандартизировать сбор телеметрии, а Sentry — централизовать ее анализ. В результате появляется целостная картина работы системы
Введение в OpenTelemetry: traces, metrics, logs
OpenTelemetry объединяет три типа телеметрии — логи, метрики и трейсы — и задает для них спецификации.
Трассировки. Трейс показывает, как выполнялся конкретный пользовательский запрос. Запрос разбивается на шаги, каждый шаг в терминологии трассировки называется span, они фиксируются с указанием времени начала и окончания. В итоге можно увидеть не просто факт ошибки, а последовательность действий, которая к ней привела.
Метрики отражают общее состояние системы: время отклика, количество запросов, долю ошибок, загрузку ресурсов. По ним видно, стабильна ли система и есть ли отклонения.
Метрики отправляются в Prometheus, а визуализируются в Grafana. Это позволяет анализировать частоту запросов, процент ошибок, нагрузку на процессор и память, работу сборщика мусора. Такие данные полезны не только для оперативного реагирования, но и для поиска долгосрочных проблем (например, утечек памяти).
Во многих библиотеках, которые мы используем, поддержка OpenTelemetry уже встроена. Поэтому подключение базовых метрик не потребовало серьезной доработки. Например, можно сразу начать собирать длительность HTTP-запросов, обращения к базе данных, информацию об ошибках и контекст выполнения.
Логи фиксируют технические детали: текст ошибки, стек вызовов, служебную информацию. Когда лог связан с конкретной трассировкой, он перестает быть отдельной записью и становится частью полной истории запроса.
Раньше логи у нас отправлялись напрямую в Elasticsearch. Это удобно для анализа инцидентов, но метрики и трейсы использовались фрагментарно. OpenTelemetry позволил стандартизировать сбор данных и привести разные сервисы к единому подходу.
Настройка экспорта трейсов через OpenTelemetry Collector
Когда появляется стандарт OpenTelemetry, следующий вопрос — куда отправлять собранные данные. Для анализа трейсов часто используют Jaeger. Но у заказчика в инфраструктуре уже был Sentry, поэтому мы остановились на привычном для заказчика решении вместо внедрения чего-то нового.
Чтобы сервисы не зависели от конкретного инструмента, используется OpenTelemetry Collector. Это промежуточный слой, который принимает данные по стандартному протоколу и дальше маршрутизирует их в нужные системы. Все сервисы отправляют телеметрию в коллектор. В нем настраивается, что трейсы идут в Sentry, а метрики — в Prometheus. При необходимости данные можно отправлять одновременно в несколько backend-систем.
Таким образом, сами сервисы ничего не знают о Sentry. Они работают только с OpenTelemetry. Если backend потребуется заменить, это делается в конфигурации коллектора.
Sentry исторически развивался отдельно от OpenTelemetry, поэтому, когда мы отправляем туда телеметрию, коллектор переводит ее в формат, который понимает Sentry. В целом это работает, но есть нюансы.Например, в Sentry выделяют дополнительную категорию спанов которые называются транзакциями и объединяют один или несколько спанов. Транзакций в спецификации OpenTelemetry нет, поэтому коллектору приходится самостоятельно группировать спаны и выделять транзакции на основе некоторых эвристик. В некоторых случаях коллектор может разбивать спаны на транзакции не совсем корректно, но в любом случае все транзакции и спаны будут отображены в Sentry.
Для развертывания в нашем кластере Kubernetes мы используем официальный хелм чарт и официальный образ otel/opentelemetry-collector-contrib. Contrib образ включает в себя все стандартные и кастомные плагины для коллектора, включая sentryexporter. При желании можно собрать свой образ только с самым необходимым.
image:
repository: otel/opentelemetry-collector-contrib
command:
name: otelcol-contrib
Пример конфига коллектораconfig: exporters:
config:
exporters:
debug: {}
prometheusremotewrite:
endpoint: http://<victoriametrics-addr>:8428/api/v1/write
resource_to_telemetry_conversion:
enabled: true
sentry:
dsn: ${env:SENTRY_DSN}
environment: dev processors:
resource:
attributes:
# добавляем общие атрибуты в телеметрию
- key: service.namespace
value: te.example
action: insert
- key: deployment.environment.name
value: dev
action: upsert service:
pipelines:
traces:
receivers:
- otlp
processors:
- resource
- memory_limiter
- batch
exporters:
- debug
- sentry
metrics:
processors:
- resource
- memory_limiter
- batch
exporters:
- debug
- prometheusremotewrite
В примере указаны минимальный набор опций для плагинов. Полные наборы опций можно найти в разделах prometheusremotewriteexporter и sentry в репозитории коллектора на гитхабе.
SENTRY_DSN можно создать в Sentry в настройках проекта в разделе Client Keys (DSN)
Интеграция с Sentry для централизованного мониторинга
В Sentry мы получаем визуальное представление трейса. Видно дерево выполнения запроса — основной span и вложенные шаги. По каждому шагу можно посмотреть длительность, статус HTTP-запроса, сервис, в котором он выполнялся.
Например, если весь запрос выполнялся долгое время, можно быстро понять, какой именно шаг занял основную часть времени. Это позволяет делать выводы о производительности без ручного анализа логов.
Sentry также группирует ошибки. Если одна и та же ошибка повторяется, система показывает частоту за выбранный период. Можно перейти к конкретной ошибке, посмотреть связанные трейсы и контекст выполнения. Повторяющиеся паттерны становятся заметны. Если ошибка происходит ежедневно в одно и то же время, это может быть фоновой задачей. Даже если в тестовом окружении это допустимо, сам факт регулярности виден сразу.
Некоторые функции, например назначение ошибок на ответственных, мы пока не используем, но при необходимости их можно подключить.
Дополнительно Sentry присылает еженедельную сводку по проектам — количество ошибок и записанных трейсов. Это позволяет отслеживать динамику без отдельного анализа.
Настройка алертов на метрики
Метрики мы отправляем в Prometheus, а для просмотра используем Grafana. Через нее же настраиваются алерты. Такая схема позволяет разделить хранение данных и их визуализацию, при этом оставаясь в рамках единой архитектуры.
Благодаря тому что OpenTelemetry задает стандартные имена для базовых метрик, дашборды собираются довольно быстро. Например, можно без дополнительной договоренности между командами отслеживать длительность HTTP-запросов, частоту обращений или долю ошибок.
Помимо прикладных показателей доступны и системные метрики. Видно потребление памяти, работу сборщика мусора, размер кучи (heap). Если начинает расти одно из поколений кучи или память не освобождается так, как ожидается, это сразу отражается на графиках.
Алерты настраиваются через условия в Grafana. Например, если появляются ошибки определенного типа или снижается количество активных консумеров, система отправляет уведомление. Когда состояние возвращается к норме, алерт автоматически закрывается.
Трассировка распределенных запросов в микросервисной архитектуре
В микросервисной системе одного локального трейса недостаточно. Запрос проходит через несколько сервисов, и чтобы сохранить целостную картину выполнения, нужно передавать контекст между ними.
Для этого используется стандарт trace context. Вместе с HTTP-запросом передается заголовок, в котором содержится идентификатор всего трейса и предыдущего шага. Получив запрос, сервис продолжает уже существующий трейс, создает свои span и при необходимости передает контекст дальше. В результате все шаги остаются связаны одним идентификатором.
Исторически существовали разные форматы передачи контекста. Например, в экосистеме Zipkin использовался формат B3. Такие системы до сих пор встречаются, особенно в legacy-приложениях. OpenTelemetry по умолчанию работает со стандартом W3C Trace Context, но при необходимости может поддерживать и другие форматы, что упрощает интеграцию со старыми компонентами.
Далее каждый сервис отправляет собранные span в коллектор, а тот передает их в Sentry, где они объединяются в единую картину выполнения запроса. Это позволяет увидеть весь путь — от первого входящего вызова до последнего шага — и понять, где именно возникла задержка или ошибка.
Практика периодического анализа ошибок
Обычно после выкладки нового функционала разработчики заходят в Sentry и смотрят, появились ли новые исключения. Это позволяет увидеть сценарии, которые не проявились во время тестирования, и понять, где требуется доработка.
При этом важно учитывать, что не каждая ошибка означает реальную проблему. В системе есть места, где возникновение исключения ожидаемо, просто оно не было явно обработано. После подключения централизованного трейсинга такие случаи тоже становятся заметными и создают дополнительный шум.
С выделением действительно критичных ошибок и обработкой допустимых сценарии еще предстоит поработать. Пока же анализ ведется по мере появления новых событий и по мере необходимости.
Итог
Подключение OpenTelemetry дало нам единый способ собирать телеметрию во всех сервисах. Через коллектор мы централизованно управляем тем, куда отправляются данные, не привязывая сами приложения к конкретному инструменту. Трейсы анализируем в Sentry, метрики — в Prometheus и Grafana.
В результате появилась связная картина работы системы. Можно увидеть, как проходит запрос через несколько сервисов, какие шаги занимают больше времени и какие ошибки повторяются. Это упрощает диагностику и помогает быстрее находить причины проблем.