В современном мире все больше IT-проектов переходят на микросервисную архитектуру. При таком подходе появляется ряд вопросов — как избитых, так и не самых очевидных. Как обеспечить корректную работу системы из большого числа микросервисов? Каким способом и с помощью каких технологий разворачивать этот зоопарк? Как отследить узкие места в производительности?
Меня зовут Максим, я Java-разработчик в SimbirSoft. Сейчас работаю на финтех-проекте, занимаюсь созданием бизнес-логики, иногда подключаюсь к различным техническим задачам. В этой статье я кратко и понятно опишу, что такое трассировки, как устроен Jaeger, а также расскажу про недавно появившийся новый стандарт распределенного трейсинга и мониторинга — OpenTelemetry.
Материал будет полезен начинающим разработчикам. Из него вы узнаете, как с небольшими усилиями подключить трассировки к своему проекту. Речь пойдет об инструменте из большого блока «Наблюдаемость» (Monitoring), а конкретно — о трассировках.
Что такое трассировка?
Трассировка — это метод, который используют для профилирования и мониторинга приложений, особенно тех, которые созданы с использованием архитектуры микросервисов. Трассировка помогает точно определить, где возникают сбои и что вызывает низкую производительность.
Трассировки оперируют понятиями span и trace. Span — это некоторое действие, которое содержит в себе следующие части:
название сервиса;
уникальный ID;
ID родительского спана;
название вызываемого метода;
время начала и окончания.
Трассировка — это набор/список span, которые соединены отношением родитель/потомок (его также можно рассматривать как направленный ациклический граф отрезков). Трассировки указывают, как запросы распространяются через наши службы и другие компоненты.
Чем она полезна?
Трассировка помогает обнаружить узкие места в производительности. Это возможно благодаря следующим функциям:
отслеживание обращения к ресурсам (БД, внешняя система, файлы и т.д);
отслеживание последовательного вызова нескольких сервисов;
сбор массива данных для последующего анализа при помощи метрик;
возможность получить наглядное представление о том, в какой последовательности вызываются сервисы.
Архитектура Jaeger
Также стоит упомянуть о Jaeger. Jaeger — это платформа распределенной трассировки с открытым исходным кодом, созданная Uber еще в 2015 году. Она состоит из инструментальных SDK, серверной части для сбора и хранения данных, пользовательского интерфейса для визуализации данных и платформы Spark/Flink для совокупного анализа трассировки.
Основные части платформы:
Инструментальный SDK — библиотеки, необходимые для сбора метрик.
Jaeger-agent — локальный агент. Если агента нет, то трейсы всех сервисов на этой машине обычно выключены.
Jaeger-collector — в него все агенты посылают собранные трейсы, а он кладет их в выбранную БД.
Jaeger-query — сервис, который ходит в базу данных и отдает уже собранные трейсы для анализа.
Jaeger-ui — веб-интерфейс для поиска и просмотров трейсов. Он ходит в Jaeger-query.
Локальный запуск Jaeger при помощи Docker в приложениях Spring Boot
Для начала создадим два простых сервиса — Consumer и Producer. Первый будет обращаться по http ко второму, получать слово “hello” и выводить слово “hello-world”.
Рассмотрим контроллер первого сервиса — Consumer. Все стандартно — RestController, GetMapping, Lombok и т.д., кроме feignClient. Способы взаимодействия между сервисами — тема не этой статьи, но в нашем случае используется обычный feignClient. В двух словах, spring-cloud-starter-openfeign позволяет с минимальными усилиями обеспечить сетевое взаимодействие между сервисами.
@RestController
@RequiredArgsConstructor
public class ConsumerController {
private final ProducerFeign producerFeign;
@GetMapping(value = "/hello-world")
public String getHelloWorld() {
return producerFeign.getHello() + " world";
}
}
Обратимся к клиенту. При обращении к методу getHello() по localhost:8081/hello получаем слово “hello”.
@FeignClient(name = "producer-feign", url = "localhost:8081")
public interface ProducerFeign {
@GetMapping(value = "/hello")
String getHello();
}
Контроллер нашего второго сервиса (клиента, к которому обращается первый) — Producer.
@RestController
@RequiredArgsConstructor
public class ProducerController {
@GetMapping(value="/hello")
public String getHello() {
return "hello";
}
}
Локальный запуск Jaeger при помощи Docker
Jaeger поставляется в качестве готового образа Docker. Для запуска образа Jaeger выполняем следующее:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.6
Далее необходимо запустить наше приложение. В этом руководстве будет использован новый стандарт OpenTelemetry. Все, что нужно сделать — это запустить jar-приложение с параметром javaagent.
OpenTelemetry — относительно новый стандарт. Он включает в себя распределенный трейсинг и мониторинг и совместим с OpenTracing и OpenCensus. Более того, последние прекращают поддержку в течение двух лет, что неотвратимо приближает нас к переходу на OpenTelemetry.
Стандарт, по сути, представляет собой некую прослойку между потребителями метрик/трейсов и их поставщиками.
В документации OpenTelemetry можно найти параметры, с которыми запускается jar (например, OTEL_TRACES_EXPORTER, OTEL_SERVICE_NAME и т.д.). Параметр Dotel.traces.exporter определяют куда opentelemetry будет складывать трейсы, в нашем случае это Jaeger.
Запуск первого сервиса:
java -javaagent:{your-path-to-agent}/opentelemetry-javaagent.jar -Dotel.traces.exporter=jaeger -Dotel.service.name=customer-consumer -jar customer-consumer-0.0.1-SNAPSHOT.jar
Запуск второго сервиса:
java -javaagent:{your-path-to-agent}/opentelemetry-javaagent.jar -Dotel.traces.exporter=jaeger -Dotel.service.name=customer-producer -jar customer-producer-0.0.1-SNAPSHOT.jar
Для перехода в пользовательский интерфейс Jaeger используем http://localhost:16686/
Вывод
Трассировка — очень мощный инструмент, который помогает локализовать сбои и с точностью определить, что приводит к низкой производительности. Этот метод позволяет стабилизировать поведение в распределенных системах, собирать данные для последующего анализа при помощи метрик и наглядно показывает цепочку вызовов компонентов системы.
Чтобы получить максимальную отдачу от использования инструмента, ваши сервисы должны отправлять трассировки на центральную панель мониторинга — например, Jaeger. В этой статье мы рассмотрели лишь часть функциональности OpenTelemetry. На самом деле, возможности стандарта намного шире.
Если вам интересна эта тема — сохраните полезные ссылки:
Документация по OpenTelemetry с дружелюбным пособием для начинающих: https://opentelemetry.io/
Документация по Jaeger: https://www.jaegertracing.io/
Спасибо за внимание!
Больше полезных материалов для backend-разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.
ris58h
Нарратор: они не узнают.
Самое главное упустили. Вот вам ссылка на спеку, вот команда запуска какого-то контейнера, а вот команда запуска агента. И что с этим делать? Пример хоть бы один рабочий. Вот 2 микросервиса у меня. Один дёргает другого по http. Как эту связь увидеть?
SSul Автор
Спасибо за замечание! Обновили статью, добавили описание методов контроллера, цепочку вызова и команду по запуску сервисов через javaagent. Надеемся, это поможет вам и другим читателям)
ris58h
Уже лучше. Теперь бы ещё рассказать как эта магия работает под капотом. Если я сервис ручками через соккет дёргаю - заработает или всё же есть какие-то ограничения?