Как внедрить наблюдаемость в микросервисное приложение с помощью OpenTelemetry, Jaeger и Prometheus

от автора

Современные веб-приложения всё чаще строятся по микросервисной архитектуре. Это даёт гибкость, масштабируемость и изоляцию компонент, но одновременно усложняет отладку, мониторинг и понимание работы системы в целом. Что, если один из сервисов начинает работать медленнее? Как понять, где в цепочке запросов «узкое место»? Как быстро определить причину сбоя или деградации производительности?

Здесь на сцену выходит концепция наблюдаемости (observability). Идея заключается в том, чтобы собрать метрики, логи и трассировки из всех компонентов системы, связать их воедино, и получить чёткую картину того, что происходит внутри распределённого приложения. Для этого существуют современные инструменты вроде:

  • OpenTelemetry — открытая спецификация и набор SDK/агентов для сбора телеметрии (метрик, логов, трассировок).

  • Jaeger — платформа для распределённого трейсинга (прослеживания запросов сквозь множество сервисов).

  • Prometheus — система мониторинга и сбора метрик, ставшая де-факто стандартом в мире Kubernetes и облачных сред.

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

Что такое наблюдаемость и зачем она нужна

Наблюдаемость — это способность системы предоставлять достаточно информации, чтобы понять её внутреннее состояние, не внося дополнительных изменений в код в момент исследования проблемы. Если раньше для отладки хватало логов, то в современных распределённых системах логи нужно дополнять метриками (измерениями производительности), трассировками (путями прохода запросов) и событиями.

Три столпа наблюдаемости:

  1. Логи: Человеко-читаемые записи событий. Они дают локальную информацию о том, что произошло внутри конкретного сервиса.

  2. Метрики: Числовые показатели (например, время ответа, количество запросов, использование памяти) по которым можно судить о производительности и состоянии системы.

  3. Трассировки: Подробный путь запроса через систему, включая длительность отдельных этапов и вызовов. Это ключ к пониманию того, где именно «застревает» запрос.

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

Краткий обзор инструментов

OpenTelemetry (OTel):
OpenTelemetry — это открытой стандарт, цель которого — единообразно собирать телеметрию из приложений. OTel поддерживает множество языков и фреймворков, позволяя вам использовать единый подход к сбору данных. Когда вы интегрируете OTel SDK в свой код, приложение начинает экспортировать метрики, логи и трассировки.

Jaeger:
Jaeger — это система для распределённого трейсинга от CNCF. Она отлично интегрируется с OpenTelemetry. Трассировки, собранные OTel SDK, можно пересылать в Jaeger для визуализации и анализа. Вы сможете видеть цепочку вызовов микросервисов и измерять время выполнения каждой операции.

Prometheus:
Prometheus — это система мониторинга и сбора метрик с собственным форматом экспозиции данных (Pull-модель). Многие библиотеки OpenTelemetry умеют экспортировать метрики в формат, удобный для Prometheus. Собрав метрики с ваших микросервисов, вы сможете строить графики, настраивать алерты и отслеживать тенденции.

Архитектура решения

Допустим, у вас есть несколько микросервисов (на Go, Java или Node.js — не важно), каждый из которых:

  1. Интегрирован с OpenTelemetry SDK.

  2. Экспортирует трассировки в OpenTelemetry Collector, который затем передаёт их в Jaeger для визуализации.

  3. Экспортирует метрики в формате, понятном Prometheus, который их периодически «забирает» (scrape).

Поток данных выглядит примерно так:

Микросервис -> OTel SDK -> OTel Collector -> Jaeger (для трассировок)                                      \                                       -> Prometheus (для метрик) 

Практический пример на Node.js

Рассмотрим простой пример на Node.js. Предположим, у нас есть микросервис orders-service, обрабатывающий запросы заказов. Мы хотим собирать трассировки и метрики при помощи OpenTelemetry, визуализировать трассировки в Jaeger и метрики в Prometheus.

Установка зависимостей

npm install @opentelemetry/api @opentelemetry/sdk-node \ @opentelemetry/sdk-trace-node @opentelemetry/sdk-metrics \ @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-prometheus \ @opentelemetry/auto-instrumentations-node 

Инициализация OpenTelemetry в коде

Создадим файл tracing.js:

// tracing.js const { NodeSDK } = require('@opentelemetry/sdk-node'); const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc'); const { PrometheusExporter } = require('@opentelemetry/exporter-metrics-prometheus'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');  // Настраиваем экспортер трассировок в OTLP формате (коллектор) const traceExporter = new OTLPTraceExporter({   url: 'http://localhost:4317', // адрес OTel Collector });  // Настраиваем экспортер метрик для Prometheus const metricsExporter = new PrometheusExporter({   port: 9464, // порт на котором будет доступен endpoint /metrics }, () => {   console.log('Prometheus scrape endpoint: http://localhost:9464/metrics'); });  // Ресурс - информация о сервисе const resource = new Resource({   [SemanticResourceAttributes.SERVICE_NAME]: 'orders-service', });  // Создаём и запускаем SDK const sdk = new NodeSDK({   traceExporter,   metricExporter: metricsExporter,   instrumentations: [getNodeAutoInstrumentations()],   resource });  sdk.start()   .then(() => console.log('OpenTelemetry started'))   .catch((error) => console.log('Error starting OpenTelemetry', error));  process.on('SIGTERM', () => {   sdk.shutdown()     .then(() => console.log('OpenTelemetry shutdown'))     .catch((error) => console.log('Error shutting down OTel', error)); }); 

Использование в основном приложении

Создадим файл app.js:

// app.js require('./tracing'); // Инициализируем сбор телеметрии  const express = require('express'); const app = express();  app.get('/order', (req, res) => {   // Симулируем обработку заказа   setTimeout(() => {     res.json({ status: 'ok', orderId: 12345 });   }, 200); // задержка 200мс });  app.listen(3000, () => {   console.log('Order service running on http://localhost:3000'); }); 

Запуская node app.js, вы получите сервис на порте 3000, метрики на :9464/metrics и трассировки, отправляемые в OTLP Collector.

Настройка OpenTelemetry Collector

OTel Collector — отдельный бинарник, который вы можете скачать с GitHub или использовать в Docker-контейнере. Его конфигурация YAML определяет, куда отправлять трассировки. Например:

receivers:   otlp:     protocols:       grpc:  exporters:   jaeger:     endpoint: "http://jaeger:14250" # адрес Jaeger (например, в Docker-сети)     tls:       insecure: true  processors:   batch:  service:   pipelines:     traces:       receivers: [otlp]       processors: [batch]       exporters: [jaeger] 

При запуске Jaeger (через Docker, например docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 16686:16686 jaegertracing/all-in-one:latest), вы сможете открыть интерфейс Jaeger по адресу http://localhost:16686 и искать трассировки от вашего orders-service.

Настройка Prometheus

Prometheus можно запустить также в Docker:

docker run -d --name prometheus -p 9090:9090 \   -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus 

А в файле prometheus.yml добавить таргет на ваш сервис:

scrape_configs:   - job_name: 'orders-service'     static_configs:       - targets: ['host.docker.internal:9464'] # если запускается локально на хосте 

Теперь метрики будут доступны Prometheus, и вы сможете видеть графики, строить запросы и настраивать алерты.

Практические советы

  1. Локальное окружение: Начните с запуска Jaeger, Prometheus и OTel Collector локально в Docker, тестируйте приложение у себя.

  2. Инструментация кода: Используйте автоинструментацию OTel и дополнительные плагины для вашего фреймворка (Express, gRPC и т.п.).

  3. Метрики производительности: Обращайте внимание на такие метрики как latency запросов, количество успешных/неуспешных ответов, использование CPU и памяти.

  4. Алертинг: Добавьте Alertmanager (компонент из экосистемы Prometheus) для отправки уведомлений при достижении критических значений метрик.

  5. Продакшен и безопасность: В продакшене настройте доступ к метрикам и трассировкам, используйте аутентификацию и шифрование, если необходимо.

Заключение

Внедрение наблюдаемости в микросервисное приложение — важный шаг к повышению его надёжности и управляемости. Используя OpenTelemetry в качестве единого слоя сбора телеметрии, Jaeger для распределённого трейсинга и Prometheus для метрик, вы получите всесторонний взгляд на состояние вашей системы.

Наблюдаемость не только упрощает отладку проблем, но и помогает принимать обоснованные решения: где оптимизировать код, какие сервисы масштабировать, какие запросы кешировать. Итог — более стабильный, предсказуемый и удобный в сопровождении продукт.

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


ссылка на оригинал статьи https://habr.com/ru/articles/865288/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *