Monitoreo de Rendimiento de Kubernetes: Herramientas y Técnicas para la Optimización

Monitorea el rendimiento de Kubernetes con métricas útiles, Prometheus, Grafana, kubectl y hábitos prácticos de ajuste de recursos.

Monitoreo de Rendimiento de Kubernetes: Herramientas y Técnicas para la Optimización

El monitoreo de rendimiento de Kubernetes no se trata solo de observar gráficos de CPU. Un clúster puede mostrar un promedio bajo de CPU mientras los usuarios experimentan solicitudes lentas. Un pod puede tener suficiente memoria la mayor parte del día y aún así ser eliminado durante un trabajo por lotes. Un nodo puede parecer saludable hasta que la presión del disco comienza a desalojar pods. Un buen monitoreo conecta las señales del clúster con la experiencia que realmente importa: ¿el servicio es rápido, disponible y predecible?

El primer error es comenzar con herramientas en lugar de preguntas. Prometheus, Grafana, metrics-server, kube-state-metrics y las plataformas de monitoreo en la nube son útiles, pero no deciden qué es importante. Tú decides eso al comprender la carga de trabajo. Una API pública se preocupa por la latencia y los errores. Un trabajador de cola se preocupa por el backlog y la tasa de procesamiento. Un trabajo nocturno se preocupa por el tiempo de finalización y los pods fallidos. Una carga de trabajo similar a una base de datos se preocupa por la latencia del disco y la presión de memoria.

Para una vista rápida, kubectl top sigue siendo útil:

kubectl top nodes
kubectl top pods -A
kubectl top pod -n production api-7d9c8f7b9d-2x4mq --containers

Estos comandos dependen de metrics-server. Proporcionan uso reciente de CPU y memoria, no un historial completo. Úsalos durante el triaje, no como tu único sistema de monitoreo. Si un pod se reinició hace diez minutos porque se quedó sin memoria, kubectl top puede no mostrar el pico que lo causó.

Prometheus es la base común para las métricas de Kubernetes porque extrae datos de series temporales y funciona bien con el descubrimiento de servicios de Kubernetes. En una configuración típica, las métricas provienen de varios lugares. El kubelet expone métricas de recursos de contenedores y pods. cAdvisor, integrado con kubelet, contribuye con datos de CPU, memoria, sistema de archivos y red del contenedor. node-exporter informa métricas a nivel de host. kube-state-metrics convierte el estado de los objetos de Kubernetes en métricas: réplicas deseadas, réplicas disponibles, fases de pods, condiciones de nodos y más.

Grafana luego convierte esas métricas en paneles. Un buen panel no es una pared de indicadores. Debe responder preguntas específicas rápidamente: qué servicio es lento, qué pods están limitados, qué nodos están bajo presión, qué Deployment está fallando al implementarse y si el autoescalado está al día.

Comienza en la capa de aplicación. Para servicios orientados al usuario, las señales más importantes son la tasa de solicitudes, la tasa de errores y la latencia. Si tienes SLOs, gráfalos. Un gráfico de CPU de un pod no te dice si el proceso de pago está fallando. Las métricas de aplicación sí lo hacen. Instrumenta los servicios con bibliotecas cliente de Prometheus, OpenTelemetry o el sistema de monitoreo que tu plataforma ya utiliza. Las métricas de Kubernetes explican por qué el servicio no está saludable; las métricas de aplicación te dicen que no está saludable.

Luego conecta los síntomas de la aplicación con los recursos del pod. El uso de CPU es fácil de malinterpretar en Kubernetes. Un contenedor con un límite de CPU puede ser limitado incluso cuando la CPU promedio no parece dramática. La limitación ocurre cuando el contenedor intenta usar más tiempo de CPU del que permite su límite dentro del período de programación. Para aplicaciones sensibles a la latencia, esto puede causar solicitudes lentas que parecen aleatorias.

Una consulta PromQL útil para la limitación es:

rate(container_cpu_cfs_throttled_periods_total{namespace="production", container!=""}[5m])

