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

Или наоборот: иногда происходит какое-то событие, достаточно редкое (сбой, или наоборот срабатывание устройства), но хотелось бы понимать, насколько часто, и не меняется ли его частота в разные периоды - ведь это может быть признаком более серьезной проблемы.

Самое очевидное решение - писать данные в базу.
Но такая таблица будет постоянно расти, занимать всё больше места, а выборка за прошлый период может отрабатывать довольно долго, особенно если требуется уместить целый год в 900 пикселей графика.

Когда-то и я наступил на эти грабли - когда база на MongoDB распухла так, что не влезла в 32-битный компьютер. Но это другая история.

При этом далеко не всегда нужно точно знать, какая температура была на датчике 26 июля 2016 года в 14:33:15 - чаще достаточно просто усредненных значений.
Вот как раз для подобного давным-давно люди придумали RRD - Round-robin database.

Принцип простой: кольцевой буфер фиксированной длины, новые значения записываются в него одно за другим, а когда доходят до конца - начинают записываться с начала.
Мы всегда знаем, какое значение актуально сейчас, а какое - Х времени назад, если это время не превышает длину буфера.
Более того - таких буферов может быть несколько, с разным "размером" ячейки и с разной длиной, просто за больший период будут храниться данные с меньшей детализацией.

Для работы с подобным давным-давно существует инструмент rrdtools.
Это несколько утилит, которые работают с файлами RRD, позволяя создавать их, наполнять данными, извлекать данные и даже строить графики.

Например, нужно хранить температуру - типовая, простая задача.
Сначала создаем под нее файл базы данных:

rrdtool create termo.rrd -s 60 \
  DS:t:GAUGE:300:-50:U \
  RRA:LAST:0.5:1:10080 \
  RRA:AVERAGE:0.5:5:8064 \
  RRA:AVERAGE:0.5:60:8760

Создается файл termo.rrd, шаг данных - 60 секунд.
В нем будет один "источник данных", DS, который условно называется "t", имеет тип GAUGE (измерение, показатель), для него установлен период 300 секунд, в течении которых ожидаются хоть какие-то данные, минимальное значение -50, максимальное - не определено (U).
Данные обрабатываются "как есть", температуры ниже -50 отбрасываются, если в течении периода 300 сек ничего не записано - считаем, что данных нет, nan.

При этот определены 3 буфера, RRA:
Первый состоит из слотов по 1 60-секундному периоду, в каждом слоте будет содержать последнее поступившее значение, и всего их таких - 10080 штук, итого весь период 604800 секунд, 168 часов, 7 дней.
Второй состоит из слотов по 5 60-секундных периодов, т.е. 5 минут, в которых среднее значение за этот слот, и всего таких - 8064 штуки, то есть 40320 минут, 672 часа, 28 дней.
Третий - слоты по 60*60-секунд (1 час), весь период 8760 часов, 365 дней. Таким образом, максимальный период хранения данных - 1 год.

Параметр "0.5" говорит о том, что в слоте допускается до половины неизвестных (nan) значений.
То есть, если по какой-то причине части данных не было - игнорируем, и считаем по имеющимся.

Размер этого файла составил чуть больше 200 килобайт, и расти он уже не будет.
Самих записей не так уж много, поэтому выборка по ним будет работать быстро.

Для внесения данных есть команда update:

rrdtools update termo.rrd N:27.54

Эта команда запишет температуру 27.54 на текущее время.
Всё остальное - выбор последнего знанения, усреднения, раскладку по буферам и прочее - система сделает сама.

Можно вместо N указать unixtime - тогда данные будут добавлены для указанного времени - но для мониторинга обычно это не нужно.

В случае "парных" величин (температура-влажность, трафик входящий-исходящий) можно при создании файла базы указывать по нескольку DS с разными именами - храниться они будут в одном файле в одинаковых RRA.
В этом случае при update очередные значенияя также указываются парами - например, "N:25.12:56.4".

Получение данных за период - fetch:

rrdtools fetch termo.rrd AVERAGE -s -3600 -e -3000

Запрошены усредненные данные за период от 3600 секунд назад до 3000 секунд назад. Конец периода можно не указывать - будет до настоящего момента. Можно использовать модификаторы типа h - час, d - день.
Ответ будет примерно таким:

1779970020: 1.1446144343e+01
1779970080: 1.1548646165e+01
1779970140: 1.1466569217e+01
1779970200: 1.1453367132e+01
1779970260: 1.1357315488e+01
1779970320: 1.1213944783e+01
1779970380: 1.1060893320e+01
1779970440: 1.0848092697e+01

Формат: unixtime: value
Например, теммпература в 1779970260 сек - 11.357315488 градуса (усредненная).
Теперь по ним можно строить графики, используя свои любимые инструменты, или запускать какую-то аналитику.

Также можно сразу получить картинку:

rrdtools graph - DEF:x=termo.rrd:t:AVERAGE LINE1:x#f00 -s -7d > out.png

Получаем картинку в PNG, для чего указываем источник данных x = t из файла termo.rrd, и график в виде линии толщиной 1px по переменной x, рисуя его красным цветом (#f00).
Период - за последние 7 дней.

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

Все это более подробно расписано в man rrdtools, я просто привел несколько базовых примеров. А вот как это оказалось можно использовать на практике (тоже есть в man, но немного модифицируем):

Допустим, у нас есть какой-то скрипт, собирающий данные с датчиков, на машине A.
Веб-сервер, показывающий дашборды - на машине B.
Плюс еще машины C, D, и вообще какие-нибудь девайсы на ESP, которые тоже могут давать данные.
И работать с файлами - ну, это не очень удобно, не копировать же их туда-сюда, тем более не везде можно установить rrdtools.

Но можно превратить rrdtools в сетевой микросервис:

Если взять старый добрый inetd (а лучше xinetd) - можно “повесить” rrdtools на какой-то порт на одной из машин.
В этом случае файлы rrd будут находиться там, а вот работать с ними, в том числе вносить, получать данные или строить графики, можно хоть с ESP.

Для inetd - нужно определить “имя сервиса” для rrd в /etc/services

rrd   23456/tcp    # примерно так

или взять оттуда любое свободное готовое.
Создать юзера rrduser, и выделить место для файлов, например в /var/rrd/
Потом указать в /etc/inetd.conf:

rrd stream tcp nowait rrduser /usr/bin/rrdtool rrdtool - /var/rrd

После запуска inetd на порту 23456 будет доступен rrdtools, которому можно отправлять команды.

Если использовать xinetd - там примерно так же, только можно еще и ограничить, на каком интерфейсе слушать порт:

/etc/xinet.d/rrd

service services
{
  bind    = 192.168.1.11
  port    = 23456
  socket_type = stream
  protocol  = tcp
  wait    = no
  user    = rrduser
  disable   = no
  server    = /usr/bin/rrdtool
  server_args = rrdtool - /var/rrd
}

Ограничивать по юзеру и интерфейсу нужно потому, что у rrdtools среди команд есть такие как list, которая позволяет просматривать каталоги на сервере (если есть права), и create в принципе может создавать rrd в любом доступном каталоге.
Можно вообще засунуть rrdtools в docker - чтобы там ничего лишнего точно не было.

Но в любом случае в итоге - сетевой микросервис.
Можно отправлять команды примерно так:

echo "update termo.rrd N:11.44" |/usr/bin/nc -N server port
echo "fetch termo.rrd LAST -s -3600 -e -3200" |/usr/bin/nc -N server port

Если к серверу есть доступ - можно создавать и работать с файлами по сети. Устанавливать на разные машины rrdtools не нужно.
Только помнить о том, что защиты как таковой там нет - вы всегда можете просто случайно или специально перезаписать существующий файл, создав его заново.
Но в ряде случаев это приемлемо.

Комментарии (0)