Документацияbreadcrumb arrow Grafanabreadcrumb arrow Документация GlaberGraphbreadcrumb arrow Учебные материалыbreadcrumb arrow Как создавать оповещения Grafana с помощью InfluxDB и языка запросов Flux

Как создавать оповещения Grafana с помощью InfluxDB и языка запросов Flux

Grafana Alerting представляет собой мощный новый подход к наблюдению за системами и управлению реагированием на инциденты. Хотя платформа оповещения, пожалуй, наиболее известна своими тесными интеграциями с Prometheus, она работает с множеством популярных источников данных, включая InfluxDB. В этом учебном руководстве мы узнаем, как создавать оповещения Grafana с помощью InfluxDB и более нового языка запросов Flux. Мы рассмотрим пять распространённых сценариев — от самых простых до самых сложных. Вместе эти пять сценариев станут отличным руководством почти для любого типа запросов оповещения, которые вы хотите создать с помощью Grafana и Flux.

Прежде чем мы углубимся в наши сценарии оповещения, стоит рассмотреть развитие двух популярных языков запросов InfluxDB: InfluxQL и Flux. Изначально InfluxDB использовала InfluxQL в качестве языка запросов, использующего синтаксис, похожий на SQL. Но начиная с InfluxDB v1.8 компания представила Flux — «открытый функциональный язык сценариев для работы с данными, предназначенный для запроса, анализа и обработки данных». «Flux, — говорится далее в официальной документации, — объединяет код для запроса, обработки, записи и воздействия на данные в единый синтаксис. Язык спроектирован так, чтобы быть удобным, читаемым, гибким, композируемым, тестируемым, расширяемым и совместно используемым».

В следующих пяти примерах мы увидим, насколько мощным и гибким может быть новый язык запросов Flux. Мы также увидим, насколько хорошо Flux сочетается с Grafana Alerting.

Пример 1: создание оповещения, когда значение выше или ниже заданного порога

В нашем первом примере используется распространённый реальный сценарий для InfluxDB и Grafana Alerting. Популярная в IoT- и пограничных приложениях, InfluxDB отлично подходит для локального наблюдения в реальном времени. В этом примере, как и во многих следующих, мы рассмотрим гипотетический сценарий, в котором мы отслеживаем несколько резервуаров с жидкостью на производственном предприятии. Этот сценарий, основанный на реальном применении InfluxDB и Alerting, позволит нам проработать различные схемы оповещения Grafana, переходя от простейшей к самой сложной.

Для примера 1 рассмотрим следующий сценарий: мы отслеживаем один резервуар A5, для которого храним данные о температуре в реальном времени. Нам нужно убедиться, что температура в этом резервуаре всегда выше 30 °C и ниже 60 °C.

Мы хотим написать оповещение Grafana, которое будет срабатывать всякий раз, когда температура в резервуаре A5 пересекает нижний порог 30 °C или верхний порог 60 °C.

Для этого мы: создадим правило оповещения Grafana, добавим запрос Flux, а затем добавим выражения в правило оповещения.

Создание правила оповещения Grafana

  1. Откройте меню оповещения Grafana и выберите Alert rules.
  2. Нажмите New alert rule.
  3. Дайте вашему правилу оповещения имя, а затем выберите Grafana managed alert. Для InfluxDB вы всегда будете создавать правило под управлением Grafana.

Добавление начального запроса Flux в правило оповещения

Находясь в разделе Step 2 страницы правила оповещения, вы увидите три блока: редактор запросов (A) и два раздела с метками B и C. Вы будете использовать эти три раздела для построения своего правила. Давайте пройдёмся по ним по одному.

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

```
 from(bucket: "RetroEncabulator")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "TemperatureData")
|> filter(fn: (r) => r["Tank"] == "A5")
|> filter(fn: (r) => r["_field"] == "Temperature")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")
```

