Сбор метрик с помощью Fluent-bit

При обсуждении систем мониторинга всё чаще упоминается связка Prometheus и Grafana как стандартное решение. Она, вероятно, приобрела популярность за счёт простоты сбора метрик. Prometheus придумали простой HTTP проткол, который позволяет получать метрики с различных систем. Но мне Prometheus не совсем подходит, и вот почему:

  • Счётчики в нём работают не так, как хотелось бы. Есть даже известная проблема, которую закрыли с пометкой “won’t fix”.
  • Использование pull-модели для получения данных. Несмотря на преимущества такого подхода, он менее удобен для отправки метрик с краткосрочных процессов.
  • Нужно запускать отдельный процесс для сбора метрик.

InfluxDB

Чтобы обойти эти проблемы, я решил использовать InfluxDB. Она не так популярна, как Prometheus, но у неё есть бесплатная OSS-версия, которую легко поставить. Вот как это делается на Debian:

# Add the InfluxData key to verify downloads and add the repository
curl --silent --location -O \
https://repos.influxdata.com/influxdata-archive.key
echo "943666881a1b8d9b849b74caebf02d3465d6beb716510d86a39f6c8e8dac7515  influxdata-archive.key" \
| sha256sum --check - && cat influxdata-archive.key \
| gpg --dearmor \
| sudo tee /etc/apt/trusted.gpg.d/influxdata-archive.gpg > /dev/null \
&& echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \
| sudo tee /etc/apt/sources.list.d/influxdata.list
# Install influxdb
sudo apt-get update && sudo apt-get install influxdb2

InfluxDB можно запускать как в распределённом режиме, так и одиночном. Для домашней сети одной ноды вполне достаточно. База данных совместима с push- и pull-моделями, причём pull-модель поддерживает протокол экспортера Prometheus, что позволяет использовать существующие агенты.

Сбор метрик

Для отправки данных в InfluxDB часто используется отдельный процесс - Telegraf. Он периодически собирает системные метрики и передаёт их в базу. Есть даже готовый дашборд в Grafana, который может красиво визуализировать эти метрики. Всю эту цепочку можно достаточно легко настроить в несколько кликов, но у неё есть один недостаток - нужно устанавливать ещё один агент. В моей сети есть несколько Raspberry PI, где оперативной памяти не так много, поэтому хотелось бы переиспользовать уже запущенный fluent-bit.

Как оказалось, он может собирать метрики в своём собственном формате, в формате Prometheus, в формате OpenTelemetry, работать как proxy других экспортёров и многое другое. Я решил получше исследовать возможности, чтобы съэкономить память. Вот, например, сравнение потребления памяти между основными конкурентами:

Агент Версия Память
fluent-bit 3.2.8 32 Мб
node_exporter 1.9.0 15 Мб
telegraf 1.34.0 123 Мб

Пример конфигурации сбора метрик аналогичных node_exporter:

inputs:
  - name: node_exporter_metrics
    tag: metrics.node_metrics
    scrape_interval: 10
    metrics: meminfo,filesystem,netdev
    processors:
      metrics:
        - name: labels
          insert: hostname ${HOSTNAME}
outputs:
  - name: influxdb
    match: metrics.*
    host: host
    database: fluentbit
    org: dernasherbrezon
    http_user: user
    http_passwd: password
    tag_keys: hostname

В этой конфигурации fluent-bit собирает метрики каждые 10 секунд и отправляет их в InfluxDB. Тут стоит обратить внимание на несколько вещей:

  • Переменная ${HOSTNAME} автоматически заменяется именем хоста (поддерживаются переменные среды).
  • Для добавления метаданных (например, имени хоста) используется секция processors, так как стандартные фильтры не применяются к метрикам.
  • Cекция processors поддерживается только в yaml конфигурации, которая была добавлена в версии 3.2.

Согласно формату node_exporter метрика node_cpu_seconds_total измеряется не в процентах, а в секундах, которые процессор провёл в том или ином режиме. Это крайне плохое решение, так как из-за задержек сети она будет плавать. Вот, например, как это будет выглядеть на графике:

Для решения этой проблемы я использовал модуль cpu:

inputs:
  - name: cpu
    tag: metrics.cpu
    Interval_Sec: 10
filters:
  - name: record_modifier
    match: metrics.*
    record: hostname ${HOSTNAME}

Grafana

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

Вот примеры запросов, которые использовались для создания графиков:

// CPU
SELECT last("cpu_p") FROM "metrics.cpu" WHERE ("hostname"::tag =~ /^$host$/) AND $timeFilter GROUP BY time($__interval), "hostname"::tag
// Memory
SELECT 100 - 100 * last("MemAvailable_bytes")/last("MemTotal_bytes") FROM "node_memory" WHERE ("hostname"::tag =~ /^$host$/) AND $timeFilter GROUP BY time($__interval), "hostname"::tag fill(null)
// Network RX
SELECT non_negative_derivative(last("receive_bytes_total"), 1s) FROM "node_network" WHERE ("hostname"::tag =~ /^$host$/ AND "device"::tag != 'lo') AND $timeFilter GROUP BY time($__interval), "hostname"::tag, "device"::tag
// Network TX
SELECT -non_negative_derivative(last("transmit_bytes_total"), 1s) FROM "node_network" WHERE ("hostname"::tag =~ /^$host$/) and ("device"::tag != 'lo') AND $timeFilter GROUP BY time($__interval), "hostname"::tag, "device"::tag
// Filesystem
SELECT 100 - 100 * last(avail_bytes) / last(size_bytes) FROM "node_filesystem" WHERE ("hostname"::tag =~ /^$host$/) and ("device"::tag =~ /^$disk$/) and  ("device"::tag != 'tmpfs') and  ("device"::tag != 'lxcfs') AND $timeFilter GROUP BY time($__interval), "hostname"::tag, "device"::tag