В этом руководстве вы научитесь создавать и визуализировать трассы с помощью OpenTelemetry Go без предварительных знаний.
Мы начнем с создания простого приложения для выполнения дел, которое использует Mongo и фреймворк Gin. Затем мы отправим данные трассировки в Jaeger Tracing и в Aspecto для визуализации. Все необходимые файлы вы можете найти в этом репозитории Github.
- Что нас ожидает
- Введение в OpenTelemetry
- Hello world: Пример OpenTelemetry Go
- Шаг 1: Создайте файл main.go для нашего приложения to-do
- Шаг 2: Установите OpenTelemetry Go
- Инструментарий Gin: gin.Context
- Визуализация с помощью Jaeger и Aspecto
- Трассировка OpenTelemetry Go и Jaeger: Экспорт трассировок в Jaeger
- Экспорт трасс в Jaeger
- Визуализация с помощью OpenTelemetry Go и Aspecto
- Экспорт трасс в Aspecto
Что нас ожидает
- Введение в OpenTelemetry
- Здравствуй мир: Пример OpenTelemetry GO
- Создание файла main.go с помощью Gin и Mongo
- Установите OpenTelemetry GO
- Инструментарий Gin: gin.Context
- Визуализация с помощью Jaeger и Aspecto
- Трассировка OpenTelemetry Go и Jaeger
- OpenTelemetry Go и Aspecto
Введение в OpenTelemetry
OpenTelemetry — это набор API и SDK, которые позволяют нам собирать, экспортировать и генерировать трассировки, журналы и метрики (также известные как три столпа наблюдаемости).
Это проект с открытым исходным кодом, управляемый сообществом CNCF (Cloud Native Computing Foundation, организация, отвечающая за Kubernetes).
В облачно-нативной среде мы используем OpenTelemetry (сокращенно OTel) для сбора данных о системных операциях и событиях. Другими словами, для инструментария наших распределенных сервисов. Эти данные позволяют нам понимать и исследовать поведение нашего программного обеспечения, а также устранять проблемы и ошибки производительности.
OpenTelemetry служит в качестве стандартной структуры наблюдаемости, которая собирает все данные в рамках единой спецификации. Она предоставляет несколько компонентов, включая:
- API и SDK для каждого языка программирования для генерации телеметрии
- OpenTelemetry Collector; получает, обрабатывает и экспортирует телеметрические данные в различные пункты назначения.
- Протокол OTLP для отправки телеметрических данных
Для более глубокого понимания этой технологии, включая ее структуру и основную мотивацию, посетите это руководство.
В этом руководстве по OpenTelemetry Golang приведены термины, которые вам необходимо знать:
-
Промежуток: Самая основная единица измерения. Промежуток представляет собой событие в нашей системе (например, HTTP-запрос или операция базы данных, которая охватывает время). Span обычно является родителем другого span, его дочерним элементом или обоими.
-
Трассировка: «стеки вызовов» для распределенных сервисов. Трассы представляют собой дерево диапазонов, связанных отношениями «ребенок/родитель». Трассы определяют последовательность выполнения запросов через различные сервисы и компоненты нашего приложения (БД, источники данных, очереди и т.д.). Например, отправка вызова API на user-service привела к запросу к БД на users-db.
-
Экспортер: После того как мы создаем span, экспортер обрабатывает отправку данных на наш бэкэнд (например, в память, Jaeger Tracing или консольный вывод).
-
Распространение контекста — механизм, позволяющий нам коррелировать события между распределенными сервисами. Контекстом называются метаданные, которые мы собираем и передаем*. Распространение* — это то, как контекст упаковывается и передается между сервисами, часто через HTTP-заголовки. Распространение контекста — это одна из областей, где OpenTelemetry сияет.
-
Инструментарий — библиотеки инструментария собирают данные и генерируют диапазоны на основе различных библиотек в наших приложениях (Kafka, Mongo, Gin и т.д.).
Есть два способа инструментировать наше приложение — вручную и автоматически:
- Автоматическое инструментирование: Автоматическое создание диапазонов на основе библиотек приложения, которые мы используем, с помощью готовых библиотек OpenTelemetry.
- Ручное инструментирование: Вручную добавлять код в ваше приложение для определения начала и конца каждого пролета и полезной нагрузки.
Чтобы понять больше жаргона OpenTelemetry, посетите официальную документацию.
Hello world: Пример OpenTelemetry Go
Мы начнем с создания нашего сервиса to-do и установки двух библиотек (Gin и Mongo), чтобы понять, как работают инструменты.
Шаг 1: Создайте файл main.go для нашего приложения to-do
1) Установите Gin и Mongo-driver
go get -u github.com/gin-gonic/gin
go get go.mongodb.org/mongo-driver/mongo
2) Настройте gin и mongo для прослушивания на «/todo».
3) Создайте несколько дел, чтобы запустить Mongo
package main
import (
"context"
"net/http"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
func main() {
connectMongo()
setupWebServer()
}
func connectMongo() {
opts := options.Client()
opts.ApplyURI("mongodb://localhost:27017")
client, _ = mongo.Connect(context.Background(), opts)
//Seed the database with todo's
docs := []interface{}{
bson.D{{"id", "1"}, {"title", "Buy groceries"}},
bson.D{{"id", "2"}, {"title", "install Aspecto.io"}},
bson.D{{"id", "3"}, {"title", "Buy dogz.io domain"}},
}
client.Database("todo").Collection("todos").InsertMany(context.Background(), docs)
}
func setupWebServer() {
r := gin.Default()
r.GET("/todo", func(c *gin.Context) {
collection := client.Database("todo").Collection("todos")
//Important: Make sure to pass c.Request.Context() as the context and not c itself - TBD
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
if findErr != nil {
c.AbortWithError(500, findErr)
return
}
results := make([]interface{}, 0)
curErr := cur.All(c, &results)
if curErr != nil {
c.AbortWithError(500, curErr)
return
}
c.JSON(http.StatusOK, results)
})
_ = r.Run(":8080")
}
Теперь, когда наше небольшое приложение todo готово, давайте познакомимся с OpenTelemetry.
Шаг 2: Установите OpenTelemetry Go
Мы будем настраивать OpenTelemetry для работы с нашим приложением Go.
1) Чтобы установить OTel SDK, запустите:
go get go.opentelemetry.io/otel /
go.opentelemetry.io/otel/sdk /
2) Инструментируйте наши библиотеки Gin и Mongo для генерации трасс.
3) Инструментирование Gin: Установите otelgin
go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin
4) Инструментарий Mongo: Установите otelmongo
go get go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo
Инструментарий Gin: gin.Context
Ранее мы обсуждали идею распространения контекста — способ передачи метаданных между распределенными сервисами для корреляции событий в нашей системе.
Фреймворк Gin имеет свой собственный тип gin.Context
, который передается в качестве параметра HTTP-обработчику. Однако контекст, который должен быть передан операциям mongo, — это стандартный объект Context библиотеки Go, доступный в gin.Context.Request.Context
.
//Make sure to pass c.Request.Context() as the context and not c itself
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
Поэтому убедитесь, что вы передаете Context в операцию mongodb. Посмотрите этот выпуск для получения дополнительной информации.
Теперь наше todo-приложение готово и оснащено инструментами. Пришло время использовать OpenTelemetry на полную мощность. Наша способность визуализировать трассы — это то место, где проявляется истинная сила этой технологии в поиске и устранении неисправностей.
Для визуализации мы будем использовать Jaeger Tracing с открытым исходным кодом и Aspecto.
Визуализация с помощью Jaeger и Aspecto
Настройка экспорта трасс в Jaeger или Aspecto относительно схожа. Следуйте за настройкой Jaeger, а затем переключитесь на Aspecto, изменив несколько строк кода.
Трассировка OpenTelemetry Go и Jaeger: Экспорт трассировок в Jaeger
Jaeger Tracing — это набор проектов с открытым исходным кодом, управляющих всем «стеком» распределенной трассировки: клиент, коллектор и пользовательский интерфейс. Jaeger UI является наиболее часто используемым открытым исходным кодом для визуализации трассировок.
Вот как выглядит настройка:
1) Установите экспортер Jaeger
go get go.opentelemetry.io/otel/exporters/jaeger
2) Создайте папку трассировки и файл jaeger.go
3) Добавьте следующий код в файл
package tracing
import (
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)
func JaegerTraceProvider() (*sdktrace.TracerProvider, error) {
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("todo-service"),
semconv.DeploymentEnvironmentKey.String("production"),
)),
)
return tp, nil
}
4) Вернитесь к файлу main.go и измените наш код, чтобы использовать только что созданную функцию JaegerTraceProvider
.
func main() {
tp, tpErr := tracing.JaegerTraceProvider()
if tpErr != nil {
log.Fatal(tpErr)
}
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
connectMongo()
setupWebServer()
}
Далее мы подключим установленные нами инструменты.
5) Добавьте инструментарий Mongo. В нашей функции connectMongo
добавьте эту строку
opts.Monitor = otelmongo.NewMonitor()
Функция должна выглядеть следующим образом
func connectMongo() {
opts := options.Client()
//Mongo OpenTelemetry instrumentation
opts.Monitor = otelmongo.NewMonitor()
opts.ApplyURI("mongodb://localhost:27017")
client, _ = mongo.Connect(context.Background(), opts)
//Seed the database with some todo's
docs := []interface{}{
bson.D{{"id", "1"}, {"title", "Buy groceries"}},
bson.D{{"id", "2"}, {"title", "install Aspecto.io"}},
bson.D{{"id", "3"}, {"title", "Buy dogz.io domain"}},
}
client.Database("todo").Collection("todos").InsertMany(context.Background(), docs)
}
Теперь добавьте инструментарий Gin.
6) Перейдите к функции startWebServer
и добавьте эту строку сразу после создания экземпляра gin
r.Use(otelgin.Middleware("todo-service"))
Функция должна выглядеть следующим образом
func startWebServer() {
r := gin.Default()
//Gin OpenTelemetry instrumentation
r.Use(otelgin.Middleware("todo-service"))
r.GET("/todo", func(c *gin.Context) {
collection := client.Database("todo").Collection("todos")
//make sure to pass c.Request.Context() as the context and not c itself
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
if findErr != nil {
c.AbortWithError(500, findErr)
return
}
results := make([]interface{}, 0)
curErr := cur.All(c, &results)
if curErr != nil {
c.AbortWithError(500, curErr)
return
}
c.JSON(http.StatusOK, results)
})
_ = r.Run(":8080")
}
Ниже приведен полный файл main.go. Теперь мы наконец готовы к экспорту в Jaeger.
package main
import (
"context"
"log"
"net/http"
"github.com/aspecto-io/opentelemerty-examples/tracing"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
"go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)
var client *mongo.Client
func main() { //Export traces to Jaeger
tp, tpErr := tracing.JaegerTraceProvider()
if tpErr != nil {
log.Fatal(tpErr)
}
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
connectMongo()
startWebServer()
}
func connectMongo() {
opts := options.Client()
//Mongo OpenTelemetry instrumentation
opts.Monitor = otelmongo.NewMonitor()
opts.ApplyURI("mongodb://localhost:27017")
client, _ = mongo.Connect(context.Background(), opts)
//Seed the database with some todo's
docs := []interface{}{
bson.D{{"id", "1"}, {"title", "Buy groceries"}},
bson.D{{"id", "2"}, {"title", "install Aspecto.io"}},
bson.D{{"id", "3"}, {"title", "Buy dogz.io domain"}},
}
client.Database("todo").Collection("todos").InsertMany(context.Background(), docs)
}
func startWebServer() {
r := gin.Default()
//gin OpenTelemetry instrumentation
r.Use(otelgin.Middleware("todo-service"))
r.GET("/todo", func(c *gin.Context) {
collection := client.Database("todo").Collection("todos")
//Make sure to pass c.Request.Context() as the context and not c itself
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
if findErr != nil {
c.AbortWithError(500, findErr)
return
}
results := make([]interface{}, 0)
curErr := cur.All(c, &results)
if curErr != nil {
c.AbortWithError(500, curErr)
return
}
c.JSON(http.StatusOK, results)
})
_ = r.Run(":8080")
}
Экспорт трасс в Jaeger
- Запустите todo-сервис с помощью go run main.go
- Сделайте HTTP GET запрос на localhost:8080/todo, чтобы сгенерировать несколько трасс в Go.
- Откройте Jaeger на http://localhost:16686/search, чтобы просмотреть эти трассировки.
Теперь вы можете увидеть пользовательский интерфейс Jaeger. Выберите todo-service и нажмите на Find traces. Вы должны увидеть трассировку справа:
Щелкнув по трассе, вы можете просмотреть более подробную информацию о ней, что позволит вам продолжить исследование самостоятельно:
Визуализация с помощью OpenTelemetry Go и Aspecto
Итак, теперь вы знаете основные концепции OpenTelemetry. Вы самостоятельно использовали его для инструментирования ваших библиотек Go, создания трасс и экспорта данных в Jaeger для визуализации.
Давайте поднимем наши возможности визуализации на новый уровень с помощью Aspecto.
Экспортировать и визуализировать наши данные в Aspecto очень просто (мы увидим это в ближайшее время). Вы можете попробовать сами, воспользовавшись планом free-forever, который не имеет ограниченных возможностей. Попробуйте эту Live Playground, чтобы получить лучшее представление.
Вот как мы это делаем:
- Создайте новый бесплатный аккаунт на www.aspecto.io.
- Установите экспортер otlp, выполнив следующие действия
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
- Создайте файл с именем aspecto.go в папке трассировки и добавьте в него код, приведенный ниже
- Замените {ASPECTO_AUTH} на уникальный идентификатор токена Aspecto — https://app.aspecto.io/app/integration/token (Settings > Integrations > Tokens).
func AspectoTraceProvider() (*sdktrace.TracerProvider, error) {
exp, err := otlptracegrpc.New(context.Background(),
otlptracegrpc.WithEndpoint("collector.aspecto.io:4317"),
otlptracegrpc.WithHeaders(map[string]string{
"Authorization": "<ADD YOUR TOKEN HERE>",
}))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("todo-service"),
semconv.DeploymentEnvironmentKey.String("production"),
)),
)
return tp, nil
}
- Вернитесь в файл main.go, замените
JaegerTraceProvider
наAspectoTraceProvider
.
Теперь главная функция должна выглядеть следующим образом
func main() { //Export traces to Aspecto
tp, tpErr := tracing.AspectoTraceProvider()
if tpErr != nil {
log.Fatal(tpErr)
}
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
connectMongo()
startWebServer()}
Экспорт трасс в Aspecto
- Запустите todo-сервис с помощью go run main.go
- Сделайте HTTP GET запрос на localhost:8080/todo для создания некоторых трасс
- Перейдите на app.aspecto.io для просмотра трасс.
Мы также отправили несколько трасс с ошибками, чтобы посмотреть, как это выглядит. Наши трассы должны выглядеть примерно так:
В представлении Trace Search, используя левую панель фильтров, просто отфильтруйте по имени сервиса (вы также можете фильтровать по ошибке, методу HTTP и многому другому).
Если мы углубимся в одну из этих трасс, мы сможем более подробно увидеть, сколько времени занял каждый запрос, и четко представить весь рабочий процесс.
В более сложных рабочих процессах трассировки будут выглядеть примерно так, как показано ниже:
Вот и все, друзья! Мы надеемся, что это руководство было информативным и простым для понимания. Все готовые к использованию файлы вы можете найти в нашем репозитории Github.
Если у вас возникнут вопросы или проблемы, обращайтесь к нам в чат.
Чтобы узнать об этом больше, ознакомьтесь с нашим OpenTelemetry Bootcamp. Это серия из 6 эпизодов на YouTube, в которой OpenTelemetry рассматривается с нуля до ста, включая внедрение OpenTelemetry в производство, безопасность, отбор проб и многое другое. Полностью бесплатный и нейтральный к поставщикам.
Следуйте этим руководствам ниже, если вы хотите установить OpenTelemetry на более официально поддерживаемых языках:
- Python
- Java
- Node