Aller au contenu
Conteneurs & Orchestration medium

Init Containers et Sidecars : préparer et accompagner vos Pods

70 min de lecture

logo kubernetes

Votre application a besoin de télécharger une config avant de démarrer ? Ou de collecter ses logs vers un système centralisé ? Les init containers et les sidecars répondent à ces besoins sans modifier votre code applicatif.

Ce guide vous montre comment utiliser ces deux patterns, quand les choisir, et comment éviter les erreurs courantes.

Prérequis : concepts des Pods Kubernetes et un cluster fonctionnel.

  • Créer un init container qui prépare des données avant le démarrage
  • Configurer un sidecar qui tourne en parallèle de votre application
  • Distinguer le pattern sidecar du sidecar natif Kubernetes
  • Comprendre quand utiliser (et quand éviter) chaque approche
  • Diagnostiquer les problèmes avec les bonnes commandes

Avant d'entrer dans les détails, rappelons pourquoi init containers et sidecars sont utiles : tous les conteneurs d'un même Pod partagent :

RessourceCe qui est partagé
RéseauMême adresse IP, même espace réseau (localhost fonctionne entre conteneurs)
VolumesMêmes volumes montés selon la configuration
SchedulingMême nœud, même unité de déploiement
Cycle de vieDémarrent et s'arrêtent ensemble

C'est ce partage qui rend les patterns init container et sidecar possibles et puissants.

Un init container est un conteneur qui s'exécute avant les conteneurs principaux du Pod. Il doit se terminer avec succès pour que l'application démarre.

Les init containers ont un comportement strict :

  • Ils s'exécutent séquentiellement, dans l'ordre de déclaration
  • Chaque init container doit réussir (exit code 0) avant le suivant
  • En cas d'échec, le kubelet relance l'init container selon la restartPolicy
  • Les conteneurs applicatifs ne démarrent qu'après la réussite de tous les init containers
Cas d'usageExemple concret
Télécharger des fichiersRécupérer une config depuis S3 ou un dépôt Git
Attendre un serviceVérifier que MySQL répond avant de lancer l'app
Initialiser des donnéesAppliquer des migrations de base de données
Préparer le filesystemCréer des répertoires, décompresser des archives

Cet init container écrit un fichier de configuration que le conteneur principal utilise :

init-container-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: init-download
image: busybox:1.36
command: ['sh', '-c', 'echo "Config chargée à $(date)" > /data/config.txt && sleep 2']
volumeMounts:
- name: shared-data
mountPath: /data
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /data/config.txt && sleep 3600']
volumeMounts:
- name: shared-data
mountPath: /data
volumes:
- name: shared-data
emptyDir: {}
Fenêtre de terminal
kubectl apply -f init-container-demo.yaml
Fenêtre de terminal
kubectl get pod init-demo -w

Sortie pendant l'initialisation :

NAME READY STATUS RESTARTS AGE
init-demo 0/1 Init:0/1 0 2s
init-demo 0/1 PodInitializing 0 4s
init-demo 1/1 Running 0 5s

Le statut Init:0/1 indique que l'init container n'est pas encore terminé.

Fenêtre de terminal
kubectl logs init-demo -c app

Sortie attendue :

Config chargée à Sun Mar 22 08:44:45 UTC 2026

Le conteneur principal a bien lu le fichier créé par l'init container.

Un cas très courant est d'attendre qu'un service soit disponible :

init-wait-service.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z mysql 3306; do echo "Attente MySQL..."; sleep 2; done']
containers:
- name: app
image: nginx:1.31.3-alpine

L'init container boucle jusqu'à ce que MySQL réponde sur le port 3306.

Sans limite de temps, un init container peut bloquer indéfiniment :

command: ['sh', '-c', 'timeout 60 sh -c "until nc -z mysql 3306; do sleep 2; done"']

Il existe deux réalités qu'il faut distinguer :

Historiquement, un sidecar désigne simplement un second conteneur dans spec.containers qui accompagne le conteneur principal. C'est un pattern d'architecture, pas une fonctionnalité Kubernetes.

spec:
containers:
- name: app
image: myapp:1.0
- name: log-collector # Sidecar "classique"
image: fluent/fluent-bit:2.0@sha256:ed49fc97830a302c5ae8dfe6dc5758afa3d6bf4b4a172de21256292faf121e7c

Les deux conteneurs démarrent ensemble, sans ordre garanti.

Depuis Kubernetes v1.29, une fonctionnalité dédiée permet de déclarer des sidecars avec un cycle de vie contrôlé. Un sidecar natif est un init container avec restartPolicy: Always.

spec:
initContainers:
- name: log-agent
image: fluent/fluent-bit:2.0@sha256:ed49fc97830a302c5ae8dfe6dc5758afa3d6bf4b4a172de21256292faf121e7c
restartPolicy: Always # C'est un sidecar natif !

