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

Продакшен отслеживает каждая инженерная команда. Немногие способны ответить на вопрос «почему система ведёт себя именно так» без долгого разбирательства. Разница между мониторингом и наблюдаемостью — не выбор инструментов, а философия устройства, от которой зависит, насколько быстро команда понимает и устраняет сбои, которых никто не предвидел.

Классический мониторинг исходит из того, что вы знаете, за чем следить. Вы задаёте пороги по процессору, проверяете коды ответов и будите дежурного, когда диск заполнен на 90%. С известными сбоями это работает. Но современные распределённые системы ломаются так, как не предусмотрел ни один экран: лёгкое ухудшение задержки в одном сервисе подпирает очередь, что проявляется как исчерпание пула соединений к базе и всплывает только на пике нагрузки. Без возможности задавать произвольные вопросы о внутреннем состоянии вы летите вслепую.

Почему наблюдаемость шире обычного мониторинга

Наблюдаемость — свойство системы, позволяющее вывести её внутреннее состояние из внешних проявлений. Мониторинг сообщает, что что-то не так; наблюдаемость даёт разобраться, что именно пошло не так, даже если этот сбой вы никогда не предсказывали. Различие принципиально: мониторинг ведом оповещениями и привязан к экранам, наблюдаемость ведома вопросами и богата данными.

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

«Мониторинг говорит, работает ли система. Наблюдаемость позволяет спросить, почему она не работает. Второе — необходимое условие эксплуатации систем, которые вы понимаете не полностью, то есть любых систем в продакшене.»

Три опоры: журналы, метрики и трассировки

Мир наблюдаемости держится на трёх взаимодополняющих видах данных, у каждого своя роль. Относиться к ним как к единому сигналу, а не как к отдельным хранилищам, — вот ключ к успешному разбору.

Журналы: неизменные записи о событиях

Журналы — это записи об отдельных событиях с отметкой времени. Это самый подробный сигнал: он фиксирует, что именно произошло в конкретный момент. Хорошо устроенная строка несёт достаточно контекста — идентификатор запроса, имя сервиса, длительность, подробности ошибки, — чтобы восстановить путь выполнения, не сводя вместе несколько источников.

Самая частая ошибка — считать журналы неструктурированным текстом. Поиск по плоским файлам работал, когда серверов было три. В масштабе неструктурированные журналы — шум. Каждая строка должна разбираться машинно, нести структурированные метаданные и следовать единой схеме во всех сервисах вашей архитектуры.

Метрики: сводные измерения во времени

Метрики — числовые представления состояния системы, снимаемые через промежутки. Они рассчитаны на экономное хранение и быстрое сведение. Они отвечают на вопросы вида «сколько запросов в секунду» и «какова задержка на 99-м процентиле». В отличие от журналов, метрики отбрасывают данные отдельных запросов: они меняют подробность на сжатие и скорость.

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

Трассировки: полный жизненный цикл запроса

Распределённые трассировки следуют за одним запросом, пока он пересекает границы сервисов. Каждая трассировка состоит из отрезков: именованных операций с отметками начала и конца, отражающих работу конкретного сервиса или функции. Трассировки — единственный сигнал, способный восстановить полный жизненный путь запроса в микросервисной архитектуре.

Без трассировок медленную загрузку страницы можно приписать любому из десятков задействованных сервисов. С трассировками вы видите, что узкое место — запрос к базе в сервисе пользователей, занимающий 800 миллисекунд, тогда как каждый другой отрезок укладывается в 50. Такой точности не даст ни журнал, ни метрика по отдельности.

«Журналы говорят, что случилось. Метрики — сколько раз это случилось. Трассировки — как всё связано между собой. Чтобы пройти через происшествие в продакшене без догадок, нужны все три.»

Настройка и инструментирование с OpenTelemetry

OpenTelemetry — отраслевой стандарт для порождения, сбора и вывода телеметрии. Он даёт единый набор интерфейсов и библиотек на разных языках, выдающих журналы, метрики и трассировки в независимом от поставщика формате. Это снимает привязку к производителю и гарантирует, что ваше инструментирование пригодится и с коммерческой платформой, и с собственным конвейером.

Автоматическое и ручное инструментирование

Большинство библиотек поддерживает автоматическое инструментирование — перехватчики без кода для распространённых каркасов и библиотек. Один вызов инициализации способен охватить HTTP-серверы, клиенты баз данных, очереди сообщений и вызовы gRPC. Автоматика покрывает обычные пути; для критичного для бизнеса кода, собственного промежуточного слоя и предметных операций нужно ручное инструментирование.

import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
import { Resource } from "@opentelemetry/resources";
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";

const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: "payment-service",
  }),
  traceExporter: new OTLPTraceExporter({
    url: "http://otel-collector:4317",
  }),
  metricExporter: new OTLPMetricExporter({
    url: "http://otel-collector:4317",
  }),
  instrumentations: [
    new HttpInstrumentation(),
    new ExpressInstrumentation(),
    new PgInstrumentation(),
  ],
});

sdk.start();
process.on("SIGTERM", () => sdk.shutdown());

Устройство сборщика OpenTelemetry

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

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

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  filter:
    error_mode: ignore
    traces:
      span:
        - 'attributes["http.target"] == "/healthz"'
        - 'attributes["http.target"] == "/metrics"'
  attributes:
    actions:
      - key: environment
        value: production
        action: upsert

exporters:
  otlp:
    endpoint: "collector.example.com:443"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, filter, batch, attributes]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch, attributes]
      exporters: [otlp]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch, attributes]
      exporters: [otlp]

Приёмы структурированного журналирования

Структурированное журналирование означает выдачу записей в машиночитаемом виде — обычно JSON — с едиными именами полей во всех сервисах. Журналы перестают быть задачей поиска и становятся задачей запроса. Когда все команды придерживаются одних соглашений об идентификаторах запросов, именах сервисов, кодах ошибок и длительностях, вы можете выполнять произвольные запросы по всей инфраструктуре, не сочиняя собственные разборщики.

Проектирование схемы журналов

Каждое структурированное событие должно содержать минимум: отметку времени, уровень важности, имя сервиса, идентификатор трассировки, идентификатор отрезка и сообщение. Сверх этого добавляйте предметные поля, но соблюдайте соглашения об именовании. Например, держитесь одного написания, ставьте общий префикс полям, связанным с пользователем, и складывайте подробности ошибки во вложенный объект, а не в текст сообщения.

  • Всегда включайте trace_id и span_id, чтобы связывать журналы с трассировками
  • Используйте уровень (debug, info, warn, error), соответствующий сигналам, требующим действий, а не удобству разработчика
  • Никогда не пишите в журнал чувствительные данные — персональные сведения, секреты или токены — даже в среде разработки
  • Держите текст сообщения неизменным, а переменные данные кладите в структурированные поля, чтобы их можно было сводить
  • Приведите формат отметки времени к единому виду во всех сервисах: RFC 3339 или миллисекунды Unix

Частые ловушки

Самая дорогая ошибка — писать в журнал слишком много. Поля высокой мощности, вроде идентификаторов пользователей или адресов, способны раздуть объём на порядки. Прореживайте объёмные отладочные журналы, а подробность приберегите для отдельных трассировок или условий ошибки. Вторая ошибка — несогласованные имена полей: один сервис с user_id, другой с customerId, третий с user.id делают сквозные запросы невозможными без слоя преобразования.

«Строка журнала, к которой нельзя обратиться запросом, всё равно что не существует. Структурированное журналирование не про читаемость — оно про то, чтобы каждая запись стала полноправным гражданином вашей платформы наблюдаемости.»

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

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

Передача контекста трассировки

Контекст трассировки должен передаваться через каждую границу сервиса: заголовки HTTP, метаданные очередей сообщений, метаданные gRPC и даже через асинхронные границы вроде заданий по расписанию или фоновых обработчиков. OpenTelemetry делает это сам, если вы используете его библиотеки для HTTP, gRPC и обмена сообщениями. Идентификатор трассировки течёт от входного шлюза через каждый последующий сервис, собирая отрезки по пути.

  • Инструментируйте все точки входа: шлюзы API, балансировщики, входные контроллеры
  • Передавайте контекст через очереди сообщений в заголовках или метаданных сообщения
  • Включайте идентификатор трассировки в вывод журналов, чтобы связывать журнал и трассировку
  • Применяйте прореживание, сохраняющее трассировки с ошибками или высокой задержкой
  • Добавляйте к отрезкам собственные атрибуты для делового контекста: уровень клиента, артикул товара, регион

Как читать трассировку

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

Оповещения: о чём предупреждать и как не притупить внимание

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

Трёхуровневая система

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

  • Будите только по симптомам, не по причинам. Пользователь видит ошибку 5xx — оповещайте об этом, а не о загрузке процессора
  • Задавайте составные условия, требующие устойчивого отклонения до срабатывания (например, пять минут выше порога)
  • Ограничьте число будящих оповещений тремя на человека за смену, чтобы сохранить качество сигнала
  • Пересматривайте и прореживайте правила каждый квартал: устаревшие оповещения подтачивают доверие
  • В каждое оповещение вкладывайте ссылку на инструкцию, чтобы отвечающий знал первые три шага

Оповещения по скорости расхода

Такие оповещения срабатывают, когда бюджет ошибок расходуется быстрее, чем предполагалось. В отличие от жёстких порогов, они прямо связаны с вашими целями уровня обслуживания. При цели 99,9% доступности за 30 дней у вас есть около 43 минут простоя. Оповещение сработает, когда прогнозируемый расход исчерпает окно раньше плана, и даст время среагировать до нарушения цели.

Как строить полезные панели

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

Дежурная панель

Она должна умещаться в один экран и отвечать на четыре вопроса: сервис жив? какова доля ошибок? как распределены задержки? израсходован ли бюджет ошибок? На каждом графике нужна ясная линия порога, чтобы сразу было видно, здорово ли текущее значение. Не ставьте больше шести графиков: во время происшествия нагрузка на внимание имеет значение.

  • Начните с метрик RED: темп (запросов в секунду), ошибки (неудачные запросы), длительность (процентили задержки)
  • Добавьте метрики USE для инфраструктуры: загрузка, насыщение и ошибки по каждому ресурсу
  • Вынесите достижение цели и скорость расхода бюджета как заметные индикаторы
  • Свяжите каждый график с его журналами или запросом трассировок, чтобы спускаться вглубь одним щелчком
  • Для задержек используйте логарифмическую шкалу: линейная прячет важные отклонения на хвостах

Частые антиприёмы

Самый распространённый антиприём — панель, показывающая все метрики, которые выдаёт инфраструктура. Такие «зелёные стены» создают ложное чувство безопасности и лишают возможности найти сигнал во время происшествия. Другие огрехи: круговые диаграммы для временных рядов, несколько метрик на несогласованных осях, неподписанные линии порогов. Если график требует пояснения, ему не место на панели.

SLI, SLO и бюджеты ошибок

Показатели, цели и бюджеты ошибок образуют договор между вашей командой и пользователями. Показатели — это сырые измерения: задержка, доля ошибок, пропускная способность. Цели — то, что вы обязуетесь выдержать: 99,9% запросов быстрее 200 миллисекунд. Бюджет ошибок — допустимый запас на сбои, те самые 0,1%, которые дают команде право выкатывать, экспериментировать и повторять без страха нарушить обязательства.

Выбор осмысленных показателей

