Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Diagnostiquer un ImagePullBackOff Kubernetes

16 min de lecture

logo kubernetes

ImagePullBackOff signifie que le kubelet n’arrive pas à télécharger l’image du conteneur depuis le registre. Le pod est assigné à un nœud (il n’est plus Pending), mais la création du conteneur échoue car l’image est introuvable, inaccessible ou refusée. Ce guide vous donne une méthodologie en 4 étapes pour identifier la cause et débloquer le déploiement.

  • Comprendre la différence entre ErrImagePull et ImagePullBackOff
  • Identifier les 7 causes principales d’un échec de pull d’image
  • Appliquer une méthodologie de diagnostic reproductible en 4 étapes
  • Configurer imagePullSecrets pour un registre privé
  • Reproduire et résoudre un ImagePullBackOff sur un cluster de test
  • Un cluster Kubernetes fonctionnel (v1.28+)
  • kubectl configuré et connecté au cluster
  • Des droits de lecture sur les pods et événements
  • Connaissances de base sur les Pods et les images de conteneurs

Quand le kubelet essaie de télécharger une image et échoue, il passe par deux états successifs :

ÉtatSignification
ErrImagePullLe pull vient d’échouer — c’est la première tentative (ou une nouvelle tentative après un backoff)
ImagePullBackOffLe kubelet a déjà échoué et attend avant de réessayer — c’est le backoff exponentiel

Ces deux états alternent en boucle : le kubelet essaie (ErrImagePull), échoue, attend (ImagePullBackOff), puis réessaie. Le délai d’attente augmente progressivement (backoff exponentiel), mais la documentation officielle ne détaille pas les timings exacts de ce backoff aussi précisément que pour CrashLoopBackOff.

Quand le kubelet doit démarrer un conteneur, il suit ces étapes :

  1. Vérifier le cache local — l’image existe-t-elle déjà sur le nœud ?
  2. Résoudre le nom de l’image — ajouter docker.io/library/ si pas de registre explicite, et :latest si pas de tag
  3. S’authentifier — si un imagePullSecret est configuré, le kubelet l’utilise pour s’authentifier auprès du registre
  4. Télécharger l’image — pull des layers manquants
  5. Vérifier l’intégrité — validation du digest SHA256

Si une de ces étapes échoue, le kubelet émet un événement Failed avec le détail de l’erreur, et le pod passe en ErrImagePull.