Les deux premières lignes du tableau expliquent à elles seules pourquoi le sidecar natif a été ajouté au projet. Avec le pattern classique, rien ne garantit que le proxy ou l'agent de logs soit opérationnel quand l'application émet sa première requête : le kubelet démarre tous les conteneurs de spec.containers en parallèle. La ligne Arrêt est tout aussi importante pour les traitements par lots : un log-collector déclaré dans spec.containers continue de tourner après la fin du conteneur principal, ce qui empêche un Job d'atteindre l'état Completed et le laisse en Running indéfiniment.

AspectPattern classiqueSidecar natif
Déclarationspec.containersspec.initContainers avec restartPolicy: Always
Ordre de démarragePas garantiDémarre avant le conteneur principal
ArrêtPas d'ordreS'arrête après le conteneur principal
RedémarragePeut crasher le PodRedémarre indépendamment

Le sidecar lit les logs écrits par l'application principale dans un fichier partagé :

sidecar-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'while true; do echo "$(date) - Requête traitée" >> /var/log/app.log; sleep 5; done']
volumeMounts:
- name: logs
mountPath: /var/log
- name: log-collector
image: busybox:1.36
command: ['sh', '-c', 'tail -f /var/log/app.log']
volumeMounts:
- name: logs
mountPath: /var/log
volumes:
- name: logs
emptyDir: {}
Fenêtre de terminal
kubectl apply -f sidecar-demo.yaml
Fenêtre de terminal
kubectl get pod sidecar-demo

Sortie :

NAME READY STATUS RESTARTS AGE
sidecar-demo 2/2 Running 0 15s

2/2 Running confirme que les deux conteneurs tournent.

Fenêtre de terminal
kubectl logs sidecar-demo -c log-collector

Sortie :

Sun Mar 22 08:45:07 UTC 2026 - Requête traitée
Sun Mar 22 08:45:12 UTC 2026 - Requête traitée

Les sidecars natifs offrent un meilleur contrôle du cycle de vie :

native-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
name: native-sidecar
spec:
initContainers:
- name: log-agent
image: busybox:1.36
restartPolicy: Always # Sidecar natif !
command: ['sh', '-c', 'tail -f /var/log/app.log 2>/dev/null || sleep infinity']
volumeMounts:
- name: logs
mountPath: /var/log
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'while true; do echo "$(date) - Log" >> /var/log/app.log; sleep 5; done']
volumeMounts:
- name: logs
mountPath: /var/log
volumes:
- name: logs
emptyDir: {}

Avantages du sidecar natif :

  • Démarre avant le conteneur principal (comme un init container)
  • Tourne pendant toute la vie du Pod (comme un sidecar classique)
  • S'arrête proprement après le conteneur principal
  • Peut redémarrer sans faire crasher le Pod entier

Après kubectl apply, kubectl get pod native-sidecar affiche bien 2/2 Running : le sidecar natif est compté dans les conteneurs prêts, alors qu'un init container ordinaire ne l'est pas. En revanche kubectl logs native-sidecar -c log-agent ne renvoie rien ici, et c'est précisément la démonstration du mécanisme : log-agent démarre avant le conteneur app, donc /var/log/app.log n'existe pas encore quand tail -f s'exécute, la commande bascule sur sleep infinity. Un vrai agent de logs gère ce cas (Fluent Bit surveille le répertoire et attend l'apparition du fichier).

Le critère de tri est la durée de vie du besoin, pas sa nature. Une tâche qui a une fin (télécharger un fichier, appliquer une migration) va dans un init container ; une tâche qui doit accompagner l'application tant qu'elle tourne (collecter, exporter, relayer) va dans un sidecar. La ligne « Attendre une dépendance » porte un ⚠️ volontaire : elle fonctionne aussi en sidecar, mais l'application démarrerait alors avant que la dépendance soit disponible, ce qui déplace le problème au lieu de le régler.

BesoinInit containerSidecar
Préparer des fichiers avant démarrage✅ Oui❌ Non
Attendre une dépendance✅ Oui (avec timeout)⚠️ Rarement
Collecter des logs pendant la vie du Pod❌ Non✅ Oui
Proxy réseau local au Pod❌ Non✅ Oui
Exécuter une migration ponctuelle✅ Oui❌ Non
Synchroniser des fichiers en continu❌ Non✅ Oui
Exporter des métriques❌ Non✅ Oui

Ces quatre familles couvrent la quasi-totalité des sidecars rencontrés en production. Le point commun qui les justifie est l'accès au contexte local du Pod : le localhost partagé pour un proxy Envoy, un volume emptyDir commun pour Fluent Bit ou git-sync. Si votre besoin ne réclame ni le réseau ni les volumes du Pod, la section suivante montre pourquoi un sidecar est alors le mauvais outil.