Хороший показатель обращён к пользователю, измерим и ведёт к действию. Доступность (доля успешных запросов), задержка (доля запросов быстрее порога) и свежесть (возраст отдаваемых данных) — самые распространённые для веб-сервисов. Важно мерить с точки зрения пользователя: запрос, который ваш сервер отрабатывает за 50 миллисекунд, но который в медленной мобильной сети занимает две секунды, для человека — неудача.

Как ставить достижимые цели

Цель 99,999% звучит внушительно, но обходится огромными инженерными затратами. Каждая следующая девятка требует примерно десятикратного вложения в резервирование, испытания и эксплуатационные инструменты. Начните с 99,9% для большинства сервисов и приберегите более высокие значения для критичных клиентских путей. Будьте честны в том, что вам по силам: постоянно нарушаемая цель хуже её отсутствия, потому что она приучает к сбоям и подтачивает доверие к измерениям.

Как бюджет ошибок даёт скорость

Бюджет ошибок превращает надёжность из ограничения в измеримый риск. Когда бюджет полон, команда выкатывает свободно, зная, что запас есть. Когда исчерпан — останавливает некритичные выпуски и занимается только надёжностью. Так возникает ясный, опирающийся на данные порядок принятия решений вместо субъективных споров о том, «достаточно ли безопасен» выпуск.

Наблюдаемость в бессерверных и пограничных средах

Бессерверные и пограничные вычисления ставят особые задачи. Функции короткоживущи, инфраструктура скрыта, а исполнение распределено по точкам присутствия по всему миру. Классический мониторинг с агентами не годится: нет узла, на котором агент мог бы жить. Здесь нужен иной подход: телеметрия, отправляемая самим кодом, подробная трассировка холодных стартов и бережное обращение с мощностью данных на глобальных развёртываниях.

Приёмы для бессерверной среды

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

  • Измеряйте длительность холодного старта отдельной метрикой: в стандартных метриках провайдера она не видна
  • Используйте распространители OpenTelemetry, совместимые с механизмом расширений вашей платформы
  • Отправляйте телеметрию пакетами, чтобы не добавлять времени к исполнению функции
  • Там, где нет интерфейса для собственных метрик, настройте метрики, выводимые из журналов
  • Помечайте всю телеметрию средой, регионом и версией функции, чтобы отбор работал эффективно

Особенности пограничных вычислений

Пограничные платформы исполняют код в десятках точек по всему миру. Такое рассеяние делает классический централизованный сбор непрактичным. Используйте инструменты, рассчитанные на границу: они собирают телеметрию в каждой точке и сводят её централизованно с малыми накладными расходами. Следите за долей ошибок по регионам: сбой у местного провайдера может испортить работу сервиса в одной зоне, пока мировое среднее выглядит здоровым.

Как вырастить культуру наблюдаемости

Инструменты и конвейеры необходимы, но их мало. Самое трудное в наблюдаемости — культура. Команда должна ценить инструментированный код так же, как покрытый тестами. Каждая новая возможность должна включать инструментирование в своё определение готовности — наравне с модульными тестами и проверкой кода. Для этого нужна опора: общие библиотеки, записанные соглашения и человек в каждой команде, который несёт эту тему.

Сделать наблюдаемость делом первого порядка

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

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

Петля обратной связи

Наблюдаемость — не разовое внедрение, а непрерывная петля: инструментировать, выкатить, наблюдать, учиться, улучшать. Когда происшествие обнажает слепое пятно — неотслеживаемую метрику, отсутствующее поле журнала, отброшенную трассировку, — считайте это дефектом вашей платформы наблюдаемости и устраняйте. Со временем телеметрия становится полнее, разбор быстрее, а команда обретает уверенность, что способна понять любой сбой, даже тот, которого прежде не видела.

«Цель наблюдаемости — не предотвратить происшествия. Цель — сделать так, чтобы, когда они случаются, у команды были данные, инструменты и уверенность решить их прежде, чем заметят пользователи.»

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

See what your own repository can account for.

Thirty minutes on a repository you choose, including the part the record cannot attribute.