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:
Trace
- прохождение пути через разные сервисы. Trace состоит из набора Span-ов. Является виртуальным объектом, т.к. определяется Span-ом, у которого нет родительского элемента.Span
- является контейнером с помощью которого замеряют время выполнения какого-либо действия, например, время запроса к внешнему сервису или время выполнения SQL запроса.Tags
- key-value, привязанное к SpanLogs
- key-value, привзяанное к Span
Пример отображения данных элементов в интерфейсе Jaeger:
Спецификация не накладывает ограничений на передачу данных между сервисами. Вместо этого предлагается использовать методы:
Inject
- для добавления данных в “пакет” отправляемый в другой сервисExtract
- извлечение данных из “пакета”
Данный подход позволяет использовать в качестве транспорта любой протокол межсервисного взаимодействия: очереди, REST API, JSON RPC, gRPC, Thrift, …
Jaeger
Jaeger является конкретным инструментом, который в случае необходимости можно заменить на любой сервис реализующий спецификацию OpenTracing без изменения исходного кода приложения. Список доступных трейсеров можно посмотреть на официальном сайте
JaegerClient
- клиентская библиотека, реализующая спецификацию OpenTracing.JaegerAgent
- устанавливается на каждый host с приложением.
Основная задача получать данные с клиента по UDP и отправлять их в коллектор.JaegerCollector
- коллектор принимает данные от агента и записывает в хранилищеStorage
- в качестве хранилища могут быть использованы Cassandra, Elasticsearch и Kafka. Существует 2 способа сохранения данных:- запись напрямую в хранилище
- запись для буфферизации в Kafka, с последующей обработкой с помощью JaegerIngester
JaegerQuery
- API для работы с хранилищем.JaegerUI
- интерфейс просмотра (не поддерживает аутентификацию и работу с пользователями).
Клиентская библиотека может быть настроена на работу как с агентом, так и с коллектором. И в том и в другом случае есть свои плюсы и минусы - подробнее можно почитать здесь.
Потеря данных
Мой любимый раздел. Обработка ошибок недоступности агента или коллектора в Jaeger реализована на высоком уровне и заслуживает внимания. Самое главное, что недоступность этих компонент не влияет на работу приложения.
В случае, если агент является нерабочим, приложение с клиентом будет работать без ошибок, т.к. данные передаются по UDP. Если же клиент настроен на работу с коллектором, с которым не возможно установить соединение, то данные о Span-ах будут накапливаться в локальном буфере, пока он не переполнится. В случае переполнения данные удаляются.
Sampling
Бывают случаи, когда нам не нужно сохранять каждый Trace, например, если наше приложение пишет очень много данных. Для этого существует настройка ограничения выборки (sampling) в клиенте:
constant
- сохраняет все трейсы.probablict
- фильтрует трейсы с указанной вероятностью, например, 10%.rate limiting
- ограничивает количество трейсов в секунду.remote
- позволяет получать централизованно настройки sampling-а с коллектора. Для этого агент и коллектор предоставляют дополнительный протокол поверх http.
Example
Из своей практики: на одном из проектов у нас была микросервисная архитектура. Сервисы были раздеплоены в нескольких инстансах между двумя датацентрами. REST API вызовы проходили через балансировщик, который отправлял запрос на любой из доступных в данный момент инстансов.
Переодически некоторые сервисы не укладывались в SLA по времени ответа. Только благодаря внедрению Jaeger-а мы точно смогли сказать, что проблема была в сети между двумя датацентрами, которая переодически начинала тормозить.
Проблема выявлялась только в тот момент, когда сервис из первого датацентра обращался к сервису из второго датацентра. При этом время в логах второго датацентра равнялось времени выполнения самого сервиса.
Отличный пример того, как правильно работать с Jaeger можно найти в блоге создателя данного иснтрумента - Юрия Шкуро
- Статья - https://medium.com/opentracing/take-opentracing-for-a-hotrod-ride-f6e3141f7941
- Исходный код - https://github.com/jaegertracing/jaeger/tree/master/examples/hotrod
- Примеры на разных языках - https://github.com/yurishkuro/opentracing-tutorial
Эволюция
В марте 2019 года началось слияние OpenTracing и OpenCensus в новый проект - OpenTelemetry OpenCensus изначально разрабатывался в Google, в последствии стал Open source. Помимо сбора информации о трейсах, OpenCensus работает с метриками.
OpenTracing | OpenCensus | OpenTelemetry | |
---|---|---|---|
Что это? | Спецификация | Спецификация и набор библиотек | Спецификации и набор библиотек |
Какие данные собирает? | Распределенные трейсы | Распределенные трейсы и метрики | Распределенные трейсы, метрики и логи |
Принцип работы | Разработчики используют инструмент имплементирующий OpenTracing | Разработчики используют стандартного OpenCensus агента. Данные могут экспортироваться в разные бэкенды. | Разработчики используют интегрированный набор библиотек и API для работы с агентом и коллектором. |
Новые проекты уже сейчас можно начинать разрабатывать с использованием OpenTelemetry.
В этой статье мы разобрали пример работы с трейсингом от велосипеда до продуктов, которые разрабатываются сейчас и имеют планы на будущее.