Пара 19 / декабрь
Наблюдаемость: метрики, логи и трассировка Выбрать сигнал для пользовательского симптома и отделить доступность API от качества модели.
Выбрать сигнал для пользовательского симптома и отделить доступность API от качества модели. Мониторинг следит за заранее выбранными показателями и условиями. Наблюдаемость помогает объяснять внутреннее поведение по внешним сигналам. Метрики удобны для тенденций и агрегатов, логи — для конкретных событий, трассировка — для пути запроса через компоненты. Для одного маленького сервиса полный tracing-stack избыточен; добавляем request_id и показываем идею на схеме. Вместо «соберём всё» задаём вопросы: доступен ли predict, сколько ошибочных запросов, выросло ли время ответа? Каждый сигнал должен иметь понятный источник и действие при отклонении.
Контекст Сигнал полезен, когда помогает принять решение.
Выбрать сигнал для пользовательского симптома и отделить доступность API от качества модели.
Мониторинг следит за заранее выбранными показателями и условиями. Наблюдаемость помогает объяснять внутреннее поведение по внешним сигналам. Метрики удобны для тенденций и агрегатов, логи — для конкретных событий, трассировка — для пути запроса через компоненты. Для одного маленького сервиса полный tracing-stack избыточен; добавляем request_id и показываем идею на схеме. Вместо «соберём всё» задаём вопросы: доступен ли predict, сколько ошибочных запросов, выросло ли время ответа? Каждый сигнал должен иметь понятный источник и действие при отклонении.
Наблюдение начинается с вопроса Сигнал полезен, когда помогает принять решение.
01 Вопрос пользователя
→ 02 Метрика: сколько
→ 03 Лог: что произошло
→ 04 Trace: где задержка
Аналогия: приборы на панели, журнал событий и карта поездки.
Мониторинг следит за заранее выбранными показателями и условиями. Наблюдаемость помогает объяснять внутреннее поведение по внешним сигналам. Метрики удобны для тенденций и агрегатов, логи — для конкретных событий, трассировка — для пути запроса через компоненты. Для одного маленького сервиса полный tracing-stack избыточен; добавляем request_id и показываем идею на схеме. Вместо «соберём всё» задаём вопросы: доступен ли predict, сколько ошибочных запросов, выросло ли время ответа? Каждый сигнал должен иметь понятный источник и действие при отклонении.
Четыре полезных сигнала Задержка, поток запросов, ошибки и насыщение ресурсов.
01 Задержка
02 Запросы / секунду
03 Ошибки
04 Насыщение
Аналогия: скорость машины не показывает, сколько пассажиров не смогли сесть.
Latency показывает время ответа, traffic — объём запросов, errors — долю неуспешных ответов, saturation — приближение к пределу ресурсов. Одной CPU-метрики недостаточно: сервис может ждать диск или внешнюю сеть. Среднее время может скрывать медленный хвост, поэтому позже исследуем p95. Ошибки клиента и ошибки сервера имеют разный смысл, их не смешиваем без объяснения. Метрики инфраструктуры дополняют пользовательский путь, но не заменяют его. Просим студентов предложить причину медленного API при низком CPU: очередь, I/O, ожидание зависимости.
Инфраструктура и ML-качество Исправный API может возвращать бесполезный прогноз.
01 API доступен
→ 02 Контракт корректен
→ 03 Артефакт известен
→ 04 Качество модели отдельно
Аналогия: исправность весов и полезность диеты — разные вопросы.
На этом курсе проверяем доступность, контракт, задержку и версию артефакта. В реальном ML-проекте качество модели, распределение входов и дрейф данных — отдельная область ответственности. Нельзя выводить точность модели из 200 OK или из того, что контейнер healthy. Не собираем сырые персональные входы для «наблюдаемости». Полезный инфраструктурный сигнал — версия модели в healthz и журнале; он помогает сопоставить изменение поведения с выпуском. StudyPulse возвращает синтетическую оценку, поэтому не обсуждаем её как научный показатель.
Команды и наблюдения Ubuntu / Bash, lab
docker compose up -d
curl -sS http://127.0.0.1:8000/healthz
curl -sS http://127.0.0.1:8000/metrics
curl -i -H "Content-Type: application/json" -d '{"hours":-1}' http://127.0.0.1:8000/predict
docker compose logs --tail 10 appНеверный вход — ожидаемая клиентская ошибка; не объявляем её падением инфраструктуры. Ожидаемое наблюдение: Есть healthz, текстовый metrics и структурированный лог отказа 400 с request_id.
Разбор результата Есть healthz, текстовый metrics и структурированный лог отказа 400 с request_id.
Неверный вход — ожидаемая клиентская ошибка; не объявляем её падением инфраструктуры.
Недоступность: внешний curl и up от Prometheus. Медленно: histogram времени, нагрузка и ресурсы. 400: контракт входа и лог request_id. 500: исключение, версия и зависимость. Неверная версия: healthz, image ID, checksum артефакта. Количество 400 может быть полезно для UX, но не всегда включается в доступность сервиса как ошибка сервера.
На macOS HTTP, метрики и operational logs в контейнерном варианте одинаковы. Native macOS unified log — другой механизм и не требует journalctl.
curl -sS http://127.0.0.1:8000/metrics
docker compose logs --tail 10 appКоманды macOS необходимо проверить на конкретном устройстве. HTTP, метрики и operational logs в контейнерном варианте одинаковы. Native macOS unified log — другой механизм и не требует journalctl.
Карта сигналов Выберите пять симптомов: недоступен, медленный, 400, 500, неверная версия. Для каждого назначьте полезную метрику или лог и проверочную команду. Сгенерируйте нормальный и неверный запрос; сопоставьте ответ с журналом. Укажите, какие вопросы о модели эти сигналы не решают. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Недоступность: внешний curl и up от Prometheus. Медленно: histogram времени, нагрузка и ресурсы. 400: контракт входа и лог request_id. 500: исключение, версия и зависимость. Неверная версия: healthz, image ID, checksum артефакта. Количество 400 может быть полезно для UX, но не всегда включается в доступность сервиса как ошибка сервера.
Что должно получиться Таблица симптом → сигнал → проверка → действие.
У каждого сигнала есть диагностическая цель; API и ML-качество не смешаны.
Недоступность: внешний curl и up от Prometheus. Медленно: histogram времени, нагрузка и ресурсы. 400: контракт входа и лог request_id. 500: исключение, версия и зависимость. Неверная версия: healthz, image ID, checksum артефакта. Количество 400 может быть полезно для UX, но не всегда включается в доступность сервиса как ошибка сервера.
Ошибка для диагностики Зелёный CPU используется как оправдание любых пользовательских проблем.
Симптом → Гипотеза → Проверка
Недоступность: внешний curl и up от Prometheus. Медленно: histogram времени, нагрузка и ресурсы. 400: контракт входа и лог request_id. 500: исключение, версия и зависимость. Неверная версия: healthz, image ID, checksum артефакта. Количество 400 может быть полезно для UX, но не всегда включается в доступность сервиса как ошибка сервера.
Основные выводы Нет, healthz проверяет выбранные признаки исправности сервиса. Чтобы сопоставлять события одного запроса без сохранения чувствительного содержимого. Нет, healthz проверяет выбранные признаки исправности сервиса.
Чтобы сопоставлять события одного запроса без сохранения чувствительного содержимого.
После пары Выбрать минимальный набор сигналов для собственного учебного ML-проекта.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://prometheus.io/docs/prometheus/latest/getting_started/ https://sre.google/sre-book/monitoring-distributed-systems/
Среднее и p95: увидеть медленный хвост Это искусственные числа для объяснения, не результат измерения. p95 считаем методом nearest rank: 19-е значение из 20. Prometheus оценивает квантиль по buckets и может дать другое число.
Пара 20 / декабрь
Prometheus: сбор метрик и PromQL Поднять Prometheus, проверить target и построить rate и p95 из метрик StudyPulse.
Поднять Prometheus, проверить target и построить rate и p95 из метрик StudyPulse. Приложение предоставляет /metrics в формате экспозиции. Prometheus по расписанию делает scrape и сохраняет samples с именем метрики, labels и временем. up показывает успешность scrape, но up=1 ещё не означает правильный predict. Targets помогают отличить ошибку сбора от отсутствия пользовательского трафика. В учебном compose target app:8000 доступен по внутренней сети; Prometheus UI публикуется только на localhost:9090. Интервал 5 секунд удобен для демонстрации, а не универсальная настройка production. Первые rate-запросы требуют нескольких наблюдений.
Контекст Prometheus периодически читает endpoint метрик.
Поднять Prometheus, проверить target и построить rate и p95 из метрик StudyPulse.
Приложение предоставляет /metrics в формате экспозиции. Prometheus по расписанию делает scrape и сохраняет samples с именем метрики, labels и временем. up показывает успешность scrape, но up=1 ещё не означает правильный predict. Targets помогают отличить ошибку сбора от отсутствия пользовательского трафика. В учебном compose target app:8000 доступен по внутренней сети; Prometheus UI публикуется только на localhost:9090. Интервал 5 секунд удобен для демонстрации, а не универсальная настройка production. Первые rate-запросы требуют нескольких наблюдений.
Pull и временные ряды Prometheus периодически читает endpoint метрик.
01 app /metrics
→ 02 Scrape каждые 5 s
→ 03 Ряды
→ 04 PromQL
Аналогия: дежурный регулярно переписывает показания счётчика.
Приложение предоставляет /metrics в формате экспозиции. Prometheus по расписанию делает scrape и сохраняет samples с именем метрики, labels и временем. up показывает успешность scrape, но up=1 ещё не означает правильный predict. Targets помогают отличить ошибку сбора от отсутствия пользовательского трафика. В учебном compose target app:8000 доступен по внутренней сети; Prometheus UI публикуется только на localhost:9090. Интервал 5 секунд удобен для демонстрации, а не универсальная настройка production. Первые rate-запросы требуют нескольких наблюдений.
Counter, gauge, histogram Тип метрики определяет корректную операцию.
01 Counter → rate
02 Gauge → текущее
03 Histogram → распределение
Аналогия: одометр, спидометр и распределение длительностей поездок.
Counter накапливает события и сбрасывается при рестарте; rate считает скорость с учётом сбросов. Gauge показывает текущее значение, например активные запросы. Histogram распределяет длительности по накопительным bucket и хранит count/sum. Для средней длительности делим rate(sum) на rate(count); для p95 используем histogram_quantile с rate bucket. p95 — оценка границы, ниже которой находится около 95% наблюдений, а не максимальное время. При малом числе событий оценка нестабильна. Наш текстовый histogram учебный и потокобезопасный, в рабочем проекте обычно берут официальный client.
Labels и кардинальность Каждая уникальная комбинация labels создаёт отдельный ряд.
01 Метрика
→ 02 Ограниченные labels
→ 03 Набор рядов
→ 04 Стоимость хранения
Аналогия: отдельная папка на каждую категорию удобна, на каждую песчинку — нет.
route=/predict и status=200 имеют ограниченное множество значений. request_id, email и произвольный URL в label создают огромное число рядов и могут раскрыть данные. В учебном приложении неизвестные пути нормализуются в route=other. Request ID оставляем в логе, не в метриках. Это не косметическое правило: число рядов влияет на память, хранение и скорость запросов. Не добавляйте label только потому, что поле существует в запросе.
Команды и наблюдения Ubuntu / Bash, lab; PromQL в UI :9090
docker compose -f compose.yaml -f compose.monitoring.yaml up -d
python3 scripts/load.py --requests 60 --concurrency 3
# PromQL expressions
up{job="studypulse"}
sum(rate(studypulse_http_requests_total{route="/predict"}[1m]))
histogram_quantile(0.95, sum by (le) (rate(studypulse_request_duration_seconds_bucket{route="/predict"}[1m])))Команды PromQL вводятся в интерфейсе Prometheus, не в Bash. Трафик ограничен своим localhost. Ожидаемое наблюдение: Target UP; rate появляется после нескольких scrape; p95 возвращается при наличии достаточных наблюдений.
Разбор результата Target UP; rate появляется после нескольких scrape; p95 возвращается при наличии достаточных наблюдений.
Команды PromQL вводятся в интерфейсе Prometheus, не в Bash. Трафик ограничен своим localhost.
Проверьте target, затем сырой /metrics, затем окно времени. После рестарта rate учитывает reset, но короткое окно и редкий трафик дадут нестабильный график. Для p95 сохраняется label le: sum by (le)(rate(...bucket[1m])). Ошибки сервера: sum(rate(studypulse_http_requests_total{route="/predict",status=~"5.."}[1m])) / sum(rate(studypulse_http_requests_total{route="/predict"}[1m])). При нулевом трафике отношение не имеет информативного значения.
На macOS Prometheus и PromQL одинаковы в контейнерном варианте. Если нужный учебный image не поддерживает архитектуру, проверьте manifest и используйте согласованную платформу/VM, а не случайную подмену версии.
docker compose -f compose.yaml -f compose.monitoring.yaml up -d
python3 scripts/load.py --requests 60 --concurrency 3Команды macOS необходимо проверить на конкретном устройстве. Prometheus и PromQL одинаковы в контейнерном варианте. Если нужный учебный image не поддерживает архитектуру, проверьте manifest и используйте согласованную платформу/VM, а не случайную подмену версии.
Из счётчика в график Откройте Targets и проверьте app:8000. Выполните ограниченную нагрузку; подождите 15–30 секунд. Сравните значение counter, rate и p95 в PromQL. Перезапустите app и объясните сброс счётчика без объявления отрицательной скорости. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Проверьте target, затем сырой /metrics, затем окно времени. После рестарта rate учитывает reset, но короткое окно и редкий трафик дадут нестабильный график. Для p95 сохраняется label le: sum by (le)(rate(...bucket[1m])). Ошибки сервера: sum(rate(studypulse_http_requests_total{route="/predict",status=~"5.."}[1m])) / sum(rate(studypulse_http_requests_total{route="/predict"}[1m])). При нулевом трафике отношение не имеет информативного значения.
Что должно получиться Три запроса PromQL с интерпретацией, а не только скриншоты.
Единицы подписаны; request_id отсутствует в labels; up не подменяет smoke-test.
Проверьте target, затем сырой /metrics, затем окно времени. После рестарта rate учитывает reset, но короткое окно и редкий трафик дадут нестабильный график. Для p95 сохраняется label le: sum by (le)(rate(...bucket[1m])). Ошибки сервера: sum(rate(studypulse_http_requests_total{route="/predict",status=~"5.."}[1m])) / sum(rate(studypulse_http_requests_total{route="/predict"}[1m])). При нулевом трафике отношение не имеет информативного значения.
Ошибка для диагностики Выражение rate без данных воспринимают как ошибку приложения.
Симптом → Гипотеза → Проверка
Проверьте target, затем сырой /metrics, затем окно времени. После рестарта rate учитывает reset, но короткое окно и редкий трафик дадут нестабильный график. Для p95 сохраняется label le: sum by (le)(rate(...bucket[1m])). Ошибки сервера: sum(rate(studypulse_http_requests_total{route="/predict",status=~"5.."}[1m])) / sum(rate(studypulse_http_requests_total{route="/predict"}[1m])). При нулевом трафике отношение не имеет информативного значения.
Основные выводы Он накопительный; нужна скорость изменения за окно. Высокая кардинальность и ненужное раскрытие идентификаторов. Он накопительный; нужна скорость изменения за окно.
Высокая кардинальность и ненужное раскрытие идентификаторов.
После пары Подготовить два PromQL-запроса и объяснить их единицы и условия интерпретации.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://prometheus.io/docs/prometheus/latest/getting_started/ https://prometheus.io/docs/practices/histograms/ https://prometheus.io/docs/practices/naming/