kube-prometheus-stack es el chart de Helm de la comunidad Prometheus que instala, en un solo paso, Prometheus Operator, Prometheus, Alertmanager, Grafana, node-exporter y kube-state-metrics, junto con paneles y reglas de alerta ya preparados para Kubernetes. En este tutorial lo desplegarás en un clúster existente, accederás a Prometheus y Grafana, harás que Prometheus recoja las métricas de tu propia aplicación con un ServiceMonitor, crearás una regla de alerta y enviarás las alertas a Slack.

Requisitos previos

Para seguir esta guía necesitas:

  • Un clúster de Kubernetes 1.28 o superior con al menos 2 nodos y unos 4 GB de RAM libres entre todos ellos, por ejemplo un clúster Kubernetes gestionado de CubePath.
  • kubectl configurado contra ese clúster en tu equipo o en un servidor de administración con Ubuntu 24.04.
  • Helm 3 o superior.
  • Una StorageClass que permita crear volúmenes persistentes, si quieres que los datos de Prometheus sobrevivan a un reinicio del pod.

Comprueba que kubectl llega al clúster y que ves los nodos:

kubectl get nodes
NAME       STATUS   ROLES    AGE   VERSION
worker-1   Ready    <none>   12d   v1.31.4
worker-2   Ready    <none>   12d   v1.31.4

Paso 1: Instalar Helm

Si todavía no tienes Helm, en Ubuntu 24.04 puedes instalarlo con el snap oficial:

sudo snap install helm --classic
helm version
version.BuildInfo{Version:"v3.x.y", ...}

Añade el repositorio de charts de la comunidad Prometheus y actualiza el índice:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

Paso 2: Preparar el archivo de valores

En lugar de pasar opciones con --set, guarda la configuración en un archivo de valores que puedas versionar:

nano kps-values.yaml

Este archivo fija la contraseña de Grafana, la retención y el almacenamiento de Prometheus, y hace que Prometheus recoja cualquier ServiceMonitor y PrometheusRule del clúster, no solo los creados por el propio chart. Sustituye your_strong_password y your_storage_class:

grafana:
  adminPassword: your_strong_password

prometheus:
  prometheusSpec:
    retention: 15d
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: your_storage_class
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 20Gi

Consulta las clases de almacenamiento disponibles con kubectl get storageclass. Si no tienes ninguna, elimina el bloque storageSpec: Prometheus usará un volumen temporal y perderá los datos al reiniciarse.

Paso 3: Desplegar kube-prometheus-stack

Instala el chart en un namespace propio llamado monitoring:

helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  -f kps-values.yaml
NAME: kube-prometheus-stack
NAMESPACE: monitoring
STATUS: deployed
...

Espera uno o dos minutos y comprueba que todos los pods están en Running:

kubectl get pods -n monitoring
NAME                                                        READY   STATUS    RESTARTS   AGE
alertmanager-kube-prometheus-stack-alertmanager-0           2/2     Running   0          2m
kube-prometheus-stack-grafana-6d9f8c7b5d-x2k9p              3/3     Running   0          2m
kube-prometheus-stack-kube-state-metrics-5c8b7d9f4-7hq2m    1/1     Running   0          2m
kube-prometheus-stack-operator-7f9c6d8b5-m4n8s              1/1     Running   0          2m
kube-prometheus-stack-prometheus-node-exporter-4xk2l        1/1     Running   0          2m
kube-prometheus-stack-prometheus-node-exporter-9vj7w        1/1     Running   0          2m
prometheus-kube-prometheus-stack-prometheus-0               2/2     Running   0          2m

Hay un pod de node-exporter por nodo (es un DaemonSet). Si configuraste almacenamiento, confirma que el volumen de Prometheus está enlazado:

kubectl get pvc -n monitoring
NAME                                                                                             STATUS   VOLUME    CAPACITY   ...
prometheus-kube-prometheus-stack-prometheus-db-prometheus-kube-prometheus-stack-prometheus-0   Bound    pvc-...   20Gi       ...

Paso 4: Acceder a Prometheus y comprobar los objetivos

Los servicios del chart son de tipo ClusterIP, así que no quedan expuestos fuera del clúster. Para acceder a Prometheus abre un reenvío de puertos:

kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090:9090

Visita http://localhost:9090/targets en el navegador. Deberías ver objetivos como apiserver, kubelet, node-exporter y kube-state-metrics en estado UP.

En la pestaña Graph prueba algunas consultas PromQL útiles. Uso de CPU por pod en el namespace default:

sum by (pod) (rate(container_cpu_usage_seconds_total{namespace="default", container!=""}[5m]))

Memoria de trabajo por pod:

sum by (pod) (container_memory_working_set_bytes{namespace="default", container!=""})

Pods que se han reiniciado en la última hora:

increase(kube_pod_container_status_restarts_total[1h]) > 0

Pulsa Ctrl+C en la terminal para cerrar el reenvío cuando termines.

Paso 5: Acceder a Grafana

Abre un reenvío de puertos al servicio de Grafana, que escucha en el puerto 80 dentro del clúster:

kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80

Visita http://localhost:3000 e inicia sesión con el usuario admin y la contraseña que pusiste en kps-values.yaml. Prometheus ya está configurado como fuente de datos.

