Skip to content

Monitorización con Prometheus

Prometheus logo

Imagina que eres el director de una gran fábrica (tu infraestructura Big Data). Para que todo funcione, necesitas saber qué está pasando en cada rincón: ¿cuántas piezas se fabrican por segundo? ¿hay alguna máquina sobrecalentada? ¿cuántos obreros están libres?

Prometheus es tu Inspector Jefe. No espera a que las máquinas le envíen informes; él mismo recorre la fábrica con su libreta (base de datos de series temporales), visita cada departamento y anota los números actuales. En este tema aprenderás a configurar a este inspector para que no se le escape ningún detalle de tu clúster.


1. Arquitectura: ¿Cómo funciona el “Inspector”?

Section titled “1. Arquitectura: ¿Cómo funciona el “Inspector”?”

A diferencia de otros sistemas que “reciben” datos, Prometheus utiliza un modelo Pull. Él es quien toma la iniciativa de pedir la información.

graph LR
subgraph "Exporters (Los Departamentos)"
NE["Node Exporter<br/>(El Host: CPU/RAM)"]
CA["cAdvisor<br/>(Contenedores Docker)"]
NX["Nginx Exporter<br/>(Tráfico Web)"]
end
subgraph "Core (El Inspector Jefe)"
P["<b>Prometheus</b><br/>(Scraping & Storage)"]
end
subgraph "Visualización (El Panel de Control)"
G["Grafana"]
end
NE -- "Exponen métricas en port:9100" --> P
CA -- "Exponen métricas en port:8080" --> P
NX -- "Exponen métricas en port:9113" --> P
P -- "Pull (Scrape) cada 15s" --> NE
P -- "Pull (Scrape) cada 15s" --> CA
P -- "Pull (Scrape) cada 15s" --> NX
G -- "Consulta datos vía PromQL" --> P
style P fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style G fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
style NE fill:#fff9c4,stroke:#f57c00
style CA fill:#fff9c4,stroke:#f57c00
style NX fill:#fff9c4,stroke:#f57c00

Aquí tienes los enlaces para interactuar con el inspector y sus departamentos:

HerramientaURLUsuario / PassFunción
Prometheushttp://localhost:9090-Dashboard de consultas y estado.
Grafanahttp://localhost:3000admin / adminVisualización en paneles avanzados.
cAdvisorhttp://localhost:8080-Explorador visual de contenedores.
Node Exporterhttp://localhost:9100/metrics-Datos brutos del Sistema Operativo.
  1. Exporters: Son pequeños agentes que “traducen” las tripas del sistema (Linux, Docker, bases de datos) al idioma que entiende Prometheus.
  2. Scraping: Prometheus visita los endpoints /metrics de cada exporter periódicamente.
  3. TSDB (Time Series Database): Guarda los datos optimizando el espacio, asociando cada valor a una marca de tiempo.

Ejemplo de métricas a las que accede prometheus nombre_etiqueta{LABELS}

Métricas prometheus

Tipos de métricas: ¿Qué estamos midiendo?

Section titled “Tipos de métricas: ¿Qué estamos midiendo?”

Prometheus clasifica la realidad en cuatro tipos. Saber distinguirlos es vital para elegir la función de consulta adecuada.

Es un valor que solo aumenta. Piensa en el cuentakilómetros de un coche o el número de peticiones totales que ha recibido un servidor.

  • Uso correcto: http_requests_total, node_network_receive_bytes_total.
  • Uso incorrecto: La temperatura actual (porque puede bajar).

Con los contadores, casi siempre usaremos la función rate() para ver la “velocidad” (ej: peticiones por segundo).

Escribe la consulta a continuación en la barra de consultas y haga clic en ejecutar. go_gc_duration_seconds_count

La función rate() en PromQL toma el historial de métricas durante un período de tiempo y calcula qué tan rápido aumenta el valor por segundo. La tasa es aplicable solo en valores de contador. rate(go_gc_duration_seconds_count[5m])

Un valor que sube y baja. Como el nivel de gasolina, la temperatura o el uso de memoria RAM.

  • Uso correcto: node_memory_MemAvailable_bytes, container_memory_usage_bytes.
  • Uso incorrecto: El número total de errores desde que se inició el sistema.

go_memstats_heap_alloc_bytes

Funciones promQL como max_over_time, min_over_time y avg_over_time se puede utilizar en métricas de gauge.

Histogram (Histograma) y Summary (Resumen)

Section titled “Histogram (Histograma) y Summary (Resumen)”

Miden distribuciones, normalmente latencias. No solo te dicen que una petición tardó 200ms, sino que agrupan las peticiones en “cubetas” (ej: 50 peticiones tardaron < 100ms, 10 tardaron < 500ms).

Histogramas: Se utilizan para ver distribuciones estadísticas. Resúmenes: funcionan como los histogramas pero solamente te dan el valor total.
Por ejemplo, nos pueden dar la mediana o máximo o mínimo de por ejemplo el uso de la CPU.

Encontramos diferentes tipos de métricas:

Ejemplos de consultas que podemos realizar:

Intervalos entre los diferentes “scrapeos”
prometheus_target_interval_length_seconds

Si estamos interesados en las de 0.99, las podemos filtrar con:
prometheus_target_interval_length_seconds{quantile="0.99"}

En graph:
rate(prometheus_tsdb_head_chunks_created_total[1m])`

https://www.apptio.com/topics/kubernetes/devops-tools/cadvisor/

cAdvisor es un software de Google, escrito en Go, y programado específicamente para la captura de métricas de contenedores; necesita acceso a varios directorios del host (que mapearemos con bind-mounts en Docker) para capturar los datos. cAdvisor expone el puerto 8080 (interfaz web y REST api) y por defecto permite a Prometheus acceder a varias métricas.

cadvisor: http://localhost:8080/
cadvisor metrics: http://localhost:8080/metrics

Podemos consultar métricas como:
cadvisor_version_info container_start_time_seconds{name="cadvisor"}

rate(container_cpu_usage_seconds_total{name=“redis”}[1m])The cgroup’s CPU usage in the last minuteThe redis container
container_memory_usage_bytes{name=“redis”}The cgroup’s total memory usage (in bytes)The redis container
rate(container_network_transmit_bytes_total[1m])Bytes transmitted over the network by the container per second in the last minuteAll containers
rate(container_network_receive_bytes_total[1m])Bytes received over the network by the container per second in the last minuteAll containers

https://elpuig.xeill.net/Members/vcarceler/articulos/monitorizacion-con-prometheus-y-grafana

Cada equipo, o aplicación, que vaya a ser monitorizado con Prometheus debe publicar sus propias métricas mediante HTTP para que Prometheus las recoja.

Para monitorizar un ordenador con GNU/Linux la opción más sencilla consiste en instalar prometheus-node-exporter que se ejecutará como un servicio y publicará las métricas en el puerto 9100 del ordenador.

https://medium.com/@aravind33b/mastering-prometheus-on-macos-seamless-integration-with-grafana-5da2a1c95092


PromQL es el motor de búsqueda de Prometheus. Es extremadamente potente para operar con series temporales.

Las etiquetas son el “DNI” de las métricas. Nos permiten ser específicos:

-- ❌ Demasiado genérico: CPU de todo el mundo
node_cpu_seconds_total
-- ✅ Específico: Tiempo de CPU en modo "idle" (inactivo) para un servidor concreto
node_cpu_seconds_total{mode="idle", instance="node-exporter:9100"}
FunciónTipoAnalogía
rate([5m])Counter¿A qué velocidad vamos ahora? (Media de 5 min)
increase([1h])Counter¿Cuánto hemos recorrido en la última hora?
avg_over_time([10m])Gauge¿Cuál ha sido el nivel medio de gasolina en 10 min?
sum by (label)AgregaciónAgrupar los resultados (ej: por contenedor)

4. Práctica Guiada 1: Explorando el ecosistema

Section titled “4. Práctica Guiada 1: Explorando el ecosistema”

Tu entorno ya está activo. Vamos a verificar qué tenemos.

Entra en la interfaz de Prometheus: http://localhost:9090 y navega a Status -> Targets.

Deberías ver una lista de “objetivos” en verde:

  • prometheus: Se monitoriza a sí mismo.
  • node-exporter: Métricas de tu máquina virtual/host.
  • cadvisor: Métricas de tus contenedores Docker.

Paso 2: Tu primera consulta (Métricas del Host)

Section titled “Paso 2: Tu primera consulta (Métricas del Host)”

En la pestaña Graph, escribe y ejecuta:

-- Memoria RAM libre en GB
node_memory_MemAvailable_bytes / 1024 / 1024 / 1024

Paso 3: Monitorizando Contenedores (cAdvisor)

Section titled “Paso 3: Monitorizando Contenedores (cAdvisor)”

¿Cuánto consume tu base de datos o tu servidor web?

-- Uso de CPU por contenedor (en % de un core)
-- Usamos 'image' para agrupar
sum by (image) (rate(container_cpu_usage_seconds_total{image!=""}[5m])) * 100

5. El archivo de configuración prometheus.yml

Section titled “5. El archivo de configuración prometheus.yml”

Aunque el sistema ya funciona, es importante entender cómo le hemos dicho a Prometheus dónde buscar información:

scrape_configs:
- job_name: "node-exporter"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]

6. Práctica Guiada 2: Instrumentando tu propia APP (Python)

Section titled “6. Práctica Guiada 2: Instrumentando tu propia APP (Python)”

Hasta ahora has visto métricas que otros han creado por ti. ¿Pero qué pasa si quieres medir el rendimiento de tu propio código? Vamos a crear un pequeño servicio en Python que genera métricas personalizadas.

Necesitarás la librería oficial de Prometheus para Python:

Terminal window
pip install prometheus_client

Crea un archivo con el siguiente código. Este script simula un proceso que recibe peticiones y aumenta un contador.

monitor_app.py
from prometheus_client import start_http_server, Counter
import time
import random
# Definimos nuestra propia métrica: un Counter
# Los nombres suelen seguir el patrón: nombre_servicio_unidades_total
PEDIDOS_TOTAL = Counter('bda_app_pedidos_recibidos_total', 'Número total de pedidos en nuestra app')
if __name__ == '__main__':
# Iniciamos un servidor web en el puerto 8000 para exponer las métricas
start_http_server(8000)
print("Métricas en http://localhost:8000")
while True:
# Simulamos que llega un pedido aleatoriamente
PEDIDOS_TOTAL.inc()
print("Nuevo pedido registrado!")
time.sleep(random.uniform(1, 5))

Ejecuta el script: python monitor_app.py. Ahora puedes entrar en http://localhost:8000 y verás tu métrica bda_app_pedidos_recibidos_total.

Ahora hay que configurar al “Inspector” para que visite tu nueva app. Como Prometheus está en Docker y tu app está en tu host (Mac), usaremos la dirección especial host.docker.internal.

Modifica el archivo src/content/docs/modulo03/docker/prometheus.yml y añade este nuevo job:

# Tu nueva aplicación de pedidos
- job_name: "mi-app-python"
static_configs:
- targets: ["host.docker.internal:8000"]

Para que coja los cambios, reinicia el contenedor:

Terminal window
docker compose restart prometheus

Verificación: Ve a Prometheus -> Status -> Targets. Deberías ver mi-app-python en verde. Ahora puedes hacer un rate(bda_app_pedidos_recibidos_total[1 m]) y verás la velocidad de pedidos en tiempo real en la pestaña Graph.

Gráfica de Rate de pedidos

Cuando analizamos la gráfica de una función rate(), estamos viendo el caudal (throughput) de nuestra aplicación. En la imagen superior podemos observar:

  1. Pendiente inicial: Representa el momento en que la aplicación arranca y el “Inspector” (Prometheus) empieza a promediar los primeros datos.
  2. Mesetas (Plateaus): Los tramos llanos (como el nivel 0.30 o 0.36) indican que el tráfico es estable; la aplicación está procesando un número constante de pedidos por segundo.
  3. Variación: Si los números suben o bajan, reflejan directamente los cambios en el time.sleep() de tu script de Python o un aumento en la carga de trabajo.
  4. Unidades: El eje Y representa peticiones por segundo (req/s). Un valor de 0.38 significa que estamos recibiendo un pedido aproximadamente cada 2.6 segundos.

Hasta ahora has sido un observador pasivo. Las métricas son datos históricos bonitos, pero ¿qué pasa cuando algo sale mal a las 3 AM?

Alertmanager es tu guardaespaldas. No solo detecta problemas, sino que elige cómo avisarte: Slack, email, PagerDuty, SMS… Sin él, podrías tener tu base de datos colapsada mientras duermes tranquilamente observando gráficas.

graph LR
P["Prometheus<br/>(Evalúa Rules)"]
AR["Alert Rules<br/>(.yml)"]
AM["AlertManager<br/>(Routing & Notificación)"]
CH["Canales"]
P -- "Lee cada 30s" --> AR
P -- "Si la alerta se activa..." --> AM
AM -- "Agrupa & Enruta" --> CH
CH --> Email["📧 Email"]
CH --> Slack["💬 Slack"]
CH --> PD["🚨 PagerDuty"]
CH --> Webhook["🔗 Webhook Custom"]
style P fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style AM fill:#ffccbc,stroke:#d84315,stroke-width:2px
style AR fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px

7.2 Alert Rules: Definiendo las Condiciones Críticas

Section titled “7.2 Alert Rules: Definiendo las Condiciones Críticas”

Las Alert Rules son pequeños “si-entonces” que monitorean tus métricas. Se definen en archivos YAML y Prometheus las evalúa constantemente.

groups:
- name: "aplicacion"
interval: 30s # Evalúa cada 30 segundos
rules:
# Alerta 1: CPU muy alta
- alert: CPUAlta
expr: (100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)) > 80
for: 5m # Solo alerta si dura más de 5 minutos
labels:
severity: "warning"
annotations:
summary: "CPU > 80% en {{ $labels.instance }}"
description: "La CPU ha superado el 80% durante 5 minutos. Valor actual: {{ $value }}%"
# Alerta 2: Memoria crítica
- alert: MemoriaCritica
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
for: 2m
labels:
severity: "critical"
annotations:
summary: "RAM < 10% disponible"
description: "Solo quedan {{ $value }}% de memoria libre. ¡Actúa ya!"
# Alerta 3: Disco lleno
- alert: DiscoLleno
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
for: 10m
labels:
severity: "warning"
annotations:
summary: "Disco raíz apenas {{ $value }}% libre"
description: "El disco está casi lleno. Necesitas limpiar espacio pronto."
# Alerta 4: Contenedor usando mucha memoria
- alert: ContenedorMemoriaAlta
expr: container_memory_usage_bytes{name!="cadvisor"} / 1073741824 > 1 # > 1 GB
for: 3m
labels:
severity: "warning"
servicio: "{{ $labels.name }}"
annotations:
summary: "Contenedor {{ $labels.name }} usa {{ $value }}GB"
description: "El contenedor {{ $labels.name }} está consumiendo más de 1GB de RAM."
# Alerta 5: Target DOWN (exportador no responde)
- alert: PrometheusTargetDown
expr: up == 0
for: 1m
labels:
severity: "critical"
annotations:
summary: "Target {{ $labels.job }} DOWN"
description: "El exporter {{ $labels.instance }} no responde desde hace 1 minuto."
# Alerta 6: Tasa de errores de aplicación
- alert: TasaErroresAlta
expr: rate(bda_app_errores_total[5m]) > 0.1 # > 0.1 errores/seg
for: 5m
labels:
severity: "warning"
annotations:
summary: "Aplicación generando errores"
description: "{{ $value }} errores/segundo en los últimos 5 minutos."
CampoObligatorioExplicación
alert✅ SíNombre de la alerta (sin espacios, snake_case)
expr✅ SíExpresión PromQL que dispara la alerta
for❌ NoEsperar X tiempo antes de activar (evita falsos positivos)
labels❌ NoEtiquetas personalizadas para organizar/rutear alertas
annotations❌ NoMensajes humanizados (summary, description)

Dentro de {{ }} puedes usar variables:

annotations:
summary: "CPU > 80% en {{ $labels.instance }}"
description: "Valor actual: {{ $value }}% | Job: {{ $labels.job }}"
  • {{ $labels.nombre_etiqueta }}: Accede a cualquier etiqueta de la métrica
  • {{ $value }}: El valor numérico que disparó la alerta
  • {{ $labels.instance }}, {{ $labels.job }}: Labels estándar de Prometheus

El Alertmanager recibe las alertas de Prometheus y decide a quién avisarle y cómo. Es como un recepcionista que entiende prioridades.

Archivo de Configuración: alertmanager.yml

Section titled “Archivo de Configuración: alertmanager.yml”
global:
resolve_timeout: 5m
# Parámetros globales para email SMTP
smtp_smarthost: 'smtp.gmail.com:587'
smtp_auth_username: 'tu_email@gmail.com'
smtp_auth_password: 'tu_app_password' # ⚠️ Usa variables de entorno en producción
# Define el receptor por defecto
route:
receiver: 'default'
group_by: ['alertname', 'cluster', 'service'] # Agrupa alertas similares
group_wait: 10s # Espera 10s antes de enviar (agrupa alertas cercanas)
group_interval: 10m # Reenvía cada 10m si la alerta persiste
repeat_interval: 12h # Re-notifica cada 12h si aún está activa
# Rutas especiales para alertas críticas
routes:
- match:
severity: critical
receiver: 'oncall'
group_wait: 0s # ¡INMEDIATO para críticas!
repeat_interval: 30m
- match:
severity: warning
receiver: 'slack'
group_wait: 5m
repeat_interval: 4h
# Receptores: dónde se envían las alertas
receivers:
- name: 'default'
email_configs:
- to: 'ops@empresa.com'
from: 'alertas@empresa.com'
smarthost: 'smtp.gmail.com:587'
auth_username: 'alertas@empresa.com'
auth_password: 'contraseña'
headers:
Subject: '[Prometheus] {{ .GroupLabels.alertname }}'
- name: 'oncall'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_KEY'
description: '{{ .GroupLabels.alertname }}'
email_configs:
- to: 'oncall@empresa.com'
- name: 'slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#alertas'
title: '🚨 {{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}'
send_resolved: true
# Inhibición: suprime alertas menos importantes si hay otras activas
inhibit_rules:
# Si el host está DOWN, no alertes por métricas de ese host
- source_match:
severity: 'critical'
alertname: 'PrometheusTargetDown'
target_match_re:
severity: 'warning|info'
equal: ['instance']

Jerarquía de Rutas

Las rutas actúan como un árbol de decisiones. La primera coincidencia gana. Por eso las alertas críticas van arriba.

Crea src/content/docs/modulo03/docker/prometheus_rules.yml:

groups:
- name: "ejercicio_alertas"
interval: 15s
rules:
- alert: MemoriaDisponibleBaja
expr: (node_memory_MemAvailable_bytes / 1024 / 1024 / 1024) < 1.5
for: 2m
labels:
severity: "warning"
annotations:
summary: "Memoria disponible < 1.5 GB"
description: "Quedan {{ $value | humanize }}GB disponibles"

Añade al archivo de config de Prometheus:

rule_files:
- "/etc/prometheus/prometheus_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
Terminal window
docker compose restart prometheus alertmanager
  • Ve a Prometheus → Alerts: Verás tus reglas y su estado
  • Ve a AlertManager → http://localhost:9093: Mira las alertas activas

DO’s:

  • Nombra alertas en PascalCase: CPUAlta, MemoriaCritica
  • Usa for: para evitar ruido: no alertes por 30 segundos de CPU alta
  • Haz anotaciones descriptivas: ayuda a tu compañero a las 3 AM
  • Severidades realistas: critical solo para emergencias reales
  • Agrupa alertas relacionadas: no recibas 50 emails por el mismo problema

DON’Ts:

  • No alertes por todo: “La RAM cambió” no es una alerta
  • No uses severity: critical para warnings
  • No olvides incluir {{ $value }} en las anotaciones
  • No ignores las alertas silenciadas (mute silence rules)

A veces necesitas mantenimiento y no quieres alertas:

Terminal window
# Silenciar todas las alertas de un job por 2 horas
amtool alert add job=node-exporter duration=2h comment="Mantenimiento planificado"
# Ver silencios activos
amtool silence query

O desde la interfaz de AlertManager: Alerts → Silence


  1. “Target is DOWN”: El contenedor del exporter no está funcionando o hay un problema de red entre Prometheus y el exporter. Revisa docker compose ps.
  2. Consulta sin resultados: Verifica que has escrito bien las etiquetas. PromQL distingue entre mayúsculas y minúsculas.
  3. Grafana no conecta: Asegúrate de que, al añadir Prometheus como Data Source en Grafana, usas la URL http://prometheus:9090 (nombre interno de Docker).
  4. Las alertas no se disparan: Comprueba que rule_files está en prometheus.yml y que Alertmanager está conectado en alerting. Revisa los logs: docker compose logs prometheus.
  5. No recibo notificaciones: Verifica que AlertManager está running (docker compose ps), que el receptor está correctamente configurado (email SMTP, Slack webhook, etc.) y que las alertas aparecen en http://localhost:9093.

[!NOTE] Soluciones Las soluciones a estos ejercicios están en 1.Prometheus_SOL.txt

Accede a Prometheus y busca la métrica prometheus_build_info.

  • Reto: Identifica la versión exacta de Prometheus que estás ejecutando. ¿En qué etiqueta (label) se encuentra?

Ej. 2: El Pulso de la Máquina (Node Exporter)

Section titled “Ej. 2: El Pulso de la Máquina (Node Exporter)”

Queremos saber qué tal va nuestro servidor.

  • Reto: Escribe una consulta para obtener el porcentaje de espacio libre en disco en la partición raíz (/).
  • Pista: Usa las métricas node_filesystem_avail_bytes y node_filesystem_size_bytes.
  • Reto: Calcula la tasa de bytes recibidos por segundo (node_network_receive_bytes_total) en los últimos 5 minutos para la interfaz eth0.
  • Pista: Recuerda usar la función rate().

Ej. 4: El Top 3 de Contenedores (cAdvisor)

Section titled “Ej. 4: El Top 3 de Contenedores (cAdvisor)”
  • Reto: Crea una consulta que devuelva los 3 contenedores que más memoria RAM están usando en este momento.
  • Pista: Usa topk(3, ...) y la métrica container_memory_usage_bytes.

Si el generador de tráfico está activo, Nginx debería estar echando humo.

  • Reto: Calcula la tasa de peticiones por segundo que está recibiendo Nginx.
  • Métrica: nginx_http_requests_total.

Crea una alerta que se active cuando el uso de CPU en tu host supere el 50% durante 3 minutos.

  • Pista: La métrica es node_cpu_seconds_total con mode="idle". Convierte a porcentaje.
  • Bonificación: Añade etiquetas de severidad y anotaciones descriptivas.

Modifica alertmanager.yml para que las alertas de severidad critical se envíen a un canal diferente del default.

  • Reto: Usa match: para filtrar por severidad y receiver: para elegir dónde enviarlas.

🚀 Proyecto: Tu Cuadro de Mandos Operativo

Section titled “🚀 Proyecto: Tu Cuadro de Mandos Operativo”

Un buen ingeniero de datos nunca vuela a ciegas. Tu misión es definir un conjunto de consultas críticas para un dashboard de “Salud del Clúster”:

  1. CPU Host: % de uso total de CPU del servidor.
  2. RAM Host: % de RAM usada.
  3. Containers: Lista de contenedores activos agrupados por imagen.
count(container_last_seen) by (image)
  1. Red: Ratio de errores de red en el host.

Alertas Prometheus - Ejemplos por Tecnología

Section titled “Alertas Prometheus - Ejemplos por Tecnología”

Una colección lista para usar de Alert Rules organizadas por severidad y tecnología.


Copia cualquier bloque - alert: que necesites y pégalo en tu prometheus_rules.yml:

groups:
- name: "mi_infraestructura"
interval: 30s
rules:
# Pega aquí cualquier alerta del documento

🔴 ALERTAS CRÍTICAS (Severity: critical)

Section titled “🔴 ALERTAS CRÍTICAS (Severity: critical)”

Estas requieren acción INMEDIATA. No admiten espera.

- alert: HostDown
expr: up{job="node-exporter"} == 0
for: 1m
labels:
severity: critical
team: infraestructura
annotations:
summary: "Host {{ $labels.instance }} NO RESPONDE"
description: "El servidor {{ $labels.instance }} no está alcanzable desde Prometheus"
runbook: "https://wiki.empresa.com/host-down"
- alert: DiskCritical
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 5
for: 1m
labels:
severity: critical
team: infraestructura
annotations:
summary: "⚠️ DISCO RAÍZ CRÍTICO: Solo {{ $value }}% libre"
description: "El disco / tendrá espacio en menos de 1 hora si continúa escribiendo"
- alert: MemoriaAgotada
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 5
for: 2m
labels:
severity: critical
team: infraestructura
annotations:
summary: "RAM < 5% disponible en {{ $labels.instance }}"
description: "Queda {{ $value }}% de memoria. Riesgo de OOM Kill inminente."
- alert: SwapCritical
expr: (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes) * 100 < 10
for: 1m
labels:
severity: critical
annotations:
summary: "Swap crítico: {{ $value }}% libre"
description: "El área de swap está casi llena. Sistema en riesgo de colapso."
- alert: LoadAverage
expr: node_load15 > (count by (instance) (node_cpu_seconds_total{mode="idle"}) * 4)
for: 5m
labels:
severity: critical
annotations:
summary: "Load average crítico: {{ $value }}"
description: "La carga del sistema es {{ $value }} veces el número de CPUs disponibles"
- alert: ServiceDown
expr: up{job!="prometheus"} == 0
for: 2m
labels:
severity: critical
component: "{{ $labels.job }}"
annotations:
summary: "{{ $labels.job }} está CAÍDO"
description: "El servicio {{ $labels.job }} en {{ $labels.instance }} no responde"
- alert: ErrorRateHigh
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.5 # > 0.5 errores/segundo
for: 2m
labels:
severity: critical
team: backend
annotations:
summary: "Tasa de errores 5xx: {{ $value }} req/s"
description: "Tu aplicación está generando {{ $value | humanize }} errores por segundo. PÁGINA ROTA."
- alert: DatabaseConnectionPoolExhausted
expr: mysql_global_status_max_used_connections / mysql_global_variables_max_connections > 0.9
for: 1m
labels:
severity: critical
database: "{{ $labels.instance }}"
annotations:
summary: "Pool de conexiones BD al 90%"
description: "Quedan {{ $value | humanizePercentage }} de conexiones disponibles"

⚠️ ALERTAS DE AVISO (Severity: warning)

Section titled “⚠️ ALERTAS DE AVISO (Severity: warning)”

Requieren atención, pero no es emergencia. Planificar acción en horas.

- alert: HighCPUUsage
expr: (100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)) > 80
for: 10m # Solo si persiste 10 minutos
labels:
severity: warning
team: infraestructura
annotations:
summary: "CPU al {{ $value }}% en {{ $labels.instance }}"
description: "La CPU ha estado por encima del 80% durante los últimos 10 minutos"
grafana: "https://grafana.empresa.com/d/cpu-dashboard"
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
team: infraestructura
annotations:
summary: "RAM al {{ $value }}% en {{ $labels.instance }}"
description: "El servidor {{ $labels.instance }} está usando {{ $value | humanize }}% de RAM"
- alert: DiskSpaceRunningOut
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 20
for: 30m
labels:
severity: warning
team: infraestructura
annotations:
summary: "Disco: solo {{ $value }}% libre"
description: "A este ritmo de escritura, se llena en {{ (node_filesystem_avail_bytes / (node_filesystem_avail_bytes - (node_filesystem_avail_bytes offset 1h))) * 100 | humanize }} horas"
- alert: HighIOWait
expr: rate(node_cpu_seconds_total{mode="iowait"}[5m]) > 0.3 # > 30% tiempo esperando I/O
for: 5m
labels:
severity: warning
team: infraestructura
annotations:
summary: "IOWait al {{ $value | humanizePercentage }}"
description: "El sistema está esperando operaciones de disco. Posible cuello de botella."
- alert: NetworkErrors
expr: rate(node_network_receive_errs_total[5m]) > 0.01 or rate(node_network_transmit_errs_total[5m]) > 0.01
for: 5m
labels:
severity: warning
interface: "{{ $labels.device }}"
annotations:
summary: "Errores de red en {{ $labels.device }}"
description: "{{ $value }} errores por segundo en la interfaz {{ $labels.device }}"
- alert: ContainerMemoryWarning
expr: (container_memory_usage_bytes / 1073741824) > 0.8 # > 0.8 GB
for: 5m
labels:
severity: warning
container: "{{ $labels.name }}"
annotations:
summary: "Contenedor {{ $labels.name }} usa {{ $value | humanize }}GB"
description: "Este contenedor está consumiendo mucha memoria. Considera aumentar el límite."
- alert: ContainerRestartFrequent
expr: rate(container_last_seen{name!="cadvisor"}[5m]) > 0.1 # Reinicia > cada 10 seg
for: 2m
labels:
severity: warning
container: "{{ $labels.name }}"
annotations:
summary: "Contenedor {{ $labels.name }} reiniciando frecuentemente"
description: "El contenedor se ha reiniciado {{ $value }} veces en los últimos 5 minutos"
- alert: ContainerNetworkBandwidth
expr: (rate(container_network_receive_bytes_total[1m]) + rate(container_network_transmit_bytes_total[1m])) / 1024 / 1024 > 100 # > 100 MB/s
for: 5m
labels:
severity: warning
container: "{{ $labels.name }}"
annotations:
summary: "Red saturada en {{ $labels.name }}: {{ $value | humanize }}MB/s"
description: "Hay {{ $value | humanize }} MB/s de tráfico. Posible DOS o fuga de datos."
- alert: MySQLSlowQueries
expr: rate(mysql_global_status_slow_queries[5m]) > 0.1
for: 5m
labels:
severity: warning
database: "{{ $labels.instance }}"
annotations:
summary: "MySQL: {{ $value }} queries lentos/segundo"
description: "Tu MySQL tiene {{ $value | humanize }} queries lentos por segundo"
- alert: MySQLReplication
expr: mysql_slave_status_slave_io_running == 0 or mysql_slave_status_slave_sql_running == 0
for: 1m
labels:
severity: warning
database: "{{ $labels.instance }}"
annotations:
summary: "Replicación MySQL caída"
description: "La replicación en {{ $labels.instance }} se ha detenido"
- alert: RedisMemory
expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.9
for: 5m
labels:
severity: warning
cache: "{{ $labels.instance }}"
annotations:
summary: "Redis: {{ $value | humanizePercentage }} de memoria usada"
description: "Redis está al {{ $value | humanizePercentage }} de su límite. Aumenta maxmemory."
- alert: RedisEvictions
expr: rate(redis_evicted_keys_total[5m]) > 0
for: 2m
labels:
severity: warning
cache: "{{ $labels.instance }}"
annotations:
summary: "Redis: evinciones activas"
description: "Redis está expulsando {{ $value | humanize }} keys/segundo"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1 # P95 > 1 segundo
for: 5m
labels:
severity: warning
team: backend
annotations:
summary: "Latencia P95: {{ $value | humanize }}s"
description: "El 95% de las requests tardan más de {{ $value | humanize }}s"
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 # > 5%
for: 5m
labels:
severity: warning
service: "{{ $labels.job }}"
annotations:
summary: "Tasa de error: {{ $value | humanizePercentage }}"
description: "{{ $value | humanizePercentage }} de las requests están fallando"
- alert: CertificateExpiring
expr: ssl_cert_not_after - time() < 7 * 24 * 3600 # < 7 días
for: 1m
labels:
severity: warning
cert: "{{ $labels.domain }}"
annotations:
summary: "Certificado SSL expira en {{ ($value / 86400) | humanize }} días"
description: "El certificado de {{ $labels.domain }} caduca en {{ ($value / 86400) | humanize }} días"

ℹ️ ALERTAS INFORMATIVAS (Severity: info)

Section titled “ℹ️ ALERTAS INFORMATIVAS (Severity: info)”

Útiles para alertas de auditoría, cambios planificados, etc. Bajo ruido.

- alert: ScheduledMaintenance
expr: up > 0 # Placeholder - requiere activación manual
for: 1m
labels:
severity: info
team: operations
annotations:
summary: "Mantenimiento programado iniciado"
description: "Sistema en ventana de mantenimiento: 02:00-04:00 UTC"
- alert: DailyBackupCompleted
expr: rate(backup_duration_seconds[5m]) > 0 # Si hay actividad de backup
for: 1m
labels:
severity: info
annotations:
summary: "Backup diario completado"
description: "Backup de {{ $labels.database }} tardó {{ $value | humanize }}s"
- alert: LargeDeployment
expr: rate(deployment_timestamp[1m]) > 0
for: 1m
labels:
severity: info
service: "{{ $labels.service }}"
annotations:
summary: "Deploy de {{ $labels.service }} detectado"
description: "Nueva versión {{ $labels.version }} desplegada"

Plantilla Generic para crear tus propias alertas

Section titled “Plantilla Generic para crear tus propias alertas”
- alert: NombreAlertaEnPascalCase
expr: YOUR_PROMQL_QUERY_HERE
for: 5m # Ajusta según sensitivity requerida
labels:
severity: warning # critical / warning / info
team: nombre_equipo
component: "{{ $labels.LABEL_IMPORTANTE }}"
runbook: "https://wiki.empresa.com/alerts/nombre-alerta"
annotations:
summary: "[{{ $labels.severity | upper }}] Resumen corto del problema"
description: "Descripción detallada: {{ $value | humanize }} + contexto desde labels"
runbook_url: "https://wiki.empresa.com/runbooks/nombre-alerta"
dashboard: "https://grafana.empresa.com/d/ID-dashboard"

# Conversiones
{{ $value | humanize }} # 1500 → "1.5k"
{{ $value | humanizePercentage }} # 0.95 → "95%"
{{ ($value / 1024) }} # Convertir a KB desde bytes
# Comparaciones
rate(metric[5m]) # Velocidad de cambio
increase(metric[1h]) # Cambio absoluto en 1h
topk(3, metric) # Top 3 valores
# Agregaciones
sum by (label) # Suma por label
avg by (label) # Media por label
count by (label) # Conteo por label
# Temporales
offset 1h # Comparar con datos de hace 1h
histogram_quantile(0.95, ...) # Percentil 95

Timing de for:

Critical: 1-2 minutos (rápida respuesta)
Warning: 5-10 minutos (evita ruido)
Info: 15-30 minutos (muy tolerante)

Label Naming

labels:
severity: [critical, warning, info] # Obligatorio
team: [backend, infraestructura, datos]
runbook: "https://..." # Link a instrucciones

Anotaciones

annotations:
summary: "Línea 1 - Qué pasó" # < 80 chars
description: "Línea 2+ - Por qué, contexto, valores"
runbook: "Cómo arreglarlo"