Краткое описание 🚶
Создадим небольшой пример инструментирования приложения на 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
, в котором есть получатель, обработчик и экспортер. Красота!
Полезные ссылки 🔖
- Документация по OpenTelemetry.
- Коллектор OpenTelemetry на Github.
- Трассировки распределенных систем Sentry.
- Репозиторий инструментирования на Github.
Читайте также:
- Обработка сигналов в операционных системах семейства Unix на Golang
- Реализация интерфейсов в Golang
- Бенчмарки в Golang: тестируем производительность кода
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Uddeshya Singh: Golang, Opentelemetry, and Sentry — The Underrated Distributed Tracing Stack