Kubernetes en producción · Guía práctica · #003

ImagePullBackOff: por qué Kubernetes no puede descargar tu imagen

Un Pod puede existir sin llegar a iniciar sus contenedores porque el nodo no logra obtener la imagen del registry. ImagePullBackOff indica que los intentos fallaron y que Kubernetes está esperando antes de volver a probar; no identifica por sí solo la causa.

Por Jorge Lozano · Diagnóstico de Kubernetes

Empieza por los eventos del Pod. El mensaje completo suele apuntar a una referencia incorrecta de imagen, acceso denegado, problemas de red hacia el registry o una imagen incompatible con la arquitectura del nodo.

Síntomas y escenario

Se actualizó un Deployment a una versión nueva, pero las réplicas de reemplazo no logran arrancar:

NAME                         READY   STATUS
payments-api-7d5c9f8b4d-pm2sk   1/1     Running
payments-api-6b8c9d7f6c-2qj8m   0/1     ImagePullBackOff

Los usuarios todavía reciben respuestas de las réplicas anteriores, pero el rollout no puede completarse. Con la estrategia de actualización gradual predeterminada y capacidad suficiente, un Deployment conserva las réplicas antiguas disponibles mientras los Pods nuevos no estén listos. El comportamiento exacto depende de la estrategia y de sus límites de disponibilidad. Si no quedan réplicas sanas, puede producirse una interrupción completa.

Es un escenario ilustrativo: el mismo estado puede tener causas distintas. El evento del nodo aporta la evidencia para diferenciarlas.

Qué significa realmente el estado

ImagePullBackOff aparece como estado del contenedor cuando Kubernetes no puede obtener una imagen. El primer fallo suele mostrarse como ErrImagePull. Cuando kubelet empieza a espaciar los reintentos, el motivo de espera pasa a ser ImagePullBackOff. El Pod normalmente permanece en la fase Pending: el texto que muestra kubectl get pods es el motivo de espera del contenedor, no una fase independiente del Pod. Los retrasos entre intentos aumentan y pueden llegar a cinco minutos.

El proceso del contenedor todavía no empezó. Por eso este caso es distinto de un fallo de la aplicación o de OOMKilled. Reiniciar o borrar el Pod rara vez corrige la causa de fondo: el reemplazo usará la misma imagen, credenciales, red y permisos.

Investiga primero los eventos

Confirma el contexto de Kubernetes y el namespace antes de inspeccionar recursos:

kubectl config current-context
kubectl get pods -n payments -o wide
kubectl describe pod payments-api-6b8c9d7f6c-2qj8m -n payments

En la salida de describe, revisa:

  • La imagen exacta solicitada por cada contenedor e init container.
  • El nodo asignado al Pod.
  • El estado y el mensaje del contenedor que no pudo iniciar.
  • Los eventos recientes, especialmente Failed, ErrImagePull y BackOff.

Los eventos suelen aportar más que el nombre del estado. Una secuencia típica incluye un evento Failed con el error del registry y luego BackOff mientras Kubernetes espera para reintentar. El mensaje puede indicar manifest unknown o unauthorized; la redacción depende del registry y del runtime. Conserva el texto y la hora del evento. No copies tokens, contraseñas ni el contenido de Secrets en un ticket o artículo.

Para ordenar los eventos del namespace por fecha:

kubectl get events -n payments --sort-by=.metadata.creationTimestamp

Si conoces el controlador y sus Pods, revisa también la imagen declarada y el progreso del rollout:

kubectl get deployment payments-api -n payments -o yaml
kubectl rollout status deployment/payments-api -n payments

Diagnostica según el mensaje

1. El repositorio, la imagen o el tag no existen

Mensajes como manifest unknown, not found o name unknown suelen apuntar a un repositorio o tag incorrecto, una ruta incompleta o una imagen que se eliminó.

Inspecciona la referencia declarada:

kubectl get pod payments-api-6b8c9d7f6c-2qj8m -n payments \
  -o jsonpath='{range .spec.containers[*]}{.name}{"\t"}{.image}{"\n"}{end}'

Después confirma que el repositorio y el tag existan en el registry esperado. En imágenes privadas, comprueba el hostname correcto y, cuando aplique, la cuenta y región correspondientes. Un tag puede apuntar a contenido distinto con el tiempo; para despliegues reproducibles, usa tags inmutables o fija la imagen por digest.

2. El registry rechazó la autenticación o autorización

Mensajes como unauthorized, denied, pull access denied o no basic auth credentials indican que la identidad usada para descargar no tiene credenciales válidas o permiso sobre ese repositorio.

Para un registry privado genérico, Kubernetes puede usar un Secret de tipo kubernetes.io/dockerconfigjson asociado al Pod o a su ServiceAccount. El Secret debe existir en el mismo namespace que el Pod. Revisa las referencias sin mostrar sus valores:

kubectl get pod payments-api-6b8c9d7f6c-2qj8m -n payments \
  -o jsonpath='{.spec.imagePullSecrets[*].name}{"\n"}'
kubectl get serviceaccount default -n payments -o yaml
kubectl get secrets -n payments

