Prometheus
July 1, 2023

Разбираемся с диапазоном векторов в Prometheus

Диапозон векторов (Range Vectors) в Prometheus немного неинтуитивны, если только вы не прочитали и не разобрались с документаций. Есть такие? Имеется ввиду, что нужно потратить некоторое время, чтобы всё неверно понять, а затем искать случайные публикации в блогах, подобные этому, чтобы понять, как же всё-таки работает это решение с открытым исходным кодом, верно?

Шутки в сторону, давайте начнём с уточнения некоторых определений.

Что такое Вектор?

Поскольку Prometheus — это база данных временных рядов, все данные находятся в контексте некоторой временной метки (момента времени). Ряд, который сопоставляет метку времени с записанными данными, называется временным рядом . На жаргоне Prometheus набор связанных временных рядов называется вектором . Давайте обсудим пример, чтобы проиллюстрировать это лучше.

Предположим, что http_requests_totalэто вектор, представляющий общее количество HTTP-запросов, полученных службой. Векторы позволяют нам указывать дополнительные измерения, называемые «метками», чтобы мы могли помечать данные. Для примера:

// набор временных рядов, представляющих количество запросов с кодом ответа HTTP `200`.
http_requests_total{code="200"}

// набор временных рядов, представляющих количество запросов, обслуживаемых обработчиком `/api/v1/query`.
http_requests_total{handler="/api/v1/query"}

Таким образом, у нас есть вся детальная информация, связанная с количеством обслуживаемых HTTP-запросов, но при этом есть возможность ее агрегирования, если это необходимо.

Синтаксически http_requests_totalотносится ко всему набору именованых временных рядов. И, добавляя {code="200"}или {handler="/api/v1/query"}, мы лишь выбираем нужное подмножество.

Типы векторов

Кроме того, Prometheus определяет два типа векторов, в зависимости от того, с чем сопоставляются метки времени:

Мгновенный вектор (Instant vector) — набор временных рядов, где каждая временная метка соответствует одной точке данных в этот «момент». Мы можем видеть единственное значение, записанное с отметкой времени 1608481001 ниже в ответе.

curl 'http://localhost:9090/api/v1/query' \
  --data 'query=http_requests_total{code="200"}' \
  --data time=1608481001
{
  "metric": {"__name__": "http_requests_total", "code": "200"},
  "value": [1608481001, "881"]
}

Вектор диапазона (Range vector) — набор временных рядов, где каждая временная метка соответствует «диапазону» точек данных, записанных на некоторую продолжительность в прошлом. Они не могут существовать без определенной продолжительности, называемой «диапазоном», который используется для построения списка значений для каждой метки времени. В приведенном ниже примере обратите внимание на список значений, сопровождаемый отметкой времени, вплоть до30sпрошлого с1608481001.

curl 'http://localhost:9090/api/v1/query' \
  --data 'query=http_requests_total{code="200"}[30s]' \
  --data time=1608481001
{
  "metric": {"__name__": "http_requests_total", "code": "200"},
  "values": [
    [1608480978, "863"],
    [1608480986, "874"],
    [1608480094, "881"]
  ]
}

Имея все определения, мы приходим к двум выводам об этих типах векторов:

  1. Мгновенные векторы могут быть нанесены на диаграмму; Векторы диапазона не могут. Это связано с тем, что построение диаграммы предполагает отображение точки данных на оси Y для каждой метки времени на оси X. Мгновенные векторы имеют одно значение для каждой метки времени, в то время как векторы диапазона имеют много таких значений. Для построения диаграммы по метрике не определено, как отображать несколько точек данных для одной временной метки во временном ряду.
  2. Мгновенные векторы можно сравнивать и выполнять над ними арифметические действия; Векторы диапазона не могут. Это также связано со способом определения операций сравнения и арифметических операций. Для каждой временной метки, если у нас есть несколько значений, мы не знаем, как добавить [1] или сравнить их с другими временными рядами аналогичного характера.

