Monitorización de Kubernetes: Prometheus, Grafana, alertas

Instalar kube-prometheus-stack en un clúster de pyme: retención, coste real, las 8 reglas de alerta que sirven y las que hay que desactivar.

Hombre de espaldas en una sala de control frente a pantallas de monitorización y un videowall

Respuesta corta: en un clúster de Kubernetes de una pyme, la monitorización útil se resume en tres decisiones, no en trescientos paneles. Instalar kube-prometheus-stack (el chart comunitario que incluye Prometheus, Alertmanager, Grafana y las reglas de kubernetes-mixin), dimensionar la retención a 15 días en un volumen de 50 Gi —entre 2 y 5 € al mes de block storage en OVHcloud o Scaleway— y después eliminar el 80 % de las reglas de alerta que vienen por defecto para quedarse solo con una decena que describan un síntoma que el usuario percibe. Una alerta que no desencadena una acción es un error de configuración, no una precaución.

Con frecuencia nos hacemos cargo de clústeres donde Prometheus lleva un año funcionando. El patrón es siempre el mismo: un canal de Slack #alerts mudo desde hace ocho meses porque nadie soporta ya las notificaciones, un Grafana con cuarenta paneles de los que tres se han abierto al menos una vez, y una retención de 10 días en un disco que se desborda cada quince días. Mientras tanto, el incidente real —una base de datos PostgreSQL saturada de conexiones, un pod que el OOM killer mata todas las noches a las 2— pasa desapercibido. Este artículo describe la instalación que ponemos por defecto y, sobre todo, la criba de alertas.

Hombre de espaldas en una sala de control frente a pantallas de monitorización y un videowall

Qué hay que medir antes de elegir una herramienta

Desglosemos la necesidad. En un clúster que sirve los sitios y aplicaciones de una pyme, la monitorización responde a cuatro preguntas distintas, y cada una tiene su herramienta.

PreguntaNaturaleza del datoHerramienta
¿Responde el servicio, y rápido?métricas de latencia y de tasa de error HTTPPrometheus + métricas de ingress/Gateway
¿Le quedan recursos al clúster?métricas de nodos, pods, PVCPrometheus + node-exporter, kube-state-metrics
¿Por qué ha fallado este pod?registros de la aplicaciónLoki, o la plataforma de logs del proveedor
¿Qué consulta ha ralentizado este recorrido?trazas distribuidasOpenTelemetry + Tempo o Jaeger

La confusión más costosa consiste en querer hacerlo todo con Prometheus. Prometheus es una base de series temporales de baja cardinalidad: almacena métricas numéricas agregadas, no eventos individuales. Meter un identificador de petición o un correo electrónico de usuario en una etiqueta hace estallar la memoria; la documentación de Prometheus sobre buenas prácticas de nomenclatura y la de instrumentación lo repiten: cada combinación de etiquetas crea una serie distinta.

Nuestra recomendación para un clúster de pyme, en un perímetro de 3 a 10 nodos:

  • Métricas: kube-prometheus-stack, autoalojado en el clúster. Retención corta, agregación clara.
  • Registros: Loki con un bucket de objetos, o el servicio de logs del proveedor si el clúster es gestionado. Los registros son voluminosos y rara vez se consultan: el object storage a 0,01 € el GB-mes los vuelve indoloros.
  • Trazas: añadirlas solo cuando una aplicación tiene más de dos servicios que se llaman entre sí. Antes de eso, es tiempo perdido.

Instalar kube-prometheus-stack sin equivocarse con los valores

El chart kube-prometheus-stack lo mantiene la comunidad prometheus-community. Incluye el Prometheus Operator, Prometheus, Alertmanager, Grafana, node-exporter, kube-state-metrics y un juego completo de reglas procedentes del proyecto kubernetes-mixin. Con un solo comando se obtiene una monitorización que funciona… y que grita demasiado.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  -f values.yaml

El archivo values.yaml es donde se decide todo. Este es el esqueleto que utilizamos, adaptando el storageClassName al proveedor (csi-cinder-high-speed en OVHcloud, scw-bssd en Scaleway):

prometheus:
  prometheusSpec:
    retention: 15d
    retentionSize: 40GiB
    scrapeInterval: 30s
    evaluationInterval: 30s
    resources:
      requests:
        cpu: 200m
        memory: 2Gi
      limits:
        memory: 4Gi
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: csi-cinder-high-speed
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi
    # recuperar únicamente los ServiceMonitor etiquetados explícitamente
    serviceMonitorSelectorNilUsesHelmValues: false

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: csi-cinder-high-speed
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 5Gi

grafana:
  adminPassword: ""        # inyectada desde un Secret externo
  persistence:
    enabled: true
    size: 5Gi
  defaultDashboardsTimezone: Europe/Madrid

Tres ajustes merecen una explicación, porque son la causa de la mayoría de los incidentes de monitorización que resolvemos.

retention y retentionSize juntos, siempre

retention: 15d por sí solo no protege el disco: si la cardinalidad aumenta (nuevo servicio, nuevas etiquetas), el volumen se llena antes de los 15 días y Prometheus se detiene. retentionSize fija un techo duro. Se ajusta en torno al 80 % del tamaño del volumen —40 GiB para un PVC de 50 Gi— para dejar sitio a los bloques en proceso de compactación.

Para dimensionar, la regla de magnitud de la documentación de Prometheus sobre almacenamiento sigue siendo válida: espacio ≈ duración de la retención × número de muestras por segundo × tamaño medio de una muestra (del orden de 1 a 2 bytes tras la compresión). En nuestros clústeres de 5 nodos con una treintena de servicios instrumentados, observamos en la práctica entre 15 y 25 Gi para 15 días. Un PVC de 50 Gi deja, por tanto, el doble del margen necesario, por unos pocos euros al mes.

Un límite de memoria, nunca un límite de CPU

Prometheus consume memoria de forma proporcional al número de series activas, con picos durante la compactación y las consultas pesadas de los paneles. Un limits.memory evita que se lleve por delante un nodo entero; un limits.cpu, en cambio, provoca throttling durante la compactación y dispara falsos positivos por todas partes. Es la misma lógica que para las aplicaciones, descrita en nuestro artículo sobre el autoescalado de Next.js en Kubernetes con HPA y KEDA.

El scrapeInterval a 30 s, no a 15 s

Duplicar el intervalo divide por dos el volumen almacenado y la carga de CPU. Para sitios y aplicaciones de pyme, detectar una saturación en 30 segundos en lugar de 15 no cambia nada: la alerta incluye de todos modos un for: 5m para evitar el ruido. Los 15 s por defecto solo se justifican si construyes SLO al segundo.

Las ocho alertas que merecen una notificación

El juego de reglas kubernetes-mixin que viene con el chart cuenta con más de cien alertas. Muchas describen estados internos del plano de control, inservibles en un clúster gestionado de OVHcloud, Scaleway o EKS, donde ese plano de control no es tuyo. La primera acción tras la instalación consiste, por tanto, en desactivar lo que no te concierne:

defaultRules:
  create: true
  rules:
    etcd: false
    kubeApiserverAvailability: false
    kubeApiserverBurnrate: false
    kubeApiserverSlos: false
    kubeControllerManager: false
    kubeSchedulerAlerting: false
    kubeProxy: false

Después se aplica un principio sencillo, tomado de la filosofía de alertado documentada por Google en el capítulo «Monitoring Distributed Systems» del SRE Book: alertar únicamente sobre los síntomas visibles para el usuario, o sobre las causas que se convertirán inevitablemente en un síntoma. Todo lo demás va a un panel, no a una notificación.

Esta es la lista que conservamos en un clúster de pyme, con la razón de ser de cada una.

AlertaDisparadorPor qué justifica una notificación
TargetDownun objetivo de scrape inalcanzable > 10 minsin ella, todas las demás alertas se vuelven silenciosas
KubePodCrashLoopingreinicios repetidos > 15 minun despliegue roto, a menudo justo después de una puesta en producción
KubePodNotReadypod no listo > 15 minimagen no encontrada, probe mal configurada, PVC sin montar
KubeDeploymentReplicasMismatchréplicas deseadas ≠ disponibles > 15 mincapacidad reducida en silencio
KubePersistentVolumeFillingUpPVC con previsión de llenarse en < 4 díasla avería más previsible y más destructiva
KubeNodeNotReadynodo no listo > 15 minpérdida de capacidad, riesgo de pods no reubicables
KubeMemoryOvercommitsuma de los requests de memoria > capacidadningún pod podrá reubicarse si se pierde un nodo
CertificateExpiringSooncertificado TLS < 14 díasuna expiración = sitio inaccesible, y cert-manager falla en silencio

A esta base añadimos sistemáticamente una alerta de aplicación por servicio expuesto, la que por sí sola sustituye a veinte reglas de infraestructura: la tasa de errores HTTP 5xx vista por el ingress o la Gateway. El PrometheusRule se parece a esto, sobre métricas expuestas por un controlador compatible con Gateway API:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: app-availability
  namespace: monitoring
  labels:
    release: monitoring
spec:
  groups:
    - name: app-availability
      rules:
        - alert: HighHttp5xxRate
          expr: |
            sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
            /
            sum(rate(http_requests_total[5m])) by (service)
            > 0.02
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Más del 2 % de errores 5xx en {{ $labels.service }}"
            runbook: "https://wiki.interno/runbooks/http-5xx"