#CauseMessage d’erreur typiqueOù chercher
1Tag ou nom d’image incorrectmanifest unknown, not foundkubectl describe pod → Events
2Registre privé sans authentificationunauthorized, authentication requiredkubectl describe pod → Events
3imagePullSecret absent ou invalideno basic auth credentials, unauthorizedkubectl describe pod → Events + kubectl get secret
4Registre inaccessible (réseau)dial tcp: i/o timeout, no such hostkubectl describe pod → Events
5Limite de pull Docker Hubtoomanyrequests, rate limit exceededkubectl describe pod → Events
6Image ou tag supprimémanifest unknown, not foundkubectl describe pod → Events
7Architecture incompatibleno matching manifest for linux/amd64kubectl describe pod → Events
  1. Identifier les pods en ImagePullBackOff

    Fenêtre de terminal
    kubectl get pods -A | grep -E 'ImagePull|ErrImage'

    Vous pouvez aussi filtrer les événements de type pull échoué :

    Fenêtre de terminal
    kubectl get events -A --field-selector reason=Failed --sort-by='.lastTimestamp' | grep -i pull

    Notez le nom du pod, son namespace et depuis combien de temps il est dans cet état.

  2. Lire le message d’erreur exact

    Fenêtre de terminal
    kubectl describe pod <pod> -n <namespace>

    Allez directement à la section Events et cherchez les lignes Failed ou Warning. Le message contient l’information essentielle. Exemples :

    Failed to pull image "mon-registre.example.com/app:v2.1":
    rpc error: code = Unknown desc = failed to pull and unpack image:
    failed to resolve reference: pull access denied, repository does not
    exist or may require authorization

    Ce message vous dit : le registre est privé et le kubelet n’a pas les credentials.

    Failed to pull image "nginx:v99":
    rpc error: code = NotFound desc = failed to pull and unpack image:
    failed to resolve reference: docker.io/library/nginx:v99:
    not found

    Celui-ci : le tag v99 n’existe pas sur Docker Hub.

  3. Vérifier l’image et l’authentification

    Vérifier que l’image existe :

    Consultez l’interface ou l’API du registre concerné (Docker Hub, Harbor, GitLab Registry, ECR…) pour confirmer que le tag existe et qu’il est disponible pour l’architecture de vos nœuds. Pour tester un pull directement depuis un nœud, utilisez crictl :

    Fenêtre de terminal
    # Tester le pull depuis le nœud (via un Pod de debug ou en SSH)
    crictl pull mon-registre.example.com/app:v2.1

    Vérifier l’imagePullSecret :

    Fenêtre de terminal
    # Le secret existe-t-il dans le namespace du pod ?
    kubectl get secret -n <namespace> | grep docker
    # Le pod référence-t-il le secret ?
    kubectl get pod <pod> -n <namespace> -o jsonpath='{.spec.imagePullSecrets}'

    Si le pod n’a pas d’imagePullSecrets et que le registre est privé, c’est la cause du problème.

    Vérifier la connectivité réseau (depuis le contexte d’un nœud) :

    kubectl debug node/ crée un Pod de debug attaché au nœud cible. Depuis ce Pod, vous pouvez tester la résolution DNS et la connectivité réseau dans le contexte réseau du nœud :

    Fenêtre de terminal
    kubectl debug node/<node-name> -it --image=busybox:1.37
    # Une fois dans le shell du Pod de debug :
    nslookup mon-registre.example.com
    wget -q --spider https://mon-registre.example.com/v2/ && echo "OK" || echo "INACCESSIBLE"
  4. Corriger et vérifier

    Selon la cause identifiée :

    CauseAction corrective
    Tag incorrectCorriger le tag dans le manifeste (vérifier sur le registre)
    Registre privé sans authCréer un imagePullSecret et le référencer dans le pod
    Secret invalideRecréer le secret avec les bons credentials
    Registre inaccessibleVérifier le DNS, le firewall, le proxy du nœud
    Rate limit Docker HubAuthentifier les pulls ou utiliser un mirror
    Image ou tag suppriméCorriger le tag ou rebuilder/repusher l’image
    Architecture incompatibleRebuilder l’image pour l’architecture cible ou utiliser un manifest multi-arch

    Après correction, le pod retente automatiquement le pull. Si vous souhaitez forcer un re-pull immédiat :

    Fenêtre de terminal
    kubectl delete pod <pod> -n <namespace>

    Le contrôleur (Deployment, StatefulSet…) recréera un pod qui tentera le pull avec les nouveaux paramètres.

L’une des causes les plus courantes est l’absence de credentials pour un registre privé. Voici la procédure complète :

Fenêtre de terminal
kubectl create secret docker-registry mon-registre-secret \
--docker-server=mon-registre.example.com \
--docker-username=mon-user \
--docker-password=mon-token \
--docker-email=admin@example.com \
-n <namespace>
apiVersion: v1
kind: Pod
metadata:
name: app-privee
spec:
imagePullSecrets:
- name: mon-registre-secret
containers:
- name: app
image: mon-registre.example.com/app:v2.1

Pour éviter d’ajouter imagePullSecrets dans chaque pod, associez le secret au ServiceAccount par défaut du namespace :

Fenêtre de terminal
kubectl patch serviceaccount default -n <namespace> \
-p '{"imagePullSecrets": [{"name": "mon-registre-secret"}]}'

Tous les pods du namespace utilisant le ServiceAccount default hériteront automatiquement du secret.

Exemples pratiques : reproduire un ImagePullBackOff

Section intitulée « Exemples pratiques : reproduire un ImagePullBackOff »
Fenêtre de terminal
kubectl apply -f- <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pull-bad-tag
namespace: default
spec:
containers:
- name: app
image: nginx:version-inexistante
EOF

Diagnostic :

Fenêtre de terminal
kubectl describe pod pull-bad-tag | tail -10

Vous verrez Failed to pull image "nginx:version-inexistante" avec not found.

Fenêtre de terminal
kubectl apply -f- <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pull-no-auth
namespace: default
spec:
containers:
- name: app
image: mon-registre-prive.example.com/app:latest
EOF

Diagnostic :

Fenêtre de terminal
kubectl describe pod pull-no-auth | tail -10

