Работа с метриками

Victoria Metrics — это высокопроизводительное решение для обработки и анализа временных рядов данных. Для общения с Victoria Metrics используется язык запросов PromQL. Каждая метрика представляет собой временной ряд, можно сказать, отдельную таблицу. Имя таблицы формируется из набора лейблов, а значения представляют собой одно число, зафиксированное в разные моменты времени: [(day1, t1), (day2, t2), (day3, t3) ...].

Запросы могут: извлекать значения из конкретного ряда за определённый период, получать данные сразу из нескольких рядов, а также группировать или выполнять вычисления над результатами.
Для отображения на графике нам необходим хотя бы один временной ряд: время будет отображаться по горизонтали, а значение метрики — по вертикали. Чтобы нарисовать несколько линий, нужно получить несколько временных рядов.

Примеры:

Базовый запрос для Cloud Compute

libvirt_domain_info_virtual_cpus

В результате достанутся все метрики, у которых название – libvirt_domain_info_virtual_cpus, и при этом могут быть любые другие лейблы, например с разными verb или url.

Метрики с применение лейблов

Доступные лейблы (фильтры):

  • instance_name - имя виртуальный машины на которой работает продукт;
  • job - название продукта используемое в Victoria metrics;
  • cluster - имя кластера, куда входит виртуальная машина;
  • instance - адрес виртуальной машины;
  • server - путь до рабочей среды продукта


libvirt_domain_info_virtual_cpus{job="Compute libvirt"}[1m]

В результате мы получим график с фильтром по job.

Дальше в инструкции будут следующие обозначения

http_requests_total - метрика;
{} - обозначение, что внутри находятся строковые лейблы;
[] - обозначение, что внутри находятся числовые лейблы;
job, group - названия лейблов;
=, != - операторы сравнения.
В этом запросе мы использовали доступные лейблы, которые указаны непосредственно в самой метрике. Все доступные лейблы можно получить во время формирования запроса в конструкторе, а при создании запроса вручную их необходимо запросить заранее.

Какие бывают типы лейблов

Рассмотрим такой пример:

libvirt_domain_info_virtual_cpus{job="Compute libvirt"}[1m]

Здесь используются основные типы лейблов. Их всего два:
- Строки — чтобы запрашивать лейблы и их значения.
- Числа — только double, они могут участвовать в функциях.

Внутри фигурных скобок {job="Compute libvirt"} находятся строковые типы данных, название лейблов указываются не в кавычках, а их значение ВСЕГДА записывается в двойных ""

[1m] это числовой тип лейблов. В запросе, которые мы разбираем, он для каждой точки собирает массив предыдущих точек за 1 минуту.

Операторы, селекторы

Метрики и лейблы можно объединить по равно/неравно и регулярными выражениями, подошло/не подошло.

Операторы сравнения:

= - равно

!= - неравно

=~ - совпадение

!~ - не совпадает

Вложенные запросы


OR

Логическое «ИЛИ» над одним тегом делается с помощью регулярных выражений.

Внутри лейбла обозначается как |

libvirt_domain_info_virtual_cpus{instance=~"10.130.24.45|10.130.5.129"}


Вывод: вывести данные метрики с лейблами a ИЛИ b ИЛИ с

AND

Логическое «И» над одним тегом делается, если в запросе написать тег несколько раз с разными условиями.

Лейблы должны перечисляться через запятую.

libvirt_domain_info_virtual_cpus{instance=~"10.130.24.45", instance_name=~"vm_name"}


Вывод: вывести все instance, начинающиеся на 10.130.24.45 И instance_name c vm_name

Операторы для алертов

В метриках можно использовать обычную алгебру и логику.
Пример: метрика с леблом instance значения которой умножены на 3 и надо вывести только те, которые меньше или равны 10

libvirt_domain_info_virtual_cpus{instance=~"10.130.24.45|10.130.5.129"} * 3 <= 10


Запрос, который вернет пересечение: значения, у которых полностью совпадающий набор тегов в обоих запросах:

metric1 and metric2{instance="10.130.24.45"}
Запрос, который вернет разницу. Например, посчитаем сколько RAM свободно в данный момент:

Для это мы делаем запрос с вычитание libvirt_domain_memory_stats_usable_bytes(Сколько сейчас используется) из libvirt_domain_info_maximum_memory_bytes(Сколько памяти всего выделено)

libvirt_domain_info_maximum_memory_bytes{instance="10.130.24.45"} - libvirt_domain_memory_stats_usable_bytes{instance="10.130.24.45"}

Аналогично or (объединение) и unless (дополнение).

Агрегация

Предположим, есть такие метрики:

libvirt_domain_info_maximum_memory_bytes{instance_name="host1"}
и
libvirt_domain_info_maximum_memory_bytes{instance_name="host2"}

Это разные метрики, и если рисовать их на графике прямым запросом libvirt_domain_info_maximum_memory_bytes, получим две разные линии. Если мы хотим объединить их в один график, нужна агрегация.

Памятка

 Если мы не указываем лейблы внутри метрики, но применяем агрегацию, то получится следующее:
 Ко всем данным метрики, будь то разные машины или разные кластера, применится агрегация. (если это SUM, все значение будут сложены)

 Если мы хотим указать лейблы, которые должны быть суммированы то используем следующую конструкцию

  sum by (app, instance) (http_requests_total)

Дальше будут перечислены агрегации и их примеры:

Виды агрегаций


SUM

Например, у метрики libvirt_domain_info_maximum_memory_bytes - 2 и более линии на графике, из-за того, что выводятся все виртуальные машины с этой метрикой. Если мы хотим узнать общее значение по всем виртуальным машинам в метрике, то мы используем такой запрос:

