OpenTracing

Была зима 2015 год, наша команда разработки переписывала монолит на микросервисы. В результате бурного обсуждения будущего развития архитектуры мы сделали вывод: мы не знали как был устроен монолит раньше, но в ходе reverse engineering-а выяснили и смогли научиться с ним жить и даже переписали часть на микросервисы. Мы знаем как новые сервисы связаны между собой, спасибо документации, но столкнулись с тем, что не знаем в каких конкретно случаях они вызываются и в какой последовательности, ведь на один запрос от пользователя внутри мы могли совершить несколько вызовов к другим микросервисам, а те к следующим. И не понятно от чего тормозят запросы конкретных эндпоинтов. Нам хотелось иметь простой инструмент для диагностики.

Карта наших сервисов

Карта наших сервисов

Мы уже снимали логи с nginx-ов, которые стояли перед каждым сервисом, единственное, расширили их временем выполнения запроса, и отправляли данные в ELK.

Решение заключалось в следующем: при http вызове (все наши сервисы взаимодействовали через REST API) мы проверяли существует ли в GET параметре tracking_id, если он отсутствовал - генерировали UUID. Каждый сервис, при вызове другого сервиса, должен был добавить в GET параметр tracking_id={uuid}. Всяк кулик своё болото хвалит, но инструмент был рабочим и решал наши задачи. В ELK мы могли найти медленные запросы и по tracking_id отследить всю цепочку вызовов и выявить причину.

Спустя время я узнал, что существует инструмент - Jaeger от компании Uber, который решает проблемы выявления связей микросервисов, их влияния друг на друга и позволяет замерять время выполнения разных компонент. Данный инструмент реализует спецификацию OpenTracing

OpenTracing

Спецификация OpenTracing спроектирована так, что позволяет избежать vendor locki-in, для этого используется прослойка между вызывающим приложением и конкретной имплементацией трейсинга.

Давайте разберемся в терминологии OpenTracing:

Пример отображения данных элементов в интерфейсе Jaeger:

Спецификация не накладывает ограничений на передачу данных между сервисами. Вместо этого предлагается использовать методы:

Данный подход позволяет использовать в качестве транспорта любой протокол межсервисного взаимодействия: очереди, REST API, JSON RPC, gRPC, Thrift, …

Jaeger

Jaeger является конкретным инструментом, который в случае необходимости можно заменить на любой сервис реализующий спецификацию OpenTracing без изменения исходного кода приложения. Список доступных трейсеров можно посмотреть на официальном сайте

Упрощенная схема компонент

Упрощенная схема компонент

Клиентская библиотека может быть настроена на работу как с агентом, так и с коллектором. И в том и в другом случае есть свои плюсы и минусы - подробнее можно почитать здесь.

Потеря данных

Мой любимый раздел. Обработка ошибок недоступности агента или коллектора в Jaeger реализована на высоком уровне и заслуживает внимания. Самое главное, что недоступность этих компонент не влияет на работу приложения.

В случае, если агент является нерабочим, приложение с клиентом будет работать без ошибок, т.к. данные передаются по UDP. Если же клиент настроен на работу с коллектором, с которым не возможно установить соединение, то данные о Span-ах будут накапливаться в локальном буфере, пока он не переполнится. В случае переполнения данные удаляются.

Sampling

Бывают случаи, когда нам не нужно сохранять каждый Trace, например, если наше приложение пишет очень много данных. Для этого существует настройка ограничения выборки (sampling) в клиенте:

Example

Из своей практики: на одном из проектов у нас была микросервисная архитектура. Сервисы были раздеплоены в нескольких инстансах между двумя датацентрами. REST API вызовы проходили через балансировщик, который отправлял запрос на любой из доступных в данный момент инстансов.

Переодически некоторые сервисы не укладывались в SLA по времени ответа. Только благодаря внедрению Jaeger-а мы точно смогли сказать, что проблема была в сети между двумя датацентрами, которая переодически начинала тормозить.

по горизонтали - время, по вертикали - время выполнения запроса

по горизонтали - время, по вертикали - время выполнения запроса

Проблема выявлялась только в тот момент, когда сервис из первого датацентра обращался к сервису из второго датацентра. При этом время в логах второго датацентра равнялось времени выполнения самого сервиса.

Отличный пример того, как правильно работать с Jaeger можно найти в блоге создателя данного иснтрумента - Юрия Шкуро

Эволюция

В марте 2019 года началось слияние OpenTracing и OpenCensus в новый проект - OpenTelemetry OpenCensus изначально разрабатывался в Google, в последствии стал Open source. Помимо сбора информации о трейсах, OpenCensus работает с метриками.

 OpenTracingOpenCensusOpenTelemetry
Что это?СпецификацияСпецификация и набор библиотекСпецификации и набор библиотек
Какие данные собирает?Распределенные трейсыРаспределенные трейсы и метрикиРаспределенные трейсы, метрики и логи
Принцип работыРазработчики используют инструмент имплементирующий OpenTracingРазработчики используют стандартного OpenCensus агента. Данные могут экспортироваться в разные бэкенды.Разработчики используют интегрированный набор библиотек и API для работы с агентом и коллектором.

Новые проекты уже сейчас можно начинать разрабатывать с использованием OpenTelemetry.

В этой статье мы разобрали пример работы с трейсингом от велосипеда до продуктов, которые разрабатываются сейчас и имеют планы на будущее.