PatternExemple
Log collectorFluent Bit, Filebeat → Elasticsearch/Loki
ProxyEnvoy, Istio pour le service mesh
SyncGit-sync pour fichiers de config
MonitoringExporter des métriques Prometheus

Un sidecar n'est pas toujours la bonne solution :

SituationPourquoi éviterAlternative
Fonction mutualisableUn sidecar par Pod = N instancesDaemonSet (1 par nœud)
Besoin uniquement au buildPas besoin en runtimeJob CI/CD
Fonction indépendante du PodCouplage inutileDeployment séparé
Logs vers stdoutLe nœud collecte déjàAucun sidecar nécessaire
Proxy cluster-wideDuplication massiveIngress ou service mesh

Chaque conteneur supplémentaire consomme des ressources :

  • CPU et mémoire : l'init container allonge le démarrage, le sidecar consomme en continu
  • I/O disque : si le sidecar écrit beaucoup (logs, métriques)
  • Bande passante : si le sidecar envoie des données vers l'extérieur
initContainers:
- name: init-download
image: busybox:1.36
resources:
requests:
cpu: "50m"
memory: "32Mi"
limits:
cpu: "100m"
memory: "64Mi"

Déclarez aussi les resources de vos sidecars pour éviter les surprises en production.

Un Pod peut avoir les deux :

combined-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: full-stack
spec:
initContainers:
- name: download-config
image: busybox:1.36
command: ['sh', '-c', 'echo "DB_HOST=mysql" > /config/env']
volumeMounts:
- name: config
mountPath: /config
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /config/env && while true; do echo "$(date) - Request" >> /logs/app.log; sleep 5; done']
volumeMounts:
- name: config
mountPath: /config
- name: logs
mountPath: /logs
- name: log-shipper
image: busybox:1.36
command: ['sh', '-c', 'tail -f /logs/app.log']
volumeMounts:
- name: logs
mountPath: /logs
volumes:
- name: config
emptyDir: {}
- name: logs
emptyDir: {}

Flux d'exécution :

  1. download-config (init) prépare la configuration
  2. app et log-shipper démarrent ensemble
  3. log-shipper transmet les logs en continu

Sept erreurs récurrentes, classées de la plus fréquente à la plus subtile. Les deux premières lignes causent des incidents visibles et faciles à relier à leur origine. Les suivantes coûtent surtout du gaspillage de ressources et de la dette de maintenance, qui ne se remarquent qu'au moment où la facture ou la complexité deviennent gênantes. Relisez ce tableau après avoir écrit un manifeste multi-conteneurs, pas avant : plusieurs lignes ne parlent que si vous avez déjà le YAML sous les yeux.

Anti-patternConséquenceSolution
Attendre sans timeoutPod bloqué indéfinimentAjouter timeout à la commande
Logique métier lourde en initDémarrage très longExternaliser dans un Job
Port ouvert = service prêtFaux positifsTest fonctionnel léger
Sidecar pour fonction mutualisableGaspillage de ressourcesDaemonSet
chmod/chown en init containerSymptôme de mauvaise configUtiliser securityContext
Oublier resources sur les sidecarsConsommation non contrôléeDéclarer requests/limits
Plusieurs conteneurs = plus de couplageMaintenance complexeÉvaluer si vraiment nécessaire

Cinq commandes suffisent, à lancer dans cet ordre. La difficulté propre aux Pods multi-conteneurs est que kubectl logs mon-pod sans -c échoue ou choisit un conteneur au hasard : le drapeau -c devient obligatoire dès qu'il y a plus d'un conteneur. Notez aussi --previous à l'étape 3 : sans lui, vous lisez les logs de l'instance en cours, pas ceux de celle qui a planté, et un CrashLoopBackOff reste incompréhensible.

  1. Voir l'état du Pod et des conteneurs

    Fenêtre de terminal
    kubectl get pod mon-pod -o wide
    kubectl get pod mon-pod -o yaml | grep -A 30 "containerStatuses"
  2. Examiner les détails des init containers

    Fenêtre de terminal
    kubectl describe pod mon-pod | grep -A 30 "Init Containers"
  3. Lire les logs d'un init container

    Fenêtre de terminal
    kubectl logs mon-pod -c nom-init-container
    kubectl logs mon-pod -c nom-init-container --previous # Si redémarré
  4. Lire les logs d'un sidecar

    Fenêtre de terminal
    kubectl logs mon-pod -c nom-sidecar
  5. Voir les événements récents

    Fenêtre de terminal
    kubectl get events --sort-by=.lastTimestamp | grep mon-pod

