Monitoriza Linux con Prometheus y Grafana

Guía paso a paso para desplegar un stack de monitorización con Prometheus y Grafana en servidores Linux usando Node Exporter.

Por qué monitorizar

Sin monitorización, los problemas se detectan cuando ya es demasiado tarde. Un stack basado en Prometheus y Grafana permite recopilar métricas, configurar alertas y visualizar el estado de toda tu infraestructura desde un solo panel. Es más flexible pero también más laborioso de mantener que una solución todo-en-uno como Zabbix, o que un monitor de disponibilidad ligero como Uptime Kuma si solo necesitas saber si un servicio está caído.

Arquitectura del stack

El flujo es sencillo:

  1. Node Exporter expone métricas del sistema (CPU, RAM, disco, red) en cada servidor
  2. Prometheus recopila esas métricas periódicamente (scraping)
  3. Grafana las visualiza en dashboards interactivos
Servidor monitorizado


1. Node Exporter — expone métricas en :9100/metrics


2. Prometheus — scrapea :9100 y las almacena en :9090


3. Grafana — consulta Prometheus y las visualiza en :3000

Instalar Node Exporter en los servidores

Node Exporter se ejecuta en cada máquina que quieras monitorizar.

# Comprueba la última versión en https://github.com/prometheus/node_exporter/releases
wget https://github.com/prometheus/node_exporter/releases/download/v1.12.1/node_exporter-1.12.1.linux-amd64.tar.gz
tar xvfz node_exporter-1.12.1.linux-amd64.tar.gz
sudo mv node_exporter-1.12.1.linux-amd64/node_exporter /usr/local/bin/

Crear el servicio de systemd:

sudo tee /etc/systemd/system/node_exporter.service > /dev/null <<EOF
[Unit]
Description=Node Exporter
After=network.target

