--- title: "Cómo monitorizar Kubernetes con Prometheus y Grafana" description: "Despliega kube-prometheus-stack con Helm para monitorizar nodos y pods de Kubernetes, añade un ServiceMonitor, reglas de alerta propias y avisos a Slack." category: "Stack de Observabilidad" language: es source: https://cubepath.com/es/docs/stack-de-observabilidad/monitoreo-de-clusters-kubernetes-con-prometheus updated: 2026-09-25 --- # Cómo monitorizar Kubernetes con Prometheus y Grafana 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: ```bash kubectl get nodes ``` ```output NAME STATUS ROLES AGE VERSION worker-1 Ready 12d v1.31.4 worker-2 Ready 12d v1.31.4 ``` ## Paso 1: Instalar Helm Si todavía no tienes Helm, en Ubuntu 24.04 puedes instalarlo con el snap oficial: ```bash sudo snap install helm --classic helm version ``` ```output version.BuildInfo{Version:"v3.x.y", ...} ``` Añade el repositorio de charts de la comunidad Prometheus y actualiza el índice: ```bash 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: ```bash 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`: ```yaml 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. > Nota: en los clústeres gestionados el plano de control (etcd, kube-scheduler, kube-controller-manager) no es accesible desde los nodos, y esos objetivos aparecerán como caídos en Prometheus. Para no generar alertas falsas, añade al archivo `kubeEtcd.enabled: false`, `kubeScheduler.enabled: false` y `kubeControllerManager.enabled: false`, cada uno como bloque de primer nivel. ## Paso 3: Desplegar kube-prometheus-stack Instala el chart en un namespace propio llamado `monitoring`: ```bash helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring --create-namespace \ -f kps-values.yaml ``` ```output NAME: kube-prometheus-stack NAMESPACE: monitoring STATUS: deployed ... ``` Espera uno o dos minutos y comprueba que todos los pods están en `Running`: ```bash kubectl get pods -n monitoring ``` ```output 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: ```bash kubectl get pvc -n monitoring ``` ```output 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: ```bash 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`: ```text sum by (pod) (rate(container_cpu_usage_seconds_total{namespace="default", container!=""}[5m])) ``` Memoria de trabajo por pod: ```text sum by (pod) (container_memory_working_set_bytes{namespace="default", container!=""}) ``` Pods que se han reiniciado en la última hora: ```text 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: ```bash 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`: ```yaml 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`: ```bash nano my-app-servicemonitor.yaml ``` ```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: ```bash 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: ```bash nano custom-rules.yaml ``` ```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: ```bash kubectl apply -f custom-rules.yaml kubectl get prometheusrules -n monitoring custom-rules ``` ```output 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: ```yaml 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`: ```bash 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: ```bash 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 `. 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.