Зачем вообще нужны Range Vectors?

Теперь мы понимаем, что векторы диапазона нельзя использовать для построения графиков или агрегирования. Поэтому вполне естественно спросить, почему они вообще существуют? Ответ прост: счетчики (counters). Счетчик является одним из основных типов в системе мониторинга, помимо датчиков (gauges) и таймингов (timings). Мы попытаемся понять, как взаимодействуют счетчики и векторы диапазона, продолжив наш предыдущий пример.

Допустим, мы хотим узнать, сколько запросов обрабатывает наш сервис прямо сейчас. Наша метрика http_requests_total{code="200",handler="/api/v1/query"}— это мгновенный вектор со значениями, которые представляют собой монотонно увеличивающийся счетчик [2]. Этот счетчик измеряет общее количество запросов, полученных нашей службой. Мы знаем, что Prometheus неоднократно очищал этот счетчик в прошлом, поэтому мы могли бы просто начать с запроса значения счетчика:

curl 'http://localhost:9090/api/v1/query' \
  --data 'query=http_requests_total{code="200",handler="/api/v1/query"}'
{
  "metric": {"__name__": "http_requests_total", "code": "200", "handler":"/api/v1/query"},
  "value": [1608437313, "881"]
}

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

Лучший способ — взять текущее значение счетчика и вычесть значение счетчика, которое было видно пятнадцать минут назад. Это даст нам точное число запросов, полученных экземпляром (instance) за период времени. Чтобы представить это в PromQL, мы берем мгновенный вектор и добавляем нашу длительность [15m]. Эта часть называется селектором диапазона и преобразует мгновенный вектор в вектор диапазона. Затем мы используем функцию increase, которая эффективно вычитает точку данных в начале диапазона из точки в конце [3].

curl 'http://localhost:9090/api/v1/query' \
  --data 'query=increase(http_requests_total{code="200",handler="/api/v1/query"}[15m])'
{
  "metric": {"__name__": "http_requests_total", "code": "200", "handler":"/api/v1/query"},
  "values": [
    [1608437313, "18.4"]
  ]
}

Опишем запрос словами: «прирост общего количества запросов за последние пятнадцать минут». Ответ также содержит одно число, представляющее ответ, чего мы и ожидали. Результат находится в форме мгновенного вектора, который теперь может быть дополнительно нанесен на диаграмму или агрегирован.

Функции вектора диапазона

Подобно increase(range-vector), приведенные ниже функции PromQL также работают только с векторами диапазона:

Все они возвращают мгновенные векторы в результате их вычисления. Таким образом, мы можем сделать вывод, что векторы диапазона полезны в качестве входных данных для этих функций, которые работают с «диапазоном» значений.

Диапазон векторов — это нечто большее, чем просто перечисленные выше функции и curl запросы, но мы расскажем об этом в другом посте блога.

Сноски

  1. Неопределенное поведение не означает что нет способа заставить эти операции работать. Все это означает, что в данной реализация предпочли не делать этого. Возможно это было сделано для упрощения реализации или потому, что нет подходящего способа заставить его работать последовательно в различных вариантах использования.
  2. Монотонно увеличивающееся значение счетчика никогда не уменьшается; она либо увеличивается, либо остается неизменной. Prometheus допускает только один случай, когда значение счетчика может уменьшиться, и это во время перезапуска цели. Если значение счетчика упадет ниже предыдущего записанного значения, то функции вектора диапазона rate и increase будут считать, что цель перезапущена, и добавят все значение к существующему, которое известно. Вот почему мы всегда должны применять rate, а затем sum, а не sum, а затем rate.
  3. «Эффективно» здесь ключевое слово. increaseфактически также выполняет экстраполяцию, поскольку запрошенная продолжительность может не иметь точек данных, точно выровненных в «начале» и «конце» диапазона.

Источник: https://satyanash.net/software/2021/01/04/understanding-prometheus-range-vectors.html