OpenTelemetry и Sentry - недооцененные инструменты трассировки распределенных систем на Golang

Краткое описание 🚶

Создадим небольшой пример инструментирования приложения на Golang с его настройкой, использованием OpenTelemetry и передачей получаемых трассировок на бэкенд Sentry. Вот ссылка на него в Github (с этапами запуска самого примера):

Введение 👋

Трассировки распределенных систем  —  важная составляющая наблюдаемости, отслеживания и профилирования микросервисной архитектуры. Не будем подробно разбирать, что такое трассировки и как они работают: статей на эту тему так же много, как бэкендов с трассировками.

Проблема большого количества бэкендов с трассировками 🤔

Но здесь есть небольшая проблема. Бывает так, что техтребования и гайды по контексту трассировок объявлены, а вопрос инструментирования приложений по-прежнему решается в каждом конкретном бэкенде своими средствами. Потому что есть много разных бэкендов (Sentry, Jaeger, Google Cloud Tracing, New Relic, ElasticAPM и другие), которые визуализируют трассировки. И у каждого из них свои пакеты SDK для инструментирования.

Сегодня задействуем для инструментирования микросервиса пакеты SDK для Elastic APM. А что, если завтра они покажутся слишком тяжелыми? Перейдем на что-то более легкое (Jaeger или Sentry)?

Но тогда придется проводить инструментирование всего приложения заново, и с пакетом SDK, используемым только в Jaeger или Sentry. А это приведет к огромным накладным расходам.

И тут приходится кстати OpenTelemetry 🚀

С OpenTelemetry этого делать не придется: выполним инструментирование приложения один раз, после чего эти трассировки будут отправляться в коллектор OpenTelemetry, а оттуда  —  экспортироваться сразу в несколько бэкендов.

Почему Sentry? 👀

Потому что у него как инструмента трассировки распределенных систем много достоинств, среди которых мне особенно по нраву:

  • Его компактность и меньшая ресурсоемкость в сравнении с другими. Мы раньше использовали New Relic и Elastic APM, предоставлявшие полноценную поддержку логирования, трассировок и метрик. Но они очень ресурсоемки. Нужен надежный инструмент, который размещается на простой машине EC2, а не полномасштабном кластере k8s? Тогда Sentry  —  то, что надо.
  • Sentry вне конкуренции по оповещению об ошибках и их преодолению.

Есть и недостатки:

  • Чтобы реализовать перехват ошибок при трассировке стека, необходимо использовать официальный пакет SDK: в настоящее время в OpenTelemetry не поддерживаются сами безупречные перехваты трассировки стека.
  • Собственное логирование OpenTelemetry сейчас на бета-тестировании, а сама разработка находится в замороженном состоянии для нескольких ориентированных на конкретный язык пакетов SDK. Так что в этом плане такая комбинация неосуществима.

Инструментирование приложения на Golang. Практика ⚒️

Мы использовали пакет SDK для OpenTelemetry на Go, размещенный на Github, и следовали инструкциям в документации для Golang.

Пройдем шаг за шагом процесс инструментирования приложения (полностью находится в файле main.go в репозитории на Github).

Инициализация трассировщика

Сначала инициализируем трассировщик, который обрабатывается в методе initTracer(). Здесь создается new exporter (новый экспортер OLTP), который подключен к порту коллектора 4317 по протоколу gRPC (через otel-collector.observability.svc.cluster.local:4317) . То есть подключение идет к сервису otel-collector в пространстве имен наблюдаемости через порт 4317).

Затем создаем новый ресурс resource и задаем имя сервиса test-service.

Дальше получаем обработчик Span и создаем поставщика трассировок TracerProvider, который впоследствии задается в приложении глобально. Также определяем, сколько отбирать трассировок. Сейчас установлено AlwaysSample(), т. е. трассировки отбираются через tracerprovider все время.

Для среды разработки это нормально, а вот для эксплуатационной среды частоту отбора желательно понизить до 10–20% во избежание технических недоработок в системе (трассировки обычно довольно тяжелые).

Начало и завершение спана, распространение его контекста

Базовый tracerprovider готов. Разберемся, как именно начинаются, завершаются и записываются события в span на Golang.

Эта функция находится в somework/service.go. Спан  —  это непрерывный отрезок времени с определенным началом и концом. Метод tracer.Start() отмечает начало спана, а метод span.End()  —  его завершение. В какой-то момент в спане добавляются события и записываются ошибки с помощью span.AddEvent() и span.RecordError() соответственно.

Текущий контекст спана (в данном случае ctx2) просто передается в следующую функцию. Вот как это было сделано в функции ErrorWork():

Настройка коллектора и экспортера OpenTelemetry 🚢

Это простая часть: выполняется всего три настройки в файле конфигурации.

Сначала настраиваем получатели receivers, в которые экспортер пакета SDK будет передавать трассировки. Здесь получатель OLTP подключается по двум протоколам: gRPC и HTTP (порт по умолчанию 4317).

Затем понадобятся обработчики processors и расширения extensions. Здесь использованы обработчики memory_limiter и batch, чтобы коллектор не превысил пик в 512 МиБ и лимит памяти в 1500 МиБ.

Настраиваем сразу несколько экспортеров exporter: экспортер logging (который экспортирует трассировки в терминал модуля) и экспортер sentry, экспортирующий трассировки одновременно на пользовательский интерфейс Sentry.

После настройки создаем конвейер pipeline, в котором есть получатель, обработчик и экспортер. Красота!

Полезные ссылки 🔖

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Uddeshya Singh: Golang, Opentelemetry, and Sentry — The Underrated Distributed Tracing Stack

Предыдущая статьяКак проверить наличие файла или каталога в R, Python и Bash?
Следующая статьяРазбор методов Ruby