Это довольно типичный запрос Flux. Давайте разберём его функцию за функцией. Мы начинаем с использования функции from() для выбора правильного бакета, в котором находятся данные нашего резервуара. Затем мы используем функцию range() для фильтрации строк на основе временных ограничений. После этого мы пропускаем наши данные через три функции filter(), чтобы сузить результаты. Мы выбираем конкретный measurement (специальное ключевое слово в InfluxDB), затем наш рассматриваемый резервуар (A5), а затем конкретное field (ещё одно специальное ключевое слово в InfluxDB). После этого мы передаём данные в функцию aggregateWindow(), которая прореживает наши данные до определённых периодов времени, и, наконец, в функцию yield(), которая указывает, какой конечный результат мы хотим: mean.

Этот запрос Flux создаст график временного ряда, подобный этому:

оповещения grafana из запросов flux

Добавление выражений в правило оповещения Grafana

Теперь, когда данные появились в настройке нашего правила, наш следующий шаг — создать выражение. Перейдите к разделу B. Для этого сценария мы хотим создать выражение Reduce, которое сведёт вышеуказанное к одному значению. На этом изображении видно, что мы выбрали сведение наших данных временного ряда к значению Last из входа A. В этом случае оно возвращает значение 53 градуса Цельсия для резервуара A5:

оповещения grafana из запросов flux

Наконец, нам нужно создать математическое выражение, по которому Grafana будет оповещать. В нашем случае мы напишем выражение с двумя условиями, разделёнными оператором OR ||. Мы хотим запускать оповещение всякий раз, когда наш результат в разделе B меньше 30 или больше 60. Это выглядит так: $B < 30 || $B > 60:

оповещения grafana из запросов flux

Установите условие оповещения в C - expression. Теперь мы можем предварительно просмотреть наше оповещение. Вот предварительный просмотр этого оповещения, когда состояние — Normal:

оповещения grafana из запросов flux

А вот предварительный просмотр этого оповещения, когда состояние — Alerting:

оповещения grafana из запросов flux

Обратите внимание, что выражение Reduce выше необходимо. Без него при предварительном просмотре результатов Grafana отобразит invalid format of evaluation results for the alert definition B: looks like time series data, only reduced data can be alerted on.

💡Совет: если ваша локаль всё ещё упорно использует градусы Фаренгейта, мы можем изменить приведённый выше запрос Flux, добавив (перед оператором aggregateWindow) функцию map() для преобразования (или сопоставления) значений из °C в °F. Обратите внимание, что мы не создаём новое поле. Мы просто переназначаем существующее значение.

flux
|> map(fn: (r) => ({r with _value: r._value * 1.8 + 32.0}))

Заключение

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

Пример 2: как создать оповещение Grafana из двух запросов и двух условий

Давайте немного разнообразим пример 2 и покинем наш воображаемый производственный завод. Представьте, что вы ассистент великого доктора Эмметта Брауна из «Назад в будущее», и Док поставил перед вами следующую задачу: «Я хочу получать оповещение каждый раз, когда выполняются оба условия для путешествия во времени: когда скорость транспортного средства достигает 88 миль в час и объект генерирует 1,21 гигаватта электроэнергии».

Предположим, мы отслеживаем эти данные в InfluxDB и Grafana. Предположим также, что каждый из вышеуказанных источников данных происходит из разных бакетов. Как нам оповещать об этом? Как использовать Grafana и Flux для оповещения о двух различных условиях, происходящих из двух различных источников данных?

Добавление двух запросов Flux в правило оповещения Grafana

Как и в примере 1, давайте сначала подготовим наши запросы. Наш запрос для данных о транспортном средстве очень похож на наш предыдущий запрос. Мы используем from(), range() и последовательность функций filter(). Затем мы используем AggregateWindow() и yield(), чтобы ещё больше сузить наши данные. В этом случае результатом является временной ряд, отслеживающий скорость нашего DeLorean 1983 года:

flux
from(bucket: "vehicles")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "VehicleData")
|> filter(fn: (r) => r["VehicleType"] == "DeLorean")
|> filter(fn: (r) => r["VehicleYear"] == "1983")
|> filter(fn: (r) => r["_field"] == "velocity")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")

Наш второй запрос будет запускать оповещение всякий раз, когда наш ресурс электроэнергии (удар молнии в башню с часами Хилл-Вэлли) достигает необходимых 1,21 гигаватта. Такой запрос будет выглядеть очень похоже на наш запрос скорости транспортного средства:

flux
from(bucket: "HillValley")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "ElectricityData")
|> filter(fn: (r) => r["Location"] == "clocktower")
|> filter(fn: (r) => r["Source"] == "lightning")
|> filter(fn: (r) => r["_field"] == "power")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")

Теперь мы готовы изменить эти данные с помощью выражений.

Добавление выражений в правило оповещения Grafana

  1. Давайте теперь используем те же шаги, чтобы свести каждый запрос к последнему (самому свежему) значению. Сведение запроса A к одному значению может выглядеть так:

    оповещения grafana из запросов flux

  2. А здесь мы сводим запрос B:

    оповещения grafana из запросов flux

  3. Теперь в разделе C нам нужно создать математическое выражение, по которому будет происходить оповещение. В этом случае мы используем оператор AND &&, чтобы указать, что должны выполняться два условия: значение C (сведённое значение из запроса A) должно быть больше 88.0, а значение D (сведённое значение из запроса B) должно быть больше 1.21. Мы записываем это как $C > 88.0 && $D > 1.21

    оповещения grafana из запросов flux

А вот предварительный просмотр наших оповещений:

оповещения grafana из запросов flux

💡Совет: если в ваших данных в InfluxDB оказалось неоправданно много цифр после десятичной запятой (например, 1.2104705741732575, как показано выше), и вы хотите, чтобы ваши оповещения Grafana были более разборчивыми, попробуйте использовать {{ printf “%.2f” $values.D.Value }}. Например, в аннотации Summary мы могли бы написать следующее:

{{  $values.D.Labels.Source }} at the {{  $values.D.Labels.Location }} has generated {{ printf "%.2f"  $values.D.Value }} jigowatts.`

Это отобразится следующим образом: оповещения grafana из запросов flux)

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

Заключение

В этом примере мы показали, как создать оповещение на основе Flux, которое использует два различных условия из двух различных запросов, использующих данные из двух различных источников данных. Для примера 3 мы сменим направление и займёмся другим популярным сценарием оповещения: как создать оповещение на основе агрегированного (за день) значения.

Пример 3: как создать оповещение Grafana на основе агрегированного (за день) значения

Один из самых частых запросов на форуме сообщества Grafana связан с построением графиков суточного потребления и выработки электроэнергии. Такие данные очень часто хранятся в InfluxDB. В этом примере мы увидим, как агрегировать данные временных рядов в значение за день, а затем оповещать по нему.

Предположим, наш счётчик электроэнергии отправляет показания в InfluxDB раз в час и содержит общее количество кВт·ч, использованных за этот час. Мы хотим написать запрос, который агрегирует эти почасовые значения в значение за день, а затем создать оповещение, которое срабатывает, когда потребление электроэнергии (кВт·ч) превышает 5 000 кВт·ч в день.

Добавление начального запроса Flux в правило оповещения Grafana

  1. Давайте начнём с рассмотрения типичного запроса и результирующего графика временного ряда для наших почасовых данных за 7-дневный период. Такой запрос показан ниже:

    flux
    from(bucket: "RetroEncabulator")
    |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
    |> filter(fn: (r) => r["_measurement"] == "ElectricityData")
    |> filter(fn: (r) => r["Location"] == "PlantD5")
    |> filter(fn: (r) => r["_field"] == "power_consumed")
    |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
    |> yield(name: "power")

    Здесь мы видим ту же схему функций Flux, что и в примерах 1 и 2. Такой запрос создаст график, похожий на следующий:

    оповещения grafana из запросов flux

  2. Теперь давайте скорректируем наш запрос, чтобы рассчитать суточное потребление. Со многими источниками данных это может быть довольно сложной операцией. Но с Flux, просто изменив параметры функции aggregateWindow, мы можем рассчитать суточное потребление за тот же 7-дневный период:

    flux
    from(bucket: "RetroEncabulator")
      |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
      |> filter(fn: (r) => r["_measurement"] == "ElectricityData")
      |> filter(fn: (r) => r["Location"] == "PlantD5")
      |> filter(fn: (r) => r["_field"] == "power_consumed")
      |> aggregateWindow(every: 1d, fn: sum)
      |> yield(name: "power")

    Обратите внимание, как мы изменили нашу функцию aggregateWindow() на aggregateWindow(every: 1d, fn: sum). Это приводит к графику, подобному следующему:

    оповещения grafana из запросов flux

  3. Добавьте выражения в правило оповещения Grafana.

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

    Как и раньше, давайте сведём наш запрос к одному значению:

    оповещения grafana из запросов flux

    Теперь создайте математическое выражение, по которому будет происходить оповещение, и задайте поведение оценки. В этом случае мы хотим написать $B > 5000:

    оповещения grafana из запросов flux

    И теперь мы оповещаем о нашем суточном потреблении электроэнергии всякий раз, когда превышаем 5000 кВт·ч. Вот предварительный просмотр нашего оповещения:

    оповещения grafana из запросов flux

Заключение

Построение графиков и агрегирование потребления электроэнергии — распространённый вариант использования комбинации InfluxDB и Grafana. С помощью Flux мы увидели, насколько просто можно сгруппировать данные по дням, а затем оповещать по этому суточному значению. В следующих двух примерах мы рассмотрим более сложную форму оповещения Grafana: многомерные оповещения.

Пример 4: создание динамического (многомерного) оповещения Grafana с помощью Flux

Давайте вернёмся к нашим резервуарам с жидкостью из примера 1, но на этот раз предположим, что у нас есть 5 резервуаров (A5, B4, C3, D2 и E1). Теперь мы отслеживаем температуру в пяти резервуарах: A5, B4, C3, D2 и E1.

Мы хотим создать одно многомерное оповещение, которое будет уведомлять нас всякий раз, когда температура в любом резервуаре ниже 30 °C или выше 60 °C.

Добавление начального запроса Flux в правило оповещения Grafana

Мы начинаем, как всегда, с написания нашего начального запроса. Он очень похож на наш запрос в примере 1, но обратите внимание, как наша третья функция filter() захватывает данные из всех пяти резервуаров, а не только из A5:

flux
from(bucket: "HyperEncabulator")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "TemperatureData")
|> filter(fn: (r) => r["MeasType"] == "actual")
|> filter(fn: (r) => r["Tank"] == "A5" or r["Tank"] == "B4" or r["Tank"] == "C3" or r["Tank"] == "D2" or r["Tank"] == "E1")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")

💡Совет: если бы резервуары отключались каждую ночь с 23:00 до 07:00, они, возможно, опускались бы ниже порога 30 °C. Если вы не хотите получать оповещения в эти часы, можно использовать функцию Flux hourSelection(), которая фильтрует строки по значениям времени в заданном диапазоне часов.

flux
|> hourSelection(start: 7, stop: 23)`

Запрос, подобный приведённому выше, создаст график временного ряда, подобный этому:

оповещения grafana из запросов flux

Добавление выражений в правило оповещения Grafana

  1. Мы создаём выражение Reduce, которое сведёт временной ряд для каждого резервуара к одному значению. Это даёт нам пять различных температур:

    оповещения grafana из запросов flux)

  2. Создайте математическое выражение, по которому будет происходить оповещение. Это точно такое же выражение, как в примере 1, $B < 30 || $B > 60:

    оповещения grafana из запросов flux

Как видно, три резервуара находятся в пределах допустимых порогов, а два резервуара пересекли верхнюю границу. Это вызовет оповещение для резервуаров D2 и E1.

Заключение

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

Пример 5: как создать динамическое (многомерное) оповещение Grafana с использованием нескольких запросов и нескольких порогов с помощью Flux

Для этого последнего примера продолжим с нашими пятью резервуарами и их пятью наборами данных. Предположим снова, что у каждого резервуара есть контроллер температуры с заданным значением, которое хранится в InfluxDB. Давайте всё усложним и предположим, что у каждого резервуара разное заданное значение, причём нам всегда нужно находиться в пределах 3 градусов от заданного значения.

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

Чтобы лучше представить эту задачу, вот таблица, представляющая наши пять резервуаров, их заданные значения температуры и допустимый диапазон:

Резервуар Заданное значение Допустимый диапазон (±3)
A5 45 от 42 до 48
B4 55 от 52 до 58
C3 60 от 57 до 63
D2 72 от 69 до 75
E1 80 от 77 до 83

С Grafana Alerting мы можем создать одно многомерное правило, охватывающее все 5 резервуаров, и использовать Flux для сравнения заданного и фактического значения для каждого резервуара. Иными словами, одно многомерное оповещение может отслеживать 5 отдельных резервуаров, каждый с разными заданными и фактическими значениями, но все с одним общим «допустимым порогом» (то есть разницей температур ±3 градуса).

Добавление начального запроса Flux в правило оповещения Grafana

Начнём с нашего запроса данных. Он похож на наши прошлые запросы, только теперь сложнее. Мы должны добавить дополнительные функции, чтобы привести наши данные в нужный формат, включая функции pivot(), map(), rename(), keep() и drop():

flux
from(bucket: "HyperEncabulator")
 |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
 |> filter(fn: (r) => r["_measurement"] == "TemperatureData")
 |> filter(fn: (r) => r["MeasType"] == "actual" or r["MeasType"] == "setpoint")
 |> filter(fn: (r) => r["Tank"] == "A5" or r["Tank"] == "B4" or r["Tank"] == "C3" or r["Tank"] == "D2" or r["Tank"] == "E1")
 |> filter(fn: (r) => r["_field"] == "Temperature")
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> pivot(rowKey:["_time"], columnKey: ["MeasType"], valueColumn: "_value")
 |> map(fn: (r) => ({ r with _value: (r.setpoint - r.actual)}))
 |> rename(columns: {_value: "difference"})
 |> keep(columns: ["_time", "difference", "Tank"])
 |> drop(columns: ["actual", "setpoint"])
 |> yield(name: "mean")

Обратите внимание, что выше мы вычисляем разницу между фактическим значением и заданным значением. Grafana анализирует результат из InfluxDB так: если обнаружен столбец _value, предполагается, что это временной ряд. Быстрый обходной путь — добавить следующую функцию rename():

flux
 |> rename(columns: {_value: "something"})

Приведённый выше запрос даёт следующий временной ряд:

оповещения grafana из запросов flux

Добавление выражений в правило оповещения Grafana

  1. Снова создаём выражение Reduce для приведённого выше запроса, чтобы свести каждое из вышеуказанных значений к одному значению. Это значение представляет разницу температур между заданным значением каждого резервуара и его фактической температурой в реальном времени:

    оповещения grafana из запросов flux

  2. Теперь создаём математическое выражение, по которому будет происходить оповещение. На этот раз мы создадим условие, которое проверяет, больше ли абсолютное значение нашего вычисления Reduce чем 3, abs($(B))>3.0:

    оповещения grafana из запросов flux

Теперь видно, что два резервуара, D2 и E1, вычисляются как истинные. Когда мы предварительно просматриваем оповещение, мы видим, что эти два резервуара запустят уведомление и изменят своё состояние с Normal на Alerting:

оповещения grafana из запросов flux оповещения grafana из запросов flux

Заключение

Запросы Flux и Grafana Unified Alerting — мощная комбинация для выявления практически любых условий, подходящих для оповещения, в вашем наборе данных или во всей вашей системе. Для получения дополнительной информации о Grafana Alerting посетите документацию здесь. Для получения дополнительной информации о языке запросов Flux вы также можете посетить эту документацию.