✻ Урок 1.1 · Тема 1: Что и зачем мерить
Мониторинг и наблюдаемость: метрики, логи, трейсы, RED и USE
Содержание урока
Зачем это нужно
Ты вышел на первую неделю в команду интернет-магазина. В прошлом году на распродаже сайт упал, и на разборе честно сказали: «Мы не знали почему». Тебе поручили поставить приборы, чтобы в этом году знать. Я буду рядом как опытный коллега.
Начну с истории, которую знает каждый, кто дежурил. В понедельник приходит сообщение: «сайт тормозит». Я открыл график загрузки процессора (CPU: микросхема, которая выполняет все вычисления машины, как мозг компьютера). Там спокойные 40%. Я развёл руками и ответил: «У нас всё нормально». Через час выяснилось, что у покупателей зависает оплата: сервис оплаты отвечал по десять секунд, а процессор при этом скучал. Мы оба были правы и оба не понимали друг друга: я смотрел на машину, а покупатель смотрел на свой заказ. С тех пор я начинаю не с прибора, а с вопроса: что в этот момент чувствует человек на той стороне?
Весь курс про то, как ответить на этот вопрос цифрами. Сегодня ты поднимешь стенд (учебную копию магазина, на которой можно ломать без последствий) и посмотришь на один запрос тремя способами.
Шаг проекта: ты заводишь репозиторий ~/monitoring-lab (репозиторий это папка, за которой git хранит историю изменений) и кладёшь туда signals.md: заметку о том, как один запрос выглядит в логе, в метриках и в трейсе.
Что нужно знать
- Терминал,
curlиgrep: load-tester, урок 1.4 или DevOps, урок 1.2.jq: load-tester, урок 1.1. - Запуск проекта командами
docker compose up -dиdown: load-tester, урок 5.3 или DevOps, тема 4. - HTTP: у каждого ответа сервера есть трёхзначный код.
2xxзначит «успех»,4xx«ошибка в запросе клиента»,5xx«сломался сервер». - Git в объёме трёх команд (
init,add,commit): load-tester, урок 3.1.
Картина целиком
Представь автомобиль. На приборной панели горят лампочки: «двигатель», «масло», «аккумулятор». Каждая отвечает на вопрос, который кто-то придумал заранее. Но однажды машина начинает странно дёргаться, а лампочки зелёные. Тогда механик подключает диагностику, читает журнал ошибок и смотрит, что творилось с каждым узлом. Лампочки это мониторинг, диагностика это наблюдаемость (умение разобраться в неожиданном по подробным данным).
flowchart TD
U["Покупатель<br>открывает каталог"] --> S["Магазин<br>порт 8000"]
S --> M["Метрики<br>числа во времени"]
S --> L["Логи<br>строка на запрос"]
S --> T["Трейсы<br>путь запроса"]
M --> Q1["Много ли?<br>Быстро ли? Есть ли ошибки?"]
L --> Q2["Что случилось<br>с этим запросом?"]
T --> Q3["Где потерялось<br>время?"]
На схеме сервис один, а данных о нём три вида, и каждый отвечает на свой вопрос. В теории ниже пройдём по схеме сверху вниз, а потом выберем из тысяч чисел те немногие, с которых стоит начинать.
Теория
Мониторинг и наблюдаемость
Сервис ломается ночью, и о поломке ты узнаёшь от покупателя на следующий день. Хуже того, когда ты уже знаешь, что сломалось, но не можешь понять почему. Это две разные беды, и лечатся они двумя разными вещами.
Вернёмся к автомобилю. Мониторинг (monitoring) это приборная панель: заранее придуманные вопросы и лампочки на них. «Жив ли процесс?», «Сколько свободно места на диске?», «Доля ошибок выше пяти процентов?». Ты заранее знаешь, что может сломаться, и заранее рисуешь для этого индикатор. Наблюдаемость (observability) это диагностика: способность ответить на вопрос, которого ты не предусмотрел. «Почему у покупателей из одного города заказы тормозят только по вечерам?». Для такого вопроса нет лампочки, зато есть подробные данные, по которым можно копать вглубь. Аналогия ломается в одном месте: механик приезжает после поломки, а система наблюдения работает постоянно и хранит историю, чтобы можно было вернуться на день назад.
Вот как это выглядит на нашем магазине. Алерт (автоматическое сообщение «что-то не так») «доля ошибок выше 5%» это мониторинг: он сказал, что плохо. Дальше ты открываешь данные и видишь, что все ошибки на одном маршруте /api/orders, а запрос ждёт оплату десять секунд. Это уже наблюдаемость: ты нашёл причину, хотя никто заранее не рисовал лампочку «оплата тормозит».
Осторожно: наблюдаемость не «новое слово для мониторинга» и не набор программ. Можно поставить Prometheus и Grafana (об их работе позже, в теме 2) и всё равно ничего не увидеть, если сервис отдаёт мало данных и без деталей. Наблюдаемость это свойство сервиса, а программы лишь помогают её использовать.
Главное: мониторинг отвечает на заранее заданные вопросы, наблюдаемость позволяет задать новый вопрос и получить ответ по данным, которые сервис уже отдаёт.
Откуда берутся эти данные? Их три вида, и у каждого своя сила.
Три сигнала: метрики, логи, трейсы
Алерт сказал: «5% ошибок». Но какой именно запрос ломается, число не скажет. А строка о сломанном запросе не скажет, много ли таких. Одного вида данных мало, поэтому их три, и вместе их называют сигналами (telemetry, телеметрия: данные, которые сервис отправляет о самом себе).
Лучше всего это видно на примере больницы. Приборы у кровати показывают пульс и температуру: одно число каждого вида раз в минуту, легко сравнить с нормой. Это метрики (metrics). Медсестра ведёт записи: «14:05, дали лекарство, жалуется на головокружение». Это логи (logs): каждое событие отдельной строкой, с подробностями. Маршрутный лист «приёмная, рентген, палата» с временем на каждом этапе это трейс (trace, трассировка): путь одного пациента через все кабинеты. Аналогия ломается в одном месте. В больнице записи ведёт человек, а в сервисе программы сами пишут тысячи записей в секунду, и хранить всё подробно очень дорого.
Теперь то же самое в «Магазине». Метрика это число, меняющееся со временем: например, http_requests_total, сколько запросов обработано с запуска. Мало места, легко складывать, удобно рисовать и строить алерты, но про конкретный запрос она ничего не знает. Лог это строка о событии. «Магазин» пишет её на каждый запрос, и в ней есть метод, путь, код ответа, время и номер запроса. Лог хорошо отвечает «что случилось с этим запросом», но по миллиону строк общую картину глазами не увидеть. Трейс это путь запроса по частям системы: сам запрос, SQL-запрос к базе данных, вызов сервиса оплаты, и у каждого шага своё время. Он отвечает «где ушло время».
sequenceDiagram
participant П as Покупатель
participant М as Магазин
participant Б as PostgreSQL
participant О as Оплата
П->>М: POST /api/orders
М->>Б: резерв товаров
М->>О: POST /pay
О-->>М: 500 (отказ)
М-->>П: 502
Note over М: метрика: ещё один 502<br>лог: строка про этот запрос<br>трейс: оплата заняла почти всё время
На схеме один заказ, который не прошёл. Метрика просто выросла на единицу, лог оставил подробную строку, а трейс покажет, что основное время ушло на вызов оплаты. Одно и то же событие, три взгляда.
Теперь тот же сбой по-настоящему. Оплата отказывает в семи случаях из десяти (fail_rate 0.7, задержка 300 мс), с 14:06 по 14:15 по часам стенда. Начнём с метрики.
Смотри на красную полосу 502: до 14:06 её нет, потом она появляется и держится десять минут, а после возвращения оплаты в норму исчезает. Метрика отвечает «что и сколько», но не говорит, какие это были заказы.
Теперь лог: строки заказов за эту же минуту сбоя.
Первым делом смотри на красные строки и на колонку duration_ms внутри них: 502 приходит не быстро, а через 1,2 секунды, значит магазин что-то ждал. Поле trace_id красной строки ведёт в трейс.
И наконец трейс, один из этих заказов с trace_id, начинающимся на 7c1e5a9b.
Здесь видно то, чего не было ни в метрике, ни в логе: время уходит не в базу и не в Redis, а в четыре подряд вызова оплаты по 300 миллисекунд. Метрика сказала «когда», лог «какие запросы», трейс «где».
Прикинь сам: ты хочешь узнать, почему именно заказ покупателя номер 17 в пятницу вечером ждал оплату восемь секунд. С какого сигнала начнёшь?
С лога и трейса. Метрика покажет только общую картину («оплата стала медленнее у всех»), а конкретный запрос в ней растворился в сумме. Лог подскажет, что в нём было, а трейс покажет, на каком шаге пропало время.
Осторожно: «логи и есть мониторинг» (так часто говорят). Логи нужны для деталей, но по ним неудобно смотреть общую картину и строить алерты. Метрики для этого подходят гораздо лучше. А ещё метрики дёшевы: ста тысячам запросов соответствует сто тысяч строк лога, а метрика остаётся одним числом. Обратная сторона дешевизны: детали в числе теряются.
Главное: метрики показывают, что и сколько (общая картина), логи объясняют конкретное событие, трейсы показывают, где потерялось время.
Тип данных выбрали. Но чисел, которые можно мерить, тысячи. Какие брать первыми?
Четыре золотых сигнала
На странице метрик «Магазина» десятки чисел, а в реальной системе их тысячи. Если наблюдать за всеми сразу, не увидишь ничего. Нужен короткий список того, что действительно говорит о здоровье сервиса. Такой список описали инженеры Google в книге «Site Reliability Engineering» (2016), и его называют четырьмя золотыми сигналами (golden signals). Почему именно эти четыре? Каждый отвечает на отдельный вопрос, а вместе они покрывают все способы, которыми сервис плох: медленно, много, сломано, на пределе.
Представь кафе. Гость спрашивает, как быстро принесли заказ (это задержка). Владелец считает, сколько гостей обслужили за вечер (трафик), сколько блюд вернули на кухню (ошибки) и не переполнен ли зал, из-за чего гости стоят в очереди (насыщение). Температура холодильника никого не волнует, пока еда свежая. Теперь то же по порядку для сервиса.
- Задержка (latency): сколько занимает запрос, как «быстро принесли заказ». Считают отдельно для успешных и ошибочных запросов, потому что быстрая ошибка не должна «улучшать» картину: если половина запросов мгновенно падает с
502, средняя задержка на графике упадёт, хотя покупателям стало хуже. - Трафик (traffic): сколько запросов приходит, обычно «в секунду», как гости за вечер.
- Ошибки (errors): какая доля запросов закончилась неудачно, как блюда, вернувшиеся на кухню.
- Насыщение (saturation): насколько заполнен самый дефицитный ресурс, как переполненный зал. Это очередь, пул соединений с базой, диск. Насыщение предупреждает заранее: очередь растёт, а пользователи ещё ничего не заметили.
Для «Магазина» это выглядит так: задержка это сколько миллисекунд отвечает GET /api/products, трафик это сколько таких запросов в секунду, ошибки это доля ответов 5xx, а насыщение это число запросов, которые ждут свободное соединение с базой. Пул соединений (набор заранее открытых соединений с базой, которые выдают запросам по очереди) у «Магазина» маленький, и когда он кончается, запросы встают в очередь.
Вот как выглядят эти четыре сигнала на дашборде, сначала в норме, потом во время сбоя.
Смотри на цвет карточек, а не на числа: зелёные «Запросы» говорят, что покупатели приходят как обычно, красные «Ошибки» и «Заказы p95» показывают, что им плохо. Это та же картина, что в истории про «сайт тормозит»: сервер здоров, страдает конкретный путь.
А вот задержка заказов по времени, с теми же отметками начала и конца сбоя.
На графике сначала ровная линия около ста миллисекунд, потом ступенька вверх и обратно. Начинай с p95: он честнее среднего, потому что показывает, как отвечают заказы тем, кому повезло меньше всех.
Прикинь сам: покупатели жалуются, что каталог открывается долго, а ошибок нет, и запросов приходит столько же, сколько вчера. Какой из четырёх сигналов изменился?
Задержка. Трафик прежний, ошибок нет. Если копать дальше, причина, скорее всего, в насыщении: например, очередь за соединениями с базой. Так сигналы подсказывают, куда смотреть дальше.
Четыре сигнала это минимум, а не полный список. Если сервис хранит данные, к ним добавляют целостность (записанное потом читается и не потеряно), но с этого не начинают.
Главное: задержка, трафик, ошибки и насыщение: с этих четырёх чисел начинай наблюдение за любым сервисом, который отвечает пользователям.
Из этого списка выросли две сокращённые схемы, которые на собеседованиях спрашивают на каждом шагу.
RED и USE: два коротких списка
Четыре сигнала всё равно нужно разложить по полочкам: что считаем для сервиса, а что для железа. Для этого придумали две короткие схемы. Первая звучит как «красный» (RED): Rate, Errors, Duration («скорость запросов, ошибки, длительность»). Это для всего, что отвечает на запросы: Rate (сколько запросов в секунду), Errors (какая доля ответов с ошибкой), Duration (сколько времени занимает ответ). Вторая звучит как «используй» (USE): Utilization, Saturation, Errors («занятость, очередь, ошибки»). Это для ресурсов: процессор, память, диск, пул соединений. Utilization (занятость: какую долю времени ресурс занят), Saturation (очередь: сколько работы ждёт, пока ресурс освободится), Errors (ошибки самого ресурса, например сбой чтения диска).
Слово Errors стоит в обоих методах, но это разное: в RED это ответы сервиса с ошибкой, в USE ошибки самого устройства или ресурса (сбой чтения диска).
| Метод | Что мерим | Пример в «Магазине» |
|---|---|---|
| RED (сервисы) | Rate, Errors (ответы с ошибкой), Duration | запросов в секунду, доля 5xx, время ответа |
| USE (ресурсы) | Utilization, Saturation, Errors (ошибки ресурса) | занятость пула, очередь за соединением, сбои диска |
Почему два метода, а не один? RED смотрит на сервис глазами покупателя: получил ли он ответ и как быстро. USE смотрит на железо глазами инженера: чем занят ресурс и не стоит ли перед ним очередь. Начинай всегда с RED. Если покупателю хорошо, то процессор на 90% не повод будить человека ночью. К USE переходят, когда RED показал проблему и нужно найти причину. В моей истории про «сайт тормозит» процессор в 40% ничего не значил: покупатель процессор не видит, а узкое место было во внешнем сервисе оплаты. Я начал с USE и пропустил RED.
Вот USE на нашем сбое. Пул это ресурс, и его занятость с очередью видны прямо на графике.
Во время сбоя заказы держат соединения с базой, пока ждут оплату, и свободных остаётся мало, а в очереди появляются ожидающие. Это насыщение: оно выросло не из-за процессора, а потому что оплата тормозит и держит соединения. Чтобы дойти до него, нужно было сначала увидеть симптом в RED, а потом заглянуть в USE.
Проверь понимание: к какому методу относятся «доля ответов 5xx» и «число запросов, ждущих соединение с базой»?
Ответ
Доля 5xx это Errors из RED (сервис глазами покупателя). Число запросов, ждущих соединение, это Saturation из USE: пул соединений здесь ресурс, перед которым выстроилась очередь.
Главное: RED мерит сервис (запросы, ошибки, время), USE мерит ресурс (занятость, очередь, ошибки), а начинать нужно с RED.
Осталось понять, на какие числа вешать тревогу, а какие оставить для расследования.
Симптом и причина
Среди чисел на панели одни говорят «покупателям плохо», а другие «машине тяжело». Проблемы начинаются, когда мы будим человека ночью из-за второго.
Автомобильная сигнализация, которая срабатывает от каждого проезжающего грузовика, через неделю перестаёт кого-либо будить: все привыкли и не слышат. Угон пропустят. С мониторингом то же самое. Симптом (symptom) это то, что чувствует покупатель: ошибки, медленные ответы, недоступность. Причина (cause) это то, из-за чего это случилось: процессор, диск, очередь к базе, упавший сервис оплаты. Правило простое: тревогу (алерт) настраивай на симптом, а причину показывай на дашборде (экране с графиками) и ищи, когда уже проснулся.
Пример на «Магазине». Алерт «процессор выше 80% одну минуту» сработал ночью: пачка запросов быстро отработалась, покупатели ничего не заметили, а тебя разбудили зря. Алерт «доля 5xx выше 5% пять минут»: покупатели получают ошибки прямо сейчас, и действие нужно. Первое число это причина (возможная), второе симптом.
Как алерт на симптом живёт во времени, видно на нашем сбое. Условие «доля 5xx выше 5%» держится две минуты, прежде чем сработать (for: 2m в правиле стенда).
Смотри на жёлтый кусок: условие уже выполнено, но алерт ещё не сработал, чтобы одна случайная минута не разбудила дежурного. Красный участок начинается только когда симптом удержался, и заканчивается, когда оплату вернули в норму.
flowchart TD
A["Сработал алерт<br>на симптом"] --> B{"Что видно<br>в RED?"}
B -->|"ошибки"| C["Лог: какие запросы<br>и какой статус"]
B -->|"медленно"| D["Трейс: на каком<br>шаге время"]
C --> E["USE: что стоит<br>за ошибкой"]
D --> E
E --> F["Причина найдена"]
Схема показывает порядок расследования. Начинаем с симптома, уточняем его логом или трейсом и только потом смотрим на ресурсы. У исключения есть своё имя: причина, у которой есть прогноз до симптома, тоже достойна алерта (например, «диск заполнится через 4 часа»). Но «диск занят на 70%» ночью будить не должно: такое значение может держаться месяцами.
Прикинь сам: алерт «диск занят на 70%» сработал в 3 часа ночи. Что человек сделает, когда проснётся?
Скорее всего, ничего: смысла будить не было. Хороший вопрос перед созданием любого алерта: «что человек сделает, когда он придёт?». Если ничего, это не алерт, а строка на дашборде или задача на утро.
Проверь понимание: какой из двух алертов лучше для ночного дежурства: «CPU выше 80% в течение минуты» или «доля
5xxвыше 5% в течение 5 минут»? Почему?
Ответ
Второй. Он описывает симптом: покупатели прямо сейчас получают ошибки. Первый может сработать от обычной пачки запросов, не причинив пользователям никакого вреда, и приучить дежурного не доверять тревогам.
Главное: будить человека должен симптом, который чувствует покупатель, а причины ищут уже по дашбордам, логам и трейсам.
Алерт сработал и ты проснулся. Дальше стоит решить, как быстро действовать, а для этого нужно понять, насколько всё плохо.
Серьёзность: стабильно или ухудшается
Врач скорой, приехав на вызов, первым делом не ищет диагноз. Он смотрит, сколько пострадавших, насколько тяжело и держится ли состояние ровным или быстро падает. От ответа зависит, торопиться ли и рисковать ли сложным вмешательством. С сервисом то же: «что сломалось» можно выяснять потом, а «насколько это плохо» нужно знать сразу.
Для этого берут те же четыре сигнала и смотрят на них с тремя вопросами. Сколько пользователей затронуто: доля ошибок и доля медленных запросов, а не число строк в логе. Что именно не работает: сломанный каталог неприятен, сломанный заказ стоит денег. Стабильно или ухудшается: ситуацию называют стабильной, если плохое число держится на одном уровне (например, 5% ошибок уже десять минут), и ухудшающейся, если оно растёт от замера к замеру. Другой пример стабильности: p99 (время, дольше которого отвечает один запрос из ста) выше цели, но p95 (дольше которого отвечает один из двадцати) ровный: страдают единицы.
Разница меняет действия. Стабильная проблема даёт время: можно прочитать лог, открыть трейс, спокойно найти причину. Растущая времени не даёт: сначала нужно остановить рост, например откатить последнюю выкатку, а причину искать потом. И чем рискованнее действие, тем серьёзнее должна быть проблема, чтобы на него идти: порядок действий разберём в уроке 5.1.
Вот два сбоя оплаты на одной панели. В обоих с 14:06 оплата отказывает в семи случаях из десяти. Во втором в 14:10 её отказ становится полным (fail_rate 1.0).
Смотри на 14:09: обе линии лежат рядом, и по одному числу сбои не отличить. Разошлись они только во времени, поэтому для оценки серьёзности на дашборде нужен график, а не одно большое число.
Прикинь сам: алерт сработал, ошибок 8%. Пять минут назад было 8%, десять минут назад тоже 8%. Стоит ли немедленно откатывать выкатку?
Не обязательно. Ситуация стабильная: затронут примерно один запрос из двенадцати, число не растёт. Есть время открыть лог и трейс и найти причину, а откат (рискованное действие) оставить на случай, если станет хуже или причина окажется в выкатке.
Осторожно: «стабильно» не значит «безвредно». Пять процентов ошибок на заказах, держащиеся час, это сотни потерянных покупок. Стабильность говорит о том, сколько у тебя времени на разбор, а не о том, нужно ли чинить.
Главное: оценить серьёзность значит посмотреть, сколько пользователей и какая функция затронуты и растёт ли плохое число: стабильную проблему изучают, растущую сначала останавливают.
Сигналы и график показывают, что именно плохо. Иногда они же показывают, что дальше без нового инструмента не пройти.
Профилирование: четвёртый столп
Бывает так. Метрики говорят, что процессор сервиса вырос с 40% до 70%, хотя запросов столько же. Трейс показывает, что почти всё время запрос проводит внутри самого магазина, а не в ожидании базы или оплаты. В логе тишина. Три сигнала дошли до границы: они говорят «виноват этот сервис», но не говорят, какая его часть.
Тут помогает профилирование (profiling), четвёртый столп наблюдаемости рядом с метриками, логами и трейсами. Представь, что за поваром на кухне весь вечер ходит человек с секундомером и записывает, на какую операцию сколько минут ушло. Профиль делает то же с программой: через равные промежутки смотрит, какую функцию она сейчас выполняет, и по тысячам таких снимков считает, куда уходит процессорное время и память. Результат рисуют как флеймграф (flame graph, «график-пламя»): каждая функция это полоска, чем она шире, тем больше времени в ней проведено, а вложенные вызовы стоят над ней. Широкая полоса сразу показывает, что оптимизировать.
Для любопытных: как снимают профиль
Способов снять профиль несколько. У языков есть свои встроенные инструменты, например pprof у Go: сервис сам отдаёт профиль по запросу. eBPF (механизм ядра Linux, который позволяет подключиться к работающей программе и посмотреть, что она делает, без правки её кода и перезапуска) снимает профили со всей машины сразу. Непрерывное профилирование (continuous profiling, например Pyroscope) снимает их постоянно с малой частотой и хранит, как метрики: когда аномалия уже прошла, можно открыть профиль того самого получаса.
Когда его включать? Не первым. Профиль отвечает на вопрос «где в коде», и задавать его имеет смысл, когда сигналы уже привели в один сервис: рост процессора или памяти без роста трафика, подозрение на утечку памяти, медленный ответ, который трейс не объясняет вызовами других сервисов. Профилирование добавляет небольшую, но ненулевую нагрузку, поэтому его включают по поводу, а не «на всякий случай» везде и навсегда. На нашем стенде профайлера нет, поэтому здесь только идея, практики не будет.
flowchart TD
A["Метрики: аномалия<br>процессор или память"] --> B{"Трейс: время<br>уходит куда?"}
B -->|"в вызов другого<br>сервиса"| C["Идём в тот сервис:<br>его метрики и логи"]
B -->|"внутри самого<br>сервиса"| D["Профиль:<br>какая функция"]
D --> E["Флеймграф: широкая<br>полоса, правим код"]
Схема показывает место профилирования в расследовании: оно идёт после метрик и трейса, когда стало ясно, что причина внутри сервиса.
Прикинь сам: после выкатки заказы стали медленнее. Трейс показывает, что на вызов оплаты уходит 90% времени. Нужно ли включать профилирование магазина?
Нет. Время уходит в ожидание другого сервиса, а не в код магазина: профиль покажет в нём лишь «ждём ответа». Идти нужно в оплату: смотреть её метрики, логи и трейсы.
Главное: профилирование отвечает «какая функция в коде тратит время и память» и включается, когда метрики и трейс уже привели в один сервис, а не вместо них.
Остался один термин, который встретится в первой же практике: метки.
Метки и кардинальность: слово на будущее
Через урок, в 2.1, ты будешь читать метрики «Магазина», и почти в каждой есть метки, поэтому слово пригодится сразу. Метрика «ошибок 17» мало что говорит: каких, где, на каком маршруте? Чтобы число можно было разрезать на группы, к нему добавляют метки (labels): пары «имя=значение» в фигурных скобках. Например, http_requests_total{method="GET",route="/api/products",status="200"} это счётчик запросов именно для чтения каталога с ответом 200, а у заказов с ответом 502 будет свой счётчик.
Цена за удобство такая. Каждая уникальная комбинация значений меток это отдельный ряд чисел, который хранилище ведёт отдельно. Число таких комбинаций называют кардинальностью (cardinality). Метка route с десятком маршрутов безопасна. Метка user_id при тысяче покупателей даст тысячу рядов вместо одного, а при миллионе мониторинг упадёт от нехватки памяти. Поэтому в метки кладут только значения с небольшим конечным набором (метод, шаблон маршрута, код ответа), а подробности вроде номера пользователя оставляют логам. Подробно это будет в теме 2, в уроке про Prometheus.
Главное: у метки должно быть небольшое число значений: метод и маршрут да, номер пользователя нет.
Теория закончилась. Теперь посмотрим на всё это на живом стенде.
Практика
Все файлы урока складывай в ~/monitoring-lab. Нагрузку и любые поломки делай только на своём локальном стенде.
1. Подними стенд «Магазин»
Стенд это учебный интернет-магазин из нескольких контейнеров: сам магазин (сервис shop, порт 8000), оплата, база данных PostgreSQL, Redis (быстрое хранилище для корзин и сессий) и набор приборов наблюдения. Приборы оформлены как профиль (profile) Docker Compose: дополнительные сервисы, которые не стартуют, пока их явно не попросишь флагом --profile monitoring.
git clone https://github.com/distinguished-sre/learning.git ~/learning
cd ~/learning/load-tester/project/shop
cp .env.example .env
docker compose --profile monitoring up -d --build --wait --wait-timeout 300
curl -s localhost:8000/readyz
git clone скачивает репозиторий со стендом в ~/learning. cp .env.example .env создаёт файл настроек из образца. docker compose ... up -d --build --wait собирает образ магазина (--build), запускает всё в фоне (-d) и ждёт, пока у каждого контейнера пройдёт проверка здоровья (--wait, не дольше --wait-timeout 300 секунд). /readyz проверяет связь с PostgreSQL и Redis.
Первый запуск займёт несколько минут: скачиваются образы и создаётся база.
Ответ {"status":"ready"} значит, что магазин видит базу и Redis. Если пришло {"detail":{"unavailable":["postgres"]}}, база ещё не поднялась: подожди.
Типичные ошибки:
curl: (7) Failed to connect to localhost port 8000: стенд ещё не поднялся. Посмотриdocker compose --profile monitoring ps.
2. Заведи репозиторий monitoring-lab
В этой папке будет жить всё, что ты сделаешь в курсе: заметки, запросы, дашборды, правила.
mkdir -p ~/monitoring-lab/01-basics
cd ~/monitoring-lab
git init
printf '# monitoring-lab\n\nМои заметки и файлы курса «Мониторинг и SRE».\n' > README.md
git add README.md
git commit -m "init: репозиторий курса"
git init превращает папку в репозиторий, git add готовит файл к сохранению, git commit -m сохраняет первый снимок с пояснением.
Что должно получиться:
[main (root-commit) 3f2a1c9] init: репозиторий курса
1 file changed, 3 insertions(+)
create mode 100644 README.md
Типичные ошибки:
Author identity unknown: git не знает, кто ты. Задайgit config --global user.nameиuser.email, повтори коммит.
3. Один запрос тремя глазами
Отправим магазину один запрос с номером в заголовке X-Request-ID (магазин пишет его в лог) и найдём след в трёх местах.
curl -si -H 'X-Request-ID: lab-0001' localhost:8000/api/products/42
-i добавляет в вывод заголовки ответа, -H добавляет заголовок к запросу.
Что должно получиться (содержимое карточки у тебя другое, часть заголовков опущена):
HTTP/1.1 200 OK
x-request-id: lab-0001
{"id":42,"name":"...","price":...,"category_id":...,"stock":...}
Как читать вывод: 200 это ответ покупателю, в x-request-id магазин вернул наш номер, тело это JSON с карточкой товара.
Глаз первый: лог. Каждый запрос магазин записывает одной JSON-строкой в стандартный вывод контейнера, а Docker собирает такие строки. Достанем нашу:
cd ~/learning/load-tester/project/shop
docker compose logs shop --no-log-prefix | grep lab-0001
--no-log-prefix убирает название контейнера перед строкой, grep оставляет строки с нашим номером.
Что должно получиться (время, длительность и trace_id у тебя будут другими):
{"ts": "2026-10-04T09:12:31.482913+00:00", "level": "INFO", "msg": "Запрос завершён", "method": "GET", "route": "/api/products/{id}", "path": "/api/products/42", "status": 200, "duration_ms": 6.41, "request_id": "lab-0001", "trace_id": "b3a6c5f0e4d24a7e9a1f0c2d8e7b6a54"}
Как читать вывод: level важность (ERROR для ответов 5xx), route шаблон пути (все карточки товаров попадают в /api/products/{id}), status код ответа, duration_ms длительность в миллисекундах. request_id это наш номер, а trace_id номер трейса: по нему лог связан с третьим глазом.
В Grafana Explore та же строка выглядит так.
Смотри на подсвеченный lab-0001, это наш номер, и на duration_ms: 6,41 мс совпадает с суммой в метрике.
Глаз второй: метрика. Тот же запрос посчитан в /metrics: так называется страница, на которой сервис выдаёт свои числа обычным текстом. Заглянем в неё:
curl -s localhost:8000/metrics | grep -F 'route="/api/products/{id}"' | grep -E '^http_requests_total|_count|_sum'
grep -F ищет текст как есть, второй grep -E оставляет счётчик запросов, число замеров времени и их сумму.
Что должно получиться (порядок строк и числа у тебя могут отличаться, если ты уже ходил по этому маршруту):
http_requests_total{method="GET",route="/api/products/{id}",status="200"} 1.0
http_request_duration_seconds_count{method="GET",route="/api/products/{id}"} 1.0
http_request_duration_seconds_sum{method="GET",route="/api/products/{id}"} 0.00641
Как читать вывод: в первой строке имя, метки и значение: «один ответ 200». В третьей суммарное время: 0,00641 с это те же 6,41 мс из лога. Лог помнит запрос поимённо, метрика только прибавила единицу к счёту.
Глаз третий: трейс, мельком. Трейсы собирают отдельные программы (Alloy принимает, Tempo хранит, подробно в теме 4), а смотрят их в Grafana, программе с графиками и данными из хранилищ. Открой http://localhost:3000 (вход не нужен), слева Explore, источник Tempo, вставь trace_id из лога и нажми Run query. Если поле принимает только запрос, введи { resource.service.name = "shop" } и открой самый свежий трейс.
Что должно получиться: диаграмма из горизонтальных полос, «водопад». Сверху длинная полоса самого запроса GET /api/products/{id}, под ней короткие полосы с обращениями к PostgreSQL. Время каждой полосы подписано.
Как читать вывод: ширина полосы это время шага, вложенные полосы идут внутри родителя.
Вот как этот трейс выглядит в Grafana.
Сначала посмотри на ширину полос: SELECT занимает почти всё время запроса. Это здоровая картина, к ней и будем сравнивать, когда сломаем оплату.
Типичные ошибки:
grepничего не нашёл: лог ещё не доехал или номер неверный. Повториcurlи команду, проверь, что ты в~/learning/load-tester/project/shop.- В Grafana в Tempo пусто: трейсы уходят пакетами раз в секунду-две, а хранилище принимает их с небольшой задержкой. Подожди 10 секунд и обнови.
4. Разложи метрики по RED и USE
Сгенерируем движение и посмотрим список метрик магазина:
for i in 1 2 3 4 5; do curl -s -o /dev/null localhost:8000/api/products; done
curl -s localhost:8000/metrics | grep '^# TYPE'
Цикл пять раз запрашивает каталог, grep оставляет строки с типом метрики: счётчик (counter, только растёт), gauge (ходит вверх и вниз, как стрелка спидометра) или histogram (счётчики по диапазонам времени).
Что должно получиться (минимум, остальные строки зависят от того, что уже происходило):
# TYPE http_requests_total counter
# TYPE http_request_duration_seconds histogram
# TYPE http_requests_in_progress gauge
# TYPE shop_db_pool_size gauge
# TYPE shop_db_pool_available gauge
# TYPE shop_db_pool_waiting gauge
# TYPE shop_db_connection_wait_seconds histogram
Как читать вывод: http_* это запросы покупателей, shop_db_pool_* состояние пула соединений с базой (всего, свободно, ждут), а последняя метрика хранит, как долго запросы ждали соединение.
Распредели семь метрик выше по RED и USE и подпиши букву (Rate, Errors, Duration, Utilization, Saturation). Подсказка: shop_db_pool_waiting это очередь запросов за ресурсом, то есть Saturation из USE.
Что должно получиться (своя формулировка допустима, ориентир такой):
RED: http_requests_total (Rate; с меткой status=5xx это Errors)
http_request_duration_seconds (Duration)
USE: shop_db_pool_size, shop_db_pool_available (Utilization пула: занято = size - available)
shop_db_pool_waiting (Saturation пула)
shop_db_connection_wait_seconds (Saturation: как долго стоят в очереди)
http_requests_in_progress (запросов в работе: ранний признак насыщения)
Типичные ошибки:
- Всё записано под RED, потому что «это же метрики магазина»: RED относится только к тому, что видит покупатель (запросы, ошибки, время). Пул соединений это ресурс внутри магазина, и ему место в USE.
5. Запиши наблюдения и сохрани
Создай ~/monitoring-lab/01-basics/signals.md: адрес запроса и номер lab-0001, таблица «сигнал |
что я увидел | какой вопрос закрывает» из трёх строк, раздел «RED и USE» с твоей раскладкой и две строки про симптом и причину. Сохрани файл в историю: |
cd ~/monitoring-lab
git add 01-basics/signals.md
git commit -m "docs: один запрос тремя сигналами"
git log --oneline
git log --oneline покажет два коммита: твой и init.
Сломай и почини
Сломаем ситуацию, когда все лампочки зелёные, а покупатели не могут оплатить. Сначала прочитай симптом, выпиши гипотезы и только потом открывай проверки.
Симптом
Оплата отвечает отказом в семи случаях из десяти, а /healthz магазина отдаёт 200, процессор и память в норме.
curl -fsS localhost:8001/admin/config -H 'Content-Type: application/json' -d '{"delay_ms":300,"fail_rate":0.7}'
TOKEN=$(curl -fsS localhost:8000/api/login -H 'Content-Type: application/json' -d '{"email":"user0001@shop.lab","password":"password"}' | jq -r .token)
for i in $(seq 1 10); do
curl -s -o /dev/null -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' localhost:8000/api/cart/items -d '{"product_id":1,"qty":1}'
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' -X POST -H "Authorization: Bearer $TOKEN" localhost:8000/api/orders
done
curl -s localhost:8000/healthz
Порт 8001 это сервис оплаты, /admin/config меняет его поведение на лету (задержка 300 мс, 70% отказов). Цикл десять раз кладёт товар в корзину и оформляет заказ, -w печатает код ответа и время.
Что получится: десять строк «код время» (часть 201, несколько 502 дольше остальных) и в конце {"status":"ok"}.
Как читать вывод: /healthz проверяет только что процесс жив, а среди заказов есть 502, заметно дольше остальных: магазин повторяет попытки оплаты без паузы.
Гипотезы
- База данных упала.
- Процессору магазина не хватает ресурсов.
- Оплата отвечает отказами, и это видно по симптому, а не по лампочкам железа.
Проверки
Посмотри на ту же поломку тремя сигналами: что говорит метрика, что говорит лог.
curl -s localhost:8000/metrics | grep -E '^http_requests_total.*orders|^shop_payment_requests_total'
cd ~/learning/load-tester/project/shop
docker compose logs shop --no-log-prefix | grep '"status": 502' | tail -n 1
Что получится:
http_requests_total{method="POST",route="/api/orders",status="201"} 8.0
http_requests_total{method="POST",route="/api/orders",status="502"} 2.0
shop_payment_requests_total{result="ok"} 8.0
shop_payment_requests_total{result="error"} 18.0
{"ts": "...", "level": "ERROR", "msg": "Запрос завершён", "method": "POST", "route": "/api/orders", "status": 502, "duration_ms": 1507.3, "request_id": "...", "trace_id": "...", "user_id": 1, "error": "payment failed"}
Как читать вывод: серия 502 на /api/orders растёт, у оплаты попыток error больше, чем ok (магазин повторяет попытки: одна ошибка заказа это до четырёх неудачных попыток), а в логе error: "payment failed" прямо называет причину.
Тот же запрос в интерфейсе Prometheus на вкладке Table даёт две строки.
Смотри на колонку Value: ошибок 18 против 8 удачных попыток, хотя заказов 8 прошло и 2 провалились. Разница в том, что неудачный заказ делает до четырёх попыток.
Исправление
Разбор
Верна гипотеза 3. Симптом (502 на /api/orders, RED: Errors) был виден в метрике с первой минуты, причину назвал лог (payment failed). База отвечает (гипотеза 1 неверна), процессор ни при чём (гипотеза 2). /healthz был зелёным всё время: проверка «процесс жив» не говорит, довольны ли покупатели. Поэтому алерт ставят на симптом.
Верни оплату в норму и убедись, что заказы снова проходят:
curl -fsS localhost:8001/admin/config -H 'Content-Type: application/json' -d '{"delay_ms":50,"fail_rate":0}'
После этого новые заказы снова отвечают 201, а серия status="502" перестаёт расти (счётчик остаётся на достигнутом значении, он только растёт).
ИИ в помощь
Нейросеть помогает разложить метрики по методам, но путает RED и USE. Общие правила: ИИ-помощник.
Задача: разложить метрики по RED и USE.
Вот список метрик сервиса интернет-магазина: http_requests_total (счётчик запросов с метками method, route, status), http_request_duration_seconds (гистограмма времени ответа), http_requests_in_progress (запросов в работе), shop_db_pool_available (свободных соединений в пуле с базой), shop_db_pool_waiting (запросов, ждущих соединение).
Разложи их по методам RED и USE. Для каждой метрики напиши букву (Rate, Errors, Duration, Utilization, Saturation) и одной фразой объясни, почему.
Проверь ответ: сверь с таблицей в практике. Типичная ошибка нейросетей: относят пул соединений к RED, потому что «это метрика магазина». RED мерит то, что видит покупатель, а пул это ресурс внутри.
Задача: отличить симптом от причины.
Три алерта магазина: (1) CPU выше 80% одну минуту; (2) доля ответов 5xx выше 5% пять минут; (3) диск занят на 70%. Для каждого скажи: симптом или причина, будить ли ночью, и что сделает человек, когда придёт.
Проверь ответ: симптомом должно быть названо только второе правило. Типичная ошибка: нейросеть соглашается, что «CPU 80%» стоит ночной тревоги.
Не принимай готовое: сначала разложи метрики сам (практика 4), потом сравнивай с нейросетью.
Словарик урока
| Термин | Простыми словами |
|---|---|
| Наблюдаемость (observability) | Умение ответить на вопрос, которого не ждали, по подробным данным сервиса |
| Золотые сигналы | Задержка, трафик, ошибки, насыщение: минимум для любого сервиса |
| RED | Rate, Errors, Duration: метод для сервисов, которые отвечают на запросы |
| USE | Utilization, Saturation, Errors: метод для ресурсов (процессор, пул, диск) |
| Метка (label) | Пара «имя=значение» в метрике, делящая число на группы |
| Кардинальность (cardinality) | Число уникальных комбинаций значений меток |
| Симптом и причина | Что чувствует покупатель и из-за чего это случилось |
| Стабильная и ухудшающаяся проблема | Плохое число держится на уровне или растёт: от этого зависит, торопиться ли |
| Профилирование (profiling) | Замер, какие функции кода тратят процессорное время и память |
| Флеймграф (flame graph) | График профиля: чем шире полоса функции, тем больше времени в ней |
| eBPF | Механизм ядра Linux: смотреть на работающую программу без правки кода и перезапуска |
Вопросы с собеседований
Раздел для повторения: ответь вслух, потом открой ответ. Короткие вопросы с пометкой [на скорость] тренируй на время: ответ за 30 секунд.
1. [junior] [часто] Чем мониторинг отличается от наблюдаемости?
Ответ
Мониторинг отвечает на заранее заданные вопросы: заранее нарисованные лампочки вроде «процесс жив» или «доля ошибок выше 5%». Наблюдаемость позволяет задать новый вопрос и получить ответ по данным, которые сервис уже отдаёт: например, почему заказы тормозят только у части покупателей. Алерт сказал, что плохо (мониторинг), по подробным данным нашли причину (наблюдаемость). Это свойство сервиса, а не набор программ.
Что хотят услышать: заранее заданные вопросы против новых, пример, мысль о том, что наблюдаемость не равна Prometheus и Grafana.
Красный флаг: «это одно и то же» или «это Grafana».
2. [junior] [часто] Назови три сигнала телеметрии и для чего каждый.
Ответ
Метрики: числа во времени, дёшевы, годятся для общей картины и алертов («сколько ошибок»). Логи: строки о событиях с подробностями («что случилось с этим запросом»). Трейсы: путь одного запроса по частям системы с временем каждого шага («где потерялось время»). Один сигнал не заменяет другие: метрика не знает про конкретный запрос, лог неудобен для общей картины.
Что хотят услышать: три сигнала и сильная сторона каждого.
Красный флаг: «логи и есть мониторинг».
3. [junior] [часто] [на скорость] Назови четыре золотых сигнала.
Ответ
Задержка (сколько занимает запрос, отдельно для успешных и ошибочных), трафик (сколько запросов в секунду), ошибки (доля неудачных запросов) и насыщение (насколько заполнен самый дефицитный ресурс, например очередь к базе).
Что хотят услышать: все четыре и по примеру на сервис.
Красный флаг: вместо насыщения называют загрузку процессора как единственный показатель.
4. [junior] [часто] Чем RED отличается от USE и когда какой применять?
Ответ
RED мерит сервис, отвечающий на запросы: Rate, Errors, Duration, то есть глазами покупателя. USE мерит ресурсы: Utilization (занятость), Saturation (очередь), Errors (ошибки ресурса), то есть глазами инженера. Начинают с RED: если покупателям хорошо, нагрузка процессора не повод для паники. К USE переходят, когда RED показал проблему и нужна причина.
Что хотят услышать: объект каждого метода и порядок применения.
Красный флаг: путают, какой для сервиса, а какой для ресурса.
5. [junior] [на скорость] Что такое Saturation и какой пример в веб-сервисе?
Ответ
Насыщение это насколько ресурс заполнен и сколько работы ждёт в очереди. В веб-сервисе это, например, очередь запросов за свободным соединением с базой, заполненный пул соединений или длинная очередь на диске. Оно предупреждает заранее: очередь растёт, а покупатели ещё ничего не заметили.
Что хотят услышать: очередь или заполнение ресурса и пример.
Красный флаг: «это загрузка процессора».
6. [junior] [часто] Чем симптом отличается от причины и на что вешать алерт?
Ответ
Симптом это то, что чувствует пользователь: ошибки, медленные ответы, недоступность. Причина это то, из-за чего это случилось: процессор, диск, очередь, упавшая оплата. Алерт вешают на симптом, например «доля 5xx выше 5% пять минут», а причины показывают на дашборде и ищут по логам и трейсам. Тревога на причину («процессор выше 80% минуту») часто будит человека напрасно. Исключение: причина с прогнозом до симптома («диск заполнится через 4 часа»).
Что хотят услышать: определения, пример и вопрос «что сделает человек, когда проснётся».
Красный флаг: «чем больше алертов, тем надёжнее».
7. [junior] [на скорость] Что такое усталость от алертов (alert fatigue)?
Ответ
Это привычка игнорировать тревоги, потому что многие из них ложные или не требуют действий. Каждый лишний алерт снижает доверие ко всем остальным, и настоящий инцидент можно пропустить. Лечат удалением шумных алертов и переносом причин с алертов на дашборды.
Что хотят услышать: причина и последствие.
Красный флаг: предлагают добавить ещё алертов.
8. [middle] [часто] Что такое кардинальность метрики и почему важно, какие метки в неё класть?
Ответ
Метки это пары «имя=значение», по которым метрику разрезают на группы. Каждая уникальная комбинация значений это отдельный ряд чисел, и число таких комбинаций называют кардинальностью. Метки с небольшим набором значений (метод, шаблон маршрута, код ответа) безопасны. Метка вроде user_id с миллионом значений даёт миллион рядов и валит хранилище по памяти. Подробности вроде номера пользователя оставляют логам.
Что хотят услышать: ряд на комбинацию значений, примеры безопасных и опасных меток.
Красный флаг: «метки бесплатны».
9. [junior] Почему лампочка /healthz может быть зелёной, а заказы не проходить?
Ответ
/healthz проверяет только то, что процесс жив и отвечает. Оплата, база и очередь он не проверяет. Поэтому при отказе оплаты процессор в норме, /healthz отвечает ok, а заказы возвращают 502. Алерт нужно вешать на симптом (доля ошибок на /api/orders), а не только на «жив».
Что хотят услышать: разница между «жив» и «работает», симптом как основа алерта.
Красный флаг: «если /healthz зелёный, значит всё в порядке».
10. [junior] [на скорость] Чем метрика отличается от лога?
Ответ
Метрика это число, меняющееся со временем: мало места, легко складывать, удобно для графиков и алертов, но про конкретный запрос ничего не знает. Лог это строка о конкретном событии с подробностями: легко найти «что случилось с запросом», но по миллиону строк общую картину глазами не увидеть.
Что хотят услышать: общая картина против деталей.
Красный флаг: «логи заменяют метрики».
11. [middle] Что такое профилирование и когда его включают?
Ответ
Профилирование это замер, какие функции кода тратят процессорное время и память. Результат рисуют флеймграфом: чем шире полоса функции, тем больше времени в ней. Это четвёртый столп наблюдаемости рядом с метриками, логами и трейсами. Его включают не первым, а когда сигналы уже привели в один сервис: процессор или память растут без роста трафика, подозрение на утечку, а трейс показывает время внутри самого сервиса. Снять профиль можно встроенным инструментом языка (например, pprof у Go), через eBPF (механизм ядра Linux, смотрит на работающую программу без правки кода) или непрерывным профилированием (например, Pyroscope), которое хранит профили постоянно.
Что хотят услышать: «какая функция в коде»; после метрик и трейса; флеймграф; pprof, eBPF, непрерывное профилирование.
Красный флаг: профилирование включают вместо метрик или «везде и всегда» без повода.
12. [middle] Как по сигналам оценить серьёзность проблемы и понять, стабильна она или ухудшается?
Ответ
Смотрят на четыре золотых сигнала с тремя вопросами: сколько пользователей затронуто (доля ошибок и медленных запросов), какая функция не работает (заказ важнее каталога) и растёт ли плохое число. Ситуация стабильна, если число держится на одном уровне (например, 10% ошибок уже десять минут) или если p99 выше цели, но p95 ровный. Если число растёт, сначала останавливают рост (например, откатом), а причину ищут потом. Стабильная проблема даёт время на разбор, но не значит, что чинить не надо.
Что хотят услышать: охват, важность функции, динамика; разные действия для стабильного и растущего.
Красный флаг: сразу откатывать всё подряд или считать стабильную проблему несущественной.
Проверено на версиях
Стенд «Магазин» из load-tester/project/shop: Docker Compose v2, Prometheus 3.15, Grafana 13.2, Loki 3.7, Tempo 2.10, Alloy 1.20, jq 1.7. Октябрь 2026.
Итог урока: ты умеешь
- Объяснить разницу между мониторингом и наблюдаемостью на примере «Магазина».
- Перечислить четыре золотых сигнала и применить их к сервису.
- Разложить метрики по RED и USE и объяснить, почему начинать нужно с RED.
- Отличить симптом от причины и выбрать, на что вешать ночной алерт.
- Оценить серьёзность по сигналам (сколько затронуто, стабильно ли) и сказать, когда нужно профилирование.
Где это применить
- DevOps, урок 8.1: наблюдаемость и цели надёжности: тот же материал на сервисе «Заметки» с
awkпо access-логу nginx. - load-tester, урок 7.1: метрики и Prometheus: как числа из
/metricsпопадают в хранилище и каких бывает четыре типа.
Дальше: урок 1.2. SLI, SLO и бюджет ошибок: почему не среднее, где «хорошо работает» превратится в число, цель и допустимую долю сбоев.
Проверь себя
Короткий тест по уроку: 5 вопросов из банка в 30. Засчитывается только полностью правильный ответ, порог 60%. Каждая новая попытка даёт другие вопросы, пока банк не закончится. Ответы видны после проверки.
Тест работает с включённым JavaScript.