Grafana Alloy: Configuración como Colector OpenTelemetry
Grafana Alloy es la evolución del Grafana Agent, un colector de telemetría de código abierto que unifica la recolección de métricas, logs, trazas y perfiles en un único agente configurable mediante el lenguaje de flujo River. Compatible con OpenTelemetry, Prometheus, Loki y Pyroscope, Alloy actúa como pieza central del stack de observabilidad de Grafana, permitiendo recolectar, procesar y enrutar señales de telemetría hacia múltiples destinos simultáneamente. Esta guía cubre la instalación, los componentes principales de su pipeline y la integración con el stack LGTM (Loki, Grafana, Tempo, Mimir).
Requisitos Previos
- Ubuntu 20.04/22.04, Debian 11+ o CentOS 8+/Rocky Linux 8+
- 256 MB de RAM mínimo (varía según el volumen de datos)
- Acceso a las fuentes de telemetría (puertos de métricas, archivos de log, etc.)
- Destinos configurados: Loki, Mimir, Tempo, Grafana Cloud o cualquier receptor OTLP
Instalación de Grafana Alloy
Instalación en Ubuntu/Debian
# Añadir el repositorio de Grafana
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor \
| sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update && sudo apt install -y alloy
# Habilitar e iniciar el servicio
sudo systemctl enable alloy --now
sudo systemctl status alloy
Instalación en CentOS/Rocky Linux
# Añadir repositorio de Grafana
sudo tee /etc/yum.repos.d/grafana.repo > /dev/null <<EOF
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
sudo dnf install -y alloy
sudo systemctl enable alloy --now
Instalación con Docker
docker run -d \
--name alloy \
-v /etc/alloy:/etc/alloy:ro \
-v /var/log:/var/log:ro \
-p 12345:12345 \
-p 4317:4317 \
-p 4318:4318 \
grafana/alloy:latest run \
--server.http.listen-addr=0.0.0.0:12345 \
/etc/alloy/config.alloy
Lenguaje de Configuración River
Alloy usa el lenguaje River (.alloy) para definir pipelines de telemetría mediante componentes conectados:
// Estructura básica de una configuración River
// Los componentes se definen con: tipo.nombre { ... }
// Los componentes se conectan referenciando sus outputs: componente.nombre.output
prometheus.scrape "mi_scraper" {
targets = [{"__address__" = "localhost:9090"}]
forward_to = [prometheus.remote_write.mimir.receiver]
}
prometheus.remote_write "mimir" {
endpoint {
url = "http://mimir:8080/api/v1/push"
}
}
El archivo de configuración principal se encuentra en /etc/alloy/config.alloy.
Recolección de Métricas (Prometheus)
// /etc/alloy/config.alloy - Sección de métricas
// Descubrimiento automático de servicios via Docker
discovery.docker "contenedores" {
host = "unix:///var/run/docker.sock"
}
// Filtrar solo contenedores con la etiqueta de métricas habilitada
discovery.relabel "contenedores_con_metricas" {
targets = discovery.docker.contenedores.targets
rule {
source_labels = ["__meta_docker_container_label_metrics_enabled"]
regex = "true"
action = "keep"
}
// Extraer el puerto de métricas de la etiqueta del contenedor
rule {
source_labels = ["__meta_docker_container_label_metrics_port"]
target_label = "__address__"
replacement = "${1}"
}
}
// Hacer scraping de los targets descubiertos
prometheus.scrape "docker_metricas" {
targets = discovery.relabel.contenedores_con_metricas.output
forward_to = [prometheus.remote_write.mimir.receiver]
scrape_interval = "15s"
scrape_timeout = "10s"
}
// Scraping estático de métricas del sistema
prometheus.scrape "node_exporter" {
targets = [{"__address__" = "localhost:9100", "job" = "node"}]
forward_to = [prometheus.remote_write.mimir.receiver]
}
// Exportar métricas a Mimir (o Prometheus compatible)
prometheus.remote_write "mimir" {
endpoint {
url = "http://mimir:8080/api/v1/push"
// Para autenticación básica
basic_auth {
username = "usuario"
password = env("MIMIR_PASSWORD")
}
}
external_labels = {
cluster = "produccion",
region = "eu-west-1",
}
}
Recolección de Logs (Loki)
// Leer archivos de log con el componente loki.source.file
local.file_match "logs_nginx" {
path_targets = [{"__path__" = "/var/log/nginx/access.log", "job" = "nginx"}]
sync_period = "5s"
}
loki.source.file "nginx_access" {
targets = local.file_match.logs_nginx.targets
forward_to = [loki.process.nginx.receiver]
}
// Procesar y parsear logs de Nginx
loki.process "nginx" {
forward_to = [loki.write.loki_server.receiver]
// Parsear el log con una expresión regular
stage.regex {
expression = `(?P<ip>\S+) \S+ \S+ \[(?P<timestamp>[^\]]+)\] "(?P<metodo>\S+) (?P<ruta>\S+) \S+" (?P<status>\d+) (?P<bytes>\d+)`
}
// Extraer el status como label para filtrar en Loki
stage.labels {
values = {
status = "",
metodo = "",
}
}
// Añadir labels adicionales
stage.static_labels {
values = {
entorno = "produccion",
servidor = env("HOSTNAME"),
}
}
}
// Leer logs de contenedores Docker
loki.source.docker "contenedores" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.contenedores.targets
forward_to = [loki.write.loki_server.receiver]
relabel_rules = loki.relabel.docker_labels.rules
}
// Renombrar labels de Docker para Loki
loki.relabel "docker_labels" {
rule {
source_labels = ["__meta_docker_container_name"]
regex = "/(.*)"
target_label = "container"
}
}
// Enviar logs a Loki
loki.write "loki_server" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
external_labels = {
cluster = "produccion",
}
}
Recolección de Trazas (OpenTelemetry)
// Recibir trazas, métricas y logs via OTLP
otelcol.receiver.otlp "receptor_otlp" {
// Recibir via gRPC
grpc {
endpoint = "0.0.0.0:4317"
}
// Recibir via HTTP
http {
endpoint = "0.0.0.0:4318"
}
output {
metrics = [otelcol.processor.batch.procesador.input]
logs = [otelcol.processor.batch.procesador.input]
traces = [otelcol.processor.batch.procesador.input]
}
}
// Agrupar señales en lotes para envío eficiente
otelcol.processor.batch "procesador" {
timeout = "5s"
send_batch_size = 10000
output {
metrics = [otelcol.exporter.prometheus.exportador.input]
logs = [otelcol.exporter.loki.exportador.input]
traces = [otelcol.exporter.otlp.tempo.input]
}
}
// Exportar métricas OTLP a Prometheus/Mimir
otelcol.exporter.prometheus "exportador" {
forward_to = [prometheus.remote_write.mimir.receiver]
}
// Exportar logs OTLP a Loki
otelcol.exporter.loki "exportador" {
forward_to = [loki.write.loki_server.receiver]
}
// Exportar trazas a Tempo (Grafana)
otelcol.exporter.otlp "tempo" {
client {
endpoint = "tempo:4317"
tls {
insecure = true
}
}
}
Procesamiento y Transformaciones
// Filtrar métricas: eliminar métricas de alta cardinalidad
prometheus.relabel "filtrar_metricas" {
forward_to = [prometheus.remote_write.mimir.receiver]
// Eliminar etiquetas de alta cardinalidad
rule {
action = "labeldrop"
regex = "pod_uid|container_id"
}
// Eliminar métricas de debug que no necesitamos
rule {
source_labels = ["__name__"]
regex = "go_gc_.*|go_memstats_.*"
action = "drop"
}
}
// Transformar atributos de trazas OTLP
otelcol.processor.attributes "enriquecer" {
action {
key = "deployment.environment"
value = "produccion"
action = "upsert"
}
action {
key = "service.region"
value = env("AWS_REGION")
action = "insert"
}
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
// Muestreo de trazas (para reducir volumen en producción)
otelcol.processor.tail_sampling "muestreo" {
decision_wait = "10s"
num_traces = 100000
// Mantener siempre las trazas con errores
policy {
name = "errores"
type = "status_code"
status_code {
status_codes = ["ERROR"]
}
}
// Mantener el 10% de las trazas normales
policy {
name = "porcentaje_aleatorio"
type = "probabilistic"
probabilistic {
sampling_percentage = 10
}
}
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
Integración con Grafana Cloud y LGTM
Para enviar datos al stack LGTM autohospedado (Loki + Grafana + Tempo + Mimir):
// Configuración completa para stack LGTM autohospedado
// Credenciales desde variables de entorno (más seguro que texto plano)
// Crear /etc/alloy/env:
// LOKI_URL=http://loki:3100
// MIMIR_URL=http://mimir:8080
// TEMPO_URL=tempo:4317
prometheus.remote_write "mimir" {
endpoint {
url = env("MIMIR_URL") + "/api/v1/push"
queue_config {
capacity = 10000
max_shards = 200
max_samples_per_send = 500
}
}
}
loki.write "loki" {
endpoint {
url = env("LOKI_URL") + "/loki/api/v1/push"
}
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = env("TEMPO_URL")
tls {
insecure = true
}
}
}
# Verificar que Alloy está leyendo la configuración correctamente
sudo systemctl status alloy
# Ver el estado de los componentes en la interfaz web de Alloy
# Alloy expone una UI en http://localhost:12345
curl http://localhost:12345/-/ready
curl http://localhost:12345/-/healthy
Solución de Problemas
Alloy no arranca:
# Ver logs del servicio
sudo journalctl -u alloy -f
# Validar la configuración antes de aplicarla
alloy fmt /etc/alloy/config.alloy
alloy run --dry-run /etc/alloy/config.alloy
Los componentes muestran errores en la UI:
# Acceder a la interfaz de depuración de Alloy
# http://tu-ip:12345 - muestra el grafo de componentes y su estado
# Ver el estado de los componentes via API
curl http://localhost:12345/api/v0/web/components | python3 -m json.tool
No llegan métricas a Mimir:
# Verificar que Alloy puede alcanzar el destino
alloy run --inspect /etc/alloy/config.alloy
# Revisar las métricas internas de Alloy
curl http://localhost:12345/metrics | grep "prometheus_remote_write"
# agent_prometheus_remote_write_sent_samples_total debería incrementarse
Logs que no llegan a Loki:
# Verificar que los archivos de log existen y son legibles
ls -la /var/log/nginx/access.log
sudo -u alloy cat /var/log/nginx/access.log | head -5
# Si Alloy corre como servicio, puede necesitar permisos adicionales
sudo usermod -aG adm alloy
sudo systemctl restart alloy
Consumo de memoria elevado:
// Limitar el buffer en los exportadores para reducir memoria
prometheus.remote_write "mimir" {
endpoint {
url = "http://mimir:8080/api/v1/push"
queue_config {
capacity = 2500 // Reducir el buffer
max_shards = 10 // Menos shards paralelos
}
}
}
Conclusión
Grafana Alloy simplifica enormemente la configuración del stack de observabilidad al consolidar la recolección de métricas, logs, trazas y perfiles en un único agente con una sintaxis de configuración expresiva. Su compatibilidad nativa con OpenTelemetry garantiza que es a prueba de futuro, mientras que la interfaz web integrada facilita el diagnóstico de problemas en los pipelines de telemetría. Para equipos que ya usan herramientas de Grafana, Alloy es la evolución natural del Prometheus Agent y el Promtail, reduciendo la complejidad operativa de mantener múltiples agentes en cada nodo.


