Как начать работу с OpenTelemetry Go

В этом руководстве вы научитесь создавать и визуализировать трассы с помощью OpenTelemetry Go без предварительных знаний.

Мы начнем с создания простого приложения для выполнения дел, которое использует Mongo и фреймворк Gin. Затем мы отправим данные трассировки в Jaeger Tracing и в Aspecto для визуализации. Все необходимые файлы вы можете найти в этом репозитории Github.

Что нас ожидает

  1. Введение в OpenTelemetry
  2. Здравствуй мир: Пример OpenTelemetry GO
    1. Создание файла main.go с помощью Gin и Mongo
    2. Установите OpenTelemetry GO
    3. Инструментарий Gin: gin.Context
  3. Визуализация с помощью Jaeger и Aspecto
    1. Трассировка OpenTelemetry Go и Jaeger
    2. OpenTelemetry Go и Aspecto

Введение в OpenTelemetry

OpenTelemetry — это набор API и SDK, которые позволяют нам собирать, экспортировать и генерировать трассировки, журналы и метрики (также известные как три столпа наблюдаемости).

Это проект с открытым исходным кодом, управляемый сообществом CNCF (Cloud Native Computing Foundation, организация, отвечающая за Kubernetes).

В облачно-нативной среде мы используем OpenTelemetry (сокращенно OTel) для сбора данных о системных операциях и событиях. Другими словами, для инструментария наших распределенных сервисов. Эти данные позволяют нам понимать и исследовать поведение нашего программного обеспечения, а также устранять проблемы и ошибки производительности.

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

  1. API и SDK для каждого языка программирования для генерации телеметрии
  2. OpenTelemetry Collector; получает, обрабатывает и экспортирует телеметрические данные в различные пункты назначения.
  3. Протокол 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

  1. Запустите todo-сервис с помощью go run main.go
  2. Сделайте HTTP GET запрос на localhost:8080/todo, чтобы сгенерировать несколько трасс в Go.
  3. Откройте Jaeger на http://localhost:16686/search, чтобы просмотреть эти трассировки.

Теперь вы можете увидеть пользовательский интерфейс Jaeger. Выберите todo-service и нажмите на Find traces. Вы должны увидеть трассировку справа:

Щелкнув по трассе, вы можете просмотреть более подробную информацию о ней, что позволит вам продолжить исследование самостоятельно:

Визуализация с помощью OpenTelemetry Go и Aspecto

Итак, теперь вы знаете основные концепции OpenTelemetry. Вы самостоятельно использовали его для инструментирования ваших библиотек Go, создания трасс и экспорта данных в Jaeger для визуализации.

Давайте поднимем наши возможности визуализации на новый уровень с помощью Aspecto.

Экспортировать и визуализировать наши данные в Aspecto очень просто (мы увидим это в ближайшее время). Вы можете попробовать сами, воспользовавшись планом free-forever, который не имеет ограниченных возможностей. Попробуйте эту Live Playground, чтобы получить лучшее представление.

Вот как мы это делаем:

  1. Создайте новый бесплатный аккаунт на www.aspecto.io.
  2. Установите экспортер otlp, выполнив следующие действия
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc 
Войдите в полноэкранный режим Выйдите из полноэкранного режима
  1. Создайте файл с именем aspecto.go в папке трассировки и добавьте в него код, приведенный ниже
  2. Замените {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
}
Войдите в полноэкранный режим Выйдите из полноэкранного режима
  1. Вернитесь в файл 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

  1. Запустите todo-сервис с помощью go run main.go
  2. Сделайте HTTP GET запрос на localhost:8080/todo для создания некоторых трасс
  3. Перейдите на app.aspecto.io для просмотра трасс.

Мы также отправили несколько трасс с ошибками, чтобы посмотреть, как это выглядит. Наши трассы должны выглядеть примерно так:

В представлении Trace Search, используя левую панель фильтров, просто отфильтруйте по имени сервиса (вы также можете фильтровать по ошибке, методу HTTP и многому другому).

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

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

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

Если у вас возникнут вопросы или проблемы, обращайтесь к нам в чат.

Чтобы узнать об этом больше, ознакомьтесь с нашим OpenTelemetry Bootcamp. Это серия из 6 эпизодов на YouTube, в которой OpenTelemetry рассматривается с нуля до ста, включая внедрение OpenTelemetry в производство, безопасность, отбор проб и многое другое. Полностью бесплатный и нейтральный к поставщикам.

Следуйте этим руководствам ниже, если вы хотите установить OpenTelemetry на более официально поддерживаемых языках:

  • Python
  • Java
  • Node

Оцените статью
devanswers.ru
Добавить комментарий