Содержание урока · 9 разделов
Ответ на вопрос «какие маршруты ошибаются» должен занимать секунды, а не воспоминание о том, где лежит нужное выражение. Промежуточный вариант из прошлого урока — интерфейс Prometheus на порту 9090 — хорош для отладки одного запроса, но набирать его заново каждое утро, а потом ещё и объяснять коллеге, куда вписать статус, — занятие из ненужных движений. Эту витрину и собирает Grafana: grafana дашборд — набор панелей, каждая панель — сохранённый запрос плюс способ его нарисовать. Содержимое панели не живёт отдельно от Prometheus: вы пишете тот же PromQL, Grafana ходит с ним к источнику данных и рисует ответ, а хранятся только настройки отображения. Практический порядок от этого не меняется: сначала запрос в интерфейсе Prometheus, потом панель — выражение, возвращающее пустоту или мусор на 9090, в Grafana чиниться не станет. Панель принимает и несколько запросов сразу: линии будут наложены на одни оси — удобно для трафика и его доли ошибок бок о бок, но злоупотреблять не стоит: пять линий на панели читаются, пятнадцать — нет.
Grafana дашборд: от источника данных до первой панели#
Первый проход занимает минуты. В свежей Grafana добавляется источник данных: Configuration, Data sources, Prometheus, адрес http://prometheus:9090 — в docker-compose контейнеры ходят друг к другу по именам сервисов. Дальше New dashboard, Add visualization, выбор источника — открывается редактор панели: слева типы визуализации, ниже редактор запросов, в который вставляется выражение, отлаженное на 9090. Time series — рабочая лошадка мониторинга: линии по времени; Stat показывает одно текущее число крупно, Gauge добавляет к нему шкалу с порогами, Table удобен для табличных срезов вроде «топ маршрутов». Вверху справа живёт общее время дашборда: относительный диапазон вроде Last 1 hour применяется ко всем панелям сразу, и переключение на Last 24 hours перерисовывает всё окно без правки единого запроса. Там же включается автообновление с интервалами от секунд до минут; ставить его чаще скрейп-интервала бессмысленно — новых точек Prometheus всё равно не отдал, и это прямое следствие pull-модели из первого урока. Для разового вопроса без сохранения есть отдельный режим Explore: тот же редактор запросов без витрины — очередь между «проверил на 9090» и «сделал панель».
Панели Grafana: время, легенда и единицы#
Три настройки дают девяносто процентов читаемости панели. Первая — легенда: у каждого ряда есть формат отображения, и шаблон в фигурных скобках подставляет значение лейбла; конструкция {{route}} заменит длинную строку серии на короткое имя маршрута из урока про лейблы. Вторая — единицы измерения: p99 возвращает доли секунды, и с единицей s Grafana сама покажет 340 ms вместо сырого 0.34; для долей есть percent, для памяти — bytes. Третья — пороги с цветом на Stat-панелях: зелёный до двух процентов ошибок, жёлтый до пяти, красный дальше — витрина начинает говорить сама. На этом месте ловится классика: панель p99 без юнита выглядит как подозрительно маленькое число, читатель решает, что это миллисекунды, — и половина команды принимает решения на неверной шкале. Юнит задаётся один раз при создании панели, и это дешевле, чем потом объяснять на планёрке, почему у всех числа разные.
Переменные: один дашборд вместо десяти копий#
Как только панелей становится больше трёх, появляется желание срезать всё сразу по маршруту или инстансу. Копировать дашборд на каждый вариант не нужно: в настройках создаётся переменная типа Query, источником значений служит запрос label_values(http_requests_total, route) — Grafana спросит у Prometheus все значения лейбла и превратит их в выпадающий список над дашбордом. В запросах панелей подставляется $route, и выбор значения перерисовывает все панели разом. Две детали обязательны. Первая: разрешив выбор нескольких значений, меняйте точное равенство на регулярку route=~"$route" — мультивыбор разворачивается в регулярное выражение с чередованием, и равенство перестаёт находить серии. Вторая: переменная — часть экспортируемого JSON дашборда, поэтому шаблон переживает пересоздание витрины и переезжает между стендами. Переменные превращают дашборд из личного файла в общий инструмент: один экран на команду, а срезы каждый выбирает себе сам.
Три запроса — три панели первого ряда
GO# Трафик по маршрутам
sum by (route) (rate(http_requests_total[5m]))
# Доля ошибок
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# p99 латентности
histogram_quantile(
0.99,
sum by (le, route) (rate(http_request_duration_seconds_bucket[5m]))
)Каждое выражение живёт в своей панели: трафик рисуется линиями с легендой {{route}}, доля ошибок — одной линией с единицей percent, p99 — линией с единицей s. Окна и группировки взяты из двенадцатого урока без изменений; панель добавляет только оформление. Все три запроса сначала проверяются в Prometheus: панель наследует результат, а не чинит его. Деление доли ошибок возвращает число от нуля до единицы — потому и единица percent, а не проценты в лоб.
Переменная и её использование в запросе
GO# Источник переменной: все значения лейбла route
label_values(http_requests_total, route)
# Запрос панели со срезом по переменной
sum by (status) (rate(http_requests_total{route=~"$route"}[5m]))label_values — не функция PromQL, а специальный запрос редактора переменных к API Prometheus: он возвращает все значения лейбла route живых серий. В панели переменная подставляется на место $route, а матчер со знаком равенства и тильдой делает выбор устойчивым к мультивыбору, когда переменная разворачивается в регулярку с чередованием значений.
Четыре панели из встроенных метрик#
Обещание из урока про встроенные метрики закрывается четырьмя панелями без единой собственной метрики. Скорость процессора: rate(process_cpu_seconds_total[5m]) — линия в ядрах; упор в лимит контейнера виден как прямая вдоль потолка графика. Резидентная память: process_resident_memory_bytes с единицей bytes — вечный рост при ровной куче повод заглянуть в профили, а не в настройки GC. Дескрипторы: process_open_fds — стабильный уровень норма, вечный подъём означает незакрытые файлы и соединения. Аптайм: time() - process_start_time_seconds — пила перезапусков, на которой плановый деплой отличается от ночных падений с одного взгляда. Типы визуализации расставьте по смыслу: четыре линии — Time series, аптайм — Stat с порогом «меньше часа — красный». Эти панели одинаковы для любого Go-сервиса, поэтому их удобно держать в шаблоне, с которого начинаются новые проекты.
Плотность точек и окна: за что отвечает Prometheus#
Панель не умеет показывать деталей больше, чем есть в данных: при скрейпе раз в 30 секунд между точками всегда будет полминуты тишины, и ни одна настройка Grafana это не поправит. Второе ограничение — окно запроса: линия с rate по окну 5m на диапазоне сутки рисуется из сотен точек, каждая из которых уже сглажена, и всплеск пятиминутной давности на ней тонет; тот же запрос на диапазоне час показывает пик целиком. Отсюда рабочая пара: обзорная панель с окном час и суточным диапазоном — тренды и сравнение дней, и тревожная с окном 5m и диапазоном час — то, что открывается, когда обзорная кольнула. Диапазон при этом влияет и на стоимость: доля ошибок за месяц с окном в минуту — тяжёлый запрос, который Prometheus честно посчитает, но медленно; правило из двенадцатого урока про четыре скрейп-интервала в окне остаётся неизменным, а ширина окна выбирается под вопрос панели, а не под привычку.
Панели первого дашборда#
| Панель | Запрос | Единицы | Что отвечает |
|---|---|---|---|
| Трафик | sum by (route) (rate(http_requests_total[5m])) | reqps | сколько запросов в секунду и на каких маршрутах |
| Доля ошибок | деление rate пятисотых на rate всех | percent | какой процент запросов падает |
| p99 латентности | histogram_quantile по бакетам | s | какой порог не проходят 1% самых медленных |
| CPU | rate(process_cpu_seconds_total[5m]) | ядра | сколько ядер съедает процесс |
| Аптайм | time() - process_start_time_seconds | s | как давно живёт процесс и когда перезапускался |
Первая панель новичка — http_requests_total без rate: линия уходит вверх и остаётся там навсегда, а после рестарта обрывается к нулю и растёт снова. Информации в таком графике ноль: счётчик отвечает «сколько запросов было суммарно», а не «сколько идёт сейчас», и всплеск трафика на нём не виден. На панели ставьте только rate-выражения для счётчиков; сырую серию оставьте табличному режиму Prometheus для разбора. Из той же серии мелочь с датчиками: gauge без единицы отдаётся числом вроде 8.7e+06, и не каждый читатель дашборда расшифрует это как мегабайты.
Готовый дашборд экспортируется в JSON и живёт в репозитории: конфигурация витрины проходит код-ревью и переживает пересоздание Grafana. В серверных инсталляциях дашборды провижинятся с диска — Grafana читает JSON из каталога при старте, а переменные и источники данных едут вместе с файлом. На старте не увлекайтесь: пять панелей красного метода полезнее каталога из сорока графиков, который никто не открывает.
Проверка: сырой счётчик на панели
Что произойдёт, если вставить в панель сырой счётчик http_requests_total без rate?
Проверка: легенда панели
Как в легенде панели показать значение лейбла route вместо полного набора лейблов?
Сколько линий нарисует панель
Прочитайте код и выберите, что он выведет:
# Лейбл route принимает 3 значения, лейбл status — 2
sum by (route) (rate(http_requests_total[5m]))Соберите панель со срезом по переменной
Допишите источник переменной и её подстановку в запрос панели.
Путь запроса до сохранённой панели
Расставьте шаги превращения отлаженного запроса в панель дашборда.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: панели и переменные
У дашборда есть принципиальная граница: он отвечает только тогда, когда на него смотрят. Днём витрина выручает, но в три ночи никто не разглядывает линии, и следующим шагом становятся алерты — правило с порогом, которое выполняет запрос вместо человека и находит дежурного; конвейер собираем в следующем уроке. Практика требует запущенного стека: Go-сервис из урока про первый /metrics endpoint, Prometheus и Grafana поднимаются локально в docker-compose — браузерная песочница сайта не исполняет ни Grafana, ни client_golang. Соберите ряд из пяти панелей таблицы выше, добавьте переменную по маршруту, сравните обзорное окно и тревожное на одном дне данных — и дайте ссылку коллеге: витрина, которую нельзя открыть по ссылке, существует только для её автора. Если метрики кладёт свой middleware, пригодится урок про собственный middleware chi, где таймер и статус пишутся руками; на gin то же самое делает кастомная middleware. Запросы к панелям опираются на урок про PromQL, вся программа — на странице Prometheus.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Панель — сохранённый запрос плюс способ отрисовки; запросы пишутся тем же PromQL и сначала проверяются на 9090.
- Общее время дашборда применяется ко всем панелям сразу; автообновление короче скрейп-интервала бессмысленно.
- Легенда с шаблоном {{route}} и правильные юниты — настройки, без которых панель читается неверно.
- Переменная из label_values делает один дашборд на команду; для мультивыбора нужен матчер с тильдой.
- Четыре панели из встроенных серий — CPU, память, дескрипторы, аптайм — закрывают базу без собственных метрик.
- Сырой счётчик на панели рисует вечный рост: счётчики рисуются только через rate.
- Дашборд отвечает, когда на него смотрят; непрерывный контроль — задача алертов из следующего урока.
Домашнее задание
Поднимите локально стек из сервиса, Prometheus и Grafana и соберите ряд из пяти панелей таблицы урока: трафик, доля ошибок, p99, CPU, аптайм. Задайте каждой панели юниты и легенду с шаблоном по лейблу, затем добавьте переменную по маршруту и проверьте мультивыбор. Сначала вставьте в одну панель сырой счётчик без rate — посмотрите на вечный рост, потом замените на rate и сравните. Финал — экспортируйте дашборд в JSON и положите рядом с кодом сервиса: так витрина переживёт пересоздание стенки.
Личные заметкиПусто
Вопросы и ответы по уроку
Grafana хранит данные метрик сама?
Нет: Grafana — витрина и алертный фронтенд, а данные остаются в источнике. Панель ходит к Prometheus с вашим запросом при каждом обновлении, поэтому форма графика зависит от ретеншена Prometheus, а не от настроек Grafana.
Почему панель p99 показывает 0.34, а коллега видит 340 ms?
Разные единицы на панели: сырые доли секунды против выбранного юнита s, при котором Grafana переводит значения в миллисекунды для читаемости. Договоритесь об одном юните на панели — число станет одинаковым.
Нужно ли программировать дашборды, или можно собрать мышкой?
На старте — мышкой: редактор панелей закрывает всё базовое, включая переменные и пороги. JSON-экспорт и провижининг с диска подключаются, когда дашборд признан правильным и должен жить в репозитории рядом с кодом сервиса.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
WebSocket в Go: real-time чаты и уведомления
Курс WebSocket на Go: gorilla/websocket, эхо-сервер, hub-паттерн, комнаты, авторизация соединений, ping/pong, reconnect и масштабирование. 25 уроков.
- Поднимать WS-сервер и апгрейдить HTTP-соединения
- Писать readPump/writePump без гонок на каждое соединение
- Строить hub: регистрацию, рассылку и комнаты
- Авторизовывать соединения и ограничивать сообщения