Учебник веб-разработки
Разделы учебника
На этой странице

XI. DevOps

Наблюдаемость: логи, метрики и трассировки

Оглавление · Готовность · Лаборатория диагностики · Эксплуатация

Задача: выбирать наблюдения под вопрос о работе системы. Глава вводит понятия; Prometheus, Grafana, OpenTelemetry и новые сервисы здесь не подключаются.

Наблюдение — проекция состояния

Пусть x — внутреннее состояние, y = h(x) — доступное наблюдение. Если два разных состояния дают одинаковое y, по этому наблюдению их не различить. Например, 503 от content-health может означать отказ транспорта CMS или ошибку её данных. Для различения нужен дополнительный сигнал, а не повторение того же вывода.

Исходник схемы
flowchart LR
  System["Запрос, приложение и зависимости"] --> Logs["Логи: отдельные события"]
  System --> Metrics["Метрики: агрегаты во времени"]
  System --> Traces["Трассировки: связанные операции"]
  Logs --> Question["Вопрос и проверяемая гипотеза"]
  Metrics --> Question
  Traces --> Question

Логи описывают события, метрики — числовые измерения, трассировки — путь операции через участки системы. Они дополняют друг друга; наличие одного инструмента не создаёт все виды сигналов. OpenTelemetry задаёт средства инструментирования и передачи телеметрии, но сам по себе не является хранилищем и панелью Grafana. Сигналы OpenTelemetry.

Лог должен отвечать на конкретный вопрос

Полезная запись задаёт время, компонент, событие, результат, длительность и идентификатор связи, когда он есть. Для машинного разбора удобны поля JSON; свободный текст остаётся полезен для сообщения человеку. Пример будущего учебного формата, не фактический формат логов Atmanki:

{
  "time": "2026-10-06T10:00:00Z",
  "service": "web",
  "event": "cms.read.failed",
  "operation": "news.list",
  "result": "timeout",
  "durationMs": 2500,
  "requestId": "lesson-request-1"
}

Идентификатор помогает связать записи, но не является удостоверением пользователя. Входные IDs требуют правил принятия и размера; в логи не нужно переносить полный payload, Authorization, cookies или .env. Отбирайте поля по диагностической роли. Ошибки библиотек тоже могут содержать URL и данные, поэтому «логируем Error» не означает автоматически безопасный публичный отчёт.

Время события и время доставки лога могут различаться. Часы разных хостов могут расходиться; сортировка timestamp не доказывает причинный порядок. Для локальной длительности используют монотонный таймер, а для связи операций — ID и контекст. Повторная попытка должна отличаться от первого выполнения, иначе число записей можно принять за число пользовательских операций.

Docker logs обычно показывает stdout/stderr контейнера. Если приложение пишет в файл или logging driver отправляет записи в другой backend, доступность через эту команду зависит от настройки. Логи Docker. В Compose проекта нет явного logging override; реальные ограничения хранения и ротации daemon по одному репозиторию не устанавливаются.

Метрики: величина, единица, окно и популяция

Counter накапливает события и обычно сбрасывается при перезапуске; gauge показывает текущее значение и может расти или падать. Histogram хранит распределение по buckets, число и сумму наблюдений; summary — другая форма агрегирования, в том числе quantiles. Типы метрик Prometheus.

ВопросИзмерениеЧто зафиксировать
Сколько запросов?Counter завершённых запросовRoute, результат, интервал
Сколько сейчас выполняется?Gauge активных запросовМомент наблюдения и сервис
Насколько медленно?Распределение длительностиЕдиница, точка начала/конца, окно
Насколько часто сбой?Ошибки / все подходящие запросыЧто считаем ошибкой и знаменателем
Насколько загружен ресурс?CPU, память, диск, соединенияЛимиты, единицы и источник

Для окна W доля ошибок e(W)/n(W) определена только при n(W)>0. Пустое окно — отсутствие наблюдений, не автоматически 100% успеха. Частота запросов делит число на время; это не то же, что доля ошибок. Доля 5xx не включает все неудачи пользователя: зависший запрос может не завершиться, а HTTP 200 может нести неверный контент.

Label создаёт отдельную time series для каждого сочетания значений. Шаблон /news/[slug] даёт ограниченную группу; сырой slug, email, request ID или полный URL могут создать огромное число series. IDs подходят для отдельных logs/traces, а не для безусловного label каждого запроса. Labels и cardinality Prometheus.

Среднее и перцентили отвечают на разные вопросы

Среднее скрывает хвост распределения. p95 — граница, ниже или на которой находится примерно 95% наблюдений по выбранному определению quantile; это не среднее «самых медленных пяти процентов». В маленькой выборке способы вычисления различаются. В лаборатории определение задано явно.

Перцентили отдельных групп нельзя усреднить и получить общий перцентиль. Доли ошибок тоже объединяют через суммы числителей и знаменателей, а не среднее процентов без весов. Histogram позволяет агрегировать совместимые buckets; готовые quantiles summary так не объединяются. Bucket-границы ограничивают точность оценки. Histogram и summary.

Сравнивайте одинаковые route, среду и нагрузку. Новый p95 после релиза может измениться из-за состава запросов; совпадение по времени ещё не доказывает причину. Фиксируйте знаменатель и число наблюдений рядом с красивым графиком.

Trace — связанные участки операции

Trace содержит spans: интервалы отдельных операций с атрибутами, событиями и связями. Parent/child описывает вложенность; trace context передают между сервисами. Произвольный заголовок request ID не создаёт instrumented spans автоматически. Sampling ограничивает объём, поэтому отсутствие trace не доказывает отсутствие запроса. Трассировки OpenTelemetry.

Исходник схемы
sequenceDiagram
  participant Client as Клиент
  participant Web as Web
  participant CMS as CMS
  participant DB as PostgreSQL
  Client->>Web: Запрос страницы
  Note over Web: Span server request
  Web->>CMS: Чтение контента с контекстом
  Note over CMS: Span CMS request
  CMS->>DB: SQL
  DB-->>CMS: Данные
  CMS-->>Web: Контент
  Web-->>Client: Ответ

Это учебная схема возможной инструментированной операции, не реализованный trace проекта. При cache HIT web может вообще не вызвать CMS. Spans могут перекрываться: сумма их длительностей не равна времени ответа. Для задержки исследуют критический путь и ожидания. Profiling задаёт другой вопрос — где процесс тратит CPU/память; профиль CPU не объяснит полностью сетевое ожидание.

SLI, SLO и сигнал для действия

SLI — измерение выбранного качества, SLO — цель на заданном окне, SLA — соглашение с последствиями. Например, доля корректно завершённых чтений страницы за месяц требует определения «корректно», границы измерения и исключений. Service level objectives, Google SRE. В проекте такого измеряемого SLO сейчас нет; слово healthy его не создаёт.

Alert должен иметь смысл для действия: устойчивое ухудшение пользовательского пути важнее единичного колебания CPU. Порог, окно, объём запросов и пропущенные данные влияют на результат. Проверка самой доставки alert — часть системы: наличие правила в Git не доказывает, что кто-то получил уведомление.

Что доступно в Atmanki

  • Статус, ограниченные Docker logs, docker stats, df и PostgreSQL-снимки состояния — эксплуатация и жизненный цикл БД.
  • /api/health отвечает ok, /api/content-health читает CMS без кеша и при ошибке возвращает общий unavailable/503. Ответ не указывает упавшую коллекцию или причину.
  • Webhook-handler логирует исключение инвалидирования кеша; нет собственного сквозного request/trace ID между этими handlers, CMS и PostgreSQL.
  • В production Caddyfile нет директивы log для HTTP access logs. Runtime-сообщения Caddy не являются журналом всех запросов. Caddy log.
  • В checkout добавлен отдельный стек Prometheus/Grafana, Umami и GlitchTip; практическая глава описывает конфигурацию и проверку выпуска. Наличие файлов не подтверждает работу на VPS. Distributed tracing не подключён.

Не запускайте частый content-health как дешёвый uptime ping: он делает реальные чтения CMS. Выберите частоту по нагрузке и цели. Новое логирование или collector вводят отдельным изменением после определения вопроса, полей и объёма.

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