Мониторинг и трассировка в Go: от Prometheus до Jaeger
Привет, Хабр!
Когда приложения становятся все более сложными и распределенными, и эффективное управление ими требует глубокого понимания происходящего внутри. Мониторинг позволяет нам наблюдать за состоянием приложения, собирать ценные метрики о его работе и предоставлять нам обратную связь в реальном времени. Это помогает в обнаружении и устранении проблем до того, как они станут серьезными. Мониторинг также дает возможность планировать масштабирование и оптимизацию, основываясь на данных, а не на предположениях.
Инструменты мониторинга, такие как Prometheus, позволяют нам собирать метрики, настраивать правила оповещения и создавать графики для визуализации данных.
Prometheus — это система мониторинга с открытым исходным кодом, разработанная для наблюдения за распределенными системами. Он предоставляет инструменты для сбора и хранения временных рядов данных, а также для создания пользовательских запросов и алертинга на основе этих данных. Prometheus предлагает нативную поддержку для сбора метрик от приложений, что делает его идеальным выбором для мониторинга Go-приложений.
Трассировка, с другой стороны, позволяет нам отслеживать путь выполнения запросов и выявлять проблемы производительности и узкие места в наших сервисах. Jaeger — это инструмент для трассировки с открытым исходным кодом, который обеспечивает сбор и анализ трассировочных данных. Он позволяет нам визуализировать путь выполнения запросов и идентифицировать бутылочные горлышки в наших приложениях.
Мониторинг с Prometheus
Prometheus — это инструмент мониторинга с открытым исходным кодом, созданный для эффективного сбора и хранения временных рядов данных о состоянии системы. Он обладает модульной архитектурой, которая позволяет настраивать и расширять его функциональность под ваши потребности.
Архитектура и компоненты
Архитектура 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)
}
Метрики и экспортеры
Прометеус собирает метрики с помощью экспортеров, которые могут быть как встроенными, так и созданными вами. Например, 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-приложения
Использование 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.
Метрики собственного приложения
Представим, у нас есть веб-сервер на 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
Конфигурация правил оповещения
Алерты в 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 минуты.
Интеграция с системами оповещения
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-приложения позволяет отслеживать выполнение запросов и операций в распределенных системах.
Использование 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 и настраивает его с параметрами по умолчанию. Вы можете настроить его в соответствии с вашими потребностями, включая выбор сэмплера, хранилища и адаптеров.
Создание и отправка трассировочных данных
Теперь, когда 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
Интерфейс Jaeger UI
Jaeger предоставляет удобный веб-интерфейс, известный как Jaeger UI, для анализа трассировочных данных. Этот интерфейс позволяет вам визуализировать пути выполнения запросов, анализировать производительность и находить проблемы в вашем приложении.
Когда Jaeger запущен и интегрирован с вашим приложением, вы можете получить доступ к Jaeger UI, обычно по адресу http://localhost:16686
(если запущен на локальной машине).
Jaeger UI предоставляет следующие ключевые возможности:
Поиск трассировок: Вы можете использовать различные фильтры, такие как сервис, теги и диапазоны времени, чтобы найти трассировки, соответствующие вашим критериям.
Визуализация трассировок: Jaeger показывает дерево выполнения запроса, где каждый узел представляет собой компонент системы, через который прошел запрос.
Статистика времени выполнения: Вы можете видеть, сколько времени заняло выполнение каждой операции в запросе и определить, где требуется оптимизация.
Детальные сведения: Для каждой трассировки вы можете просматривать теги, логи и другие сведения о запросе.
Отладка проблем с помощью трассировки
Jaeger не только позволяет вам визуализировать пути выполнения запросов, но и помогает вам отлаживать проблемы в распределенных системах.
Идентификация узких мест: Вы можете легко выявить участки кода, в которых запросы затрачивают слишком много времени. Например, если видите, что операция на базе данных занимает много времени, это может указывать на неэффективные запросы.
Диагностика ошибок: При возникновении ошибок или исключений, Jaeger может показать вам, где именно ошибка произошла. Вы сможете увидеть, как запрос прошел через систему, и точно определить, где возникла проблема.
Изучение производительности системы: Вы можете анализировать производительность вашей системы в реальном времени или в исторической перспективе, что помогает в идентификации трендов и улучшении производительности.
Пример Jaeger UI с визуализацией трассировки:
Интеграция Prometheus и Jaeger
Мониторинг и трассировка — два важных аспекта обеспечения стабильной работы и эффективной отладки распределенных систем. Переплетение этих двух аспектов позволяет разработчикам получить более полное представление о работе своего приложения.
Почему это так важно:
Понимание производительности: Мониторинг с Prometheus предоставляет вам информацию о производительности системы, включая метрики, такие как скорость запросов, использование ресурсов и динамику изменения данных. Это позволяет выявлять узкие места и оптимизировать код.
Отслеживание запросов: Трассировка с Jaeger дает вам возможность следить за тем, как запросы перемещаются через вашу систему. Вы можете видеть путь выполнения запросов и определить, где возникают задержки и проблемы.
Диагностика ошибок: Совмещение мониторинга и трассировки упрощает выявление и решение проблем. Вы можете увидеть, какие метрики связаны с определенными трассировками, и быстро найти причины ошибок.
Улучшенное планирование и оптимизация: Используя данные из мониторинга и трассировки, вы можете принимать более обоснованные решения о масштабировании, оптимизации кода и улучшении производительности вашей системы.
Prometheus и Jaeger — это два разных инструмента, но они могут интегрироваться вместе для обеспечения полного мониторинга и отладки ваших приложений:
Сбор метрик: Prometheus отвечает за сбор и хранение метрик, таких как скорость запросов, загрузка ЦП, использование памяти и другие. Эти метрики могут предоставить вам широкий обзор состояния вашей системы.
Создание трассировочных данных: В вашем Go-приложении вы можете инструментировать код с использованием OpenTracing API для создания и отправки трассировочных данных в Jaeger. Эти данные описывают путь выполнения запросов через вашу систему.
Интеграция с Jaeger Collector: Jaeger может быть настроен для отправки трассировочных данных в Jaeger Collector, который агрегирует и сохраняет эти данные.
Визуализация и анализ: 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-сервис. Вы хотите мониторить его производительность и отслеживать выполнение запросов.
Настройка Prometheus: Создайте конфигурацию Prometheus для сбора метрик вашего HTTP-сервиса. Например, вы можете использовать
promhttp
пакет для экспорта метрик. Здесь вы настраиваетеjob_name
,target
, и выбираете метрики для мониторинга.
scrape_configs:
- job_name: 'my-http-service'
static_configs:
- targets: ['localhost:8080']
metrics_path: /metrics
Инструментирование кода: Ваше приложение должно быть инструментировано с использованием 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()))
}
Интеграция 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()
// Обработка запроса
}
Анализ и визуализация: Теперь вы можете использовать Jaeger UI для анализа трассировочных данных и Prometheus для мониторинга. Смотрите на данные вместе, чтобы выявить связь между метриками и трассировкой, и решать проблемы.
Пример 2: Мониторинг микросервисной архитектуры с Prometheus и Jaeger
Предположим, у вас есть микросервисная архитектура, и вы хотите мониторить каждый сервис и отслеживать выполнение запросов между ними.
Настройка 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
Инструментирование кода: Инструментируйте код каждого сервиса с использованием 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()))
}
Интеграция Jaeger: Добавьте интеграцию Jaeger для каждого сервиса, чтобы отслеживать выполнение запросов и взаимодействие между сервисами.
Анализ и визуализация: Используйте 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, зачем она нужна, и какие задачи может решить.