Si las credenciales están asociadas a una ServiceAccount, confirma que el Pod use esa cuenta. Nunca imprimas el campo data del Secret.

En una configuración manual, kubectl create secret docker-registry puede recibir valores desde variables de entorno cargadas por un gestor aprobado de secretos o por un entorno de CI protegido. Aun así, los argumentos expandidos pueden ser visibles para otros procesos con privilegios mientras se ejecuta el comando. En producción, prefiere la integración con un gestor de secretos o un proveedor de credenciales del registry.

3. El nodo no puede llegar al registry

Un timeout, un fallo de resolución DNS, una conexión rechazada o un error TLS pueden señalar problemas de DNS, rutas, proxy, firewall, reglas de salida, certificados o disponibilidad del registry.

Kubelet obtiene la imagen a través del runtime del nodo. Que una solicitud funcione desde un contenedor de diagnóstico dentro del clúster no demuestra que el nodo tenga la misma ruta, DNS, proxy o confianza de certificados. Si el evento indica conectividad, identifica el nodo afectado y revisa los logs y métricas de kubelet o del runtime con el equipo que administra los nodos. Compara con un nodo sano y verifica el estado del registry.

Si el problema solo afecta a Pods de un nodo, es más probable que sea local a ese nodo. Si afecta a varios, investiga cambios compartidos de credenciales, red, DNS, proxy o el propio registry.

4. La imagen no es compatible con la arquitectura o el runtime

Un error de plataforma no compatible o de manifiesto incompatible puede indicar que la imagen no publica una variante para la arquitectura del nodo. Compara la arquitectura del nodo con las plataformas publicadas para la imagen. Esto puede ocurrir en clústeres con nodos amd64 y arm64.

Investiga presión de disco del nodo o la salud del runtime solo cuando los eventos o las métricas apunten a esas causas.

Consideraciones para Amazon EKS y ECR

En EKS, la ruta de autorización depende de dónde se ejecute el Pod:

  • Nodos EC2 (administrados o propios): kubelet accede a ECR con el rol IAM del nodo. Confirma que tenga permisos de descarga y que la política del repositorio permita el acceso, sobre todo si está en otra cuenta.
  • Fargate: revisa el rol de ejecución del Pod de Fargate.
  • Clústeres privados: revisa la conectividad desde la subred del nodo hacia ECR. Según el diseño de red, puede hacer falta salida NAT o endpoints privados para las APIs del registry y las capas de imagen.

No agregues permisos al rol de la aplicación por reflejo. La descarga suele ocurrir antes de que el contenedor empiece y usa el rol del nodo o el rol de ejecución de Fargate. Confirma el tipo de cómputo y el mensaje del evento antes de cambiar permisos IAM.

Resuelve y verifica

Corrige la causa respaldada por la evidencia: ajusta la ruta o el tag, publica la imagen faltante, restaura el permiso mínimo necesario, corrige la referencia al Secret o repara la conexión de red. Si el rollout está gestionado por GitOps, Helm o Kustomize, actualiza la fuente declarativa.

Después, confirma que las réplicas nuevas descarguen y ejecuten la imagen esperada:

kubectl rollout status deployment/payments-api -n payments
kubectl get pods -n payments -o wide
kubectl describe pod <new-pod> -n payments

Comprueba que las réplicas estén listas, que el rollout finalice y que la aplicación pase su health check. Si la imagen usa un tag mutable con imagePullPolicy: IfNotPresent, el nodo podría reutilizar una imagen que ya tiene en caché. Cambiar el tag en el manifiesto inicia un nuevo rollout, pero no garantiza que un tag mutable represente el mismo contenido en todos los nodos. Los digests evitan esa ambigüedad.

Si el rollout deja al servicio sin capacidad suficiente, páusalo o vuelve a una revisión conocida según el procedimiento operativo del equipo. Primero conserva el texto del evento y la referencia exacta de la imagen para poder investigar la causa.

Prevención

  • Publica la imagen antes de actualizar el manifiesto que la referencia.
  • Usa tags únicos e inmutables o digests, y valida la imagen en CI.
  • Entiende la política de descarga predeterminada: si se omite, Kubernetes usa Always para :latest o cuando no hay tag, e IfNotPresent para tags distintos de latest y digests. El valor se define al crear el objeto; cambiar el tag después no vuelve a calcularlo. Con Always, el runtime resuelve la referencia al iniciar cada contenedor, aunque puede reutilizar capas en caché.
  • Gestiona credenciales de forma segura, limita el acceso por repositorio y evita credenciales que expiren sin un mecanismo de renovación.
  • En EKS, verifica el rol del nodo o el rol de ejecución de Fargate, las políticas entre cuentas y la conectividad del nodo.
  • Monitorea los fallos al descargar imágenes y la duración de los rollouts para detectarlos antes de agotar las réplicas sanas.

La idea principal

ImagePullBackOff describe el efecto visible. El evento señala la capa que falla: referencia de imagen, autorización, conectividad o compatibilidad. Primero reúne esa evidencia, cambia una cosa a la vez y verifica el rollout.

Referencias técnicas