July 7

Прекратите запускать по 5 агентов на каждом сервере — используйте Grafana Alloy

Это перевод оригинальной статьи Stop Running 5 Agents Per Server — Use Grafana Alloy Instead.

Перевод сделан специально для телеграм-канала Мониторим ИТ. Подписывайтесь! Там еще больше полезных постов о мониторинге.

Как я заменил Prometheus + Node Exporter + Promtail + MySQL Exporter одним бинарным файлом

Я управлял более чем 10 серверами с классическим стеком мониторинга: Prometheus для сбора метрик, Node Exporter на каждом хосте, Promtail для отправки логов, а также MySQL Exporter, Apache Exporter и другими.

Это означает 4–5 агентов на сервер. Каждому требуется настройка, обновления и устранение неполадок.

Затем я открыл для себя Grafana Alloy.

Один бинарный файл. Один конфигурационный файл. Метрики, логи и трассировки — всё в одном месте.

Так я это настроил.

Что такое Grafana Alloy?

Alloy — это унифицированный сборщик телеметрии Grafana (ранее Grafana Agent). Он заменяет:

  • ✅ Prometheus (сбор метрик)
  • ✅ Node Exporter
  • ✅ MySQL Exporter
  • ✅ Apache Exporter
  • ✅ Promtail
  • ✅ OpenTelemetry Collector

Один агент для управления всем стеком.

Архитектура

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ MySQL Server │    │  Web Server  │    │ Docker Host  │
│    Alloy     │    │    Alloy     │    │    Alloy     │
└──────┬───────┘    └──────┬───────┘    └──────┬───────┘
       │                   │                   │
       └───────────────────┼───────────────────┘
                           ▼
              ┌────────────────────────┐
              │   Monitoring Server    │
              │  Prometheus + Loki +   │
              │       Grafana          │
              └────────────────────────┘

Рассмотрим, как это настроить.

Часть 1: Сервер мониторинга

Я использую Docker Compose. Простой и портативный способ.

Создайте структуру каталогов.

mkdir -p /opt/monitoring/{prometheus,loki,grafana/provisioning/datasources}
cd /opt/monitoring

docker-compose.yml

version: "3.8"
services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-remote-write-receiver'
    ports:
      - "9090:9090"
    restart: unless-stopped
  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
      - GF_USERS_ALLOW_SIGN_UP=false
    ports:
      - "3000:3000"
    restart: unless-stopped
  loki:
    image: grafana/loki:latest
    volumes:
      - ./loki/loki-config.yml:/etc/loki/local-config.yaml
      - loki_data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    ports:
      - "3100:3100"
    restart: unless-stopped
volumes:
  prometheus_data:
  grafana_data:
  loki_data:

prometheus/prometheus.yml

global:
  scrape_interval: 15s
scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

Ключевая настройка: --web.enable-remote-write-receiver позволяет Alloy напрямую передавать метрики в Prometheus.

loki/loki-config.yml

auth_enabled: false
server:
  http_listen_port: 3100
common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory
schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h
limits_config:
  retention_period: 30d
compactor:
  working_directory: /loki/compactor
  retention_enabled: true
  delete_request_store: filesystem

⚠️ Важно: строка delete_request_store: filesystem обязательна, когда включено хранение (retention). Без неё Loki не запустится.

grafana/provisioning/datasources/datasources.yml

apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100

Запускаем

docker compose up -d

Проверка:

Часть 2: Мониторинг сервера MySQL

Теперь давайте установим Alloy на сервер базы данных MySQL.

Установка Alloy

curl -fsSL https://apt.grafana.com/gpg.key | gpg --dearmor -o /usr/share/keyrings/grafana.gpg
echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list
apt update && apt install alloy -y

Создаём пользователя мониторинга MySQL

CREATE USER 'alloy'@'localhost' IDENTIFIED BY 'secure_password';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'alloy'@'localhost';
FLUSH PRIVILEGES;

Конфигурация Alloy

Создайте /etc/alloy/config.alloy:

// Node metrics (replaces node_exporter)
prometheus.exporter.unix "node" { }
prometheus.scrape "node" {
  targets    = prometheus.exporter.unix.node.targets
  forward_to = [prometheus.relabel.add_labels.receiver]
}
// MySQL metrics (replaces mysqld_exporter)
prometheus.exporter.mysql "db" {
  data_source_name = "alloy:secure_password@(localhost:3306)/"
}
prometheus.scrape "mysql" {
  targets    = prometheus.exporter.mysql.db.targets
  forward_to = [prometheus.relabel.add_labels.receiver]
}
// Add server label to all metrics
prometheus.relabel "add_labels" {
  rule {
    action       = "replace"
    target_label = "server"
    replacement  = "mysql-01"
  }
  forward_to = [prometheus.remote_write.default.receiver]
}
// Ship to Prometheus
prometheus.remote_write "default" {
  endpoint {
    url = "http://192.168.0.23:9090/api/v1/write"
  }
}
// Ship logs to Loki
loki.source.file "logs" {
  targets = [
    {__path__ = "/var/log/mysql/error.log", job = "mysql-error", server = "mysql-01"},
    {__path__ = "/var/log/syslog", job = "syslog", server = "mysql-01"},
  ]
  forward_to = [loki.write.default.receiver]
}
loki.write "default" {
  endpoint {
    url = "http://192.168.0.23:3100/loki/api/v1/push"
  }
}

Исправьте права доступа и запустите.

usermod -aG adm alloy
usermod -aG mysql alloy
systemctl enable alloy
systemctl start alloy

Убедитесь, что всё работает.

curl -s "http://192.168.0.23:9090/api/v1/query?query=mysql_up" | jq .