Un valor creciente significa que el contenedor está siendo limitado. Combínalo con el uso de CPU y la latencia de solicitudes. Si los picos de latencia coinciden con la limitación, considera aumentar o eliminar el límite de CPU, aumentar las réplicas u optimizar la ruta del código. Algunos equipos establecen solicitudes de CPU pero evitan los límites de CPU para servicios sensibles a la latencia, confiando en solicitudes, autoescalado y controles de capacidad del nodo. Eso puede ser razonable, pero requiere disciplina a nivel de clúster para que las cargas de trabajo ruidosas no priven a otras.

La memoria se comporta de manera diferente. La CPU puede ser limitada; la memoria no puede ralentizarse de la misma manera. Si un contenedor excede su límite de memoria, puede ser OOMKilled. Busca razones de reinicio:

kubectl describe pod -n production api-7d9c8f7b9d-2x4mq
kubectl get pod -n production api-7d9c8f7b9d-2x4mq -o jsonpath='{.status.containerStatuses[*].lastState}'

En Prometheus, observa la memoria de trabajo y compárala con los límites:

container_memory_working_set_bytes{namespace="production", container!=""}

No ajustes la memoria basándote en una hora tranquila. Observa el tráfico pico, las ventanas de lotes, las implementaciones y el comportamiento de recolección de basura. Los servicios Java, Go, Node.js y Python tienen diferentes perfiles de memoria. Un límite que parece generoso durante el tráfico normal puede ser demasiado ajustado durante el inicio, el calentamiento de caché o una solicitud grande.

Las solicitudes de recursos importan porque el programador las usa para colocar pods. Si las solicitudes son demasiado bajas, Kubernetes puede empaquetar demasiados pods ocupados en el mismo nodo. Todo parece eficiente hasta que esos pods se vuelven ocupados al mismo tiempo. Si las solicitudes son demasiado altas, el clúster desperdicia capacidad y el autoescalado puede agregar nodos antes de lo necesario. La mejor solicitud generalmente se basa en el uso observado más un margen, no en un valor copiado de otro servicio.

El Vertical Pod Autoscaler puede ayudar recomendando solicitudes basadas en el uso histórico. Muchos equipos ejecutan VPA en modo de recomendación primero porque las actualizaciones automáticas pueden reiniciar pods dependiendo de la configuración y el tipo de carga de trabajo. Trata las recomendaciones como entrada, no como ley. Un servicio con picos raros pero importantes puede necesitar más margen del que sugiere su historial promedio.

El Horizontal Pod Autoscaler es útil cuando más réplicas realmente mejoran el rendimiento. Funciona bien para servicios web sin estado y trabajadores que pueden compartir la carga. No soluciona un cuello de botella de un solo hilo, un bloqueo de base de datos o una dependencia descendente que ya está saturada.

Un HPA básico podría verse así:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Monitorea el comportamiento del HPA, no solo el número de réplicas. Si escala constantemente hacia arriba y hacia abajo, ajusta las ventanas de estabilización, los objetivos o la métrica. Si alcanza maxReplicas y la latencia sigue siendo mala, el problema puede ser la capacidad, el código o una dependencia. Si nunca escala mientras los pods están claramente sobrecargados, verifica la disponibilidad de métricas y si las solicitudes están establecidas. Los objetivos de utilización de CPU dependen de las solicitudes de CPU; las solicitudes faltantes o poco realistas pueden hacer que el autoescalado sea engañoso.

La salud del nodo es la siguiente capa. Un problema de pod que aparece en muchos servicios en un nodo suele ser un problema de nodo. Observa la saturación de CPU, la carga promedio, la memoria disponible, la presión del disco, el uso de inodos, la latencia del sistema de archivos, los errores de red y la salud del kubelet. Las condiciones del nodo como MemoryPressure, DiskPressure y PIDPressure deben ser visibles en paneles y alertas.

Usa kubectl describe node cuando un nodo parezca sospechoso:

kubectl describe node worker-12

Observa las condiciones, los recursos asignados, los eventos y los pods programados en el nodo. Un nodo puede estar sobrecomprometido por límites, solicitudes o uso real. La sección de recursos asignados te ayuda a ver si las suposiciones de programación coinciden con la realidad.

El monitoreo del plano de control importa incluso si tus pods de aplicación se ven bien. La latencia del servidor API puede ralentizar implementaciones, autoescalado y controladores. La latencia de etcd o problemas de disco pueden hacer que todo el clúster se sienta lento. Los problemas del controller manager y del scheduler pueden retrasar la colocación de pods o la reconciliación. En Kubernetes administrado, es posible que no veas todos los componentes del plano de control, pero los proveedores de nube generalmente exponen algunas métricas de salud y latencia de API.

Los eventos son útiles durante incidentes, pero no son un almacén de métricas a largo plazo. Aun así, a menudo explican lo que acaba de suceder:

kubectl get events -A --sort-by=.lastTimestamp

Busca programación fallida, errores de extracción de imágenes, fallos de sondas, desalojos y mensajes de back-off. Los eventos pueden ser ruidosos, así que filtra por espacio de nombres u objeto involucrado cuando sea necesario.

Las sondas merecen un monitoreo cuidadoso. Las sondas de liveness demasiado agresivas pueden reiniciar una aplicación lenta pero en recuperación y empeorar un incidente. Las sondas de readiness que fallan correctamente pueden proteger a los usuarios al eliminar pods malos del servicio. Rastrea los fallos de sondas y correlaciónalos con la limitación de CPU, pausas de GC, tiempos de espera descendentes y despliegues.

Para cargas de trabajo intensivas en almacenamiento, la CPU y memoria del contenedor no son suficientes. Observa la latencia del volumen persistente, el rendimiento del disco, la profundidad de la cola y la capacidad del sistema de archivos. Un pod que espera en almacenamiento lento puede mostrar baja CPU porque está bloqueado. Si una base de datos o cola se ejecuta en Kubernetes, las métricas de almacenamiento son parte del rendimiento de la aplicación, no trivia de infraestructura.

Un camino práctico de solución de problemas comienza amplio y se estrecha. Primero, confirma el síntoma visible para el usuario: latencia, errores, trabajos fallidos o backlog. Segundo, identifica el alcance: un pod, un Deployment, un nodo, un espacio de nombres o todo el clúster. Tercero, verifica cambios recientes: implementaciones, actualizaciones de configuración, actividad del autoescalador, rotaciones de nodos o picos de tráfico. Cuarto, inspecciona el comportamiento de recursos del pod: limitación de CPU, presión de memoria, reinicios y fallos de sondas. Quinto, inspecciona la salud del nodo y las dependencias.

Las alertas deben evitar despertar a personas por ruido inofensivo. Alerta primero sobre el impacto en el usuario: alta tasa de errores, alta latencia, plazo de trabajo incumplido, edad creciente de la cola. Luego alerta sobre indicadores principales sólidos: OOMKills frecuentes, limitación sostenida de CPU en servicios sensibles a la latencia, pods no disponibles por debajo de las réplicas deseadas, presión en nodos, pods persistentemente pendientes y HPA atascado en maxReplicas mientras las métricas del servicio son malas.

El objetivo no es una utilización perfecta. Un clúster que funciona al 95 por ciento de uso de recursos todo el día puede parecer eficiente hasta que un nodo falla y no hay espacio para reprogramar pods. Deja capacidad para implementaciones, reintentos, ráfagas de tráfico y fallos. La optimización debe reducir el desperdicio sin eliminar el búfer que mantiene los incidentes pequeños.

Un buen monitoreo de rendimiento de Kubernetes se siente práctico. Puedes abrir un panel y ver la salud del servicio, la salud del pod, la salud del nodo y el comportamiento de escalado sin buscar en veinte pestañas. Puedes responder si una ralentización es código, límites de recursos, presión en nodos, almacenamiento, red o el plano de control. Y cuando cambias solicitudes, límites o autoescalado, puedes ver si el cambio ayudó en lugar de adivinar.

