ADG Оставить заявку
Блог DevOps 4 мин чтения

OpenTelemetry: слияние OpenTracing и OpenCensus и что это значит для наших микросервисов

Анонс OpenTelemetry заставил нас пересмотреть самописные метки трассировки и начать переход на семантические конвенции нового стандарта в managed-проектах.

Контекст момента

Слияние OpenTracing и OpenCensus в единый проект OpenTelemetry - анонс в феврале 2019 под эгидой CNCF

В феврале пришла новость, которую в сообществе давно ждали и одновременно не верили: OpenTracing и OpenCensus объявили о слиянии в единый проект - OpenTelemetry под эгидой CNCF. Два конкурирующих стандарта инструментирования, которые несколько лет существовали параллельно и создавали головную боль всем, кто хотел просто писать трассировки не думая о деталях - решили объединиться.

Для нас это был хороший повод наконец-то честно посмотреть на то, что происходит с трассировкой в клиентских микросервисах.

Что было до анонса

Ситуация до слияния была, мягко говоря, веселой. У нас в нескольких проектах сложился зоопарк: в одном сервисе разработчики взяли OpenTracing с Jaeger, в другом - OpenCensus потому что там уже был Stackdriver, в третьем вообще самописный middleware который добавлял в заголовки correlation ID по какой-то своей логике. Когда запрос проходил через все три - сквозная трассировка превращалась в детективное расследование.

Spans назывались как придётся. В одном сервисе операция называлась db_query, в другом database.query, в третьем postgres_select. Grafana и Jaeger показывали что-то, но агрегировать это по имени операции было невозможно - никакой согласованности. Семантических конвенций никто не придерживался, потому что до анонса OpenTelemetry они были у каждого проекта свои и спорить было не о чем.

Что даёт слияние

OpenTelemetry - это не просто «давайте возьмём лучшее от обоих». Это новая спецификация с нуля: API, SDK, протокол (OTLP), и - самое важное для нас - семантические конвенции. Именно конвенции нас зацепили больше всего.

Семантические конвенции - это договоренность о том, как называть атрибуты и операции. db.system, db.statement, http.method, http.status_code - не случайные строки, а зафиксированные имена с определёнными правилами. Если все сервисы используют одни конвенции, можно писать запросы в Jaeger или любой другой backend по имени атрибута и получать осмысленный результат.

Пример того, как менялся span в одном из сервисов при рефакторинге:

# было - самописное
span.SetTag("query_type", "select")
span.SetTag("table", "orders")

# стало - по семантическим конвенциям OpenTelemetry
span.SetAttribute("db.system", "postgresql")
span.SetAttribute("db.operation", "SELECT")
span.SetAttribute("db.sql.table", "orders")

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

Что стали делать

После анонса взяли один из managed-проектов с несколькими Python-сервисами и начали методично проходить по инструментированию. Задача была не переписать всё сразу, а понять масштаб расхождений и зафиксировать что именно нужно привести в порядок.

Аудит существующих тегов. Прошлись по коду и выписали все SetTag/SetAttribute вызовы. Получился список примерно из трёх десятков уникальных имён тегов на пять сервисов. Треть из них дублировали одно и то же с разными именами.

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

Ручная замена в одном сервисе. Выбрали наименее критичный сервис (внутренний cron для агрегации данных) и заменили все теги на конвенционные. Никакого авто-инструментирования - только явные вызовы в коде. Заняло несколько часов, но зато видно где инструментирование было пропущено совсем.

Проблем при замене почти не было. Единственное место где споткнулись - атрибут error. В OpenTracing это булев тег, в черновике OpenTelemetry это атрибут error.type со строковым значением (тип исключения или описание). Несколько мест в коде устанавливали span.SetTag("error", true) - пришлось разобраться что именно там падало и поставить осмысленную строку.

Что отложили в сторону

OpenTelemetry как проект ещё очень сырой. SDK для разных языков - в разной степени готовности. OTLP-коллектор существует как ранний прототип. Миграция с OpenTracing на новые SDK - это отдельная задача, которую откладываем до тех пор, пока проект не устаканится. Сейчас разумнее начать с конвенций в существующем коде, не трогая transport layer.

Jaeger у нас и остаётся бэкендом - он умеет принимать данные по OpenTracing-протоколу, и менять это прямо сейчас не нужно. Когда OpenTelemetry-коллектор и Jaeger-экспортер из него дойдут до production-ready состояния - посмотрим на миграцию.

Где это живёт

В managed-проектах трассировка - это не только отладка, это ещё и аргумент перед заказчиком когда что-то медленно работает. «Смотрите, вот span на 400 мс - это ваш запрос к базе без индекса» работает лучше чем любые объяснения на словах. Поэтому стандартизировать инструментирование хочется не из любви к порядку, а потому что с согласованными именами гораздо проще строить алерты и дашборды.

Пока переход на конвенции OpenTelemetry идёт медленно - по одному сервису, в промежутках между основными задачами. Но теперь хотя бы понятно куда целиться.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.