Dos detalles marcan la diferencia. La etiqueta release: monitoring es imprescindible para que el Operator recoja la regla cuando el chart está instalado con ese nombre de release: es la causa n.º 1 de los PrometheusRule aplicados que nunca se disparan. Y la anotación runbook: una alerta sin procedimiento asociado será tratada de forma distinta por cada guardia, y por tanto mal.

Sala de monitorización: adultos frente a pantallas con paneles de control y un gran videowall

Enrutar las alertas: dos severidades, no cinco

Alertmanager permite árboles de enrutado arbitrariamente complejos. En un clúster de pyme, dos niveles bastan, y cualquier severidad intermedia acaba ignorada.

alertmanager:
  config:
    route:
      receiver: chat-info
      group_by: ['alertname', 'namespace']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 12h
      routes:
        - matchers: [severity="critical"]
          receiver: guardia
          repeat_interval: 1h
    inhibit_rules:
      - source_matchers: [alertname="KubeNodeNotReady"]
        target_matchers: [severity="warning"]
        equal: ['node']
    receivers:
      - name: chat-info
        webhook_configs:
          - url: https://chat.example/hooks/monitoring
      - name: guardia
        webhook_configs:
          - url: https://oncall.example/hooks/critical
  • critical → notificación que despierta, con un runbook. Un síntoma de usuario, o una avería inminente.
  • warning → mensaje en un canal que se lee en horario laboral, revisado una vez por semana.

Las inhibit_rules evitan el escenario clásico: cae un nodo y llegan treinta alertas de pods no listos al mismo tiempo que la alerta del nodo. Solo queremos una. Piensa también en los time_intervals para las tareas programadas que elevan la carga por la noche: más vale una ventana de silencio explícita que una alerta que el equipo aprende a ignorar.

Un punto que se olvida a menudo: prueba el camino de notificación, no solo la regla. Una URL de webhook revocada se comporta exactamente igual que un clúster en buen estado. Nosotros enviamos una alerta ficticia Watchdog (incluida en el chart, siempre está activa) hacia un servicio de «dead man's switch»: si el ping deja de llegar, es la propia monitorización la que se ha caído. El mismo razonamiento que para una copia de seguridad nunca restaurada, un tema que detallamos en nuestra guía de copias de seguridad de Kubernetes con Velero.

Cuánto cuesta realmente

Es la pregunta que decide entre autoalojado y servicio gestionado. Las tarifas públicas siguientes son las que muestran los proveedores en otoño de 2026; verifícalas antes de presupuestar, porque cambian.

PartidaDimensiónCoste mensual indicativo
Block storage de Prometheus (OVHcloud, Scaleway)50 Gi de alto rendimiento~4 a 6 € sin IVA
Block storage de Alertmanager + Grafana10 Gi~1 € sin IVA
CPU/RAM consumidos en el clúster~0,5 vCPU, 3 a 5 Giequivalente a un nodo pequeño compartido
Object storage para los registros (Loki)100 GB~1 a 2 € sin IVA

En la práctica, en los clústeres que explotamos, la monitorización completa cuesta menos de 10 € sin IVA al mes de almacenamiento y consume el equivalente a un tercio de nodo. El coste real está en otra parte: los pocos días de ajuste inicial y, después, la hora mensual de limpieza en las alertas.

Las ofertas SaaS de observabilidad facturan por host o por volumen ingerido. Resultan pertinentes en cuanto tienes varios clústeres, una guardia formalizada y necesidad de correlacionar métricas, registros y trazas en la misma interfaz. Por debajo de eso, kube-prometheus-stack hace el trabajo, con la misma lógica de comparación que la aplicada a la elección entre PostgreSQL en Kubernetes y una base de datos gestionada: el precio del servicio es el tiempo de explotación que te devuelve.

Consola de administración con pantalla, teclado y ratón frente a racks de servidores en un centro de datos

Los errores que vemos con más frecuencia

  • Prometheus sin volumen persistente. El chart arranca sin problema en emptyDir. Cada reinicio de pod borra el historial y, con él, cualquier análisis posterior al incidente.
  • Grafana como fuente de verdad. Los paneles creados a mano en la interfaz desaparecen con el PVC. Almacenarlos en un ConfigMap con la etiqueta grafana_dashboard: "1", versionados en Git, los hace reproducibles, exactamente igual que los manifiestos desplegados por el pipeline de GitLab CI/CD.
  • Cardinalidad sin vigilar. Una etiqueta user_id o path sin normalizar multiplica las series por mil. Vigilamos prometheus_tsdb_head_series y alertamos por encima de un umbral elegido (por ejemplo, un millón de series para los recursos anteriores).
  • Ninguna alerta sobre los certificados. cert-manager falla en silencio cuando un challenge DNS se rompe. La alerta llega entonces de los usuarios, tres semanas más tarde.
  • Alertas sin for:. Una regla que se dispara con un único punto de medición produce ruido en cada pico de 30 segundos. Por debajo de for: 5m, plantéate la pregunta.