[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter

[Install]
WantedBy=multi-user.target
EOF

Activar y arrancar:

sudo useradd -rs /bin/false node_exporter
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

Las métricas estarán disponibles en http://servidor:9100/metrics.

Instalar Prometheus

En el servidor central de monitorización:

# Comprueba la última versión en https://github.com/prometheus/prometheus/releases
wget https://github.com/prometheus/prometheus/releases/download/v3.13.1/prometheus-3.13.1.linux-amd64.tar.gz
tar xvfz prometheus-3.13.1.linux-amd64.tar.gz
sudo mv prometheus-3.13.1.linux-amd64/{prometheus,promtool} /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus

Configurar los targets

Edita /etc/prometheus/prometheus.yml:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'nodos'
    static_configs:
      - targets:
          - '192.168.1.10:9100'
          - '192.168.1.11:9100'
          - '192.168.1.12:9100'

Crear el servicio

sudo tee /etc/systemd/system/prometheus.service > /dev/null <<EOF
[Unit]
Description=Prometheus
After=network.target

[Service]
User=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus

[Install]
WantedBy=multi-user.target
EOF

sudo useradd -rs /bin/false prometheus
sudo chown -R prometheus: /var/lib/prometheus /etc/prometheus
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus

Prometheus estará accesible en http://servidor:9090.

Instalar Grafana

# RHEL/Rocky/Oracle Linux — comprueba la última versión en https://grafana.com/grafana/download?edition=oss
sudo dnf install -y https://dl.grafana.com/oss/release/grafana-13.1.1-1.x86_64.rpm

# Ubuntu/Debian
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/grafana.key > /dev/null
echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" \
  | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install grafana -y

Activar el servicio:

sudo systemctl enable --now grafana-server

Accede a http://servidor:3000 (usuario y contraseña por defecto: admin/admin).

## Importante

Grafana te pedirá cambiar la contraseña en el primer login — no la omitas. Mientras uses las credenciales por defecto, cualquiera con acceso a ese puerto puede entrar al panel.

Conectar con Prometheus

  1. Ve a Connections > Data Sources > Add data source
  2. Selecciona Prometheus
  3. En URL introduce http://localhost:9090
  4. Pulsa Save & Test

Importar un dashboard

El dashboard más popular para Node Exporter es el 1860:

  1. Ve a Dashboards > Import
  2. Introduce el ID 1860
  3. Selecciona la fuente de datos Prometheus
  4. Pulsa Import

Tendrás un panel completo con CPU, memoria, disco, red y más para cada servidor.

$ grafana-dashboard.webp
Dashboard de Grafana con paneles de uso de CPU, memoria RAM y disco en tiempo real
Ejemplo de dashboard en Grafana: series temporales de CPU y RAM, y un gauge de uso de disco.

Consultas PromQL útiles

Node Exporter expone dos tipos de métrica que se consultan de forma distinta. Las de uso instantáneo (memoria, disco) son gauges: su valor actual ya es lo que quieres graficar, sin transformación. Las acumulativas (CPU, red, disco I/O) son counters: solo suben con el tiempo (o se reinician a 0 si el proceso se reinicia), así que no tiene sentido graficar su valor absoluto — necesitas rate() para convertirlas en “cuánto por segundo” en la ventana indicada. rate() además detecta y compensa automáticamente los reinicios del contador, así que no hace falta tratarlos como caso especial.

# Uso de CPU por nodo (porcentaje)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Memoria usada (porcentaje)
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100

# Espacio en disco usado (porcentaje)
(1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100

# Tráfico de red recibido (bytes/s)
rate(node_network_receive_bytes_total{device="eth0"}[5m])

# Carga del sistema relativa a los cores disponibles (>1 = más procesos en cola de los que hay CPU)
node_load1 / count(count(node_cpu_seconds_total) by (cpu))

# Errores de red recibidos por segundo — un valor > 0 sostenido suele indicar un problema físico (cable, NIC, MTU)
rate(node_network_receive_errs_total{device="eth0"}[5m])

# Tiempo que el disco pasa ocupado con I/O (porcentaje aproximado de saturación)
rate(node_disk_io_time_seconds_total{device="sda"}[5m]) * 100

## Consejo

Las consultas anteriores usan rate(), pensado para dashboards y alertas: promedia toda la ventana ([5m]) y suaviza picos puntuales, lo que da una señal estable para decidir si algo está realmente mal. Si en cambio estás depurando un problema en vivo y quieres ver la variación más reciente sin ese suavizado, irate() usa solo las dos últimas muestras — reacciona antes a un pico, pero es más ruidoso y no es buena base para una alerta (dispararía y se resolvería con cada fluctuación breve).

Configurar alertas básicas

Una alerta de Prometheus por sí sola no notifica a nadie: define cuándo una condición es cierta. Quien decide a quién avisar y cómo es un componente aparte, Alertmanager, que se instala y configura por separado. Esta sección cubre ambas mitades.

Regla evaluada cada 15s (scrape_interval)


1. expr se cumple (ej. up == 0)


2. Estado "pending" — Prometheus espera a que se cumpla `for` (ej. 2m)

   ├── deja de cumplirse antes de `for` → se descarta, no llega a notificar (evita falsas alarmas por un blip)
   └── sigue cumpliéndose tras `for`    → estado "firing"


                                        3. Prometheus envía la alerta a Alertmanager


                                        4. Alertmanager agrupa, deduplica y enruta según `severity`

                                            ├── severity: critical → receiver con notificación inmediata
                                            └── severity: warning  → receiver con notificación agrupada

Crea un archivo de reglas /etc/prometheus/alerts.yml:

groups:
  - name: nodos
    rules:
      - alert: NodoCaido
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: 'Nodo {{ $labels.instance }} no responde'

      - alert: DiscoLleno
        expr: (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'Disco al {{ $value | humanizePercentage }} en {{ $labels.instance }}'

for: 2m es lo que separa una alerta útil de una que satura a golpe de falsos positivos: exige que la condición se mantenga cierta durante ese tiempo antes de pasar a estado “firing”, así un reinicio de 10 segundos o un pico de disco momentáneo no dispara una notificación. Cuanto más crítico el servicio, menor conviene ese margen; cuanto más ruidosa la métrica (como el disco, que puede subir y bajar con un proceso puntual), mayor.

Añade la referencia en prometheus.yml:

rule_files:
  - 'alerts.yml'

Instalar y conectar Alertmanager

# Comprueba la última versión en https://github.com/prometheus/alertmanager/releases
wget https://github.com/prometheus/alertmanager/releases/download/v0.33.1/alertmanager-0.33.1.linux-amd64.tar.gz
tar xvfz alertmanager-0.33.1.linux-amd64.tar.gz
sudo mv alertmanager-0.33.1.linux-amd64/{alertmanager,amtool} /usr/local/bin/
sudo mkdir -p /etc/alertmanager

Un /etc/alertmanager/alertmanager.yml mínimo, con un receiver de tipo webhook (válido para integrar con ntfy, Telegram vía un pequeño proxy, o cualquier endpoint HTTP propio) y una ruta que separa lo crítico de lo que no lo es:

route:
  group_by: ['alertname']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'default'
  routes:
    - match:
        severity: critical
      receiver: 'critico'
      repeat_interval: 30m

receivers:
  - name: 'default'
    webhook_configs:
      - url: 'http://localhost:8080/notificar'
  - name: 'critico'
    webhook_configs:
      - url: 'http://localhost:8080/notificar-urgente'

group_wait da un margen antes de enviar la primera notificación de un grupo nuevo, por si llegan más alertas relacionadas que conviene agrupar en un solo aviso en vez de uno por alerta. repeat_interval controla cada cuánto se re-notifica una alerta que sigue activa — más corto para critico (30m) que para el resto (4h), porque un nodo caído merece un recordatorio más frecuente que un disco al 86%.

Crea el servicio y arráncalo:

sudo tee /etc/systemd/system/alertmanager.service > /dev/null <<EOF
[Unit]
Description=Alertmanager
After=network.target

[Service]
User=alertmanager
ExecStart=/usr/local/bin/alertmanager --config.file=/etc/alertmanager/alertmanager.yml

[Install]
WantedBy=multi-user.target
EOF

sudo useradd -rs /bin/false alertmanager
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager

Y apunta Prometheus hacia él en prometheus.yml:

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

Reinicia ambos para aplicar los cambios:

sudo systemctl restart prometheus alertmanager

## Nota

Este ejemplo usa un receiver webhook genérico para no atarte a un servicio concreto. Alertmanager trae integraciones nativas para email, Slack, PagerDuty y Opsgenie con su propia sintaxis de configuración — revisa la documentación oficial de receivers si usas alguno de esos canales directamente.

## Consejo

Prometheus guarda sus datos localmente 15 días por defecto (--storage.tsdb.retention.time=15d); pasado ese plazo, las métricas más antiguas se descartan. Si necesitas comparar con el mismo periodo del año anterior, súbelo explícitamente en el ExecStart del servicio (por ejemplo --storage.tsdb.retention.time=90d), teniendo en cuenta que más retención significa más disco ocupado en /var/lib/prometheus.

Conclusión

Con Prometheus, Node Exporter, Alertmanager y Grafana tienes un stack de monitorización completo, gratuito y con notificaciones reales cuando algo falla — no solo un panel bonito que hay que estar mirando activamente. Para el próximo nivel de detalle: exporters especializados (mysqld_exporter, blackbox_exporter para comprobar endpoints HTTP desde fuera) amplían lo que este stack puede vigilar más allá de las métricas de sistema operativo que cubre Node Exporter.

## Nota

✍️ Transparencia: Este artículo ha sido creado con el apoyo de herramientas de inteligencia artificial. Toda la información técnica ha sido revisada y validada por el autor antes de su publicación.

$ curl tengoping.com/rss.xml | subscribe [suscribirse por RSS →]