Grafana Agent в режиме Flow — это компонентная сборка для наблюдаемости, где вы на одном VDS поднимаете единый бинарник, описываете конвейеры в языке River и на выходе получаете аккуратный поток metrics, logs и traces в привычные бэкенды: Prometheus (или совместимые через remote_write), Loki и Tempo. По сути, это альтернатива связке «Promtail + часть функций OpenTelemetry Collector», но в одном процессе, с предсказуемым потреблением и единообразной конфигурацией.
Зачем Flow на VDS: ключевые преимущества
Для админа и девопса на одном сервере важны предсказуемость и простота. Flow даёт:
- Единый агент вместо зоопарка демонов — меньше накладных расходов и конфликтов.
- Компонентная архитектура: явные блоки для метрик (
prometheus.scrape,prometheus.remote_write), логов (loki.source.*,loki.process,loki.write) и трейсов (otelcol.receiver.otlp,otelcol.processor.*,otelcol.exporter.otlp). - Язык River: компактно, декларативно, связи компонентов — через явные inputs/outputs.
- Минимум зависимостей: одно бинарное приложение + systemd‑юнит.
- Гибкость: легко добавлять новые источники без перезапуска всего стека наблюдаемости.
Архитектура: что именно мы поднимем
Мы сконфигурируем на VDS:
- Сбор метрик: скрейпинг Agent self‑metrics и ваших экспортёров через
prometheus.scrape, отправка в Prometheus/VictoriaMetrics поprometheus.remote_write. - Сбор логов:
loki.source.fileдля файлов (например, Nginx) иloki.source.journalдля journald; обработка черезloki.process; отправка в Loki черезloki.write. - Трейсы: приём от приложений по OTLP (gRPC 4317 и HTTP 4318) через
otelcol.receiver.otlp, буферизация/батчинг и экспорт в Tempo черезotelcol.exporter.otlp.
Идея простая: всё, что происходит на машине (системные логи, веб‑доступы, ошибки, метрики сервисов и трейсы приложения), централизованно и надёжно утекает на ваши хранилища observability.
Подготовка VDS
Наблюдаемость чувствительна к сетевым и файловым правам. Если сервера ещё нет — поднимите агент на нашем облачном VDS: достаточно 1–2 vCPU и 1 ГБ RAM для старта.
- Оцените ресурсы: 1–2 vCPU и 512–1024 МБ RAM обычно хватает для одного агента и пары лёгких конвейеров. Для высоконагруженных логов/трейсов закладывайте больше.
- Порты: для OTLP откройте 4317/tcp (гRPC) и 4318/tcp (HTTP) только для доверенных источников.
- Доступ к journald: агенту нужна группа
systemd-journalили разрешение чтения каталога/var/log/journal. - Часы и хостнейм: синхронизируйте NTP (chrony/systemd‑timesyncd). Некорректное время ломает корреляцию метрик/логов/трейсов.
# Пример базовой подготовки на Debian/Ubuntu
sudo apt update
sudo apt install -y ca-certificates curl unzip ufw
# Пользователь и директории
sudo useradd -r -s /usr/sbin/nologin grafana-agent || true
sudo mkdir -p /etc/agent
sudo mkdir -p /var/lib/agent
sudo chown -R grafana-agent:grafana-agent /etc/agent /var/lib/agent
# Чтение journald (если используете loki.source.journal)
sudo usermod -aG systemd-journal grafana-agent
# Фаервол: откроем OTLP только для доверенной сети
sudo ufw allow from 10.0.0.0/8 to any port 4317 proto tcp
sudo ufw allow from 10.0.0.0/8 to any port 4318 proto tcp
sudo ufw reload
Установка Grafana Agent (Flow)
Устанавливаем бинарник и готовим systemd‑юнит. Ниже пример: сначала попытайтесь поставить пакет из репозитория ОС; при ручной установке поместите заранее бинарник в каталог и используйте install.
# Установка через пакетный менеджер (если доступен)
sudo apt update
sudo apt install -y grafana-agent
# Либо ручная установка бинарника, заранее положив его в /tmp/agent/grafana-agent
sudo install -o root -g root -m 0755 /tmp/agent/grafana-agent /usr/local/bin/grafana-agent
# Проверка версии
/usr/local/bin/grafana-agent --version
Создадим systemd‑юнит и включим режим Flow. Важно: агенту нужен HTTP‑эндпоинт для собственных метрик (мы его будем скрейпить).
# /etc/systemd/system/grafana-agent.service
[Unit]
Description=Grafana Agent (Flow)
After=network-online.target
Wants=network-online.target
[Service]
User=grafana-agent
Group=grafana-agent
ExecStart=/usr/local/bin/grafana-agent run --server.http.listen-addr=127.0.0.1:12345 --config.file=/etc/agent/flow.river
Restart=always
RestartSec=5s
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable grafana-agent
# Пока не стартуем — сначала подготовим конфиг
River‑конфиг: каркас
Создадим базовый /etc/agent/flow.river с каркасом логирования и переменными окружения. River поддерживает обращения к env(), удобно прокидывать секреты через файловые переменные systemd или окружение.
// /etc/agent/flow.river
logging {
level = "info"
format = "logfmt"
}
// Можно задать глобальные лейблы/теги через env
// Например: export INSTANCE=shop-api-1; export ENV=prod

Метрики: prometheus.scrape и remote_write
Начнём с простого — соберём метрики самого агента и отправим их в ваш TSDB (Prometheus или совместимый по remote_write, например VictoriaMetrics). Если у вас уже есть экспортёры (node_exporter, nginx‑exporter и т.п.), просто добавьте их таргеты в targets.
// Скрейпим self‑metrics агента (по адресу, который указали в ExecStart)
prometheus.scrape "agent_self" {
job_name = "grafana-agent"
scrape_interval = "15s"
targets = [
{
"__address__" = "127.0.0.1:12345"
}
]
forward_to = [prometheus.remote_write.default.receiver]
}
// Отправляем в TSDB
prometheus.remote_write "default" {
endpoint {
url = env("PROM_REMOTE_URL") // например: vm.example:8428/api/v1/write
// При необходимости добавьте авторизацию:
// basic_auth {
// username = env("PROM_USER")
// password = env("PROM_PASS")
// }
// bearer_token = env("PROM_TOKEN")
}
}
Если вы хотите скрейпить дополнительные приложения, добавляйте их таргеты:
// Пример: скрейп Nginx‑экспортёра на localhost:9113
prometheus.scrape "nginx" {
job_name = "nginx"
scrape_interval = "15s"
targets = [
{
"__address__" = "127.0.0.1:9113"
}
]
forward_to = [prometheus.remote_write.default.receiver]
}
Логи: файлы и journald в Loki
Типичный набор для единого VDS — это логи Nginx и системный журнал. В Flow это делается через компоненты local.file_match, loki.source.file и loki.source.journal с последующей обработкой в loki.process и отправкой в loki.write.
// Куда писать логи в Loki
loki.write "default" {
endpoint {
url = env("LOKI_URL") // например: loki.example:3100/loki/api/v1/push
// basic_auth {
// username = env("LOKI_USER")
// password = env("LOKI_PASS")
// }
// headers = { "X-Scope-OrgID" = env("LOKI_TENANT") }
}
}
// Описываем файлы логов Nginx
local.file_match "nginx_logs" {
path_targets = [
{ __path__ = "/var/log/nginx/access.log" },
{ __path__ = "/var/log/nginx/error.log" }
]
}
// Источник: читаем файлы
loki.source.file "nginx" {
targets = local.file_match.nginx_logs.targets
forward_to = [loki.process.nginx.receiver]
}
// Обработка: лейблы, мультилайн и т.д.
loki.process "nginx" {
stage.static_labels {
values = {
job = "nginx",
host = env("INSTANCE"),
env = env("ENV")
}
}
// Для стектрейсов/длинных строк можно объединять строки
stage.multiline {
firstline = "^\\S+ - \\S+ \\[\\d{2}/\\w{3}/\\d{4}:\\d{2}:\\d{2}:\\d{2}"
}
forward_to = [loki.write.default.receiver]
}
// Системный журнал через journald
loki.source.journal "systemd" {
labels = {
job = "systemd",
host = env("INSTANCE"),
env = env("ENV")
}
max_age = "24h"
forward_to = [loki.process.systemd.receiver]
}
loki.process "systemd" {
stage.static_labels { values = { stream = "journal" } }
forward_to = [loki.write.default.receiver]
}
Следите за кардинальностью лейблов и пайплайнами. Подробно о практиках мы писали в материале Пайплайны лейблов для Loki: экономим и ускоряем.
Трейсы: OTLP‑приёмник и экспорт в Tempo
Трейсы примем от приложений через OTLP (gRPC/HTTP) и отправим в Tempo. Приложения на Go/Node.js/PHP/Python с библиотеками OpenTelemetry умеют отправлять OTLP почти «из коробки».
// Принимаем OTLP от приложений
otelcol.receiver.otlp "ingest" {
protocols {
grpc { endpoint = "0.0.0.0:4317" }
http { endpoint = "0.0.0.0:4318" }
}
output {
traces = [otelcol.processor.batch.default.input]
}
}
// Батчинг/окно отправки
otelcol.processor.batch "default" {
timeout = "5s"
send_batch_size = 8192
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
// Экспорт в Tempo по OTLP (чаще gRPC 4317)
otelcol.exporter.otlp "tempo" {
client {
endpoint = env("TEMPO_ENDPOINT") // например: tempo.example:4317
tls {
insecure = true // в проде настройте ca_file и проверку сертификата
}
// headers = { "X-Scope-OrgID" = env("TEMPO_TENANT") }
}
}
На стороне приложения часто достаточно указать переменные окружения, чтобы SDK OpenTelemetry стал слать трейсы в агент на localhost:
# Пример для приложения: шлём на локальный агент (gRPC)
export OTEL_EXPORTER_OTLP_ENDPOINT=127.0.0.1:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
# Для HTTP используйте порт 4318
Запуск и проверка
Когда конфиги готовы, стартуем службу и проверяем логи агента.
sudo systemctl start grafana-agent
sudo systemctl status grafana-agent
journalctl -u grafana-agent -f
Быстрая проверка:
- Метрики: Prometheus/VictoriaMetrics должны увидеть новый
job="grafana-agent". - Логи: в Loki появляются стримы с лейблами
job="nginx"иjob="systemd". - Трейсы: в Tempo начинают приходить спаны от вашего приложения.
Безопасность: доступы, TLS и минимизация поверхности
Набор базовых мер, которые не стоит пропускать:
- OTLP‑порты 4317/4318 открывайте только для доверенных источников или вовсе слушайте на 127.0.0.1, если приложение живёт на той же машине.
- Используйте аутентификацию к удалённым бэкендам:
basic_auth/Bearer вprometheus.remote_write,loki.write, заголовки/токены вotelcol.exporter.otlp. - Шифруйте трафик к внешним бэкендам по TLS и включайте верификацию сертификата. При необходимости оформите SSL-сертификаты для своих публичных эндпоинтов.
- Ограничьте системные привилегии сервиса через systemd (ProtectSystem, NoNewPrivileges и т.д.).
- Следите за составом лейблов в логах: слишком высокая кардинальность ведёт к росту счета в Loki.
Оптимизация ресурсов: частоты, буферы, кардинальность
Чтобы агент потреблял мало CPU и RAM, настройте:
- Интервалы скрейпа: для большинства сервисов достаточно 15–30s.
- Отбрасывание шума: в логах используйте
loki.processдля нормализации и объединения строк, выпиливайте бессмысленные лейблы. - Батчинг трейсов:
otelcol.processor.batchснижает overhead сетевых вызовов. - Бэкап очередей: агент выдержит кратковременные отказы бэкендов, но не превращайте его в очередь «на века».

Надёжность: что делать при падениях бэкенда
Если Loki/Tempo/TSDB временно недоступны, главное — не потерять данные и не «захлебнуть» сервер. Практические советы:
- Логи: держите размер журналов под контролем, ограничьте количество и размер файлов в
/var/log, используйтеlogrotate. Агент дочитает хвост, когда бэкенд оживёт. - Трейсы: снижайте частоту сэмплинга в приложениях при деградации (например,
0.01вместо0.1), чтобы не забить сеть/память. - Метрики: временные провалы remote_write переживут ретраи; для регулярных простоев подумайте о повышении надёжности канала.
Тонкости конфигурации Flow: читаемость и поддержка
Несколько рекомендаций по стилю River:
- Именуйте компоненты по назначению:
prometheus.scrape "nginx",loki.process "systemd"и т.д. - Группируйте блоки по доменам (metrics/logs/traces), оставляйте комментарии с форматами логов.
- Используйте переменные окружения для секретов и эндпоинтов, чтобы один и тот же конфиг легко запускался в dev/stage/prod.
- Изменения проверяйте по частям: сначала запускайте только раздел metrics, затем logs, затем traces.
Обновления и миграция со «статического» режима
Если у вас уже работал «старый» режим Grafana Agent или отдельный Promtail/OTel Collector, мигрируйте постепенно:
- Поднимите Flow в параллель с новыми портами и другим лейблом инстанса.
- Отзеркальте метрики/логи/трейсы на тестовый сторедж и сравните нагрузку и полноту данных.
- Переключите источники по одному (сначала логи, затем метрики, затем трейсы), уберите старые демоны.
Диагностика неполадок
Самые частые проблемы и быстрые проверки:
- Permission denied у journald: проверьте группу
systemd-journalи перезапуск агента. - Порты 4317/4318 заняты: найдите конфликтующий процесс (
ss -lntp), поменяйтеendpointили отключите лишнее. - Нет метрик в TSDB: проверьте
prometheus.remote_write(авторизация/URL), затемprometheus.scrape(доступность таргетов). - Нет логов в Loki: проверьте права на файлы, ротацию логов, соответствие путей и лейблов; временно поднимите
logging.level = "debug". - Нет трейсов в Tempo: убедитесь, что приложение действительно отправляет OTLP, а экспортер в агенте видит endpoint и не ругается на TLS.
Мини‑чеклист перед продом
- Стабильные DNS и NTP.
- Ограничены сетевые порты и права доступа.
- Отключены лишние лейблы с высокой кардинальностью.
- Сбор нагрузочного профиля: проверьте CPU/RAM бюджет агента.
Полный пример flow.river для старта
Соберём всё вместе в один файл, который можно использовать как основу. Замените переменные окружения на ваши значения.
// /etc/agent/flow.river
logging {
level = "info"
format = "logfmt"
}
// -------------------- METRICS --------------------
prometheus.remote_write "default" {
endpoint { url = env("PROM_REMOTE_URL") }
}
prometheus.scrape "agent_self" {
job_name = "grafana-agent"
scrape_interval = "15s"
targets = [{ "__address__" = "127.0.0.1:12345" }]
forward_to = [prometheus.remote_write.default.receiver]
}
// ---------------------- LOGS ---------------------
loki.write "default" {
endpoint { url = env("LOKI_URL") }
}
local.file_match "nginx_logs" {
path_targets = [
{ __path__ = "/var/log/nginx/access.log" },
{ __path__ = "/var/log/nginx/error.log" }
]
}
loki.source.file "nginx" {
targets = local.file_match.nginx_logs.targets
forward_to = [loki.process.nginx.receiver]
}
loki.process "nginx" {
stage.static_labels {
values = { job = "nginx", host = env("INSTANCE"), env = env("ENV") }
}
stage.multiline { firstline = "^\\S+ - \\S+ \\[" }
forward_to = [loki.write.default.receiver]
}
loki.source.journal "systemd" {
labels = { job = "systemd", host = env("INSTANCE"), env = env("ENV") }
max_age = "24h"
forward_to = [loki.process.systemd.receiver]
}
loki.process "systemd" {
stage.static_labels { values = { stream = "journal" } }
forward_to = [loki.write.default.receiver]
}
// --------------------- TRACES --------------------
otelcol.receiver.otlp "ingest" {
protocols {
grpc { endpoint = "0.0.0.0:4317" }
http { endpoint = "0.0.0.0:4318" }
}
output { traces = [otelcol.processor.batch.default.input] }
}
otelcol.processor.batch "default" {
timeout = "5s"
send_batch_size = 8192
output { traces = [otelcol.exporter.otlp.tempo.input] }
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = env("TEMPO_ENDPOINT")
tls { insecure = true }
}
}
Что дальше
Дальше можно наращивать конфигурацию: добавьте парсинг JSON‑логов, структурируйте лейблы, подключите дополнительные экспортёры метрик и автодискавери, включите ресемплинг трейсов, вынесите секреты в EnvironmentFile systemd, добавьте алерты на сбои отправки. Flow хорошо масштабируется: если завтра у вас станет два и больше VDS, вы просто повторите агент с тем же конфигом, меняя лейблы инстанса и сетевые ACL.
В итоге вы получаете полноценный стек наблюдаемости из одного бинарника, понятный и управляемый, который можно быстро «поднять, проверить и забыть» — до следующего релиза.