Las vistas a nivel de espacio de nombres son útiles cuando muchos equipos comparten un clúster. Un solo equipo puede no ver la saturación a nivel de nodo si solo observan sus propios Deployments. Los equipos de plataforma deben exponer paneles que muestren las solicitudes de CPU y memoria del espacio de nombres, el uso real, los recuentos de pods, los reinicios y la limitación. Eso hace que las conversaciones de capacidad sean menos emocionales. En lugar de decir que un equipo está usando "demasiado", puedes mostrar tendencias de solicitudes, uso pico y desperdicio.

La optimización de costos debe venir después de las señales de confiabilidad, no antes. Si un servicio nunca ha tenido solicitudes ajustadas, puedes encontrar ahorros fáciles. Pero reducir solicitudes agresivamente puede crear presión de programación y problemas de vecino ruidoso. Un buen proceso cambia una clase de carga de trabajo a la vez, observa la latencia y los reinicios, y deja notas de reversión. Trata el ajuste de recursos como código de producción: cambios pequeños, resultados medidos.

Las implementaciones en sí mismas pueden crear incidentes de rendimiento. Un despliegue que reemplaza demasiados pods a la vez puede sobrecargar cachés frías, grupos de conexiones o servicios descendentes. Observa la duración del despliegue, las réplicas no disponibles y la latencia de la aplicación durante los despliegues. Ajusta maxSurge y maxUnavailable según cómo se comporta el servicio durante el inicio. Un servicio con calentamiento lento puede necesitar un despliegue conservador incluso si el rendimiento en estado estable es bueno.

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

Esa configuración no es universalmente mejor, pero muestra la compensación: despliegue más lento, más protección contra caídas de capacidad. Para un servicio sin estado que se inicia instantáneamente, puedes elegir un despliegue más rápido. Para un servicio JVM que calienta cachés y abre muchas conexiones descendentes, más lento puede ser más seguro.

Mantén un ojo en la cardinalidad de las métricas. Las etiquetas de Kubernetes son tentadoras, pero las etiquetas de alta cardinalidad como UID de pod, ID de solicitud o ID de usuario pueden hacer que Prometheus sea costoso y lento. Usa etiquetas que te ayuden a agregar: espacio de nombres, carga de trabajo, pod, contenedor, nodo, código de estado, patrón de ruta. Evita etiquetas que creen una nueva serie temporal para cada usuario o cada solicitud. El monitoreo no debe convertirse en lo que perjudica el rendimiento del clúster.

Los registros y trazas completan el panorama. Las métricas te dicen que la latencia aumentó; las trazas pueden mostrar qué llamada descendente se volvió lenta; los registros pueden mostrar el error exacto o el tiempo de espera. OpenTelemetry se usa comúnmente para conectar estas señales, pero la herramienta importa menos que la correlación. Usa nombres de servicio, espacios de nombres, versiones e IDs de traza consistentes para que puedas pasar de una alerta a los registros relevantes sin adivinar.

Para sistemas por lotes y de trabajadores, observa la edad del backlog en lugar de solo la CPU del pod. Un trabajador de cola puede estar saludable a nivel de pod mientras se queda atrás porque el trabajo entrante excede la capacidad de procesamiento. Métricas como la edad del mensaje más antiguo, trabajos completados por minuto, reintentos y recuentos de mensajes en la cola de mensajes fallidos a menudo importan más que la utilización del contenedor. HPA puede escalar desde métricas personalizadas o externas cuando la CPU es la señal incorrecta.

Revisa los paneles después de los incidentes. Si los respondedores tuvieron que ejecutar cinco comandos manuales para responder la misma pregunta, esa pregunta pertenece a un panel o a un runbook. El monitoreo mejora con el uso. El objetivo no es predecir cada fallo; es hacer que la próxima investigación sea más corta y menos dependiente de la memoria de una persona.