En Dashboards encontrarás los paneles que instala el chart. Los más útiles para empezar son:

  • Kubernetes / Compute Resources / Cluster: CPU y memoria de todo el clúster, desglosadas por namespace.
  • Kubernetes / Compute Resources / Namespace (Pods): consumo de cada pod de un namespace.
  • Node Exporter / Nodes: CPU, memoria, disco y red de cada nodo.

Paso 6: Recoger las métricas de tu aplicación con un ServiceMonitor

Prometheus Operator no usa un archivo prometheus.yml editado a mano: descubre qué recoger a partir de recursos ServiceMonitor. Para este ejemplo supondremos una aplicación en el namespace default con un Service etiquetado app: my-app que expone métricas en un puerto llamado metrics en la ruta /metrics. El puerto debe tener nombre en el Service:

apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: default
  labels:
    app: my-app
spec:
  selector:
    app: my-app
  ports:
    - name: metrics
      port: 8080
      targetPort: 8080

Crea el ServiceMonitor:

nano my-app-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
  namespace: default
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      path: /metrics
      interval: 30s

Aplícalo:

kubectl apply -f my-app-servicemonitor.yaml

Gracias a serviceMonitorSelectorNilUsesHelmValues: false no hace falta añadir la etiqueta release: kube-prometheus-stack. En menos de un minuto aparecerá un objetivo serviceMonitor/default/my-app/0 en http://localhost:9090/targets. Si no aparece, revisa que la etiqueta del Service y el nombre del puerto coinciden exactamente con los del ServiceMonitor.

Paso 7: Crear una regla de alerta

El chart ya incluye decenas de alertas (KubePodCrashLooping, KubeNodeNotReady, NodeFilesystemSpaceFillingUp...). Para añadir las tuyas crea un recurso PrometheusRule. Este ejemplo avisa cuando un pod del namespace default usa más del 90 % del límite de memoria de su contenedor durante 10 minutos:

nano custom-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: custom-rules
  namespace: monitoring
spec:
  groups:
    - name: custom.rules
      rules:
        - alert: ContainerMemoryNearLimit
          expr: |
            sum by (namespace, pod, container) (container_memory_working_set_bytes{namespace="default", container!=""})
              /
            sum by (namespace, pod, container) (kube_pod_container_resource_limits{namespace="default", resource="memory"})
              > 0.9
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "El contenedor {{ $labels.container }} de {{ $labels.pod }} está cerca de su límite de memoria"

Aplica la regla y comprueba que existe:

kubectl apply -f custom-rules.yaml
kubectl get prometheusrules -n monitoring custom-rules
NAME           AGE
custom-rules   10s

Con el reenvío de puertos de Prometheus abierto, la regla aparecerá en http://localhost:9090/alerts dentro del grupo custom.rules.

Paso 8: Enviar las alertas a Slack

Alertmanager agrupa las alertas y las envía a sus receptores. Crea un webhook entrante en tu espacio de Slack y añade la configuración de Alertmanager al final de kps-values.yaml. Sustituye your_slack_webhook_url y el canal:

alertmanager:
  config:
    global:
      resolve_timeout: 5m
    route:
      group_by: ["namespace", "alertname"]
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 12h
      receiver: slack
      routes:
        - receiver: "null"
          matchers:
            - alertname = "Watchdog"
    receivers:
      - name: "null"
      - name: slack
        slack_configs:
          - api_url: your_slack_webhook_url
            channel: "#alerts"
            send_resolved: true
            title: '{{ .CommonLabels.alertname }}'
            text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ .Annotations.description }}{{ "\n" }}{{ end }}'

La alerta Watchdog está siempre activa a propósito para comprobar que la cadena de alertas funciona; la ruta anterior la descarta para que no llegue a Slack cada 12 horas.

Aplica los cambios con helm upgrade:

helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring -f kps-values.yaml

Comprueba que Alertmanager ha cargado la nueva configuración:

kubectl port-forward -n monitoring svc/kube-prometheus-stack-alertmanager 9093:9093

En http://localhost:9093/#/status verás la configuración activa con el receptor slack.

Solución de problemas

  • Pods en Pending: kubectl describe pod -n monitoring <pod>. Suele deberse a falta de recursos en los nodos o a que la StorageClass indicada no existe.
  • Objetivos del plano de control en DOWN: normal en clústeres gestionados; desactívalos como se explica en el paso 2.
  • Un ServiceMonitor no genera objetivos: kubectl logs -n monitoring deploy/kube-prometheus-stack-operator y revisa que el Service tiene endpoints con kubectl get endpoints my-app.
  • Las alertas no llegan a Slack: revisa kubectl logs -n monitoring alertmanager-kube-prometheus-stack-alertmanager-0 -c alertmanager.

Conclusión

Tienes Prometheus, Grafana y Alertmanager monitorizando el clúster, recogiendo las métricas de tu aplicación con un ServiceMonitor y avisando en Slack. Como siguientes pasos, publica Grafana con un Ingress y HTTPS en lugar de usar port-forward, crea paneles propios para las métricas de tu aplicación y guarda kps-values.yaml en un repositorio para desplegar el stack con GitOps.