Источник: Nuances of Programming
Краткое описание
Создадим небольшой пример инструментирования приложения на 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, в котором есть получатель, обработчик и экспортер. Красота!
Полезные ссылки
Читайте также:
Перевод статьи Uddeshya Singh: Golang, Opentelemetry, and Sentry — The Underrated Distributed Tracing Stack