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

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

Opentelemetry (Otel), один из многих протоколов наблюдения, устанавливает стандарт инструментов с открытым исходным кодом и сбора данных телеметрии.

Концепции

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

Следы

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

Трассировка будет содержать идентификатор трассировки (помимо другой информации), который свяжет остальные промежутки в этом процессе вместе.

Спаны

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

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

В протоколе OTel существует 5 видов спанов. Которые:

  • Клиент: клиентский диапазон — это запрос на услугу, которая выходит за текущие границы. Это может быть метод gRPC или исходящий HTTP-запрос к API.
  • Сервер: диапазон сервера — это входящий запрос (обычно получатель из диапазона клиента), который будет обрабатываться сервером.
  • Внутренний: внутренний диапазон — это диапазон, предназначенный для использования в логике службы, такой как запрос к базе данных, рабочая процедура и т. д. Обычно это дочерний диапазон родительского диапазона (клиент, сервер и т. д.).
  • Производитель: диапазон производителя предназначен для использования в асинхронных процессах, которые будут использоваться позже. Обычно тип удаленного пролета.
  • Потребитель: Потребительский диапазон относится к следствию процесса производителя, который был обработан спустя долгое время после его создания.

Охватывать контекст

Трассировки используют распространение контекста для ввода и чтения данных между функциями, запросами и запросами к базе данных. Это похоже на ту же концепцию распространения контекста в Golang, и это будет вашим лучшим другом. Span Context — это термин, используемый для структуры данных, которая будет содержать информацию о самой трассировке. Некоторая информация, содержащаяся в контексте:

  • Идентификатор трассы представляет трассу, частью которой является диапазон.
  • Span ID
  • Флаги трассировки, двоичная кодировка, содержащая информацию о трассировке.
  • Состояние трассировки, список пар ключ-значение, которые могут нести информацию трассировки конкретного поставщика.

Распространение контекста зависит от того, какой формат мы используем для распространения. Позже в этой статье мы рассмотрим TextMapPropagators и будем использовать соглашение W3C для распространения этих трассировок.

Примечание. При использовании этих концепций в вашей программе вы столкнетесь с такими функциями, как SpanFromContext(), SpanContextFromContext() и другими. Будьте осторожны, чтобы различать контекст трассировщика/диапазона от контекста Golang, поскольку они не относятся к одному и тому же.

Отслеживание реализации

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

Ключевой функцией в диапазоне является выполнение удаленного контекста диапазона. Это означает, что диапазон в конечном итоге покинет область действия функции (асинхронные функции) или достигнет другого пункта назначения за пределами службы или домена.

gspanCtx := trace.SpanContextFromContext(ctx) 
ctx = trace.ContextWithRemoteSpanContext(ctx, spanCtx)

Распределенная трассировка

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

Инструментирование — это не что иное, как указание вашим службам/коду, как использовать и экспортировать эти показатели в соответствии с установленным вами стандартом. Обычно пакеты поставляются с автоматическим инструментарием, но вы видите лучшие результаты с пользовательскими метриками.

Мы будем использовать соглашение W3C для объяснения распределения трассировки.

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

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

  • Родительский элемент трассировки (traceparent): это одна строка, содержащая идентификатор трассировки и идентификатор родительского участка (включая два флага). Идентификатор трассы — это 16-байтовый массив, который идентифицирует корневую трассу, а идентификатор диапазона — это 8-байтовый массив, представляющий родительский диапазон.

Пример: 00–0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331–01

  • Состояние трассировки (tracestate): это набор пар ключ-значение, которые содержат информацию из других доменов, связанных с трассировкой. Например, запросы из-за пределов вашей компании.

Пример: tutorial=t61rcWkgMzE

Перехватчики и ПО промежуточного слоя

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

При использовании такого соглашения, как W3C, необходимо настроить распространители текстовых карт Tracer для его обработки.

Цель глобального распространителя — сообщить пакету otel, что трассировщики должны быть извлечены и внедрены в соответствии со стандартом Trace Context W3C.

Распространитель будет делать две вещи: вводить (исходящие запросы) и извлекать (входящие запросы) трассировку. При использовании контекста трассировки от W3C он будет использовать имена заголовков для извлечения информации трассировки, анализа, проверки и добавления информации трассировки в контекст трассировки.

В пользовательских перехватчиках вы увидите эти две функции (внедрение и извлечение), использующие контекст и носитель. Перевозчик — это любая структура, в которую вы будете получать информацию о трассировке. Для HTTP-запросов это будут заголовки, для gRPC — метаданные, заголовки событий Kafka и т. д. Задача оператора — предоставить доступ к информации трассировки внутри этой структуры. Важно быть последовательным в том, как вы отправляете и получаете трассировки в вашей архитектуре.

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