Si te haces cargo de un clúster existente, el primer ejercicio útil no es añadir paneles: es exportar las alertas disparadas en los últimos 90 días y eliminar todas las que nunca han dado lugar a una acción. Es precisamente lo que hacemos al inicio de la misión en los servicios de mantenimiento e infraestructura, antes de añadir la menor regla. Un canal de alertas creíble vale más que mil métricas.

Preguntas frecuentes

¿Hace falta Prometheus si el clúster es gestionado en OVHcloud o Scaleway?

Sí. Los proveedores supervisan el plano de control (API server, etcd, scheduler) y te muestran su estado de salud, pero no tus aplicaciones: ni tus tasas de error 5xx, ni tus PVC que se llenan, ni tus pods en CrashLoopBackOff. El perímetro gestionado se detiene en la frontera de tus namespaces. En cambio, eso te permite desactivar todas las reglas etcd, kubeApiserver*, kubeControllerManager y kubeScheduler: no puedes hacer nada al respecto y generan falsos positivos.

¿Qué retención elegir para Prometheus?

15 días cubren las necesidades de investigación posterior a un incidente y de comparación semana contra semana. Más allá, la necesidad cambia de naturaleza: se trata de tendencias (capacidad, estacionalidad), no de diagnóstico. Para eso no se alarga la retención local: se añade un remote_write hacia Thanos, Mimir o un servicio gestionado, con métricas ya agregadas. Guardar 12 meses de datos brutos a intervalos de 30 segundos cuesta en almacenamiento y en memoria sin aportar nada.

¿Loki o el servicio de logs del proveedor?

Loki tiene sentido si quieres todo en Grafana y un coste por GB de object storage. Exige ajustes (retención, índice, límites por tenant) y un bucket S3 que gestionar. Los servicios de logs integrados en los Kubernetes gestionados evitan esa explotación, al precio de una factura por ingesta que sorprende cuando una aplicación empieza a registrar en bucle. En ambos casos: limita el caudal por namespace, porque si no un solo console.log dentro de un bucle hace estallar el presupuesto.

¿Se puede alertar sobre la latencia sin instrumentar la aplicación?

Parcialmente. Los controladores de ingress y las implementaciones de Gateway API exponen histogramas de latencia por ruta, lo que basta para un umbral sobre el percentil 95. Es, de hecho, un argumento más a favor del paso a la Gateway API en sustitución de ingress-nginx. En cambio, saber dónde se ha consumido el tiempo exige trazas y, por tanto, instrumentación de la aplicación, generalmente con los SDK de OpenTelemetry.

¿Cuántas alertas críticas por semana es normal?

Cero, la mayoría de las semanas. En los clústeres de pymes que explotamos, una alerta critical es un acontecimiento, no una rutina. Si tu guardia recibe varias por semana sin que le siga una acción correctiva, el problema no es la frecuencia: son los umbrales. El buen indicador de salud de una monitorización no es el número de alertas, es el porcentaje de alertas que han desencadenado una acción.

En resumen

  • kube-prometheus-stack instala con un solo comando Prometheus, Alertmanager, Grafana y las reglas de kubernetes-mixin; todo el trabajo está en el values.yaml y en la criba de reglas.
  • Fija siempre retention y retentionSize (≈ 80 % del volumen), con un PVC de 50 Gi para 15 días en un clúster de 3 a 10 nodos.
  • Un limits.memory en Prometheus, nunca un limits.cpu: el throttling durante la compactación genera falsos positivos.
  • En un clúster gestionado, desactiva las reglas del plano de control (etcd, kubeApiserver*, kubeScheduler, kubeControllerManager): no puedes actuar sobre ellas.
  • Mantén una decena de alertas de infraestructura más una regla de tasa de errores 5xx por servicio expuesto, cada una con un for: y una anotación runbook.
  • Dos severidades bastan: critical, que despierta, y warning, revisada en horario laboral. Más niveles = niveles ignorados.
  • Prueba el camino de notificación con un dead man's switch sobre la alerta Watchdog: una monitorización caída se parece a un clúster en buen estado.
  • Cuenta con menos de 10 € sin IVA al mes de almacenamiento y alrededor de un tercio de nodo en recursos; el coste real es el tiempo de ajuste.
  • ¿Necesitas una mirada externa sobre la monitorización de un clúster existente? Háblalo con nuestro equipo o contáctanos.