En la lección anterior enseñamos a Kubernetes a distinguir un contenedor vivo de una aplicación sana. Rutas Norte ya sabe si cada componente funciona. Lo que todavía no sabe es cuánto consume. Y ese dato no es un lujo: en 03-04 fijamos las requests y los limits de cada componente prácticamente a ojo, prometiendo que los calibraríamos observando el consumo real. Sin ese dato, la mitad de nuestro clúster está reservando memoria que nadie usa mientras la otra mitad sufre throttling de CPU en silencio.
Esta lección presenta la primera fuente de datos de consumo real del clúster: metrics-server, un componente ligero que recoge el uso de CPU y memoria de nodos y pods y lo publica dentro de la propia API de Kubernetes. Es la pieza que hace funcionar kubectl top, y también la que hará funcionar el autoescalado del módulo 9. Vamos a entender exactamente qué mide, de dónde saca los datos, y —tan importante como lo anterior— qué no puede hacer, porque esas limitaciones son justo la razón por la que en la lección siguiente instalaremos Prometheus.
Contenido
- Qué lugar ocupa metrics-server en el ecosistema de observabilidad
- De dónde salen los datos: kubelet, cAdvisor y la Summary API
- Cómo se publica en la API: la capa de agregación y el
APIService - Instalación: el addon de minikube y el manifiesto oficial
kubectl top nodes: leer correctamente cada columnakubectl top pods:--containers,-A,--sort-byy sus matices- Las limitaciones esenciales y por qué hace falta Prometheus
- El uso central: recalibrar las
requestsylimitsde Rutas Norte - Por qué el HPA y el VPA dependen de este componente
- Diagnóstico de los fallos típicos
- Errores comunes y consejos
- Ejercicios
- Qué lugar ocupa metrics-server en el ecosistema de observabilidad
Antes de instalar nada conviene situar la pieza, porque es el componente que más confusión genera del módulo. Mucha gente lo instala esperando un sistema de monitorización y se lleva una decepción.
metrics-server no es un sistema de monitorización. Es un componente de infraestructura interna del clúster con un propósito muy concreto y muy acotado:
Proporcionar a la API de Kubernetes el consumo actual de CPU y memoria de nodos y pods, para que los mecanismos de autoescalado del propio Kubernetes puedan tomar decisiones.
Todo lo demás —que puedas verlo con kubectl top— es un efecto secundario útil.
| Aspecto | metrics-server | Prometheus (07-03) |
|---|---|---|
| Propósito de diseño | Alimentar al HPA y al VPA | Observar, consultar y alertar |
| Métricas que recoge | Solo CPU y memoria | Cualquier métrica expuesta |
| Histórico | Ninguno (~1-2 min en memoria) | Semanas o meses en disco |
| Consultas | No hay lenguaje de consulta | PromQL completo |
| Alertas | No | Sí, con Alertmanager |
| Almacenamiento | Memoria volátil | Base de datos de series temporales |
| Consumo de recursos | Muy bajo (~50 Mi) | Alto (GB de RAM y disco) |
| ¿Es obligatorio? | Prácticamente sí | No, pero imprescindible en producción |
| Granularidad | 15 s por defecto | Configurable, típico 15-30 s |
Los dos coexisten en cualquier clúster serio: metrics-server porque el HPA lo necesita, Prometheus porque hace falta saber qué pasó ayer.
Una tercera confusión frecuente: metrics-server no sustituye a las sondas de 07-01. Las sondas responden "¿está sano?"; metrics-server responde "¿cuánto gasta?". Un pod puede consumir 5 milicores y estar completamente colgado.
- De dónde salen los datos: kubelet, cAdvisor y la Summary API
Para no tratar metrics-server como una caja negra, hay que seguir el camino del dato desde el kernel hasta tu terminal.
El origen: los cgroups del kernel
Cuando el runtime de contenedores crea el contenedor de api-reservas, el kernel de Linux lo mete en un grupo de control (cgroup). El kernel contabiliza ahí, entre otras cosas:
cpuacct.usage: nanosegundos de CPU acumulados por ese cgroup desde que arrancó.memory.current: bytes de memoria en uso ahora mismo por ese cgroup.
Esos son los datos primarios. Todo lo demás son capas de transporte.
cAdvisor: el lector de cgroups
cAdvisor (Container Advisor) es un componente de Google que lee los cgroups y los traduce a métricas de contenedor con sentido. Desde hace años está integrado dentro del binario del kubelet: no es un pod aparte y no hay que instalarlo. Cada kubelet lleva su propio cAdvisor que examina los contenedores de su nodo.
La Summary API del kubelet
El kubelet expone lo que cAdvisor recoge en un endpoint HTTPS autenticado:
Devuelve un JSON con el consumo del nodo y de cada pod y contenedor. Una porción simplificada:
{
"node": {
"nodeName": "rutas-norte-worker-1",
"cpu": { "time": "2026-08-06T09:14:20Z", "usageNanoCores": 842000000 },
"memory": { "workingSetBytes": 3221225472 }
},
"pods": [
{
"podRef": { "name": "api-reservas-7d9f8c4b5-x2klm", "namespace": "rutas-norte-pro" },
"containers": [
{
"name": "api",
"cpu": { "usageNanoCores": 187000000 },
"memory": { "workingSetBytes": 312475648 }
}
]
}
]
}Dos campos merecen atención especial, porque explican todo lo que verás después:
usageNanoCores: nanocores consumidos. Un core = 1 000 000 000 nanocores. Los 187 000 000 del ejemplo son 187 milicores, es decir,187men la notación de Kubernetes.workingSetBytes: el "conjunto de trabajo". Es la memoria residente menos las páginas de caché de fichero que el kernel podría recuperar sin problema. Es la métrica que el kubelet usa para decidir a quién desalojar por presión de memoria, y es la que verás enkubectl top. No es lo mismo que el RSS que te daps, y por eso a veces los números no cuadran con lo que ves dentro del contenedor.
metrics-server: el agregador
metrics-server es un único Deployment (una réplica en clústeres pequeños) que:
- Descubre los nodos consultando la API de Kubernetes.
- Consulta la Summary API de cada kubelet cada 15 segundos (parámetro
--metric-resolution). - Guarda los dos últimos puntos de cada serie en memoria.
- Calcula la tasa de CPU dividiendo el incremento de nanocores acumulados entre el tiempo transcurrido.
- Sirve esos valores a través de la API de Kubernetes.
Punto clave: no escribe nada en disco y no guarda histórico. Si el pod de metrics-server se reinicia, kubectl top deja de funcionar durante unos 30 segundos y luego vuelve, sin ningún dato del pasado.
Esa división también explica por qué la CPU tarda en aparecer: como es una tasa, hacen falta dos muestras. Un pod recién creado no muestra CPU hasta que metrics-server tiene dos lecturas suyas.
- Cómo se publica en la API: la capa de agregación y el
APIService
APIServiceAquí está la parte elegante del diseño. metrics-server no expone un puerto propio al que tú te conectes. En su lugar, se registra dentro de la API de Kubernetes mediante la capa de agregación (API Aggregation Layer).
El mecanismo es el mismo que vimos en 06-06 para las CRDs, pero por otra vía: en lugar de que el API Server guarde los objetos en etcd, delega las peticiones de un grupo de API concreto a un servicio externo.
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1beta1.metrics.k8s.io
spec:
group: metrics.k8s.io # el grupo de API que se delega
version: v1beta1
groupPriorityMinimum: 100
versionPriority: 100
service:
name: metrics-server # a qué Service se reenvían las peticiones
namespace: kube-system
port: 443
insecureSkipTLSVerify: true # en clústeres de prueba; en producción, caBundleEsto significa que cuando ejecutas:
lo que ocurre por debajo es una petición REST normal y corriente a la API de Kubernetes:
El API Server ve que la ruta pertenece al grupo metrics.k8s.io, consulta su tabla de APIService, y reenvía la petición al Service metrics-server de kube-system. La respuesta vuelve al cliente como si la hubiera generado el propio API Server.
flowchart TD
U["kubectl top pods"] --> API[API Server]
API -->|"grupo metrics.k8s.io<br/>delegado por APIService"| MS[metrics-server<br/>Deployment en kube-system]
MS -->|"HTTPS :10250<br/>/stats/summary<br/>cada 15 s"| K1[kubelet nodo 1]
MS -->|"HTTPS :10250"| K2[kubelet nodo 2]
MS -->|"HTTPS :10250"| K3[kubelet nodo 3]
K1 --> CA1[cAdvisor integrado]
K2 --> CA2[cAdvisor integrado]
K3 --> CA3[cAdvisor integrado]
CA1 --> CG1[(cgroups del kernel)]
CA2 --> CG2[(cgroups del kernel)]
CA3 --> CG3[(cgroups del kernel)]
API -.->|"misma API"| HPA[HorizontalPodAutoscaler]
Las consecuencias prácticas de este diseño son importantes:
- Autenticación y autorización unificadas: el RBAC que estudiaremos en 08-01 se aplica igual a
kubectl topque akubectl get pods. Quien no tenga permisogetsobrepods.metrics.k8s.iono verá nada. - Un solo punto de entrada: no hay que abrir puertos ni exponer servicios adicionales.
- Fragilidad concreta: si el
APIServiceestáFalse(por ejemplo, porque el pod de metrics-server no arranca), algunas operaciones de descubrimiento de la API se ralentizan para todo el clúster. Verás avisos comocouldn't get resource list for metrics.k8s.io/v1beta1. Es un efecto secundario molesto que conviene reconocer.
Comprobar el estado del APIService:
AVAILABLE: True es la señal de que todo está bien. Si pone False (MissingEndpoints) o False (FailedDiscoveryCheck), ve directo al apartado 10.
- Instalación: el addon de minikube y el manifiesto oficial
En nuestro clúster de prácticas
Desde el módulo 1 venimos trabajando con minikube en el perfil rutas-norte, y ya declaramos metrics-server entre los addons necesarios. Si no lo has activado todavía:
# Activar el addon en nuestro perfil
minikube addons enable metrics-server -p rutas-norte
# Comprobar que el addon aparece habilitado
minikube addons list -p rutas-norte | grep metrics-serverEl addon despliega el Deployment en kube-system con la configuración ya adaptada a minikube (incluido el flag del apartado siguiente).
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/metrics-server 1/1 1 1 2m18s
NAME READY STATUS RESTARTS AGE
pod/metrics-server-7bf7d58749-qk4hn 1/1 Running 0 2m18sEspera entre 30 y 60 segundos antes de esperar datos: metrics-server necesita al menos dos ciclos de recolección.
En un clúster real
En un clúster que no sea minikube se instala aplicando el manifiesto oficial del proyecto:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlEse manifiesto crea, en kube-system: un ServiceAccount, un ClusterRole con permisos de lectura sobre nodos y pods, los ClusterRoleBinding correspondientes (incluida la delegación de autenticación al API Server), el Service, el Deployment y el APIService que vimos antes.
El flag --kubelet-insecure-tls y su advertencia
Este es el punto que más veces bloquea una instalación. metrics-server se conecta por HTTPS al puerto 10250 de cada kubelet. Por defecto verifica el certificado del kubelet contra la CA del clúster. El problema: en muchas instalaciones (minikube, kubeadm sin rotación de certificados de servidor del kubelet, algunos clústeres gestionados) el kubelet usa un certificado autofirmado que no está firmado por la CA del clúster, o cuyo nombre no coincide con la IP del nodo.
Resultado: metrics-server no puede conectarse y kubectl top no devuelve nada. La solución que aparece en todos los foros:
# Fragmento del Deployment de metrics-server
spec:
template:
spec:
containers:
- name: metrics-server
args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls # <-- el flag en cuestiónAdvertencia de seguridad.
--kubelet-insecure-tlsdesactiva la verificación del certificado del kubelet. La conexión sigue cifrada, pero metrics-server ya no comprueba que el interlocutor sea realmente el kubelet legítimo del nodo. Un atacante con capacidad de interponerse en la red del plano de control podría suplantar a un kubelet y devolver métricas falsas. Eso, en un clúster con HPA, es un vector de ataque real: métricas de CPU inventadas pueden provocar un escalado masivo (coste) o impedir un escalado necesario (denegación de servicio).Es aceptable en minikube y en entornos de desarrollo. No lo es en
rutas-norte-pro. La solución correcta en producción es habilitar la rotación de certificados de servidor del kubelet (--rotate-server-certificates=trueen el kubelet y aprobación de las CSRkubernetes.io/kubelet-serving), de modo que los certificados los firme la CA del clúster y la verificación funcione. En un clúster gestionado (EKS, AKS, GKE, que veremos en 10-06), el proveedor ya lo trae resuelto y el flag no hace falta.
El resto de argumentos, brevemente:
| Argumento | Para qué sirve |
|---|---|
--metric-resolution=15s |
Cada cuánto se consulta a los kubelets. No bajar de 10 s: satura los kubelets |
--kubelet-preferred-address-types |
En qué orden intentar direccionar al nodo. InternalIP primero suele ser lo correcto |
--kubelet-use-node-status-port |
Usar el puerto que el propio nodo declara en su estado, en vez de asumir 10250 |
--cert-dir=/tmp |
Dónde guarda su certificado de servicio propio |
kubectl top nodes: leer correctamente cada columna
kubectl top nodes: leer correctamente cada columnaCon metrics-server funcionando, ya podemos mirar.
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
rutas-norte-control 412m 20% 1854Mi 48%
rutas-norte-worker-1 1834m 45% 5218Mi 67%
rutas-norte-worker-2 2701m 67% 6944Mi 89%
rutas-norte-worker-3 287m 7% 1102Mi 14%Cómo se lee esto correctamente, columna a columna:
CPU(cores): milicores consumidos ahora mismo por todo lo que corre en el nodo (pods del sistema incluidos).1834msignifica 1,834 núcleos ocupados de forma continua.CPU%: el porcentaje sobre la capacidad asignable del nodo (node.status.allocatable.cpu), no sobre la capacidad total ni sobre la suma derequests. Es un detalle importante:allocatable= capacidad total menos lo reservado para el sistema operativo y para el kubelet.MEMORY(bytes): elworkingSetBytesdel nodo. Repito el matiz del apartado 2: no incluye la caché de página recuperable, así que casi siempre es menor que lo que muestrafree -mdentro del nodo.MEMORY%: porcentaje sobreallocatable.memory.
Comprobar el allocatable de un nodo para verificar el cálculo:
kubectl get node rutas-norte-worker-2 \
-o jsonpath='{.status.allocatable.cpu}{"\t"}{.status.allocatable.memory}{"\n"}'Con 4 núcleos asignables y 2701m consumidos: 2701 / 4000 = 67,5 %. Cuadra.
Lectura operativa de la tabla anterior para Rutas Norte:
rutas-norte-worker-2está al 89 % de memoria. Peligroso: cuando el kubelet detecta presión de memoria empieza a desalojar pods, empezando por los de QoSBestEffortyBurstableque exceden susrequests(repasa 03-05). Hay que investigar qué corre ahí y probablemente reequilibrar.rutas-norte-worker-3está prácticamente vacío al 7 % y 14 %. Eso apunta a un problema de planificación: quizá tiene un taint que no habíamos previsto, o los pods tienen reglas de afinidad que lo excluyen (06-05).
Flags útiles:
# Ordenar por consumo de CPU descendente
kubectl top nodes --sort-by=cpu
# Ordenar por memoria
kubectl top nodes --sort-by=memory
# Solo los nodos con una etiqueta concreta
kubectl top nodes -l node-role.kubernetes.io/worker=
# Sin cabeceras, para procesar con scripts
kubectl top nodes --no-headers
kubectl top pods: --containers, -A, --sort-by y sus matices
kubectl top pods: --containers, -A, --sort-by y sus maticesNAME CPU(cores) MEMORY(bytes)
api-reservas-7d9f8c4b5-x2klm 187m 305Mi
api-reservas-7d9f8c4b5-mn8pq 203m 318Mi
api-reservas-7d9f8c4b5-vr4tz 174m 297Mi
postgres-reservas-0 340m 1720Mi
redis-cache-0 28m 412Mi
tienda-web-6c8b9d7f4-hj3ks 12m 38Mi
tienda-web-6c8b9d7f4-lp9wx 9m 36Mi
worker-notificaciones-5f7c8b9d4-tz2mv 45m 186MiDiferencia crítica respecto a top nodes: aquí NO hay columnas de porcentaje. kubectl top pods no muestra ningún %, porque no sabría respecto a qué calcularlo (¿sobre el limit? ¿sobre la request? ¿sobre el nodo?). Esa comparación tienes que hacerla tú, y en el apartado 8 la haremos.
--containers: desglose por contenedor
Con la llegada de los sidecars en 06-04, un pod ya no es un contenedor. postgres-reservas-0 tiene el contenedor de PostgreSQL y el exportador de métricas; worker-notificaciones tiene el worker y el adaptador de logs. Sumarlos oculta información valiosa:
POD NAME CPU(cores) MEMORY(bytes)
postgres-reservas-0 postgres 327m 1698Mi
postgres-reservas-0 exportador-pg 13m 22Mi
worker-notificaciones-5f7c8b9d4-tz2mv worker 41m 171Mi
worker-notificaciones-5f7c8b9d4-tz2mv adaptador-logs 4m 15Mi
api-reservas-7d9f8c4b5-x2klm api 187m 305MiAhora sabemos que el sidecar exportador cuesta 13m de CPU y 22Mi: barato, como esperábamos. Y que el adaptador de logs cuesta 4m y 15Mi. Estos números importan porque, multiplicados por el número de réplicas, un sidecar aparentemente inocuo puede consumir un nodo entero en un clúster grande.
Otras opciones
# Todos los namespaces del clúster, ordenado por memoria descendente
kubectl top pods -A --sort-by=memory
# Solo los pods de un componente concreto (usa el selector de etiquetas de 02-07)
kubectl top pods -n rutas-norte-pro -l app=api-reservas
# Todos los componentes de la plataforma, en los tres entornos
kubectl top pods -A -l app.kubernetes.io/part-of=rutas-norte --sort-by=cpu
# Un pod concreto, desglosado
kubectl top pod postgres-reservas-0 -n rutas-norte-pro --containers
# Sin cabeceras y ordenado, para un script de informe
kubectl top pods -n rutas-norte-pro --no-headers --sort-by=memory--sort-by solo admite cpu o memory. Ojo con un comportamiento poco intuitivo: el ordenamiento se hace sobre los valores numéricos, pero la salida se imprime en las unidades de Kubernetes, y 1024Mi va después de 2Gi alfabéticamente aunque sea menor. Confía en el orden que da --sort-by, no en la lectura visual.
Comparativa rápida de comandos
| Comando | Qué responde |
|---|---|
kubectl top nodes |
¿Qué nodos están saturados? ¿Hay desequilibrio? |
kubectl top pods -A --sort-by=memory |
¿Quién se está comiendo la memoria del clúster? |
kubectl top pods -n X --containers |
¿Cuánto cuesta cada sidecar? |
kubectl top pods -l app=api-reservas |
¿Consumen igual todas las réplicas de un componente? |
Esa última pregunta es más útil de lo que parece. Si de seis réplicas de api-reservas una consume 800m y las demás 190m, tienes un problema de reparto de tráfico o una petición patológica atendida por ese pod concreto.
- Las limitaciones esenciales y por qué hace falta Prometheus
Este apartado es el más importante de la lección, porque define cuándo no debes usar esta herramienta.
Limitación 1: no hay histórico. Ninguno.
metrics-server guarda los dos últimos puntos de cada serie en memoria. No hay base de datos, no hay fichero, no hay retención configurable. Preguntas que no puedes responder con kubectl top:
- "¿Cuánta memoria consumía
api-reservasanoche a las 03:00, cuando cayó?" - "¿Ha ido creciendo el consumo de
postgres-reservasen las últimas dos semanas?" - "¿Cuál fue el pico de CPU durante el puente de mayo?"
Todas ellas son exactamente las preguntas que se hacen después de un incidente, y para todas la respuesta de metrics-server es silencio.
Limitación 2: son datos instantáneos con resolución gruesa
Con --metric-resolution=15s y ventanas de cálculo de la tasa de CPU de decenas de segundos, un pico de CPU de 3 segundos no aparece. Se promedia y desaparece. Para api-reservas, cuyos picos de latencia se miden en segundos, esa resolución es insuficiente para diagnosticar.
También significa que dos ejecuciones consecutivas de kubectl top pueden dar el mismo valor: si ambas caen dentro del mismo ciclo de recolección, estás leyendo la misma muestra.
Limitación 3: solo CPU y memoria
Nada de disco, nada de red, nada de métricas de aplicación. No sabrás cuántas peticiones por segundo atiende api-reservas, ni cuántas reservas se han confirmado, ni cuántas conexiones tiene abiertas PostgreSQL. Nada del negocio.
Limitación 4: no se puede alertar
No hay consultas, no hay umbrales, no hay notificaciones. kubectl top es una herramienta interactiva: alguien tiene que estar delante escribiéndola. A las tres de la madrugada no hay nadie delante.
Limitación 5: no distingue el throttling
En 03-04 vimos que un contenedor que alcanza su limits.cpu no se mata: se le estrangula (throttling). El síntoma es latencia alta con un consumo de CPU que se queda pegado al límite. kubectl top te muestra los milicores, pero no el contador de periodos estrangulados (container_cpu_cfs_throttled_periods_total), que es el dato que confirma el diagnóstico. Ese contador solo lo verás en Prometheus.
Tabla resumen: qué preguntar a quién
| Pregunta | metrics-server | Prometheus |
|---|---|---|
¿Cuánto consume api-reservas ahora? |
✅ | ✅ |
| ¿Cuánto consumía ayer a las 03:00? | ❌ | ✅ |
| ¿Está creciendo su consumo con el tiempo? | ❌ | ✅ |
| ¿Cuántas peticiones por segundo atiende? | ❌ | ✅ |
| ¿Está sufriendo throttling de CPU? | ❌ | ✅ |
| ¿Cuál es el percentil 95 de latencia? | ❌ | ✅ |
| Avísame si sube de X | ❌ | ✅ |
| Escalar automáticamente por CPU | ✅ | ✅ (vía adaptador) |
| ¿Qué nodo está más cargado ahora mismo? | ✅ | ✅ |
| Coste de instalación y operación | Trivial | Considerable |
La conclusión honesta: metrics-server es una fotografía; Prometheus es la película. Necesitas las dos cosas, y por razones distintas. La foto es gratis y siempre está ahí; la película cuesta dinero, disco y mantenimiento, pero es la única que te dice qué pasó.
- El uso central: recalibrar las
requests y limits de Rutas Norte
requests y limits de Rutas NorteEste es el uso legítimo y de más valor de kubectl top, y el que cierra la promesa de 03-04.
Recordatorio de por qué importa
requests: lo que el planificador reserva en el nodo. Determina dónde cabe el pod y qué QoS tiene. Reservar de más desperdicia clúster (nodos con el 30 % de uso real y "llenos" según el planificador); reservar de menos hace que los pods vayan a nodos donde no caben de verdad y sufran.limits: el techo. Superarlo en CPU provoca throttling; superarlo en memoria provocaOOMKilled.
El método de recalibración
No se recalibran las requests mirando una sola vez. El procedimiento honesto:
- Observar durante un periodo representativo: al menos una semana, incluyendo un pico. Para Rutas Norte hay que incluir un puente o el inicio de las vacaciones.
- Tomar muestras periódicas, no una sola lectura. Como metrics-server no guarda histórico, esto significa un script que ejecute
kubectl topcada pocos minutos y acumule. - Calcular percentiles, no medias. La media miente cuando hay picos.
- Aplicar los criterios:
| Recurso | Criterio de cálculo |
|---|---|
requests.cpu |
Percentil 50-70 del uso observado. No hace falta reservar el pico: la CPU es comprimible |
limits.cpu |
2-4 veces la request, o sin límite si el componente tolera bien la variabilidad |
requests.memory |
Percentil 95-99 del uso observado, más margen. La memoria no es comprimible |
limits.memory |
Igual o muy cercano a la request, más un 20-30 % de margen |
La asimetría entre CPU y memoria es fundamental y hay que entenderla: si te pasas de CPU, el sistema te ralentiza y sobrevives. Si te pasas de memoria, el kernel te mata. Por eso la memoria se dimensiona por el percentil alto y la CPU por el central.
Script de recolección para una semana
#!/bin/bash
# recolecta-consumo.sh
# Muestrea el consumo de los pods de Rutas Norte cada 2 minutos.
# Limitación deliberada: esto es un apaño porque metrics-server no guarda
# histórico. En 07-03 lo sustituiremos por Prometheus, que hace esto de serie.
SALIDA="/var/log/rutasnorte/consumo-$(date +%Y%m%d).csv"
echo "marca_tiempo,namespace,pod,contenedor,cpu_milicores,memoria_mib" > "$SALIDA"
while true; do
AHORA=$(date --iso-8601=seconds)
kubectl top pods -A --containers --no-headers \
-l app.kubernetes.io/part-of=rutas-norte 2>/dev/null |
while read -r ns pod contenedor cpu mem; do
# Limpiar sufijos: 187m -> 187, 305Mi -> 305
cpu_num="${cpu%m}"
mem_num="${mem%Mi}"
echo "$AHORA,$ns,$pod,$contenedor,$cpu_num,$mem_num" >> "$SALIDA"
done
sleep 120
doneNota importante para principiantes: el comando anterior con -A devuelve el namespace como primera columna, por eso read toma cinco campos. Si lo ejecutas sin -A, quita la variable ns.
Y el análisis de los datos recogidos:
# Percentil 95 de memoria de api-reservas durante la semana
awk -F, '$4=="api" {print $6}' /var/log/rutasnorte/consumo-*.csv |
sort -n |
awk '{v[NR]=$1} END {print "p95 memoria: " v[int(NR*0.95)] " Mi"}'
# Percentil 70 de CPU de api-reservas
awk -F, '$4=="api" {print $5}' /var/log/rutasnorte/consumo-*.csv |
sort -n |
awk '{v[NR]=$1} END {print "p70 CPU: " v[int(NR*0.70)] "m"}'El antes y el después de Rutas Norte
Tras dos semanas de observación (incluido el puente del 1 de mayo), estos son los datos y las decisiones:
| Componente | requests de 03-04 |
Uso p50 | Uso p95 | requests nuevas |
Veredicto |
|---|---|---|---|---|---|
tienda-web |
cpu 200m / mem 256Mi | 11m / 37Mi | 24m / 42Mi | cpu 50m / mem 64Mi | Sobredimensionado 4x. nginx sirviendo estáticos gasta muy poco |
api-reservas |
cpu 100m / mem 128Mi | 185m / 302Mi | 640m / 458Mi | cpu 200m / mem 512Mi | Infradimensionado. Explica el throttling que nadie sabía diagnosticar |
postgres-reservas |
cpu 500m / mem 1Gi | 335m / 1710Mi | 1250m / 1980Mi | cpu 500m / mem 2560Mi | Memoria muy corta: riesgo real de OOMKilled en picos |
redis-cache |
cpu 100m / mem 512Mi | 27m / 410Mi | 55m / 486Mi | cpu 50m / mem 768Mi | CPU sobrada; memoria justa y creciendo con las plazas cacheadas |
worker-notificaciones |
cpu 200m / mem 256Mi | 43m / 180Mi | 380m / 240Mi | cpu 100m / mem 320Mi | Muy variable: ráfagas al enviar lotes de correos |
informes-ocupacion (CronJob) |
cpu 500m / mem 512Mi | 890m / 720Mi | 1100m / 810Mi | cpu 1 / mem 1Gi | Se quedaba corto: los informes tardaban el doble por throttling |
Tres hallazgos que merece la pena comentar, porque son los típicos:
api-reservasestaba pidiendo 100m y usando 185m de media. Con QoSBurstable, eso funciona mientras el nodo tenga hueco, pero el planificador estaba colocando pods como si cada uno ocupara 100m: sobrevendía el nodo. Y ellimits.cpude 500m provocaba throttling en los picos de 640m, lo que explica los picos de latencia que nadie sabía atribuir. Este es el hallazgo más valioso de todo el ejercicio.tienda-webreservaba 4 veces lo que usa. Multiplicado por sus réplicas y por tres entornos, eso son varios núcleos y varios GB de clúster reservados para nada, contando además contra laResourceQuotadel namespace (03-04).- El CronJob
informes-ocupacionsufría throttling silencioso. Sulimits.cpude 1000m con un uso de 1100m deseados hacía que el informe nocturno tardara 40 minutos en vez de 18. Nadie se quejaba porque nadie estaba despierto.
Los manifiestos recalibrados
# k8s/entornos/pro/api-reservas-recursos.yaml
# Valores basados en dos semanas de observación con kubectl top
# CPU: p70 observado como request; limit generoso para absorber picos
# Memoria: p95 + 12% de margen, con limit igual a request (QoS Guaranteed
# para el componente crítico del negocio)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-reservas
namespace: rutas-norte-pro
spec:
template:
spec:
containers:
- name: api
resources:
requests:
cpu: "200m" # antes 100m — p50 observado 185m
memory: "512Mi" # antes 128Mi — p95 observado 458Mi
limits:
cpu: "1500m" # antes 500m — permitía llegar a los 640m del p95
memory: "512Mi" # igual que request → QoS Guaranteed# k8s/entornos/pro/tienda-web-recursos.yaml
# Componente sobredimensionado: liberamos clúster reduciendo las requests
apiVersion: apps/v1
kind: Deployment
metadata:
name: tienda-web
namespace: rutas-norte-pro
spec:
template:
spec:
containers:
- name: nginx
resources:
requests:
cpu: "50m" # antes 200m — p95 observado 24m
memory: "64Mi" # antes 256Mi — p95 observado 42Mi
limits:
cpu: "300m"
memory: "128Mi"Una advertencia sobre el proceso: recalibra en rutas-norte-pre primero, con carga sintética representativa, y solo después lleva los valores a rutas-norte-pro. Bajar una requests.memory de golpe en producción puede provocar que el planificador meta más pods en un nodo del que caben de verdad, con desalojos posteriores.
Y no lo hagas a mano indefinidamente. En 09-02 veremos el VPA (Vertical Pod Autoscaler), que hace exactamente este análisis de forma continua y puede recomendar o aplicar los valores automáticamente.
- Por qué el HPA y el VPA dependen de este componente
Aunque el módulo 9 desarrolla el autoescalado, hay que dejar clara aquí la dependencia, porque es la razón de ser de metrics-server.
Cuando en 09-01 escribamos un HorizontalPodAutoscaler como este:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-reservas
namespace: rutas-norte-pro
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-reservas
minReplicas: 4
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 70 % de la REQUEST, no del limitel controlador del HPA, que vive dentro del kube-controller-manager, consulta exactamente la misma API que kubectl top:
Cada 15 segundos por defecto. Con el resultado, calcula el uso medio de CPU de los pods del Deployment, lo divide entre la suma de sus requests para obtener el porcentaje de utilización, y decide cuántas réplicas debe haber.
Dos consecuencias que hay que grabar a fuego:
Consecuencia 1: sin metrics-server, el HPA no funciona. No falla ruidosamente: se queda con el estado unknown y no escala. El síntoma:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
api-reservas Deployment/api-reservas <unknown>/70% 4 20 4 12mEse <unknown> es la firma de que metrics-server no está disponible. Y kubectl describe hpa lo confirma:
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True SucceededGetScale the HPA controller was able to get the target's current scale
ScalingActive False FailedGetResourceMetric failed to get cpu utilization: unable to get metrics for
resource cpu: unable to fetch metrics from resource metrics
API: the server could not find the requested resourceDurante el puente de mayo, con el HPA "instalado" pero sin metrics-server, api-reservas se habría quedado en 4 réplicas mientras el tráfico se multiplicaba por seis.
Consecuencia 2: el porcentaje del HPA se calcula sobre las requests. Un averageUtilization: 70 con requests.cpu: 100m significa escalar al llegar a 70 milicores. Si recalibras la request a 200m, el mismo HPA escalará al llegar a 140 milicores. Cambiar las requests cambia el comportamiento del autoescalado. Por eso el orden correcto es: primero calibrar con kubectl top (esta lección), después configurar el HPA (09-01).
El VPA (09-02) usa la misma fuente, pero con un histórico propio que él mismo mantiene, precisamente porque metrics-server no lo tiene.
- Diagnóstico de los fallos típicos
error: Metrics API not available
Significa que el API Server no puede servir el grupo metrics.k8s.io. Secuencia de diagnóstico:
NAME SERVICE AVAILABLE AGE
v1beta1.metrics.k8s.io kube-system/metrics-server False (MissingEndpoints) 3m0/1 con reinicios: el pod arranca pero su propia readiness falla. Al log:
Error de certificado del kubelet
E0806 09:22:14.882134 1 scraper.go:149] "Failed to scrape node" err="Get
\"https://192.168.49.2:10250/metrics/resource\": tls: failed to verify certificate:
x509: cannot validate certificate for 192.168.49.2 because it doesn't contain any IP SANs"
node="rutas-norte-worker-1"Este es el caso descrito en el apartado 4. Solución en desarrollo:
kubectl -n kube-system patch deployment metrics-server --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'Recuerda la advertencia: en rutas-norte-pro, la solución correcta es la rotación de certificados de servidor del kubelet, no este parche.
Valores vacíos recién arrancado
W0806 09:15:02.114 Metrics not available for pod rutas-norte-pro/api-reservas-7d9f8c4b5-x2klm, age: 22.4s
error: metrics not available yetNo es un fallo. Es el comportamiento esperado durante los primeros 30-60 segundos tras arrancar metrics-server o tras crear un pod nuevo: hacen falta dos muestras para calcular la tasa de CPU. Espera un minuto y vuelve a probar. Muchísima gente reinstala metrics-server innecesariamente por no esperar.
El nodo no es alcanzable
E0806 09:24:01 "Failed to scrape node" err="Get \"https://rutas-norte-worker-3:10250/metrics/resource\":
dial tcp: lookup rutas-norte-worker-3 on 10.96.0.10:53: no such host" node="rutas-norte-worker-3"metrics-server intenta llegar al nodo por su nombre de host y el DNS no lo resuelve. Solución: forzar el uso de la IP interna.
Un pod concreto no aparece
Causas habituales, en orden de frecuencia:
- El pod lleva menos de 30 segundos vivo.
- El pod está en
Pending: no tiene contenedores ejecutándose, así que no hay nada que medir. - El nodo donde vive el pod no es alcanzable para metrics-server (mira los logs).
- No tienes permisos RBAC sobre ese namespace.
Tabla resumen de diagnóstico
| Síntoma | Causa probable | Comprobación |
|---|---|---|
Metrics API not available |
Pod caído o APIService no disponible |
kubectl get apiservice v1beta1.metrics.k8s.io |
metrics not available yet |
Menos de 60 s desde el arranque | Esperar y reintentar |
x509: cannot validate certificate |
Certificado autofirmado del kubelet | Logs de metrics-server; flag --kubelet-insecure-tls |
no such host al escanear nodo |
Resolución DNS del nombre del nodo | --kubelet-preferred-address-types=InternalIP,... |
HPA con <unknown> |
metrics-server no responde | kubectl top pods en el mismo namespace |
| Falta un pod concreto | Pod nuevo o en Pending |
kubectl get pod <nombre> -o wide |
Error from server (Forbidden) |
RBAC insuficiente | kubectl auth can-i get pods.metrics.k8s.io -n <ns> |
Errores Comunes y Consejos
1. Esperar que metrics-server sea un sistema de monitorización. Es la confusión número uno. No guarda histórico, no alerta y solo mide dos cosas. Instalar metrics-server y dar la observabilidad por resuelta es dejar la plataforma casi tan ciega como antes.
2. Confundir kubectl top con kubectl describe node. Son datos completamente distintos y responden a preguntas distintas:
Allocated resources:
Resource Requests Limits
-------- -------- ------
cpu 3200m (80%) 6500m (162%)
memory 6144Mi (78%) 9216Mi (118%)Esto son requests y limits declarados, es decir, lo que el planificador tiene reservado. kubectl top muestra el consumo real. Un nodo puede estar al 80 % de reservas y al 20 % de uso real: eso es precisamente lo que estamos corrigiendo en el apartado 8.
3. Recalibrar con una sola lectura. Un kubectl top un martes a las 11:00 no te dice nada sobre el puente de mayo. Mide durante al menos una semana y usa percentiles.
4. Bajar --metric-resolution por debajo de 10 s. Se satura la Summary API de los kubelets, que en clústeres grandes es una operación cara. No mejora la calidad de los datos y sí puede degradar el nodo.
5. Dejar --kubelet-insecure-tls en producción. Lo repito porque se hace constantemente: se pone para desatascar la instalación y nadie vuelve a quitarlo. En rutas-norte-pro es un riesgo real, no teórico.
6. Ejecutar más de una réplica de metrics-server sin ajustar la configuración. Con alta disponibilidad hace falta añadir --enable-aggregator-routing=true en el API Server y ajustar la anti-afinidad. Con una réplica, la única consecuencia de que se caiga es perder kubectl top unos segundos: aceptable en la mayoría de casos.
7. Interpretar MEMORY(bytes) como el RSS del proceso. Es workingSetBytes, que excluye caché de página recuperable. No cuadrará con ps ni con free dentro del contenedor, y eso es normal.
8. Usar kubectl top para investigar un incidente pasado. No hay pasado. Si el incidente terminó, kubectl top te enseña el presente sano y no aporta nada. Esa es literalmente la razón de la próxima lección.
9. Olvidar que las requests afectan al HPA. Cambiar requests.cpu sin revisar el averageUtilization del HPA cambia silenciosamente el umbral de escalado.
10. No comprobar los sidecars con --containers. Desde 06-04 tenemos sidecars en varios componentes. Sumarlos al contenedor principal esconde su coste y complica el dimensionado.
Ejercicios
Ejercicio 1 — Instalar, verificar y recorrer el camino del dato
En tu minikube rutas-norte:
- Activa el addon
metrics-servery verifica que elAPIServicequedaAVAILABLE: True. - Obtén las métricas de
api-reservassin usarkubectl top, hablando directamente con la API agregada. - Explica de dónde salió exactamente ese número: qué componente lo leyó, de dónde y por qué ruta llegó a tu terminal.
Ejercicio 2 — Detectar el desequilibrio y proponer una recalibración
Estos son los datos de dos semanas de worker-notificaciones en rutas-norte-pro:
Percentil 50 CPU: 43m Percentil 50 memoria: 180Mi
Percentil 70 CPU: 95m Percentil 95 memoria: 240Mi
Percentil 95 CPU: 380m Percentil 99 memoria: 268Mi
Percentil 99 CPU: 520m Máximo memoria: 291MiSu configuración actual:
- ¿Qué clase de QoS tiene ahora este pod? (repasa 03-05)
- Identifica dos problemas graves en esta configuración a la vista de los datos.
- Propón una configuración nueva y justifica cada valor.
Ejercicio 3 — Diagnosticar un HPA que no escala
Durante un pico de tráfico, api-reservas sigue en 4 réplicas pese a tener un HPA configurado. Un compañero te pasa esta información:
$ kubectl -n rutas-norte-pro get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
api-reservas Deployment/api-reservas <unknown>/70% 4 20 4 2d
$ kubectl top pods -n rutas-norte-pro
error: Metrics API not available
$ kubectl -n kube-system get pods -l k8s-app=metrics-server
NAME READY STATUS RESTARTS AGE
metrics-server-7bf7d58749-qk4hn 0/1 Running 0 18m- ¿Cuál es la relación de causalidad entre los tres síntomas?
- Escribe la secuencia de comandos que ejecutarías para llegar a la causa raíz.
- Suponiendo que el log muestre
x509: cannot validate certificate ... because it doesn't contain any IP SANs, ¿qué harías enrutas-norte-devy qué harías enrutas-norte-pro?
Soluciones
Solución 1
1. Activación y verificación.
minikube addons enable metrics-server -p rutas-norte
# Esperar a que el pod esté listo
kubectl -n kube-system wait --for=condition=Ready pod \
-l k8s-app=metrics-server --timeout=120s
# Verificar el APIService
kubectl get apiservice v1beta1.metrics.k8s.io2. Consulta directa a la API agregada.
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/rutas-norte-pro/pods" \
| jq '.items[] | select(.metadata.name | startswith("api-reservas")) |
{pod: .metadata.name,
cpu: .containers[0].usage.cpu,
memoria: .containers[0].usage.memory}'También se puede consultar un pod concreto:
kubectl get --raw \
"/apis/metrics.k8s.io/v1beta1/namespaces/rutas-norte-pro/pods/api-reservas-7d9f8c4b5-x2klm" | jq3. El camino del dato, paso a paso.
- El kernel de Linux contabiliza el uso del cgroup del contenedor
apiencpuacct.usageymemory.current. - cAdvisor, integrado en el binario del kubelet del nodo donde vive ese pod, lee esos ficheros de cgroup.
- El kubelet expone el agregado en
https://<ip-nodo>:10250/stats/summary. - metrics-server consulta ese endpoint cada 15 segundos, guarda las dos últimas muestras en memoria y calcula la tasa de CPU como
(nanocores_t2 - nanocores_t1) / (t2 - t1). - metrics-server sirve el resultado en el grupo de API
metrics.k8s.io/v1beta1, expuesto a través de un Service enkube-system. - El
APIServicev1beta1.metrics.k8s.iohace que el API Server delegue en ese Service cualquier petición a ese grupo. kubectlpide/apis/metrics.k8s.io/v1beta1/...al API Server, que actúa de proxy, y devuelve el JSON.
El detalle esencial: para kubectl esto es indistinguible de un kubectl get pods. Mismo endpoint, misma autenticación, mismo RBAC.
Solución 2
1. Clase de QoS.
Para que un pod sea Guaranteed, requests y limits deben coincidir en todos los recursos de todos sus contenedores. Aquí la memoria sí coincide (256Mi = 256Mi), pero la CPU no (200m ≠ 250m). Por tanto la clase de QoS es Burstable: en caso de presión de memoria en el nodo, este pod es candidato al desalojo antes que cualquier pod Guaranteed.
2. Los dos problemas graves.
Problema A: throttling severo de CPU. El limits.cpu es 250m, pero el percentil 95 del uso deseado es 380m y el percentil 99 llega a 520m. Eso significa que en el 5 % del tiempo el worker está estrangulado, y en ese 5 % es precisamente cuando envía las ráfagas de correos de confirmación. Efecto real para el negocio: los correos de confirmación de compra se retrasan justo cuando más compras hay. Además, kubectl top mostraría el consumo pegado a 250m sin decir que hay estrangulamiento: haría falta Prometheus para confirmarlo.
Problema B: riesgo de OOMKilled y request de CPU inflada. El limits.memory es 256Mi y el máximo observado es 291Mi. Ya se ha superado el límite: ese worker está siendo matado por OOM periódicamente, probablemente sin que nadie lo relacione con los correos que no llegan. Y en el otro sentido, la requests.cpu: 200m está muy por encima del p50 de 43m: se reservan 200m de los que se usan 43m de media, desperdiciando clúster.
3. Configuración propuesta.
resources:
requests:
# CPU: p70 observado = 95m. La CPU es comprimible: reservar el central
# y dejar que el limit absorba las ráfagas de envío de correos.
cpu: "100m"
# Memoria: p99 = 268Mi, máximo 291Mi. Reservamos por encima del máximo
# observado porque la memoria NO es comprimible.
memory: "320Mi"
limits:
# 6x la request: cubre holgadamente el p99 de 520m y da margen a las
# ráfagas. Al ser comprimible, no hay riesgo de que "gaste de más".
cpu: "600m"
# Un 20% sobre la request: margen sobre el máximo observado (291Mi)
# sin permitir una fuga descontrolada.
memory: "384Mi"Justificación resumida:
requests.cpubaja de 200m a 100m → se liberan 100m por réplica en laResourceQuotadel namespace.limits.cpusube de 250m a 600m → desaparece el throttling en las ráfagas.requests.memorysube de 256Mi a 320Mi → por encima del máximo observado de 291Mi: se acaban losOOMKilled.limits.memorya 384Mi → margen del 20 % que absorbe un pico inesperado pero corta una fuga de memoria antes de que afecte al nodo.
Nota de proceso: aplicarlo primero en rutas-norte-pre y confirmar con kubectl top pods --containers durante una semana que el consumo se mantiene en el rango previsto. Confirmar también que el contador de reinicios (RESTARTS) se queda a cero, que es la prueba de que los OOMKilled han cesado.
Solución 3
1. Relación de causalidad.
Los tres síntomas son el mismo problema visto desde tres sitios, y la cadena va de abajo arriba:
metrics-server 0/1 (no listo, no puede escanear los kubelets)
↓
El Service de metrics-server no tiene Endpoints (readiness fallando)
↓
El APIService v1beta1.metrics.k8s.io pasa a AVAILABLE: False
↓
kubectl top → "Metrics API not available"
↓
El controlador del HPA no puede leer las métricas de CPU
↓
HPA con TARGETS <unknown> → NO ESCALA
↓
api-reservas se queda en 4 réplicas durante el pico de tráficoDetalle importante: el HPA no falla ruidosamente. No genera un evento de error visible en el Deployment ni deja el objeto en estado de error obvio. Simplemente no hace nada. Esa es la razón por la que en 07-04 configuraremos una alerta específica sobre la condición ScalingActive del HPA.
2. Secuencia de diagnóstico.
# 1. Confirmar el estado del APIService (la conexión entre síntomas)
kubectl get apiservice v1beta1.metrics.k8s.io
# 2. Ver por qué el pod no está listo: los eventos y la condición Ready
kubectl -n kube-system describe pod -l k8s-app=metrics-server | tail -25
# 3. La causa raíz suele estar en el log
kubectl -n kube-system logs -l k8s-app=metrics-server --tail=50
# 4. Confirmar qué está diciendo el HPA exactamente
kubectl -n rutas-norte-pro describe hpa api-reservas | grep -A 10 Conditions
# 5. Verificar conectividad al kubelet desde dentro del clúster (opcional)
kubectl -n kube-system exec deploy/metrics-server -- \
wget -q -O- --no-check-certificate https://192.168.49.2:10250/healthz3. Actuación diferenciada por entorno.
En rutas-norte-dev — desbloquear rápido, riesgo aceptable:
kubectl -n kube-system patch deployment metrics-server --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
kubectl -n kube-system rollout status deployment/metrics-server
sleep 45
kubectl top nodesEn rutas-norte-pro — el flag no es aceptable. La solución correcta es que el certificado del kubelet lo firme la CA del clúster:
- Habilitar en cada kubelet la rotación de certificados de servidor:
-
Reiniciar el kubelet en cada nodo (
systemctl restart kubelet), lo que genera una CSR por nodo. -
Aprobar las CSR pendientes de tipo
kubernetes.io/kubelet-serving:
En un clúster real esto se automatiza con un aprobador (por ejemplo, kubelet-csr-approver), porque las CSR se renuevan periódicamente.
- Verificar que metrics-server funciona sin el flag inseguro:
kubectl -n kube-system get deploy metrics-server \
-o jsonpath='{.spec.template.spec.containers[0].args}' | tr ',' '\n'
# No debe aparecer --kubelet-insecure-tls
kubectl top nodesMedida preventiva imprescindible: que este fallo pasara 18 minutos desapercibido durante un pico de tráfico es el verdadero problema. En 07-04 crearemos una alerta que dispare cuando el APIService de métricas no esté disponible o cuando un HPA lleve más de cinco minutos con ScalingActive=False. Nadie debería enterarse de esto porque el tráfico se cae.
Conclusión
En esta lección hemos instalado la primera fuente de datos de consumo real de Rutas Norte y hemos aprendido a leerla con criterio:
- metrics-server no es un sistema de monitorización, sino un componente de infraestructura cuyo propósito es alimentar al HPA y al VPA. Que
kubectl topfuncione es una consecuencia agradable. - El dato viaja desde los cgroups del kernel → cAdvisor dentro del kubelet → la Summary API en el puerto 10250 → metrics-server cada 15 segundos → el
APIServicede la capa de agregación → tu terminal. Entender ese camino convierte cada mensaje de error en un diagnóstico. kubectl top nodesda porcentajes sobre elallocatabledel nodo;kubectl top podsno da porcentajes y el desglose por contenedor con--containerses imprescindible desde que tenemos sidecars.- Su uso más valioso hoy ha sido cerrar la deuda de 03-04: hemos recalibrado las
requestsy loslimitsde los seis componentes con datos reales, descubriendo queapi-reservasestaba infradimensionada y sufriendo throttling, quetienda-webreservaba cuatro veces lo que usa, y que el CronJob nocturno tardaba el doble de lo necesario por un límite demasiado bajo. - Y hemos visto sus límites infranqueables: sin histórico, sin consultas, sin alertas, solo CPU y memoria, con una resolución de decenas de segundos. Para "¿qué pasó anoche a las tres?" no sirve.
Esa última limitación es exactamente el gancho de la próxima lección. Rutas Norte necesita un sistema que recuerde, que permita preguntar y que sepa avisar. En 07-03 desplegaremos Prometheus con el operador que anunciamos en 06-07, conectaremos por fin el sidecar exportador de métricas de postgres-reservas que añadimos en 06-04, instrumentaremos api-reservas con métricas de negocio, y aprenderemos PromQL para responder a preguntas que hoy no podemos ni formular: cuántas reservas por minuto se confirman, cuál es el percentil 95 de latencia y cuánto falta para que se llene el disco de la base de datos.
Curso de Kubernetes
Módulo 1: Introducción a Kubernetes
- ¿Qué es Kubernetes?
- Arquitectura de Kubernetes
- Conceptos y Terminología Clave
- Configuración de un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objetos, Manifiestos YAML y el Modelo Declarativo
- El Proyecto del Curso: la Plataforma Rutas Norte
Módulo 2: Componentes Principales de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualizaciones, Rollbacks y Estrategias de Despliegue
- Servicios
- Namespaces
- Etiquetas, Selectores y Anotaciones
Módulo 3: Gestión de Configuración y Secretos
- ConfigMaps
- Secrets
- Variables de Entorno
- Cuotas y Límites de Recursos
- LimitRanges y Clases de Calidad de Servicio (QoS)
- ServiceAccounts y Acceso a la API desde los Pods
Módulo 4: Redes en Kubernetes
- Redes de Clúster
- Tipos de Servicios
- DNS Interno y Descubrimiento de Servicios
- Controladores de Ingress
- TLS y Gestión de Certificados con cert-manager
- Políticas de Red
Módulo 5: Almacenamiento en Kubernetes
- Volúmenes
- Volúmenes Persistentes
- Reclamaciones de Volúmenes Persistentes
- Clases de Almacenamiento
- Aprovisionamiento Dinámico, Expansión y Snapshots
- Copias de Seguridad y Restauración de Datos
Módulo 6: Conceptos Avanzados de Kubernetes
- StatefulSets
- DaemonSets
- Trabajos y CronJobs
- Init Containers, Sidecars y Patrones Multi-Contenedor
- Planificación: Afinidad, Taints y Tolerations
- Definiciones de Recursos Personalizados (CRDs)
- Operadores y el Patrón Controlador
Módulo 7: Monitoreo y Registro
- Verificaciones de Salud y Sondas
- Servidor de Métricas y kubectl top
- Monitoreo con Prometheus
- Visualización y Alertas con Grafana y Alertmanager
- Registro Centralizado con Elasticsearch, Fluentd y Kibana (EFK)
- Depuración de Aplicaciones y Eventos del Clúster
Módulo 8: Seguridad en Kubernetes
- Control de Acceso Basado en Roles (RBAC)
- Contextos de Seguridad y Endurecimiento del Contenedor
- Políticas de Seguridad de Pods y Pod Security Standards
- Seguridad de Red
- Seguridad de Imágenes
- Auditoría, Escaneo y Gestión de Vulnerabilidades
Módulo 9: Escalado y Rendimiento
- Autoescalado Horizontal de Pods
- Autoescalado Vertical de Pods
- Autoescalado de Clúster
- Escalado por Eventos y Métricas Personalizadas con KEDA
- Alta Disponibilidad: PodDisruptionBudgets y Topología
- Ajuste de Rendimiento
Módulo 10: Ecosistema y Herramientas de Kubernetes
- Minikube y Entornos Locales con kind
- Kubeadm
- Helm
- Kustomize
- GitOps con Argo CD y Flux
- Kubernetes Gestionado: EKS, AKS y GKE
Módulo 11: Estudios de Caso y Aplicaciones del Mundo Real
- Despliegue de una Aplicación Web
- Ejecución de Aplicaciones con Estado
- CI/CD con Kubernetes
- Estrategias de Despliegue: Blue-Green y Canary
- Gestión Multi-Clúster
- Operación en Producción: Incidencias, Runbooks y Costes