В результате вы должны увидеть сервер mysql-01 .

Часть 3: Мониторинг веб-сервера Apache

Схема та же — установить Alloy, настроить экспортеры.

Включаем статус Apache

a2enmod status

Создайте /etc/apache2/sites-available/000-localhost-status.conf:

<VirtualHost 127.0.0.1:80>
    ServerName 127.0.0.1
    
    <Location "/server-status">
        SetHandler server-status
        Require local
    </Location>
</VirtualHost>
a2ensite 000-localhost-status
systemctl restart apache2

Полезный совет: если используется Laravel или другой фреймворк, перехватывающий маршруты, создайте отдельный vhost. Префикс 000- гарантирует, что он загрузится первым.

Настройка Alloy

prometheus.exporter.unix "node" { }
prometheus.scrape "node" {
  targets    = prometheus.exporter.unix.node.targets
  forward_to = [prometheus.relabel.add_labels.receiver]
}
prometheus.exporter.apache "web" {
  scrape_uri = "http://127.0.0.1/server-status?auto"
}
prometheus.scrape "apache" {
  targets    = prometheus.exporter.apache.web.targets
  forward_to = [prometheus.relabel.add_labels.receiver]
}
prometheus.relabel "add_labels" {
  rule {
    action       = "replace"
    target_label = "server"
    replacement  = "web-server"
  }
  forward_to = [prometheus.remote_write.default.receiver]
}
prometheus.remote_write "default" {
  endpoint {
    url = "http://192.168.0.23:9090/api/v1/write"
  }
}
loki.source.file "apache_logs" {
  targets = [
    {__path__ = "/var/log/apache2/access.log", job = "apache-access", server = "web-server"},
    {__path__ = "/var/log/apache2/error.log", job = "apache-error", server = "web-server"},
  ]
  forward_to = [loki.write.default.receiver]
}
loki.write "default" {
  endpoint {
    url = "http://192.168.0.23:3100/loki/api/v1/push"
  }
}

Часть 4: Мониторинг контейнеров Docker

Для хостов Docker используйте cAdvisor для сбора метрик контейнеров.

Запустите cAdvisor

docker run -d \
  --name=cadvisor \
  --restart=unless-stopped \
  --privileged \
  -p 8081:8080 \
  -v /:/rootfs:ro \
  -v /var/run:/var/run:ro \
  -v /sys:/sys:ro \
  -v /var/lib/docker/:/var/lib/docker:ro \
  gcr.io/cadvisor/cadvisor:latest

Настройте Alloy для сбора данных с cAdvisor.

prometheus.exporter.unix "node" { }
prometheus.scrape "node" {
  targets    = prometheus.exporter.unix.node.targets
  forward_to = [prometheus.relabel.add_labels.receiver]
}
prometheus.scrape "cadvisor" {
  targets = [
    {"__address__" = "localhost:8081", "job" = "cadvisor"},
  ]
  forward_to = [prometheus.relabel.add_labels.receiver]
}
prometheus.relabel "add_labels" {
  rule {
    action       = "replace"
    target_label = "server"
    replacement  = "docker-host"
  }
  forward_to = [prometheus.remote_write.default.receiver]
}
prometheus.remote_write "default" {
  endpoint {
    url = "http://192.168.0.23:9090/api/v1/write"
  }
}
// Collect container logs directly
discovery.docker "containers" {
  host = "unix:///var/run/docker.sock"
}
loki.source.docker "containers" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.docker.containers.targets
  labels     = {server = "docker-host", job = "docker"}
  forward_to = [loki.write.default.receiver]
}
loki.write "default" {
  endpoint {
    url = "http://192.168.0.23:3100/loki/api/v1/push"
  }
}

Не забудьте:

usermod -aG docker alloy
systemctl restart alloy

Советы по дашбордам Grafana

Добавьте выпадающий список серверов

  1. Настройки дашборда → Переменные → Создать
  2. Имя: server
  3. Тип: Запрос
  4. Запрос: label_values(up, server)
  5. Включите Multi-value и Include All

Теперь используйте {server=~"$server"} в своих запросах:

rate(mysql_global_status_queries{server=~"$server"}[5m])

Ключевые метрики для мониторинга

MySQL:

  • mysql_up — доступен ли сервер
  • mysql_global_status_threads_connected — количество активных подключений
  • rate(mysql_global_status_slow_queries[5m]) — скорость медленных запросов

Apache:

  • apache_up — доступен?
  • apache_workers{state="busy"} — активные воркеры
  • rate(apache_accesses_total[5m]) — количество запросов в секунду

Host:

  • 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) — загрузка CPU %
  • node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes — используемая оперативная память (RAM)

Распространённые проблемы

«Компонент не существует»

В Alloy нет всех экспортеров. Например, prometheus.exporter.php_fpm просто не существует.

Решение: Запустить автономный экспортер и собирайте метрики с него, или предоставьте доступ к метрикам через HTTP.

Конфигурация не загружается

alloy fmt /etc/alloy/config.alloy  # Check syntax
alloy run /etc/alloy/config.alloy  # See actual error

Нет доступа к логам

usermod -aG adm alloy
usermod -aG www-data alloy  # for Apache logs
systemctl restart alloy

Результат

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

После: 1 агент, 1 конфигурация, единая ситема наблюдаемости.

Теперь мой стек мониторинга:

  • Проще разворачивать (один бинарник)
  • Проще настраивать (один конфигурационный файл River)
  • Проще диагностировать проблемы (всё в одном месте)

Попробуйте Alloy. В будущем вы скажете себе спасибо.

Ресурсы

Подписывайтесь на телеграм-канал Мониторим ИТ, там еще больше полезной информации о мониторинге!