SUM(libvirt_domain_info_maximum_memory_bytes)

Если требуется объединить графики с одинаковыми лейблами, например (app - приложение, instance - сервер ), мы используем следующее построение метрики:

sum by (app, instance) (libvirt_domain_info_maximum_memory_bytes)



MIN

Для того, чтобы выбрать  минимальные значения каждого шага в нескольких графиках, используйте такой запрос:

MIN(libvirt_domain_info_maximum_memory_bytes)


MAX

Для того, чтобы выбрать  максимальные значения каждого шага в нескольких графиках, используйте такой запрос:

MAX(libvirt_domain_info_maximum_memory_bytes)


AVG

Для того, чтобы выбрать  средние значения каждого шага в нескольких графиках, используйте такой запрос:

AVG(libvirt_domain_info_maximum_memory_bytes)


GROUP

Для того, чтобы выбрать равные  значения каждого шага в нескольких графиках, используйте такой запрос:

GROUP(libvirt_domain_info_maximum_memory_bytes)


COUNT

Агрегация, которая подсчитывает количество элементов в запросе:

COUNT(libvirt_domain_info_maximum_memory_bytes)


COUNT_VALUES

Агрегация, которая подсчитывает количество элементов с одинаковым значением:

COUNT_VALUES(libvirt_domain_info_maximum_memory_bytes)

Или, второй пример:

count_values("version", build_version)


BOTTOMK
Агрегация, которая подсчитывает наименьшее число k элементов по выборочному значению:

BOTTOMK(k, libvirt_domain_info_maximum_memory_bytes)

Или, например необходимо получить 5 самых наименьших значений количества HTTP-запросов во всех экземплярах:

BOTTOMK(5, libvirt_domain_info_maximum_memory_bytes)


TOPK
Агрегация, которая подсчитывает наибольшее число k элементов по выборочному значению:

TOPK(k, libvirt_domain_info_maximum_memory_bytes)

Или, например необходимо получить 5 самых больших значений количества памяти во всех экземплярах:

TOPK(5, libvirt_domain_info_maximum_memory_bytes)


QUANTILE

Квантили — это значения, которые делят ряд наблюдений на равные части. В запросе ниже, мы выводим значения между 0 и 3 для метрики:

QUANTILE(0, libvirt_domain_info_maximum_memory_bytes, 3)


LIMITK

Для выборки  временных рядов из n - элементов, например, для проверки меток и их значений:

LIMITK(n, libvirt_domain_info_maximum_memory_bytes)

Для выборки 10 временных рядов, например, для проверки меток и их значений, мы могли бы написать:

LIMITK(10, libvirt_domain_info_maximum_memory_bytes)

LIMIT_RATIO

Представляет собой отношение между фактическим использованием ресурса и его лимитом. Это позволяет понять, насколько близко использование ресурса к установленному пределу. Элементы выборки с приблизительно 𝑟 отношением, если `𝑟 > 0`, и дополнение таких выборок, если `𝑟 = -(1.0 - 𝑟)`:

LIMIT_RATIO(r, libvirt_domain_info_maximum_memory_bytes)


Для детерминированной выборки примерно 10% временных рядов мы могли бы написать

LIMIT_RATIO(0.1, libvirt_domain_info_maximum_memory_bytes)

Функции

RATE
Используется для вычисления скорости изменения счетчика за заданный период времени. Она особенно полезна для анализа метрик, которые представляют собой счетчики, такие как количество запросов, байтов переданных по сети или использованное время CPU. Применяется к возрастающим счетчикам, чтобы показать прирост за какое-то время.

Например, количество обработанных запросов, переданных байт и т.д.:

rate(libvirt_domain_info_maximum_memory_bytes{app="nginx"}[5m])

Выполняющие действия функции RATE

  • считает скорость прироста в секунду (запросов/сек);
       
  •  подходит только для `counter` т.к. полагается на возрастание;
       
  •  учитывает сбросы метрики на 0, например при рестартах приложений;
       
  •  экстраполируется;
       
  •  есть `irate` для резко прыгающих счетчиков.

Дополнение

Сервисы, которые выставляют метрики, не живут вечно — они рано или поздно обновятся или перезапустятся. Их метрики при этом сбросятся и начнут отсчитываться с нуля. Это нормально и учтено в модели данных и запросов Prometheus. Важно об этом помнить, чтобы использовать подходящие типы и функции, например:

rate() учитывает такую ситуацию, но ожидает, что значение метрики всегда возрастает — без этого математика не сойдется. Поэтому совершенно нормально сделать счетчик «сколько приложение обработало запросов», и начинать его с нуля после рестарта.


С другой стороны, deriv() работает с gauge, но не обрабатывает ситуации, когда метрика сбросилась в ноль. Обычно это и не нужно: gauge используется для показателей, которые не отсчитываются с нуля, например, свободная RAM. Когда приложение перезапустилось, метрика сразу будет показывать актуальное значение и периодически его обновлять.
Таким образом, не нужно писать метрики в БД на своей стороне или как-то обеспечивать их персистентность. Кроме того, вам поначалу может быть вообще все равно, потому что это такой частный случай: ну и что, подумаешь — иногда метрику штормит при деплоях или когда приложение упало, какая-то секунднаямелочь. Но это станет важно в дальнейшем, когда вы захотите сделать алерты. С алертами эта особенность станет огромной головной болью, причиной ложных срабатываний, костылей и переписываний. Лучше сразу разобраться, как ведут себя метрики при рестартах приложений, и пользоваться подходящими функциями в запросах. Даже если на первый взгляд кажется, что некоторые функции похожи и делают одно и то же.