香港服务器部署Grafana Alloy:统一遥测数据采集替代Prometheus+Loki多Agent方案

香港服务器部署Grafana Alloy:统一遥测数据采集替代Prometheus+Loki多Agent方案

随着可观测性体系的建设,服务器上往往运行着多个采集 Agent:Prometheus Node Exporter 采集指标、Promtail 采集日志、Jaeger Agent 采集链路追踪……每个 Agent 独立运行,各自消耗资源,配置分散难以管理。Grafana Alloy(Grafana Agent 的继任者)将所有遥测数据的采集统一到一个 Agent 中,用 River 配置语言编写声明式管道,显著简化可观测性栈的运维复杂度。


一、Alloy 的能力范围

遥测类型传统方案Grafana Alloy 替代
系统指标Prometheus Node Exporter内置 prometheus.exporter.unix
应用指标各类 Exporterprometheus.scrape + 内置 Exporter
日志采集Promtailloki.source.file / loki.source.docker
链路追踪Jaeger/OTEL Agentotelcol.receiver.otlp
ProfilesPyroscope Agentpyroscope.scrape

二、安装 Grafana Alloy

<code"># Ubuntu/Debian 安装(官方仓库)
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor > /usr/share/keyrings/grafana.gpg
echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" \
  > /etc/apt/sources.list.d/grafana.list
apt update && apt install -y alloy

# 验证安装
alloy --version

# 或者 Docker 安装
docker run -d \
  --name alloy \
  --restart unless-stopped \
  --network host \
  -v /opt/alloy/config:/etc/alloy \
  -v /var/log:/var/log:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  grafana/alloy:latest \
  run /etc/alloy/config.alloy

三、River 配置文件(config.alloy)

<code"># /etc/alloy/config.alloy
# River 语言:声明式,模块化,类似 Terraform HCL

# ══════════════════════════════════════════
# 第一部分:系统指标采集(替代 Node Exporter)
# ══════════════════════════════════════════

prometheus.exporter.unix "node" {
  # 启用所需的指标收集器
  enable_collectors = [
    "cpu", "diskstats", "filesystem",
    "loadavg", "meminfo", "netdev",
    "stat", "time", "uname",
  ]
}

prometheus.scrape "node" {
  targets = prometheus.exporter.unix.node.targets
  forward_to = [prometheus.remote_write.grafana.receiver]
  scrape_interval = "30s"
}

# ══════════════════════════════════════════
# 第二部分:应用指标采集(Nginx、Redis 等)
# ══════════════════════════════════════════

prometheus.exporter.redis "redis_local" {
  redis_addr = "redis://localhost:6379"
}

prometheus.scrape "redis" {
  targets = prometheus.exporter.redis.redis_local.targets
  forward_to = [prometheus.remote_write.grafana.receiver]
}

# 从 Nginx stub_status 采集
prometheus.scrape "nginx" {
  targets = [{
    __address__ = "localhost:8888",
    __metrics_path__ = "/nginx_status",
    instance = env("HOSTNAME"),
    job = "nginx",
  }]
  forward_to = [prometheus.remote_write.grafana.receiver]
}

# ══════════════════════════════════════════
# 第三部分:日志采集(替代 Promtail)
# ══════════════════════════════════════════

# Nginx 访问日志
loki.source.file "nginx_access" {
  targets = [{
    __path__  = "/var/log/nginx/access.log",
    job       = "nginx",
    log_type  = "access",
    host      = env("HOSTNAME"),
  }]
  forward_to = [loki.write.grafana.receiver]
}

# Nginx 错误日志
loki.source.file "nginx_error" {
  targets = [{
    __path__  = "/var/log/nginx/error.log",
    job       = "nginx",
    log_type  = "error",
    host      = env("HOSTNAME"),
  }]
  forward_to = [loki.write.grafana.receiver]
}

# Docker 容器日志
loki.source.docker "docker" {
  host    = "unix:///var/run/docker.sock"
  targets = discovery.docker.containers.targets
  forward_to = [loki.write.grafana.receiver]
}

discovery.docker "containers" {
  host = "unix:///var/run/docker.sock"
}

# ══════════════════════════════════════════
# 第四部分:OTLP 链路追踪接收器
# ══════════════════════════════════════════

otelcol.receiver.otlp "default" {
  grpc { endpoint = "0.0.0.0:4317" }
  http { endpoint = "0.0.0.0:4318" }

  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}

otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "https://tempo-prod.grafana.net:443"
    auth     = otelcol.auth.basic.grafana_cloud.handler
  }
}

otelcol.auth.basic "grafana_cloud" {
  username = env("GRAFANA_CLOUD_INSTANCE_ID")
  password = env("GRAFANA_CLOUD_API_KEY")
}

# ══════════════════════════════════════════
# 第五部分:数据输出目标
# ══════════════════════════════════════════

# 方案A:发送到 Grafana Cloud(托管)
prometheus.remote_write "grafana" {
  endpoint {
    url = "https://prometheus-prod.grafana.net/api/prom/push"
    basic_auth {
      username = env("GRAFANA_CLOUD_METRICS_INSTANCE_ID")
      password = env("GRAFANA_CLOUD_API_KEY")
    }
  }
}

loki.write "grafana" {
  endpoint {
    url = "https://logs-prod.grafana.net/loki/api/v1/push"
    basic_auth {
      username = env("GRAFANA_CLOUD_LOGS_INSTANCE_ID")
      password = env("GRAFANA_CLOUD_API_KEY")
    }
  }
}

# 方案B:发送到自托管 Grafana 栈
# prometheus.remote_write "local" {
#   endpoint {
#     url = "http://prometheus:9090/api/v1/write"
#   }
# }
# loki.write "local" {
#   endpoint { url = "http://loki:3100/loki/api/v1/push" }
# }

四、环境变量配置

<code"># /etc/default/alloy(systemd 环境变量文件)
HOSTNAME=hk-web-server
GRAFANA_CLOUD_API_KEY=glc_xxxxxxxxxxxxx
GRAFANA_CLOUD_METRICS_INSTANCE_ID=123456
GRAFANA_CLOUD_LOGS_INSTANCE_ID=789012
GRAFANA_CLOUD_INSTANCE_ID=345678

# 启动 Alloy
systemctl enable --now alloy
systemctl status alloy

# 查看日志
journalctl -u alloy -f

五、Alloy UI(本地调试)

<code"># Alloy 内置调试 UI,默认在 :12345 端口
# http://服务器IP:12345

# UI 功能:
# - 可视化 River 配置的数据流图(组件关系)
# - 实时查看各组件的状态和指标
# - 调试配置问题(哪个组件未运行)

# 通过 Nginx 安全暴露(加 Basic Auth)
server {
    listen 127.0.0.1:12346;
    location / {
        auth_basic "Alloy Debug UI";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:12345;
    }
}

六、与传统方案资源对比

方案进程数内存占用配置文件数
Node Exporter + Promtail + OTEL Agent3约 150MB3个文件
Grafana Alloy(统一)1约 60MB1个文件
节省-2 进程-60%-67%

七、总结

Grafana Alloy 是可观测性栈走向成熟的标志性工具:一个 Agent、一个配置文件、一套声明式管道,替代了过去需要分别安装、配置、维护的多个 Agent。对于已经使用 Grafana 生态(Prometheus + Loki + Tempo)的团队,迁移到 Alloy 几乎是无缝的——原有的 Prometheus scrape 配置和 Promtail pipeline_stages 都可以直接在 River 语言中使用,但管理复杂度大幅降低。

Telegram