---
title: "Сквозное трассирование с использованием OpenTelemetry - API7.ai"
description: "Эта статья описывает, как начать свой путь в области observability и использовать сквозное трассирование с помощью OpenTelemetry."
url: "https://api7.ai/ru/blog/end-to-end-tracing-opentelemetry"
---

[Blog](https://api7.ai/ru/blog)

[Products](https://api7.ai/ru/category/products)[Technology](https://api7.ai/ru/category/technology)[Ecosystem](https://api7.ai/ru/category/ecosystem)[Case Study](https://api7.ai/ru/category/usercase)[Team & Community](https://api7.ai/ru/category/community)

Search

⌘ + K

[Blog](https://api7.ai/blog)

Ecosystem

# Сквозное трассирование с использованием OpenTelemetry

[![Nicolas Fränkel](https://avatars.githubusercontent.com/u/752258?v=4)](https://github.com/nfrankel)

[Nicolas Fränkel](https://github.com/nfrankel)

August 31, 2022

[Ecosystem](https://api7.ai/category/ecosystem)

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

Однако мониторинг — это только начало пути. Когда запросы начинают массово завершаться с ошибками, вам нужно агрегированное представление по всем компонентам. Это называется **трассировкой**, и это один из столпов *наблюдаемости*; два других — это метрики и логи.

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

## Спецификация W3C Trace Context

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

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

![известный комикс XKCD](https://static.api7.ai/2022/10/03/633a3d1d1acbc.png?imageMogr2/format/webp)

Однако иногда случается чудо: рынок принимает новую спецификацию. В данном случае Trace Context — это спецификация W3C, и, похоже, она справилась с задачей:

> Эта спецификация определяет стандартные HTTP-заголовки и формат значений для передачи контекстной информации, которая позволяет реализовать сценарии распределенной трассировки. Спецификация стандартизирует, как контекстная информация отправляется и изменяется между сервисами. Контекстная информация уникально идентифицирует отдельные запросы в распределенной системе, а также определяет способ добавления и передачи контекстной информации, специфичной для поставщика. -- <https://www.w3.org/TR/trace-context/>

Из документа вытекают два ключевых понятия:

- **Трассировка** следует за путем запроса, который проходит через несколько компонентов.
- **Спан** (span) связан с одним компонентом и связан с другим спаном через отношение родитель-потомок.

!\[Трассировки и спаны]\(<https://static-site.apiseven.com/wp-content/uploads/2022/08/trace-spans-1024x393.png> "Трассировки и спаны" "Трассировки и спаны")

На момент написания этой статьи спецификация является рекомендацией W3C, что является **финальной** стадией.

Trace Context уже имеет [множество реализаций](https://github.com/w3c/trace-context/blob/main/implementations.md). Одна из них — OpenTelemetry.

## OpenTelemetry как золотой стандарт

Чем ближе вы к операционной части IT, тем выше вероятность, что вы слышали об OpenTelemetry:

> OpenTelemetry — это набор инструментов, API и SDK. Используйте его для инструментирования, генерации, сбора и экспорта данных телеметрии (метрик, логов и трассировок), чтобы помочь вам анализировать производительность и поведение вашего программного обеспечения. OpenTelemetry доступен для нескольких языков и готов к использованию. -- <https://opentelemetry.io/>

OpenTelemetry — это проект, управляемый CNCF. До OpenTelemetry существовали два проекта:

- OpenTracing, ориентированный на трассировки, как следует из названия.
- OpenCensus, целью которого было управление метриками и трассировками.

Оба проекта объединились и добавили логи. Теперь OpenTelemetry предлагает набор "слоев", ориентированных на наблюдаемость:

- API для инструментирования на различных языках.
- Канонические реализации, также на разных языках.
- Инфраструктурные компоненты, такие как коллекторы.
- Форматы взаимодействия, такие как Trace Context от W3C.

Обратите внимание, что хотя OpenTelemetry является реализацией Trace Context, он делает больше. Trace Context ограничивается HTTP, в то время как OpenTelemetry позволяет спанам пересекать не веб-компоненты, такие как Kafka. Это выходит за рамки данного блога.

## Пример использования

Мой любимый пример — это интернет-магазин, так что давайте оставим его. В данном случае магазин построен на микросервисах, каждый из которых доступен через REST API и защищен API Gateway. Чтобы упростить архитектуру для блога, я буду использовать только два микросервиса: `catalog` управляет продуктами, а `pricing` обрабатывает цены на продукты.

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

![Пример потока запросов через несколько компонентов](https://static.api7.ai/2022/10/03/633a3d1d29f7e.png?imageMogr2/format/webp)

Чтобы сделать вещи более интересными, `catalog` — это Spring Boot приложение, написанное на Kotlin, а `pricing` — это приложение на Python Flask.

Трассировка должна позволить нам отслеживать путь запроса через шлюз, оба микросервиса и, если возможно, базы данных.

## Трассировка на уровне шлюза

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

> Apache APISIX предоставляет богатые возможности управления трафиком, такие как балансировка нагрузки, динамический апстрим, канареечные релизы, разрыв цепи, аутентификация, наблюдаемость и т.д. -- <https://apisix.apache.org/>

Apache APISIX основан на архитектуре плагинов и предлагает плагин OpenTelemetry:

> Плагин `opentelemetry` может использоваться для отправки данных трассировки в соответствии со спецификацией OpenTelemetry. Плагин поддерживает только бинарно-кодированный OLTP через HTTP. -- <https://apisix.apache.org/docs/apisix/plugins/opentelemetry/>

Давайте настроим плагин `opentelemetry`:

```
apisix:
  enable_admin: false #1
  config_center: yaml #1
plugins:
  - opentelemetry #2
plugin_attr:
  opentelemetry:
    resource:
      service.name: APISIX #3
    collector:
      address: jaeger:4318 #4
```

1. Запустите Apache APISIX в автономном режиме, чтобы упростить демонстрацию. В любом случае, это хорошая практика в продакшене.
2. Настройте `opentelemetry` как глобальный плагин.
3. Установите имя сервиса. Это имя будет отображаться в компоненте отображения трассировок.
4. Отправляйте трассировки в сервис `jaeger`. Следующий раздел опишет его.

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

```
global_rules:
  - id: 1
    plugins:
      opentelemetry:
        sampler:
          name: always_on #1
```

1. Трассировка влияет на производительность. Чем больше мы трассируем, тем больше влияем. Следовательно, мы должны тщательно балансировать влияние на производительность и преимущества наблюдаемости. Однако для демо мы хотим трассировать каждый запрос.

## Сбор, хранение и отображение трассировок

Хотя Trace Context — это спецификация W3C, а OpenTelemetry — это *де-факто* стандарт, на рынке существует множество решений для сбора, хранения и отображения трассировок. Каждое решение может предоставлять все три возможности или только часть из них. Например, стек Elastic обрабатывает хранение и отображение, но для сбора вам нужно полагаться на что-то другое. С другой стороны, [Jaeger](https://www.jaegertracing.io/docs/1.18/opentelemetry/) и [Zipkin](https://zipkin.io/) предоставляют полный набор для выполнения всех трех возможностей.

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

В рамках этого блога конкретное решение не имеет значения, так как нам нужны только возможности. Я выбрал Jaeger, потому что он предоставляет все-в-одном Docker-образ: каждая возможность имеет свой компонент, но они все встроены в один образ, что значительно упрощает настройку.

[Соответствующие порты](https://www.jaegertracing.io/docs/1.37/getting-started/#all-in-one) образа следующие:

| Порт    | Протокол | Компонент | Функция                                                      |
| ------- | -------- | --------- | ------------------------------------------------------------ |
| `16686` | HTTP     | query     | предоставление интерфейса                                    |
| `4317`  | HTTP     | collector | прием OpenTelemetry Protocol (OTLP) через gRPC, если включен |
| `4318`  | HTTP     | collector | прием OpenTelemetry Protocol (OTLP) через HTTP, если включен |

Часть Docker Compose выглядит так:

```
services:
  jaeger:
    image: jaegertracing/all-in-one:1.37 #1
    environment:
      - COLLECTOR_OTLP_ENABLED=true #2
    ports:
      - "16686:16686" #3
```

1. Используйте образ `all-in-one`.
2. Очень важно: включите сборщик в формате OpenTelemetry.
3. Откройте порт для интерфейса.

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

## Трассировка в приложениях Flask

Сервис `pricing` — это простое приложение [Flask](https://flask.palletsprojects.com/). Оно предоставляет единственный эндпоинт для получения цены одного продукта из базы данных.

```
@app.route('/price/<product_str>')                           #1-2
def price(product_str: str) -> Dict[str, object]:
    product_id = int(product_str)
    price: Price = Price.query.get(product_id)               #3
    if price is None:
        return jsonify({'error': 'Product not found'}), 404
    else:
        low: float = price.value - price.jitter              #4
        high: float = price.value + price.jitter             #4
        return {
            'product_id': product_id,
            'price': round(uniform(low, high), 2)            #4
        }
```

1. Эндпоинт.
2. Маршрут требует идентификатора продукта.
3. Получение данных из базы данных с использованием SQLAlchemy.
4. Настоящие системы ценообразования никогда не возвращают одну и ту же цену со временем; давайте немного рандомизируем цену для интереса.

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

Теперь пришло время инструментировать приложение. Доступны два варианта: автоматическое инструментирование и ручное инструментирование. Автоматическое — это низкие усилия и быстрый результат; ручное требует времени на разработку. Я бы рекомендовал начать с автоматического и добавлять ручное только при необходимости.

Нам нужно добавить несколько пакетов Python:

```
opentelemetry-distro[otlp]==0.33b0
opentelemetry-instrumentation
opentelemetry-instrumentation-flask
```

Нам нужно настроить несколько параметров:

```
pricing:
  build: ./pricing
  environment:
    OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1
    OTEL_RESOURCE_ATTRIBUTES: service.name=pricing #2
    OTEL_METRICS_EXPORTER: none #3
    OTEL_LOGS_EXPORTER: none #3
```

1. Отправляйте трассировки в Jaeger.
2. Установите имя сервиса. Это имя будет отображаться в компоненте отображения трассировок.
3. Нас не интересуют ни логи, ни метрики.

Теперь вместо использования стандартной команды `flask run` мы оборачиваем ее:

```
opentelemetry-instrument flask run
```

Только с этим мы уже собираем спаны из вызовов методов и маршрутов Flask.

При необходимости мы можем вручную добавить дополнительные спаны, например:

```
from opentelemetry import trace

@app.route('/price/<product_str>')
def price(product_str: str) -> Dict[str, object]:
    product_id = int(product_str)
    with tracer.start_as_current_span("SELECT * FROM PRICE WHERE ID=:id", attributes={":id": product_id}) as span: #1
        price: Price = Price.query.get(product_id)
    # ...
```

1. Добавьте дополнительный спан с настроенной меткой и атрибутом.

## Трассировка в приложениях Spring Boot

Сервис `catalog` — это реактивное приложение [Spring Boot](https://spring.io/projects/spring-boot), разработанное на Kotlin. Оно предоставляет два эндпоинта:

- Один для получения одного продукта.
- Другой для получения всех продуктов.

Оба сначала ищут в базе данных продуктов, затем запрашивают вышеупомянутый сервис `pricing` для получения цены.

Как и в случае с Python, мы можем использовать автоматическое и ручное инструментирование. Начнем с низко висящих фруктов — автоматического инструментирования. На JVM мы достигаем этого через агент:

```
java -javaagent:opentelemetry-javaagent.jar -jar catalog.jar
```

Как и в Python, это создает спаны для каждого вызова метода и точки входа HTTP. Это также инструментирует вызовы JDBC, но у нас реактивный стек, и мы используем R2DBC. Для справки, [GitHub issue](https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/2515) открыт для добавления поддержки.

Нам нужно настроить поведение по умолчанию:

```
catalog:
  build: ./catalog
  environment:
    APP_PRICING_ENDPOINT: http://pricing:5000/price
    OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1
    OTEL_RESOURCE_ATTRIBUTES: service.name=orders #2
    OTEL_METRICS_EXPORTER: none #3
    OTEL_LOGS_EXPORTER: none #3
```

1. Отправляйте трассировки в Jaeger.
2. Установите имя сервиса. Это имя будет отображаться в компоненте отображения трассировок.
3. Нас не интересуют ни логи, ни метрики.

Как и в Python, мы можем улучшить ситуацию, добавив ручное инструментирование. Доступны два варианта: программное и на основе аннотаций. Первое немного сложнее, если мы не вводим Spring Cloud Sleuth. Давайте добавим аннотации.

Нам нужна дополнительная зависимость:

```
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-instrumentation-annotations</artifactId>
    <version>1.17.0-alpha</version>
</dependency>
```

Будьте осторожны, артефакт был недавно перемещен из `io.opentelemetry:opentelemetry-extension-annotations`.

Теперь мы можем аннотировать наш код:

```
@WithSpan("ProductHandler.fetch")                                               //1
suspend fun fetch(@SpanAttribute("id") id: Long): Result<Product> {             //2
    val product = repository.findById(id)
    return if (product == null) Result.failure(IllegalArgumentException("Product $id not found"))
    else Result.success(product)
}
```

1. Добавьте дополнительный спан с настроенной меткой.
2. Используйте параметр как атрибут, с ключом `id` и значением параметра во время выполнения.

## Результат

Теперь мы можем поиграть с нашей простой демо, чтобы увидеть результат:

```
curl localhost:9080/products
curl localhost:9080/products/1
```

Ответы не интересны, но давайте посмотрим на интерфейс Jaeger. Мы находим обе трассировки, по одной на вызов:

![Список трассировок в интерфейсе Jaeger](https://static.api7.ai/2022/10/03/633a3b371df19.jpg?imageMogr2/format/webp)

Мы можем углубиться в спаны одной трассировки:

![Все спаны, составляющие одну трассировку](https://static.api7.ai/2022/10/03/633a3d1cac655.jpg?imageMogr2/format/webp)

Обратите внимание, что мы можем вывести последовательность потока без вышеупомянутой UML-диаграммы. Еще лучше, последовательность отображает вызовы внутри компонента.

Каждый спан содержит атрибуты, которые добавило автоматическое инструментирование, и те, которые мы добавили вручную:

![Атрибуты спана](https://static.api7.ai/uploads/2023/08/02/c9716Cc1_span-attributes.jpg?imageMogr2/format/webp)

## Заключение

В этом посте я продемонстрировал трассировку, отслеживая запрос через API Gateway, два приложения на разных технологических стеках и их соответствующие базы данных. Я лишь поверхностно коснулся трассировки: в реальном мире трассировка, вероятно, будет включать компоненты, не связанные с HTTP, такие как Kafka и очереди сообщений.

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

Полный исходный код для этого поста можно найти на [GitHub](https://github.com/nfrankel/opentelemetry-tracing).

**Для дальнейшего изучения:**

- [Спецификация W3C Trace Context](https://www.w3.org/TR/trace-context/)
- [Реализации Trace Context](https://github.com/w3c/trace-context/blob/main/implementations.md)
- [Руководство для начинающих по OpenTelemetry](https://medium.com/@magstherdev/opentelemetry-d71d369c83d7)
- [Автоматическое инструментирование Python](https://opentelemetry.io/docs/instrumentation/python/automatic/)
- [Python Distro](https://opentelemetry.io/docs/instrumentation/python/distro/)
- [Начало работы с Jaeger](https://www.jaegertracing.io/docs/next-release/getting-started/)

Tags:

[OpenTelemetry](https://api7.ai/tags/opentelemetry)

[API Gateway Concept](https://api7.ai/tags/api-gateway-concept)

Share article link

Share

Related Posts

[![Как интегрировать API Gateway и Consul?](https://static.api7.ai/apisix-webp/2022/blog/0818/ecosystem/HashiCorp%20Consul.webp)](https://api7.ai/blog/integrate-consul-kv-with-apache-apisix)

## [Как интегрировать API Gateway и Consul?](https://api7.ai/blog/integrate-consul-kv-with-apache-apisix)

[![Как спроектировать API Gateway для обеспечения высокой доступности (HA)?](https://static.api7.ai/uploads/2025/03/19/2olPvCDj_api-gateway-guide-13.webp)](https://api7.ai/learning-center/api-gateway-guide/api-gateway-high-availability)

## [Как спроектировать API Gateway для обеспечения высокой доступности (HA)?](https://api7.ai/learning-center/api-gateway-guide/api-gateway-high-availability)

Table of Contents

- [Спецификация W3C Trace Context](#%D1%81%D0%BF%D0%B5%D1%86%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F-w3c-trace-context)
- [OpenTelemetry как золотой стандарт](#opentelemetry-%D0%BA%D0%B0%D0%BA-%D0%B7%D0%BE%D0%BB%D0%BE%D1%82%D0%BE%D0%B9-%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82)
- [Пример использования](#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
- [Трассировка на уровне шлюза](#%D1%82%D1%80%D0%B0%D1%81%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0-%D0%BD%D0%B0-%D1%83%D1%80%D0%BE%D0%B2%D0%BD%D0%B5-%D1%88%D0%BB%D1%8E%D0%B7%D0%B0)
- [Сбор, хранение и отображение трассировок](#%D1%81%D0%B1%D0%BE%D1%80-%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B8-%D0%BE%D1%82%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-%D1%82%D1%80%D0%B0%D1%81%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%BE%D0%BA)
- [Трассировка в приложениях Flask](#%D1%82%D1%80%D0%B0%D1%81%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B2-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F%D1%85-flask)
- [Трассировка в приложениях Spring Boot](#%D1%82%D1%80%D0%B0%D1%81%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B2-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F%D1%85-spring-boot)
- [Результат](#%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82)
- [Заключение](#%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)

## Related Posts

[![Использование Amazon для экосистемы и роста Apache APISIX](https://static.api7.ai/uploads/2023/01/12/I6dWwBSn_aws.png)](https://api7.ai/ru/blog/leverage-amazon-for-apisix-ecosystem-and-growth)

Ecosystem

October 26, 2022

#### [Использование Amazon для экосистемы и роста Apache APISIX](https://api7.ai/ru/blog/leverage-amazon-for-apisix-ecosystem-and-growth)

Эта статья знакомит с Apache APISIX и его плагинами для AWS, рассказывает о том, как APISIX работает с процессорами AWS, и о том, как API7 Cloud использует Amazon для достижения быстрого роста.

[APISIX Basics](https://api7.ai/tags/apisix-basics)

[API Gateway Concept](https://api7.ai/tags/api-gateway-concept)

[API7 Cloud](https://api7.ai/tags/api7-cloud)

[![Что такое gRPC? Как работать с APISIX?](https://static.api7.ai/uploads/2023/01/12/uMWcrTdz_grpc.png)](https://api7.ai/ru/blog/what-is-grpc-and-how-to-work-with-apisix)

Ecosystem

September 28, 2022

#### [Что такое gRPC? Как работать с APISIX?](https://api7.ai/ru/blog/what-is-grpc-and-how-to-work-with-apisix)

gRPC — это современный высокопроизводительный фреймворк для удаленного вызова процедур (RPC) с открытым исходным кодом, который может работать в любой среде. Мы реализуем клиент gRPC в экосистеме OpenResty.

[APISIX Basics](https://api7.ai/tags/apisix-basics)

[API Gateway Concept](https://api7.ai/tags/api-gateway-concept)

[gRPC](https://api7.ai/tags/grpc)

[![Сравнение производительности серверов на базе ARM в GCP, AWS и Azure](https://static.api7.ai/2022/11/18/6377328aa91b2.png)](https://api7.ai/ru/blog/arm-performance-google-aws-azure-with-apisix)

Ecosystem

August 12, 2022

#### [Сравнение производительности серверов на базе ARM в GCP, AWS и Azure](https://api7.ai/ru/blog/arm-performance-google-aws-azure-with-apisix)

В этой статье используется Apache APISIX для сравнения производительности серверов на базе ARM от Google, AWS и Azure в сценариях с интенсивной нагрузкой на сетевой ввод-вывод.

[API Gateway Concept](https://api7.ai/tags/api-gateway-concept)

[Comparison to Competitors](https://api7.ai/tags/comparison-to-competitors)
