Мониторинг и трассировка в Go: от Prometheus до Jaeger

—

от автора

Привет, Хабр!

Когда приложения становятся все более сложными и распределенными, и эффективное управление ими требует глубокого понимания происходящего внутри. Мониторинг позволяет нам наблюдать за состоянием приложения, собирать ценные метрики о его работе и предоставлять нам обратную связь в реальном времени. Это помогает в обнаружении и устранении проблем до того, как они станут серьезными. Мониторинг также дает возможность планировать масштабирование и оптимизацию, основываясь на данных, а не на предположениях.

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

Prometheus — это система мониторинга с открытым исходным кодом, разработанная для наблюдения за распределенными системами. Он предоставляет инструменты для сбора и хранения временных рядов данных, а также для создания пользовательских запросов и алертинга на основе этих данных. Prometheus предлагает нативную поддержку для сбора метрик от приложений, что делает его идеальным выбором для мониторинга Go-приложений.

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

Мониторинг с Prometheus

Prometheus — это инструмент мониторинга с открытым исходным кодом, созданный для эффективного сбора и хранения временных рядов данных о состоянии системы. Он обладает модульной архитектурой, которая позволяет настраивать и расширять его функциональность под ваши потребности.

1. Архитектура и компоненты

Архитектура Prometheus представляет собой серверное приложение, которое собирает, хранит и предоставляет метрики. Основные компоненты архитектуры включают:

  • Prometheus Server: Это центральное звено системы, ответственное за сбор данных. Он выполняет периодические запросы к приложениям и устройствам для получения метрик.

  • Storage: Prometheus хранит метрики во временных рядах (time series). Это позволяет анализировать данные на различных временных интервалах.

  • Exporter: Экспортеры — это приложения, которые обеспечивают доступ к метрикам из различных источников. Например, Go-приложение может использовать Prometheus Go-клиент для экспорта метрик в формате, понятном Prometheus.

  • Query Language: Prometheus предоставляет мощный язык запросов PromQL, который позволяет фильтровать и агрегировать данные для создания пользовательских запросов и алертинга.

Пример кода (на Go) для экспорта метрик с использованием Prometheus Go-клиента:

import (     "github.com/prometheus/client_golang/prometheus"     "github.com/prometheus/client_golang/prometheus/promhttp"     "net/http" )  func main() {     // Создаем счетчик метрик     metric := prometheus.NewCounterVec(         prometheus.CounterOpts{             Name: "my_custom_metric",             Help: "An example custom metric",         },         []string{"label_name"},     )     prometheus.MustRegister(metric)      // Пример инкрементирования счетчика     metric.WithLabelValues("example_label").Inc()      // Запускаем HTTP-сервер для экспорта метрик     http.Handle("/metrics", promhttp.Handler())     http.ListenAndServe(":9090", nil) } 

2. Метрики и экспортеры

Прометеус собирает метрики с помощью экспортеров, которые могут быть как встроенными, так и созданными вами. Например, Go-приложение может использовать Prometheus Go-клиент для создания и экспорта метрик. Метрики в Prometheus имеют следующие характеристики:

  • Имя: Уникальное имя метрики, которое используется для идентификации.

  • Метки (Labels): Дополнительные метаданные, позволяющие категоризировать метрику. Метки позволяют группировать и агрегировать метрики.

  • Значение: Числовое значение, представляющее метрику.

  • Таймстэмп: Время, когда метрика была собрана.

Пример создания метрики и экспорта её с использованием Prometheus Go-клиента:

import (     "github.com/prometheus/client_golang/prometheus"     "github.com/prometheus/client_golang/prometheus/promhttp"     "net/http" )  func main() {     metric := prometheus.NewGauge(         prometheus.GaugeOpts{             Name: "example_gauge",             Help: "An example gauge metric",         },     )     prometheus.MustRegister(metric)      metric.Set(42.0) // Установка значения метрики      http.Handle("/metrics", promhttp.Handler())     http.ListenAndServe(":9090", nil) }

Интеграция Prometheus в Go-приложения

1. Использование Prometheus клиента для экспорта метрик

Прометеус предоставляет набор клиентских библиотек для разных языков, включая Go. Эти клиенты упрощают сбор и экспорт метрик из ваших приложений:

import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "net/http" )  func main() { // Создаем счетчик метрик counter := prometheus.NewCounter(prometheus.CounterOpts{ Name: "example_counter", Help: "An example counter metric", })  // Регистрируем счетчик в реестре метрик prometheus.MustRegister(counter)  // Инкрементируем счетчик counter.Inc()  // Запускаем HTTP-сервер для экспорта метрик http.Handle("/metrics", promhttp.Handler()) http.ListenAndServe(":9090", nil) }

В этом примере мы создаем счетчик метрик с именем «example_counter» и регистрируем его с помощью prometheus.MustRegister(). Затем мы инкрементируем счетчик на единицу. По умолчанию, метрики экспортируются через HTTP-сервер, который слушает на порту 9090.

2. Метрики собственного приложения

Представим, у нас есть веб-сервер на Go, и мы хотим собирать метрики о количестве обработанных запросов и времени ответа:

package main  import ( "fmt" "net/http" "time"  "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" )  var ( requestsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests.", }, []string{"method"}, ) requestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "Duration of HTTP requests.", }, []string{"method"}, ) )  func main() { prometheus.MustRegister(requestsTotal, requestDuration)  http.Handle("/metrics", promhttp.Handler()) http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }  func handler(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { method := r.Method elapsed := time.Since(start).Seconds() requestsTotal.WithLabelValues(method).Inc() requestDuration.WithLabelValues(method).Observe(elapsed) }()  // Ваш код обработки запроса здесь fmt.Fprintf(w, "Hello, World!") }

В этом примере мы создаем счетчик http_requests_total для подсчета обработанных запросов и гистограмму http_request_duration_seconds для измерения времени ответа. Мы используем метки для категоризации метрик по методу HTTP (например, GET или POST).

Алертинг и оповещения в Prometheus

1. Конфигурация правил оповещения

Алерты в Prometheus основаны на конфигурационных файлах, в которых определены правила оповещений. Эти правила определяют условия, при которых следует срабатывать алертам, и какие действия следует предпринимать при срабатывании.

Пример конфигурации правила оповещения:

groups:   - name: example     rules:       - alert: HighRequestLatency         expr: http_request_duration_seconds > 1         for: 5m         labels:           severity: critical         annotations:           summary: "High request latency on {{ $labels.instance }}"           description: "{{ $labels.instance }} is experiencing high request latency."        - alert: ServiceDown         expr: up == 0         for: 1m         labels:           severity: critical         annotations:           summary: "Service {{ $labels.instance }} is down"           description: "Service {{ $labels.instance }} is not responding to requests."

В этом примере мы определяем два правила оповещения: «HighRequestLatency» и «ServiceDown». Первое правило срабатывает, если среднее время ответа на запросы превышает 1 секунду в течение 5 минут. Второе правило срабатывает, если целевой сервис недоступен в течение 1 минуты.

2. Интеграция с системами оповещения

Prometheus не предоставляет функциональность отправки оповещений напрямую, но он интегрируется с различными системами оповещения, такими как Alertmanager. Alertmanager — это отдельный компонент, предназначенный для управления оповещениями и их дальнейшей рассылки.

Пример конфигурации Alertmanager:

route:   group_by: ['alertname']   group_wait: 30s   group_interval: 5m   repeat_interval: 12h  receivers:   - name: 'email-notifications'     email_configs:       - to: 'admin@example.com'         from: 'alertmanager@example.com'         smarthost: 'smtp.example.com:25'         auth_username: 'your_username'         auth_password: 'your_password'

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

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

Трассировка с Jaeger

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

Преимущества трассировки:

  • Отслеживание запросов: Трассировка позволяет отслеживать, какой путь проходит запрос, начиная с его входа в систему и заканчивая ответом клиенту. Это особенно полезно в распределенных системах, где запрос может проходить через множество сервисов.

  • Идентификация узких мест: Трассировка может помочь выявить узкие места в вашем приложении, где запросы затрачивают больше времени, чем ожидалось. Это позволяет вам улучшить производительность и оптимизировать код.

  • Диагностика ошибок: Трассировка упрощает диагностику ошибок. Вы можете увидеть, в каком сервисе или части приложения возникла проблема, и оперативно реагировать на нее.

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

Процесс трассировки включает следующие этапы:

  • Инструментирование: Ваши приложения должны быть инструментированы для сбора трассировочных данных. Это означает, что в коде приложения должны быть вставлены точки сбора метрик, которые регистрируют информацию о времени и пути выполнения запросов.

  • Сбор данных: Собранные данные отправляются в Jaeger Agent, который агрегирует и передает их на центральный сервер Jaeger Collector.

  • Хранение данных: Данные хранятся в центральной базе данных Jaeger. Вы можете настроить различные хранилища данных, включая Elasticsearch, Cassandra и другие.

  • Анализ и визуализация: Данные анализируются и визуализируются с помощью Jaeger UI. Вы можете видеть путь выполнения запросов, статистику времени ответа и многое другое.

Пример инструментирования Go-приложения для сбора трассировочных данных с использованием Jaeger:

import (     "github.com/opentracing/opentracing-go"     "github.com/uber/jaeger-client-go"     "github.com/uber/jaeger-client-go/config" )  func main() {     // Создание и настройка Jaeger Tracer     cfg := config.Configuration{         ServiceName: "my-service",         Sampler: &config.SamplerConfig{             Type:  "const",             Param: 1,         },         Reporter: &config.ReporterConfig{             LogSpans: true,         },     }     tracer, closer, _ := cfg.NewTracer(config.Logger(jaeger.StdLogger))     defer closer.Close()      // Установка Jaeger Tracer как глобального     opentracing.SetGlobalTracer(tracer)      // Ваш код приложения }

Этот код инициализирует Jaeger Tracer и инструментирует ваше приложение для сбора трассировочных данных.

Интеграция Jaeger в Go-приложения

Интеграция Jaeger в ваши Go-приложения позволяет отслеживать выполнение запросов и операций в распределенных системах.

1. Использование OpenTracing API

OpenTracing — это стандартный API для создания и отправки трассировочных данных в различные системы, включая Jaeger. Используя OpenTracing API, вы можете инструментировать ваше приложение для сбора информации о трассировке.

Прежде всего, вам потребуется установить необходимые библиотеки. В Go, вы можете использовать пакет opentracing-go, который предоставляет реализацию OpenTracing API.

import (     "log"     "github.com/opentracing/opentracing-go"     "github.com/opentracing/opentracing-go/ext"     "github.com/opentracing/opentracing-go/log" )

Далее, создайте Jaeger Tracer и настройте его для вашего приложения:

import (     "github.com/uber/jaeger-client-go"     "github.com/uber/jaeger-client-go/config" )  func initJaeger(serviceName string) (opentracing.Tracer, io.Closer) {     cfg := config.Configuration{         ServiceName: serviceName,         Sampler: &config.SamplerConfig{             Type:  "const",             Param: 1,         },         Reporter: &config.ReporterConfig{             LogSpans: true,         },     }      tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))     if err != nil {         log.Fatalf("Could not initialize jaeger tracer: %s", err.Error())     }     return tracer, closer }

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

2. Создание и отправка трассировочных данных

Теперь, когда Jaeger Tracer настроен, вы можете начать создавать и отправлять трассировочные данные в вашем приложении. Это делается с помощью методов OpenTracing API.

Пример создания и отправки трассировочных данных:

func main() {     // Инициализация Jaeger Tracer     tracer, closer := initJaeger("my-service")     defer closer.Close()      // Начало новой трассировки     span := tracer.StartSpan("my-operation")     defer span.Finish()      // Определение тегов и журналирование     ext.SpanKindRPCClient.Set(span)     span.SetTag("custom-tag", "tag-value")     span.LogFields(log.String("event", "my-event"))      // Ваш код выполнения операции }

В этом примере мы создаем новую трассировку с именем «my-operation» и указываем ее завершение с помощью defer span.Finish(). Мы также добавляем теги и логируем события, чтобы обогатить трассировку информацией.

Интеграция Jaeger в Go-приложения помогает вам отслеживать выполнение операций в распределенных системах и быстро находить и решать проблемы. Процесс интеграции не только улучшает мониторинг и отладку ваших приложений, но и предоставляет ценную информацию для оптимизации и улучшения производительности.

Анализ и визуализация трассировочных данных в Jaeger

1. Интерфейс Jaeger UI

Jaeger предоставляет удобный веб-интерфейс, известный как Jaeger UI, для анализа трассировочных данных. Этот интерфейс позволяет вам визуализировать пути выполнения запросов, анализировать производительность и находить проблемы в вашем приложении.

Когда Jaeger запущен и интегрирован с вашим приложением, вы можете получить доступ к Jaeger UI, обычно по адресу http://localhost:16686 (если запущен на локальной машине).

Jaeger UI предоставляет следующие ключевые возможности:

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

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

  • Статистика времени выполнения: Вы можете видеть, сколько времени заняло выполнение каждой операции в запросе и определить, где требуется оптимизация.

  • Детальные сведения: Для каждой трассировки вы можете просматривать теги, логи и другие сведения о запросе.

  1. Отладка проблем с помощью трассировки

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

  • Идентификация узких мест: Вы можете легко выявить участки кода, в которых запросы затрачивают слишком много времени. Например, если видите, что операция на базе данных занимает много времени, это может указывать на неэффективные запросы.

  • Диагностика ошибок: При возникновении ошибок или исключений, Jaeger может показать вам, где именно ошибка произошла. Вы сможете увидеть, как запрос прошел через систему, и точно определить, где возникла проблема.

  • Изучение производительности системы: Вы можете анализировать производительность вашей системы в реальном времени или в исторической перспективе, что помогает в идентификации трендов и улучшении производительности.

Пример Jaeger UI с визуализацией трассировки:

Интеграция Prometheus и Jaeger

Мониторинг и трассировка — два важных аспекта обеспечения стабильной работы и эффективной отладки распределенных систем. Переплетение этих двух аспектов позволяет разработчикам получить более полное представление о работе своего приложения.

Почему это так важно:

  1. Понимание производительности: Мониторинг с Prometheus предоставляет вам информацию о производительности системы, включая метрики, такие как скорость запросов, использование ресурсов и динамику изменения данных. Это позволяет выявлять узкие места и оптимизировать код.

  2. Отслеживание запросов: Трассировка с Jaeger дает вам возможность следить за тем, как запросы перемещаются через вашу систему. Вы можете видеть путь выполнения запросов и определить, где возникают задержки и проблемы.

  3. Диагностика ошибок: Совмещение мониторинга и трассировки упрощает выявление и решение проблем. Вы можете увидеть, какие метрики связаны с определенными трассировками, и быстро найти причины ошибок.

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

Prometheus и Jaeger — это два разных инструмента, но они могут интегрироваться вместе для обеспечения полного мониторинга и отладки ваших приложений:

  1. Сбор метрик: Prometheus отвечает за сбор и хранение метрик, таких как скорость запросов, загрузка ЦП, использование памяти и другие. Эти метрики могут предоставить вам широкий обзор состояния вашей системы.

  2. Создание трассировочных данных: В вашем Go-приложении вы можете инструментировать код с использованием OpenTracing API для создания и отправки трассировочных данных в Jaeger. Эти данные описывают путь выполнения запросов через вашу систему.

  3. Интеграция с Jaeger Collector: Jaeger может быть настроен для отправки трассировочных данных в Jaeger Collector, который агрегирует и сохраняет эти данные.

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

Примеры совместной конфигурации

Процесс интеграции Prometheus и Jaeger начинается с настройки обоих инструментов. Примеры конфигурации для обоих инструментов:

Пример конфигурации Prometheus для сбора метрик:

global:   scrape_interval: 15s  scrape_configs:   - job_name: 'my-app'     static_configs:       - targets: ['localhost:9090']  # Адрес вашего приложения

Пример интеграции Jaeger с Prometheus для сбора трассировочных данных:

agent:   collectd:     listen-addr: "localhost:8080"  collector:   zipkin:     http-port: 9411   pprof-http:     http-port: 5778

Эти примеры позволяют Prometheus собирать метрики из вашего приложения и Jaeger агрегировать трассировочные данные. Важно подстраивать конфигурацию под ваши потребности и требования.

Примеры

Пример 1: Мониторинг HTTP-сервиса с Prometheus и отладка с Jaeger

Предположим, у вас есть Go-приложение, предоставляющее HTTP-сервис. Вы хотите мониторить его производительность и отслеживать выполнение запросов.

  1. Настройка Prometheus: Создайте конфигурацию Prometheus для сбора метрик вашего HTTP-сервиса. Например, вы можете использовать promhttp пакет для экспорта метрик. Здесь вы настраиваете job_name, target, и выбираете метрики для мониторинга.

scrape_configs:   - job_name: 'my-http-service'     static_configs:       - targets: ['localhost:8080']     metrics_path: /metrics
  1. Инструментирование кода: Ваше приложение должно быть инструментировано с использованием Prometheus клиента для регистрации метрик. Например, вы можете измерять время выполнения HTTP-обработчиков и собирать информацию о запросах.

import (     "net/http"     "github.com/prometheus/client_golang/prometheus"     "github.com/prometheus/client_golang/promhttp" )  func main() {     http.Handle("/metrics", promhttp.Handler())     http.HandleFunc("/api", myHandler)     http.ListenAndServe(":8080", nil) }  func myHandler(w http.ResponseWriter, r *http.Request) {     start := time.Now()     // Обработка запроса     elapsed := time.Since(start)     httpDuration.WithLabelValues("myHandler").Observe(float64(elapsed.Nanoseconds())) }
  1. Интеграция Jaeger: Теперь вы интегрируете Jaeger, чтобы отслеживать выполнение запросов через ваш HTTP-сервис. Инструментируйте код, создавайте и отправляйте трассировочные данные.

import (     "github.com/opentracing/opentracing-go"     "github.com/uber/jaeger-client-go/config" )  func main() {     tracer, closer := initJaeger("my-service")     defer closer.Close()      http.Handle("/metrics", promhttp.Handler())     http.HandleFunc("/api", myHandler)      http.ListenAndServe(":8080", nil) }  func myHandler(w http.ResponseWriter, r *http.Request) {     span := tracer.StartSpan("myHandler")     defer span.Finish()      // Обработка запроса }
  1. Анализ и визуализация: Теперь вы можете использовать Jaeger UI для анализа трассировочных данных и Prometheus для мониторинга. Смотрите на данные вместе, чтобы выявить связь между метриками и трассировкой, и решать проблемы.

Пример 2: Мониторинг микросервисной архитектуры с Prometheus и Jaeger

Предположим, у вас есть микросервисная архитектура, и вы хотите мониторить каждый сервис и отслеживать выполнение запросов между ними.

  1. Настройка Prometheus: Создайте конфигурацию Prometheus для каждого сервиса, собирая метрики из экспортеров, таких как node_exporter и blackbox_exporter. Учтите, что каждый сервис может иметь свой собственный порт для экспорта метрик.

scrape_configs:   - job_name: 'my-service-1'     static_configs:       - targets: ['service-1:9100']  # node_exporter   - job_name: 'my-service-2'     static_configs:       - targets: ['service-2:9100']  # node_exporter
  1. Инструментирование кода: Инструментируйте код каждого сервиса с использованием Prometheus клиента для регистрации метрик. Экспортируйте метрики, связанные с выполнением запросов между сервисами.

func main() {     http.Handle("/metrics", promhttp.Handler())     http.HandleFunc("/api", myHandler)     http.ListenAndServe(":8080", nil) }  func myHandler(w http.ResponseWriter, r *http.Request) {     start := time.Now()     // Обработка запроса     elapsed := time.Since(start)     httpDuration.WithLabelValues("myHandler").Observe(float64(elapsed.Nanoseconds())) }
  1. Интеграция Jaeger: Добавьте интеграцию Jaeger для каждого сервиса, чтобы отслеживать выполнение запросов и взаимодействие между сервисами.

  2. Анализ и визуализация: Используйте Jaeger UI для анализа трассировочных данных, чтобы видеть, как запросы перемещаются между сервисами. Проанализируйте метрики Prometheus, чтобы мониторить производительность каждого сервиса и отлавливать аномалии.

Пример 3: Оповещения и автоматизация

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

route:   group_by: ['alertname']   group_wait: 30s   group_interval: 5m   repeat_interval: 3h  receivers:   - name: 'slack'     slack_configs:       - send_resolved: true         username: 'Prometheus'         channel: '#alerts'         api_url: 'https://slack.com/api/chat.postMessage'

Это позволяет автоматизировать процесс оповещения при возникновении проблем.

Заключение

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

И напоследок хочу порекомендовать бесплатный урок курса Golang Developer. Professional, на котором вы узнаете что такое кодогенерация в Go, зачем она нужна, и какие задачи может решить.


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