La colonne « Symptôme » se lit dans la sortie de kubectl get pods, colonne STATUS. Le préfixe Init: est votre point de repère : il indique que le Pod est encore dans sa phase d'initialisation, et donc que l'application n'a même pas été lancée. Inutile de chercher du côté du conteneur applicatif tant que ce préfixe est présent. Le chiffre qui suit (Init:0/1) compte les init containers terminés sur le total déclaré.

SymptômeCause probableSolution
Init:0/1 qui persisteInit container bloqué ou en échecVérifier les logs : kubectl logs mon-pod -c init-xxx
Init:CrashLoopBackOffCommande qui échoue (exit code ≠ 0)Corriger la commande ou l'image
Init:ImagePullBackOffImage introuvableVérifier le nom et le registry
READY 1/2Un des conteneurs ne démarre paskubectl describe pod pour les événements
Sidecar natif qui ne tourne pasrestartPolicy: Always oubliéAjouter le champ dans l'init container

La section Events de kubectl describe pod raconte le démarrage dans l'ordre chronologique. L'élément le plus utile est le préfixe spec.initContainers{...} ou spec.containers{...} devant chaque message : il dit sans ambiguïté quel conteneur a produit l'événement, ce que la seule colonne Reason ne permet pas de deviner quand plusieurs conteneurs tirent la même image.

Fenêtre de terminal
kubectl describe pod init-demo
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12s default-scheduler Successfully assigned default/init-demo to k3d-doc-vague8-server-0
Normal Pulling 12s kubelet spec.initContainers{init-download}: Pulling image "busybox:1.36"
Normal Pulled 8s kubelet spec.initContainers{init-download}: Successfully pulled image "busybox:1.36" in 3.524s (3.524s including waiting). Image size: 2217006 bytes.
Normal Created 8s kubelet spec.initContainers{init-download}: Created container init-download
Normal Started 8s kubelet spec.initContainers{init-download}: Started container init-download
Normal Pulled 6s kubelet spec.containers{app}: Container image "busybox:1.36" already present on machine
Normal Created 6s kubelet spec.containers{app}: Created container app
Normal Started 6s kubelet spec.containers{app}: Started container app

Il n'existe pas d'événement signalant la fin réussie d'un init container : la réussite se déduit du fait que les événements passent aux spec.containers{...}. Un enchaînement qui s'arrête après le Started de l'init container signifie que celui-ci tourne encore, ou qu'il boucle.

En examen, voici le workflow efficace :

Fenêtre de terminal
# 1. Créer le manifest
kubectl run mypod --image=nginx --dry-run=client -o yaml > pod.yaml
# 2. Éditer pour ajouter initContainers (même niveau que containers)
vim pod.yaml
# 3. Appliquer
kubectl apply -f pod.yaml
# 4. Vérifier l'état
kubectl get pod mypod -w
# 5. Voir les logs de l'init container
kubectl logs mypod -c init-container-name
# 6. Voir les logs du conteneur principal
kubectl logs mypod -c nginx

Ce squelette est le plus rentable à mémoriser pour l'examen, parce qu'il condense les trois pièges d'indentation qui coûtent des points. initContainers est au même niveau que containers, tous deux sous spec : les décaler d'un cran est l'erreur la plus fréquente et elle passe le kubectl apply sans broncher avant d'échouer plus loin. La section volumes se place également sous spec, et chaque conteneur qui veut y accéder doit répéter son volumeMounts avec le même name. Un montage déclaré côté init mais oublié côté app donne un fichier préparé que l'application ne voit jamais.

apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
initContainers: # Même niveau que containers
- name: init-xxx
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
command: ['sh', '-c', 'echo done']
volumeMounts:
- name: data
mountPath: /data
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /data
volumes: # Partagé entre tous les conteneurs
- name: data
emptyDir: {}
  1. Init container : s'exécute et se termine AVANT le conteneur principal
  2. Sidecar classique : conteneur dans containers, tourne EN PARALLÈLE
  3. Sidecar natif : init container avec restartPolicy: Always (stable v1.33)
  4. Les init containers s'exécutent séquentiellement, chacun doit réussir
  5. Les init containers ne supportent pas les probes ni lifecycle hooks
  6. Un port TCP ouvert ne garantit pas qu'un service est prêt
  7. Les conteneurs d'un Pod partagent réseau et volumes
  8. Chaque conteneur supplémentaire consomme des ressources
  9. Un sidecar qui peut être mutualisé devrait être un DaemonSet
  10. Commandes clés : kubectl logs -c, kubectl describe, kubectl get events

Ce quiz reprend les distinctions qui piègent le plus : ordre de démarrage, différence entre pattern classique et sidecar natif, absence de probes sur les init containers. Comptez cinq minutes et visez 80 % avant de passer à la suite ; en dessous, relisez la section « Différences entre les deux approches », c'est là que se concentrent les confusions.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn