Monitorización completa con Prometheus, Grafana y Loki: métricas, logs y contenedores Docker
Etiquetas: prometheus,grafana,loki,monitoring,docker,node-exporterUn servidor sin monitorización es un servidor ciego. No sabes cuándo se llena el disco, qué contenedor está consumiendo demasiada RAM, o cuántas peticiones 404 está generando tu web. Este artículo documenta cómo configuré el stack completo: Prometheus + Node Exporter + Grafana + Loki + Promtail.
La arquitectura
[Servidor doméstico]
├── node-exporter → métricas del sistema (CPU, RAM, disco, red)
├── docker-stats- → métricas de contenedores (textfile collector)
│ collector
├── prometheus → recolecta y almacena métricas
├── loki → agrega y almacena logs
├── promtail → envía logs de Nginx y syslog a Loki
└── grafana → dashboards de todo lo anterior
Todos los servicios corren en Docker, coordinados por el mismo docker-compose.yml.
Métricas del sistema: Node Exporter
Node Exporter expone métricas del hardware y del SO. El truco: tiene que correr con network_mode: host para ver las interfaces de red reales del servidor. Si corre en red de Docker, solo ve la interfaz eth0 del contenedor.
node-exporter:
image: prom/node-exporter:v1.8.2
network_mode: host
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
- ./textfile-collector:/textfile:ro
command:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/rootfs
- --web.listen-address=127.0.0.1:9100
- --collector.textfile.directory=/textfile
Escucha en 127.0.0.1:9100. Prometheus lo alcanza por 172.17.0.1:9100 (la IP del host desde la red Docker).
Métricas de contenedores: docker stats + textfile collector
El problema con cAdvisor es que no funciona con Docker 29 y el driver de almacenamiento overlayfs en cgroupv2 — falla con «failed to identify read-write layer ID».
La solución: un contenedor ligero que ejecuta docker stats cada 30 segundos y escribe el resultado en formato Prometheus en un archivo que Node Exporter lee.
#!/bin/bash
# docker_stats.sh
OUTFILE="/textfile/docker_stats.prom"
TMPFILE="${OUTFILE}.tmp"
{
echo "# HELP docker_container_cpu_percent CPU usage percentage per container"
echo "# TYPE docker_container_cpu_percent gauge"
# ... más definiciones ...
docker stats --no-stream --format \
'{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}|{{.NetIO}}' 2>/dev/null | \
while IFS='|' read -r name cpu mem net; do
cpu_val=$(echo "$cpu" | tr -d '%' | tr ',' '.')
# ... conversión de unidades ...
echo "docker_container_cpu_percent{name=\"${name}\"} ${cpu_val}"
echo "docker_container_memory_bytes{name=\"${name}\"} ${mem_used_bytes}"
echo "docker_container_running{name=\"${name}\"} 1"
done
# Contenedores parados
docker ps -a --filter "status=exited" --format '{{.Names}}' 2>/dev/null | \
while read -r name; do
echo "docker_container_running{name=\"${name}\"} 0"
done
} > "$TMPFILE" && mv "$TMPFILE" "$OUTFILE"
La escritura atómica (tmp → final) evita que Prometheus lea un archivo a medias.
docker-stats-collector:
image: docker:27-cli
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./textfile-collector:/textfile
- ./docker_stats.sh:/docker_stats.sh:ro
entrypoint: sh -c "apk add --no-cache bc > /dev/null 2>&1; while true; do sh /docker_stats.sh; sleep 30; done"
Prometheus: recolectar y retener
prometheus:
image: prom/prometheus:v2.51.2
networks:
- monitoring
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=30d
- --web.enable-lifecycle
Configuración de scraping:
global:
scrape_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: [localhost:9090]
- job_name: node
static_configs:
- targets: [172.17.0.1:9100]
relabel_configs:
- target_label: host
replacement: servidor-casa
172.17.0.1 es la IP del host accesible desde la red Docker bridge. Los datos se retienen 30 días.
Logs: Loki + Promtail
Loki almacena logs sin indexar el contenido completo — solo las etiquetas (labels). Promtail los recoge y los envía con etiquetas como job, host, filename.
promtail:
image: grafana/promtail:3.3.2
user: root
networks:
- monitoring
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml:ro
- ./promtail-data:/tmp/promtail
- ~/infra/web/logs:/logs/nginx:ro
- /var/log:/logs/host:ro
Necesita correr como root para leer /var/log.
Grafana: dashboards
Grafana se conecta a Prometheus y Loki como data sources. Los dashboards más útiles:
Sistema (Node Exporter):
– CPU total y por núcleo
– RAM usada / libre / cache
– Disco: uso por partición, IOPS, throughput
– Red: tráfico de entrada/salida por interfaz
Contenedores (docker stats):
– CPU % por contenedor
– RAM por contenedor vs límite
– Estado (running/stopped)
– Tráfico de red por contenedor
Logs (Loki):
– Logs de Nginx en tiempo real
– Peticiones por código de estado (200, 301, 404, 500)
– Top de IPs con más peticiones
– Top de rutas más accedidas
Problema: [$__range] en consultas instantáneas de Loki
Al usar paneles de tipo «stat» o «piechart» con Loki, la variable [$__range] no se resuelve — Grafana devuelve «empty duration string». La solución es usar una duración fija:
# MAL (en paneles stat/piechart):
sum by(status) (count_over_time({job="nginx"} | pattern ... [$__range]))
# BIEN:
sum by(status) (count_over_time({job="nginx"} | pattern ... [24h]))
Los paneles de tipo «time series» sí admiten [$__interval] correctamente.
Seguridad del stack
- Prometheus y Loki no tienen acceso externo — solo en la red interna
monitoring - Grafana es el único punto de acceso, protegido con Traefik y Let’s Encrypt
GF_AUTH_ANONYMOUS_ENABLED=falseyGF_USERS_ALLOW_SIGN_UP=falseen Grafana- Node Exporter escucha solo en
127.0.0.1, no expuesto en todas las interfaces
Resultado
Con este stack tienes visibilidad completa del servidor: qué procesos consumen recursos, qué contenedores fallan, qué peticiones recibe tu web y qué errores genera. Todo en dashboards accesibles desde monitor.serviciosrogeliowar.com.
Equipamiento recomendado
- Raspberry Pi 3 B+ — Servidor ligero de bajo consumo para empezar tu homelab
- Raspberry Pi 4 (4GB) — La base perfecta para homelab, Docker y monitorización
Enlaces de afiliado. Sin coste extra para ti.