У Grafana есть возможность показывать статус, у Grafana есть возможность показывать данные во времени. Однако, как это ни парадоксально, у Grafana до настоящего момента не было удобного способа показывать статус во времени!
Мы представляем свой плагин — Statusmap panel. Он позволяет наглядно отобразить состояния набора объектов за выбранный промежуток времени. В качестве примера, демонстрирующего работу плагина, представим себе множество локаций, в которых для кого-то готовят кофе:
Можно увидеть, как Никки экономит электроэнергию, Герри быстро пополняет запасы воды, кофемашина Валеры частенько барахлит, а на Бифросте Wi-Fi явно лучше, чем на лунной станции, где, похоже, с водой совсем туго.
Выглядит интересно? Но начнём с того, как мы вообще к этому пришли.
Зачем?
Для лучшей визуализации данных мы поставили перед собой простую задачу: отобразить состояния набора timeseries за промежуток времени. Под набором объектов подразумеваются разные timeseries: они могут отличаться набором лейблов и именем. При этом значения timeseries должны удобно, т.е. без костылей, отображаться в текст и цвет.
Актуальные для нашего бизнеса примеры использования такой визуализации — это здоровье серверов или подов Kubernetes, результаты проверки HTTP-сервисов. Так в компании «Флант» и родился плагин к Grafana под названием Statusmap. Размышляя над великим множеством возможностей его применения и для других задач, мы быстро приняли на себя обязательство поделиться кодом с мировым сообществом. Но неужели никто до нас не решал эту задачу?
Почему не готовое?
Задача в действительности популярная, так что первопроходцами мы в ней не стали. Началось всё с того, что у нас было несколько дашбордов с крутыми плагинами Status Panel и Status Dot. Эти плагины позволяют отобразить текущее состояние набора объектов, например, хостов или подов… или кофемашин в разных частях света.
Всё шло хорошо, пока нам не захотелось видеть статусы этих объектов во времени. Первым, самым простым решением было добавить обычный граф с галочкой stacked.
По задумке Status Panel + stacked Graph позволили бы видеть состояние объектов «на сейчас» и развитие ситуации во времени. Однако stacked Graph не очень нагляден:
- цветом отмечаются различные timeseries, а не значения, которые отображаются цветом для Status Dot или Status Panel. То есть цвета для двух графиков не одинаковые и это сбивает с толку;
- если среди значений появляется
null
, то графики проваливаются.
Попробовали приспособить стандартный Heatmap — не получилось: плагин работает с осью Y только на уровне значений и не умеет выводить там лейблы. Тогда мы попробовали следующие плагины для Grafana:
- Carpet plot — группирует значения по дню и по выбранному фрагменту дня;
- Discrete Panel — хороший плагин, но нам нужно дискретно показывать статусы во времени;
- Status By Group Panel — хорошее улучшение к Status panel, позволяющее отображать множество статусов, однако всё ещё без нужных нам возможностей.
По результатам всех проведённых исследований мы сформулировали следующие требования к плагину:
- выделенная чёткая строка графика для каждого объекта;
- имя объекта отображается по оси Y и задаётся в поле легенды;
- по одному объекту может быть несколько статусов — в таких случаях самый значимый будет отображаться цветом, а остальные показываться в tooltip’е;
- корзины (buckets) отображать шириной не менее заданной (5 px), т.к. в однопиксельные неудобно наводить мышкой;
- ручное управление цветом — возможность задать цвет каждому числовому значению из дискретного набора.
Позвольте теперь сделать небольшое отступление про графики Heatmap, Prometheus и дискретные статусы…
Немного теории
Классический heatmap — это 3-мерный график:
- по оси X откладывается время,
- по оси Y — возможные значения некоторой величины,
- по оси Z — количество наблюдаемых значений в данный момент времени.
Стандартный плагин Heatmap отображает ось Z цветом — например, от белого до красного или через градиент зелёный-жёлтый-красный. Это очень хорошо работает для непрерывных значений: времени отклика, длины очереди, количества запросов к серверу… В случае дискретных статусов для набора объектов нужно следующее: по оси Y отобразить имена объектов, которые мы мониторим, а по оси Z — показать для каждого объекта наблюдаемые в данный момент времени статусы… Но стойте! Что значит множество статусов объекта в момент времени? Попробую описать.
Те, кто использует Prometheus с Grafana, знают про step
или interval
— настройку на закладке Query. Если там указать 1m
, а данные вы собираете с интервалом в 5s
, то при выполнении простого запроса метрики coffee_maker_status
Prometheus вернёт каждое 12-ое значение, а 11 значений на графике уже никак не увидеть. Как улучшить ситуацию?
Первым, что приходит в голову, — воспользоваться функциями агрегации — например, *_over_time(coffee_maker_status[1m])
. Какую именно функцию взять? Время разобраться с тем, как представляется статус в метриках Prometheus. В большинстве случаев статус обозначается неким набором значений. Например, для coffee_maker_status
могут быть такие значения статуса:
- 0 — ok,
- 1 — off,
- 2 — no beans,
- 3 — no water,
- 4 — fail.
Далее казалось бы всё просто: взять количество нулей, единиц, двоек и т.д. в течение одной минуты… и мы имеем отличные данные для отображения на графике! Но у Prometheus свой взгляд на это: coffee_maker_status[1m]
— это range vector, а потому выражения вроде max_over_time(coffee_maker_status[1m]==2)
или count_values_over_time(coffee_maker_status[1m], 3)
, которые очень бы подошли, невозможны.
Всё отлично работает, если в метрике есть два значения: 0
(статус не наблюдался) и 1
(статус наблюдался), — а сам статус хранится в лейбле. Тогда можно составлять такие запросы: (max_over_time(coffee_maker_status{status="3"}[1m]) == 1) *3
Что же делать с метрикой, у которой несколько значений? Заметка «Composing range vector functions in PromQL» дала идею превратить метрику с дискретными значениями в метрики с лейблами. Это можно сделать с помощью такого recording rule:
- record: coffee_maker_status:discrete
expr: |
count_values("status", coffee_maker_status)
Это правило трансформирует метрику coffee_maker_status
так: если пришло значение 3
, то Prometheus создаёт метрику coffee_maker_status:discrete{status="3"}
со значением 1. И так — для каждого наблюдаемого значения.
Обычно статусы определены заранее, поэтому можно составить набор запросов, чтобы не пропускать нужные значения. Легенда у всех запросов должна совпадать, чтобы можно было сгруппировать значения:
Теперь, если в течение минуты кофемашина была выключена 30 секунд (статус off — 1
), а остальное время работала (статус ok — 0
), то у нас будет информация о выключении, т.к. плагин получит два значения с одной легендой за один момент во времени: 0
от query A и 1
от query B.
Хорошо: мы придумали, как агрегировать данные о дискретных статусах и при этом не терять информацию. Осталось придумать, как объединять данные на основе легенды и отрисовывать их на панели.
Плагин Statusmap
К тому, что описано выше мы, конечно же, пришли не сразу, но когда всё это сложилось воедино, стало понятно, что по сути не хватает механизма отрисовки. Теперь такой механизм есть — Statusmap panel plugin, который умеет следующее:
- значения в каждой точке времени группируются в корзины по совпадению текста легенд, указанных в Query;
- каждому тексту легенды соответствует своя строка на графике и текст отображается как метка на оси Y, а пустые значения отображаются пробелом или как
0
: - для любого значения можно задать точный цвет корзины:
- если в корзину попало несколько значений, то цвет будет взят для того значения, которое определено выше на вкладке Colors, а при наведении на корзину отображаются все значения, которые попали в неё:
- плагин умеет формировать
interval
для запроса к Prometheus, чтобы корзины не превращались в пиксельные линии.
В итоге получается очень удобное представление статуса нескольких объектов. Причем можно посмотреть как текущий статус (это самые правые корзины), так и статус объекта во времени.
Где взять?
Исходный код Grafana Statusmap plugin распространяется под свободной лицензией MIT (по аналогии с другими плагинами для Grafana). На данный момент он доступен в нашем GitHub. И мы искренне надеемся, что в ближайшее время он попадёт и в репозиторий плагинов Grafana.
И напоследок — иллюстрация, как Statusmap помогает визуализировать данные со статусами подов из production-кластера Kubernetes:
Автор: diafour