Le message indiquera unauthorized ou authentication required.

Fenêtre de terminal
kubectl apply -f- <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pull-bad-name
namespace: default
spec:
containers:
- name: app
image: "nginx ::latest"
EOF

Diagnostic :

Fenêtre de terminal
kubectl describe pod pull-bad-name | tail -10

Le message indiquera une erreur de résolution de référence avec un nom d’image invalide.

Fenêtre de terminal
kubectl delete pod pull-bad-tag pull-no-auth pull-bad-name --force --grace-period=0 2>/dev/null

Si certains pods démarrent normalement et d’autres pas, le problème est probablement local au nœud :

  • Le nœud n’a pas de connectivité réseau vers le registre (proxy manquant, DNS défaillant)
  • Le disque du nœud est plein (plus de place pour stocker les layers)
  • Le runtime de conteneur a un problème de configuration (containerd, CRI-O)

Ouvrez une session de debug sur le nœud affecté avec kubectl debug node/<node-name> puis vérifiez la résolution DNS, la connectivité réseau vers le registre et l’état du runtime CRI. Pour l’inspection du runtime, utilisez crictl :

Fenêtre de terminal
# Session de debug interactive sur le nœud
kubectl debug node/<node-name> -it --image=busybox:1.37
# Depuis le pod de debug, testez la connectivité
nslookup mon-registre.example.com
Fenêtre de terminal
# Sur le nœud (SSH ou debug), inspecter le runtime
crictl info
crictl images | grep mon-image

Docker Hub applique des limites de pull sur une fenêtre glissante de 6 heures. Les usages non authentifiés et certains comptes non payants peuvent être limités, tandis que les abonnements payants n’ont pas de limite de pull documentée. Dans un cluster avec plusieurs nœuds partageant la même IP publique, ces limites sont vite atteintes.

Le message d’erreur contient toomanyrequests :

toomanyrequests: You have reached your pull rate limit.
You may increase the limit by authenticating and upgrading:
https://www.docker.com/increase-rate-limit

Solutions :

  • Authentifier les pulls avec un compte Docker Hub pour bénéficier d’une limite plus élevée
  • Mettre en place un registry mirror (Harbor, Nexus, registry:2)
  • Utiliser imagePullPolicy: IfNotPresent pour éviter les pulls inutiles

Si un upgrade de cluster change le runtime de conteneur (par exemple de Docker à containerd), le cache d’images local est peut-être perdu. Tous les pods doivent re-puller leurs images, ce qui peut provoquer des ImagePullBackOff simultanés si le registre ou le réseau ne tient pas la charge.

SymptômePremière actionCommande
ImagePullBackOff + not foundVérifier le tagkubectl describe pod <pod> → chercher le nom exact de l’image
ImagePullBackOff + unauthorizedVérifier l’imagePullSecretkubectl get pod <pod> -o jsonpath='{.spec.imagePullSecrets}'
ImagePullBackOff + i/o timeoutProblème réseauTester la connectivité depuis le nœud vers le registre
ImagePullBackOff + toomanyrequestsRate limit Docker HubAuthentifier les pulls ou mettre en place un mirror
ErrImageNeverPullimagePullPolicy: Never mais image absenteChanger la policy ou pré-charger l’image sur le nœud
Un seul nœud en erreurProblème local au nœudVérifier disque, DNS, proxy et runtime du nœud
  • ErrImagePull est l’erreur immédiate, ImagePullBackOff est l’attente entre deux tentatives — les deux apparaissent en alternance
  • kubectl describe pod est la commande clé — le message d’erreur dans les Events contient la raison exacte de l’échec
  • Le tag :latest est un piège : s’il est supprimé du registre, les nouveaux pods échouent alors que les anciens continuent de fonctionner. Utilisez des tags explicites
  • Pour un registre privé, il faut un imagePullSecret dans le même namespace que le pod — pensez à l’associer au ServiceAccount pour ne pas l’oublier
  • Docker Hub a un rate limit sur une fenêtre de 6 h — authentifiez vos pulls ou utilisez un mirror local en production
  • Si le problème ne touche qu’un seul nœud, c’est probablement un souci réseau, disque ou runtime local
  • Après correction, le kubelet retente le pull automatiquement — si vous voulez forcer un retry immédiat, supprimez le pod

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn