1. Три столпа Observability: концептуальное различие между метриками, логами и распределенными трассировками
Три столпа Observability: концептуальное различие между метриками, логами и распределенными трассировками
Представьте интернет-магазин в разгар ноябрьских распродаж. Загрузка процессоров на серверах держится на комфортных 40%, оперативной памяти достаточно, сетевой пинг до базы данных не превышает 5 миллисекунд. Дашборды администраторов светятся зеленым, показывая абсолютное здоровье инфраструктуры. При этом социальные сети разрываются от жалоб сотен пользователей, которые не могут оплатить корзину. Традиционный мониторинг утверждает, что система работает идеально. Реальность же такова, что бизнес теряет деньги каждую секунду из-за специфической блокировки в базе данных, возникающей только при определенной комбинации товаров в корзине.
Этот парадокс иллюстрирует фундаментальную разницу между классическим мониторингом и концепцией Observability (наблюдаемости). Мониторинг отвечает на вопрос «Работает ли система?». Observability дает ответ на вопрос «Почему она не работает так, как ожидается?».
Термин Observability пришел в IT из теории управления. В инженерном смысле система является наблюдаемой, если вы можете понять ее внутреннее состояние, анализируя исключительно ее внешние выходные данные, без необходимости внедрять новый код для отладки. Чтобы достичь такого уровня прозрачности в современных распределенных архитектурах, инженеры опираются на три фундаментальных типа данных, которые часто называют «тремя столпами наблюдаемости»: метрики, логи и распределенные трассировки.
Метрики: пульс и компас системы
Метрика — это числовое значение, измеренное в определенный момент времени. Это агрегированные данные, которые описывают поведение системы в виде временных рядов (Time-Series Data).
Структурно метрика предельно проста. Она состоит из имени, временной метки (timestamp), самого числового значения и набора пар «ключ-значение», которые называются лейблами (labels) или тегами.
Например, запись http_requests_total{method="POST", status="500", service="billing"} 42 говорит о том, что к текущему моменту сервис биллинга вернул 42 ошибки со статусом 500 при обработке POST-запросов.
Метрики обладают тремя важнейшими характеристиками:
В мире метрик существует строгий запрет, нарушение которого приводит к падению систем мониторинга — это проблема высокой кардинальности (High Cardinality). Кардинальность — это количество уникальных комбинаций лейблов у одной метрики.
Если вы добавите в метрику лейбл http_method (GET, POST, PUT, DELETE), кардинальность увеличится в 4 раза. Это нормально. Но если неопытный разработчик решит добавить в метрику лейбл user_id, чтобы знать, сколько запросов сделал каждый клиент, система рухнет. Если у вас миллион пользователей, база данных метрик (например, Prometheus) попытается создать миллион независимых временных рядов для одной метрики. Метрики предназначены для агрегированного взгляда на систему, а не для отслеживания индивидуальных сущностей.
Логи: детализированный бортовой самописец
Если метрики показывают, что в системе произошел всплеск ошибок, они редко могут сказать, в чем именно заключалась ошибка. Для получения контекста нужны логи.
Лог — это неизменяемая запись о дискретном событии, произошедшем в системе. В отличие от метрик, логи не агрегируются при создании. Каждое событие (ошибка аутентификации, старт фонового процесса, таймаут соединения) порождает отдельную строку.
Исторически логи представляли собой простой неструктурированный текст, предназначенный для чтения человеком:
2023-10-27 15:42:01 [ERROR] Failed to connect to database 10.0.0.5: Connection timed out after 3000ms.
Такой формат удобен, если вы читаете файл глазами. Но в масштабах Enterprise-архитектуры, где тысячи контейнеров генерируют терабайты логов ежедневно, поиск по простому тексту становится катастрофически медленным. Поэтому современный стандарт Observability требует использования структурированных логов, чаще всего в формате JSON:
Структурированный лог позволяет системам агрегации мгновенно фильтровать события. Вы можете легко выполнить запрос: «покажи все логи уровня error для сервиса billing_api, где таймаут был больше 2000 мс». Заметьте, что здесь мы свободно используем user_id — для логов высокая кардинальность не является проблемой, так как они хранятся как отдельные документы, а не как временные ряды.
Главный недостаток логов — их стоимость. Сохранение, индексация и хранение каждого чиха системы требует огромных вычислительных ресурсов и дискового пространства. Поэтому в зрелых архитектурах логи часто делят на уровни (INFO, WARN, ERROR) и в штатном режиме отправляют в централизованное хранилище только предупреждения и ошибки, оставляя детальные отладочные сообщения (DEBUG) только для локальной разработки или временно включая их при расследовании инцидентов.
Распределенные трассировки: GPS для микросервисов
В эпоху монолитных приложений метрик и логов было достаточно. Если запрос выполнялся медленно, вы открывали лог монолита и видели весь путь запроса сверху вниз. С переходом на микросервисную архитектуру ситуация усложнилась: один клик пользователя в браузере может породить цепочку из десятков HTTP-вызовов и обращений к базам данных между разными сервисами, написанными на разных языках и развернутыми на разных серверах.
Распределенная трассировка (Distributed Tracing) решает проблему потери контекста между сервисами. Она позволяет визуализировать весь жизненный цикл одного запроса от момента входа в систему до возврата ответа пользователю.
!Жизненный цикл запроса в распределенной трассировке
В основе трассировки лежат два ключевых понятия:
Магия трассировки заключается в механизме передачи контекста (Context Propagation). Когда Frontend-сервис принимает запрос от пользователя, он генерирует уникальный Trace ID (например, a1b2c3d4). Когда Frontend делает HTTP-запрос к API-сервису, он внедряет этот Trace ID в специальные HTTP-заголовки (часто используются стандарты W3C Trace Context или B3). API-сервис читает этот заголовок, понимает, что он является частью существующего трейса, выполняет свою работу (создавая новые спаны) и передает тот же Trace ID дальше по цепочке — в базу данных или сервис биллинга.
Трассировки незаменимы для поиска «бутылочных горлышек» (bottlenecks) производительности. Вы можете увидеть, что общий ответ занял 5 секунд не потому, что база данных тормозит, а потому, что один из микросервисов в середине цепочки делал 50 последовательных быстрых запросов вместо одного пакетного (проблема N+1).
Однако внедрение трассировок — самый сложный технический шаг. Он требует модификации кода приложений или использования интеллектуальных агентов (например, OpenTelemetry), которые перехватывают сетевые вызовы. Кроме того, сохранять 100% трейсов в высоконагруженных системах нецелесообразно. Поэтому применяется семплирование (Sampling) — система случайным образом или по заданным правилам сохраняет, например, только 1% от всех успешных запросов, но при этом 100% запросов, завершившихся ошибкой.
Синергия трех столпов: расследование инцидента
Метрики, логи и трассировки не заменяют, а дополняют друг друга. Попытка построить Observability только на одном из столпов неизбежно приведет к слепым зонам. Мощь современного стека мониторинга раскрывается, когда эти данные связаны между собой.
!Схема перехода между метриками, трейсами и логами при инциденте
Рассмотрим классический сценарий расследования инцидента в зрелой гибридной архитектуре:
http_request_duration_seconds для эндпоинта /checkout показывает, что 99-й перцентиль времени ответа вырос с 200 мс до 4 секунд. Метрики невероятно быстры, поэтому дежурный инженер получает уведомление в Telegram уже через минуту после начала деградации. Метрика сказала: «У нас проблема с производительностью чекаута»./checkout за последние 5 минут. Открыв визуализацию трейса, он видит каскад вызовов. Frontend отработал быстро, API Gateway тоже, а вот спан вызова сервиса Inventory (складские остатки) занял 3.8 секунды. Внутри этого спана видно, что проблема не в сети, а в конкретном SQL-запросе к базе данных PostgreSQL. Трейс сказал: «Проблема находится в сервисе Inventory при выполнении запроса UPDATE».Trace ID из проблемного спана и переходит в систему агрегации логов (например, Loki). Он делает поиск по этому Trace ID и мгновенно получает ровно те строки логов, которые были сгенерированы сервисом Inventory именно во время этого конкретного зависшего запроса. В логах он видит сообщение: [ERROR] Transaction deadlock detected: waiting for lock on table 'inventory_items' held by process 4829. Лог сказал: «Причина — взаимная блокировка транзакций в базе данных».Без метрик мы бы узнали о проблеме только от разъяренных пользователей. Без трассировок мы бы потратили часы, пытаясь угадать, какой именно из 50 микросервисов тормозит процесс чекаута. Без логов мы бы знали, что тормозит база данных сервиса Inventory, но не понимали бы физическую причину (дедлок, нехватка соединений или упавший диск).
Именно поэтому современный стек мониторинга строится как гибридная экосистема. Инструменты вроде Zabbix могут отлично справляться с метриками инфраструктуры (железо, сеть), Prometheus становится стандартом де-факто для сбора динамических метрик с микросервисов, а Loki обеспечивает экономичную агрегацию логов. Связывание этих сущностей через единые идентификаторы (Trace ID) и общие дашборды (Grafana) превращает разрозненные данные в настоящую наблюдаемость, позволяя инженерам видеть систему насквозь.