Depura un Pod que falla: describe, logs, exec y port-forward
Una lista de comprobación repetible para averiguar por qué un Pod está Pending, se cae o no responde.
Tarde o temprano un Pod no arrancará, se reiniciará constantemente o estará en ejecución pero no responderá. La solución casi siempre es visible con un puñado de comandos de kubectl usados en el orden correcto. Esta guía te da ese orden: leer el estado, pedir los eventos, leer los logs, entrar en el contenedor y luego rastrear la ruta de red.
Lee primero el estado
Empieza por la columna de estado:
kubectl get pods -o wide
Los estados más comunes y dónde mirar primero:
- Pending. El Pod no está asignado a un nodo. Normalmente no hay suficiente CPU o memoria, se agotó una cuota o un volumen no puede enlazarse.
describete dirá cuál. - ContainerCreating. El nodo está preparando el contenedor. Si se queda así, busca un volumen ausente o una descarga de imagen lenta.
- ErrImagePull / ImagePullBackOff. No se puede descargar la imagen, casi siempre por un error tipográfico en el nombre o la etiqueta, o por faltar credenciales de un registro privado.
- CrashLoopBackOff. El contenedor arranca, termina y se reinicia repetidamente con retrasos crecientes. La aplicación está fallando, así que lee sus logs.
- CreateContainerConfigError. El Pod referencia un ConfigMap, Secret o clave que no existe.
- Running pero
READY 0/1. El contenedor está levantado pero falla su readiness probe. - OOMKilled (se muestra en
describe). El contenedor superó su límite de memoria.
Pregúntale a Kubernetes por qué: describe y eventos
kubectl describe pod POD_NAME
Desplázate hasta la sección Events al final. Se lee como una línea de tiempo: Scheduled, Pulling, Failed, BackOff. Mensajes como 0/3 nodes are available: insufficient cpu o Failed to pull image "ngnix:1.25" suelen nombrar el problema directamente. Más arriba, mira el State y el Last State de cada contenedor para ver códigos de salida y motivos. Para una vista de todo lo ocurrido en el namespace:
kubectl get events --sort-by=.metadata.creationTimestamp
Lee la salida del contenedor
kubectl logs POD_NAME
kubectl logs POD_NAME -c CONTAINER_NAME
kubectl logs POD_NAME --all-containers
kubectl logs POD_NAME --previous
kubectl logs POD_NAME -f --since=10m --tail=100
Usa -c cuando un Pod tiene varios contenedores, -f para transmitir en directo, --since y --tail para limitar la salida, y --previous para leer los logs de la última instancia que se cayó, que es lo que necesitas en un crash loop. Para probarlo, crea un Deployment cuyo contenedor falle a propósito:
kubectl create deployment crashy --image=busybox:1.36 -- \
sh -c 'echo starting; echo "fatal: DB_URL is not set" >&2; exit 1'
kubectl get pods -l app=crashy
kubectl logs deployment/crashy --previous
El estado pasa a CrashLoopBackOff y el log muestra por qué. Limpia con kubectl delete deployment crashy.
Ten en cuenta que los logs pertenecen al Pod. Cuando se elimina o se reemplaza, sus logs se limpian con él, así que para tener historial necesitas un recolector de logs como Fluentd o Fluent Bit que los envíe a un lugar duradero.
Entra: exec, port-forward y cp
kubectl exec -it POD_NAME -- sh
kubectl exec POD_NAME -- env
kubectl port-forward pod/POD_NAME 8080:80
curl http://localhost:8080
kubectl cp POD_NAME:/path/to/file ./file
exec ejecuta un comando en el contenedor, port-forward conecta un puerto local con un puerto del Pod (también funciona con svc/NAME) y cp copia archivos hacia dentro o hacia fuera. Muchas imágenes de producción son mínimas y no tienen shell ni curl. En ese caso, kubectl debug -it POD_NAME --image=busybox:1.36 --target=CONTAINER_NAME adjunta un contenedor de depuración temporal que comparte los namespaces del Pod.
Todo lo que cambies dentro de un contenedor en ejecución, como editar un archivo de configuración, se pierde en cuanto se reemplaza el Pod. Usa exec para investigar, pero haz la corrección en la imagen, en el manifiesto o en un ConfigMap.
Trabaja hacia atrás por la ruta de la petición
Si el Pod parece sano pero la aplicación no es accesible, rastrea la ruta de dentro hacia fuera:
- ¿El Pod está en ejecución y
READY? Arregla primero el Pod. - ¿El selector del Service coincide con las etiquetas del Pod?
kubectl get pods --show-labelsykubectl describe service NAMEdeberían coincidir. - ¿El Service tiene endpoints?
kubectl get endpointslices -l kubernetes.io/service-name=NAMEdebería listar IP de Pods. Una lista vacía significa un selector que no coincide o ningún Pod listo. - ¿Coinciden los puertos? El
targetPortdel Service debe ser el puerto en el que el contenedor escucha realmente. - ¿Funciona desde otro Pod?
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME. Esto prueba el DNS y el Service, descartando todo lo que esté fuera del clúster. - ¿Podría estar bloqueándolo una NetworkPolicy? Lístalas con
kubectl get networkpolicy. - Por último, revisa el Ingress o el balanceador de carga externo que hay delante.
Cada paso acota el problema a una capa, lo que es más rápido que adivinar.
Guías populares
-
Qué es Kubernetes y los problemas que resuelve Por qué los contenedores por sí solos dejan de escalar y cómo Kubernetes convierte un grupo de máquinas en una sola plataforma. -
Cómo funciona un clúster de Kubernetes: plano de control y nodos Los componentes que hacen funcionar un clúster y qué ocurre entre kubectl apply y un contenedor en ejecución. -
Deployments: escalado, autorreparación y rolling updates Ejecuta réplicas de un Pod, actualízalas sin tiempo de inactividad y revierte cuando un despliegue sale mal. -
Services: networking estable para Pods efímeros Dale a los Pods una dirección estable, exponlos dentro y fuera del clúster y restringe quién puede hablar con quién.