E-NO
DevOps 10 min de lecture

Architecture des images Docker : guide pratique avec exemples réels

calendar_today Publié : 2026-10-05
update Dernière mise à jour : 2026-10-05
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Architecture des images Docker : guide pratique avec exemples réels ».

Introduction

Travailler avec des conteneurs sans comprendre l'architecture des images Docker, c'est comme piloter un avion sans instruments. Vous resterez peut-être en l'air un moment, mais tôt ou tard, vous percutez une montagne invisible. Les images ne sont pas des blocs opaques ; elles sont composées de couches, mises en cache et reconstruites selon des règles que vous pouvez contrôler. Ce guide explique comment les images Docker sont construites, stockées et utilisées en pratique. Vous apprendrez à inspecter les couches, à utiliser efficacement le cache de construction, à réduire la taille des images et à éviter les erreurs courantes qui provoquent des builds gonflés, des déploiements cassés ou des environnements non reproductibles.

Nous aborderons des commandes concrètes, des exemples réels de Dockerfile et des flux de travail de débogage. À la fin, vous saurez diagnostiquer pourquoi un build a utilisé le cache ou l'a manqué, voir exactement ce qui a changé entre deux versions d'image et concevoir des images rapides à construire, légères à distribuer et sûres à exécuter en production.

Inventaire de la version et de l'environnement

Avant de modifier une image ou un conteneur en cours d'exécution, capturez l'état exact de votre environnement. Exécutez d'abord ces commandes en lecture seule :

# Version de Docker et détails de l'API
docker version

# Informations détaillées à l'échelle du système : pilote de stockage, répertoire racine, options de sécurité
docker system info

# Liste des conteneurs en cours d'exécution dans un tableau propre
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"

Pour un conteneur spécifique, inspectez ses métadonnées complètes, y compris l'ID de l'image, les montages et les variables d'environnement :

docker inspect <nom_du_conteneur>

Si vous utilisez Docker Compose, vérifiez l'état du projet et la santé des services avec :

docker compose ps
docker compose logs --tail=50 <nom_du_service>

Détails clés à enregistrer : la version du moteur Docker, le pilote de stockage (overlay2, etc.), l'ID et le condensat de l'image pour chaque image déployée, et l'état de santé actuel du conteneur. Si vous dépannez un problème de données, confirmez où les fichiers sont stockés avant de toucher quoi que ce soit. Un volume nommé tel que app_data:/var/lib/app est géré par Docker et réutilisé lors des reconstructions de conteneurs. Un montage de type bind comme ./data:/var/lib/app mappe directement un répertoire hôte et est utile en développement, mais il peut entraîner des problèmes de permissions ou de portabilité si le même chemin n'existe pas sur une autre machine.

Effectuez un test de redémarrage pour vérifier la persistance des données : arrêtez le conteneur, recréez-le et confirmez que l'application voit toujours les fichiers attendus. Si les données disparaissent, l'application écrivait dans la couche inscriptible du conteneur au lieu d'un volume ou d'un montage. Exemple :

# Arrêter et supprimer le conteneur
docker rm -f mon_app
# Le recréer à partir de la même image
docker run -d --name mon_app -v app_data:/var/lib/app mon_image:latest
# Vérifier les journaux pour les erreurs d'accès aux fichiers
docker logs mon_app --tail 20

Comment les images Docker sont construites

Une image Docker est un modèle en lecture seule composé d'un empilement de couches. Chaque instruction d'un Dockerfile crée une nouvelle couche par-dessus la précédente. L'image finale est la combinaison de toutes les couches, et les conteneurs ajoutent une couche inscriptible par-dessus au moment de l'exécution. Cette conception en couches rend les images Docker efficaces : les couches peuvent être partagées entre les images et réutilisées lors des builds.

Le système de fichiers en couches

Examinons un Dockerfile typique :

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY . /app
RUN pip install -r /app/requirements.txt
CMD ["python3", "/app/app.py"]

Chaque ligne crée une couche distincte :

  1. FROM ubuntu:22.04 récupère les couches de l'image de base.
  2. RUN apt-get update && apt-get install -y python3 crée une nouvelle couche avec les paquets installés.
  3. COPY . /app copie le répertoire courant dans l'image, créant une autre couche.
  4. RUN pip install -r /app/requirements.txt installe les dépendances Python dans une nouvelle couche.
  5. CMD est une métadonnée qui ne crée pas de couche mais est stockée dans la configuration de l'image.

Vous pouvez afficher les couches d'une image avec :

docker history mon_image:latest

Exemple de sortie :

IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
f1a2b3c4d5e6   2 hours ago   CMD ["python3", "/app/app.py"]               0B        buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c pip install -r /app/require...   45MB      buildkit.dockerfile.v0
<missing>      2 hours ago   COPY . /app # buildkit                          1.2MB     buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c apt-get update && apt-get i...   220MB     buildkit.dockerfile.v0
<missing>      2 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      2 weeks ago   /bin/sh -c #(nop) ADD file:5c3a9d... in /       72.8MB

Notez la colonne SIZE : chaque couche contribue à la taille totale de l'image. Les couches volumineuses proviennent souvent de l'installation de paquets ou de la copie de fichiers. La colonne CREATED BY montre la commande exacte (tronquée) qui a produit la couche.

Fonctionnement du cache de couches

Docker met en cache les couches pour accélérer les reconstructions. Lorsque vous exécutez docker build, Docker vérifie chaque instruction par rapport au cache. Si une instruction et ses couches parentes sont inchangées, Docker réutilise la couche en cache au lieu de la réexécuter. Cependant, si une couche change, toutes les couches suivantes sont reconstruites.

Cela a des implications importantes pour l'ordre des instructions dans un Dockerfile. Considérons deux Dockerfiles :

Ordre inefficace (les changements de code d'application invalident le cache des dépendances) :

FROM node:16
COPY . /app
RUN npm install
CMD ["node", "/app/index.js"]

Ordre efficace (les dépendances sont installées en premier) :

FROM node:16
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "index.js"]

Dans la première version, tout changement dans n'importe quel fichier de . invalide la couche COPY, donc npm install s'exécute à chaque fois. Dans la deuxième version, seuls les changements dans package.json ou package-lock.json invalident la couche npm install ; les changements dans d'autres fichiers ne déclenchent pas de réinstallation, ce qui rend les builds beaucoup plus rapides.

Question rapide 1 sur 2

D'après l'article, qu'est-ce qu'une image Docker ?

L'article indique : « Une image Docker est un modèle en lecture seule composé d'une pile de couches. »

Stratégies efficaces de cache de construction

Au-delà de l'ordre, plusieurs techniques peuvent maximiser les succès de cache.

Utiliser des builds multi-étapes

Les builds multi-étapes vous permettent d'utiliser des images intermédiaires pour la compilation ou l'installation des dépendances, puis de copier uniquement les artefacts nécessaires dans l'image finale. Cela réduit considérablement la taille de l'image finale et peut également améliorer la mise en cache.

Exemple pour une application Go :

# Étape 1 : construction
FROM golang:1.21 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server

# Étape 2 : exécution
FROM alpine:3.18
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /usr/local/bin/server
ENTRYPOINT ["server"]

L'image finale ne contient que le binaire compilé et les dépendances d'exécution minimales, réduisant souvent la taille de plusieurs centaines de mégaoctets à quelques mégaoctets. Les caches de l'étape de construction sont toujours utilisés pendant les builds, mais l'image finale n'inclut pas la chaîne d'outils Go ni le code source.

Épingler les versions des images de base

L'utilisation de l'étiquette latest peut conduire à des builds non reproductibles car l'image de base peut changer entre deux builds sans aucun changement dans votre Dockerfile. Épinglez l'image de base à un condensat ou à une étiquette de version spécifique :

# Épingler par étiquette
FROM node:16.20.2-alpine3.18

# Ou épingler par condensat pour une reproductibilité maximale
FROM node@sha256:3b3c8e31a8a1b6c9e1e414f2b8f1b3cd0e0f8a2b6c9e1e414f2b8f1b3cd0e0f

L'utilisation d'un condensat garantit que l'image de base est exactement la même à chaque build, ce qui est crucial pour les builds de production et les audits de sécurité.

Séparer l'installation des dépendances de la copie du code

Comme indiqué ci-dessus, copiez d'abord uniquement le manifeste des dépendances, installez les dépendances, puis copiez le reste du code. Ce modèle s'applique à la plupart des langages :

  • Python : copier requirements.txt, exécuter pip install, puis copier le code.
  • Node.js : copier package.json et package-lock.json, exécuter npm install, puis copier le code.
  • Java (Maven) : copier pom.xml, exécuter mvn dependency:go-offline, puis copier le code source.
  • Ruby : copier Gemfile et Gemfile.lock, exécuter bundle install, puis copier le code.

Éviter les contextes de construction volumineux

Le contexte de construction est l'ensemble des fichiers envoyés au démon Docker lorsque vous exécutez docker build. Si vous avez un dépôt énorme ou si vous incluez des fichiers inutiles, le contexte de construction peut être massif, ralentissant les builds, surtout avec des démons BuildKit distants. Utilisez un fichier .dockerignore pour exclure les fichiers non pertinents :

.git
*.log
node_modules
.DS_Store
*.md
docker-compose.yml

Vous pouvez vérifier la taille du contexte de construction en exécutant :

docker build --no-cache -t test . 2>&1 | grep "Sending build context"

Inspection et gestion des images

Savoir inspecter les images est essentiel pour le débogage et l'optimisation.

Afficher l'historique et les couches d'une image

docker history montre la commande qui a créé chaque couche et sa taille. Pour une inspection plus détaillée, utilisez docker inspect :

docker inspect mon_image:latest

Cela produit un document JSON avec des métadonnées, y compris l'architecture de l'image, les condensats des couches, la configuration, etc. Pour voir uniquement les IDs des couches, analysez la sortie :

docker inspect mon_image:latest | jq '.[0].RootFS.Layers'

Exemple :

[
  "sha256:9f3c1a2b...",
  "sha256:2d9e8f7c...",
  "sha256:..."
]

Trouver ce qui a changé entre deux images

Utilisez docker diff sur un conteneur pour voir les modifications apportées à son système de fichiers depuis son démarrage. Cela aide à identifier ce qu'une application écrit à l'exécution.

# Démarrer un conteneur
docker run -d --name test mon_image:latest
# Faire quelques modifications dans le conteneur
docker exec test touch /fichier_nouveau
# Voir le diff
docker diff test

Sortie :

C /fichier_nouveau
A /var/log/monapp.log
C /etc/config.ini

A signifie ajouté, C signifie modifié, D signifie supprimé. Ces informations vous aident à décider quels fichiers doivent être persistés dans des volumes ou inclus dans une nouvelle image.

Utiliser les condensats d'image

Chaque image a un condensat, qui est un hachage unique de son contenu et de ses métadonnées. Utilisez les condensats pour référencer les images sans ambiguïté :

# Obtenir le condensat de l'image actuelle
docker inspect --format='{{index .RepoDigests 0}}' mon_image:latest
# Sortie : monrepo/mon_image@sha256:a1b2c3...

# Récupérer par condensat
docker pull monrepo/mon_image@sha256:a1b2c3...

L'utilisation de condensats dans les manifestes de déploiement garantit que vous déployez exactement l'image testée, et non une version ultérieure qui a écrasé la même étiquette.

Optimisation de la taille et de la sécurité des images

Les images volumineuses sont plus lentes à déployer et présentent une surface d'attaque plus grande. Voici des moyens pratiques de réduire la taille et d'améliorer la sécurité.

Minimiser les couches et combiner les commandes

Chaque commande RUN crée une nouvelle couche. Combinez les commandes liées dans un seul RUN avec && pour réduire le nombre de couches, et nettoyez les fichiers temporaires dans la même couche :

RUN apt-get update && apt-get install -y --no-install-recommends \
    python3 \
    python3-pip \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*

L'utilisation de --no-install-recommends évite d'installer des paquets inutiles, et le nettoyage des listes de paquets économise des dizaines de mégaoctets.

Utiliser des images de base distroless ou allégées

Remplacez une image de base d'un système d'exploitation complet par une variante allégée ou une image distroless qui ne contient que le runtime et votre application.

  • python:3.11-alpine vs python:3.11
  • node:18-alpine vs node:18
  • gcr.io/distroless/base-debian11 pour les binaires statiques

Exemple pour un petit service Python :

FROM python:3.11-alpine
COPY . /app
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "app.py"]

La variante Alpine fait généralement environ 50 Mo contre 900 Mo pour l'image complète basée sur Debian.

Supprimer les fichiers inutiles et les secrets

N'intégrez jamais de secrets comme des clés API ou des mots de passe dans les images. Même si vous les supprimez dans une couche ultérieure, ils restent dans les couches précédentes et peuvent être extraits. Utilisez plutôt les secrets Docker ou les variables d'environnement à l'exécution.

Vérifiez les fichiers sensibles copiés accidentellement avec des outils comme docker scan ou trivy :

trivy image mon_image:latest

Utilisez également .dockerignore pour empêcher la copie de .env, .git et d'autres fichiers sensibles.

Question rapide 2 sur 2

Quelle instruction Dockerfile de l'exemple ne crée PAS de nouvelle couche ?

L'article mentionne que CMD est une métadonnée qui ne crée pas de couche mais qui est stockée dans la configuration de l'image.

Débogage pratique des builds

Lorsqu'un build échoue ou se comporte de manière inattendue, utilisez ces commandes pour diagnostiquer.

Activer la sortie BuildKit

Par défaut, les versions récentes de Docker utilisent BuildKit, qui fournit une progression et des informations de mise en cache plus détaillées. S'il n'est pas activé, définissez DOCKER_BUILDKIT=1.

Pour voir pourquoi une couche particulière n'a pas été mise en cache, exécutez le build avec --progress=plain et inspectez l'état du cache pour chaque étape :

DOCKER_BUILDKIT=1 docker build --progress=plain -t mon_image . 2>&1 | grep -E "CACHED|RUN"

Exemple de sortie :

#7 [3/5] RUN npm install
#7 CACHED
#8 [4/5] COPY . .
#8 DONE 0.0s
#9 [5/5] RUN npm run build
#9 DONE 12.5s

La ligne CACHED indique que la couche a été réutilisée depuis le cache.

Tester une couche sans reconstruire toute l'image

Si une commande échoue dans une étape RUN, vous pouvez démarrer un conteneur à partir de la dernière couche réussie et exécuter la commande manuellement pour déboguer. Trouvez l'ID de l'image de la dernière couche réussie à partir de la sortie du build ou utilisez docker history.

# Trouver l'ID de l'image de la dernière couche réussie
docker history mon_image:latest | head -3
# Démarrer un shell dans cette couche
docker run -it <id_image> sh

Reproduisez ensuite la commande en échec et inspectez l'environnement.

Utiliser des builds multi-architectures

Si vous avez besoin d'images pour différentes architectures de processeur (par exemple, amd64 et arm64), utilisez buildx :

docker buildx create --name monbuilder --use
docker buildx build --platform linux/amd64,linux/arm64 -t monrepo/mon_image:latest --push .

Cela construit et pousse une liste de manifestes contenant les deux architectures, permettant à Docker de récupérer automatiquement la bonne.

Pièges courants et comment les éviter

1. Utiliser excessivement l'étiquette latest

Utiliser latest peut mener à l'imprévisibilité : vous ne savez jamais exactement quelle version vous exécutez, et les retours en arrière sont impossibles. Utilisez toujours des étiquettes de version explicites ou des condensats en production. Pour le développement local, définissez également une étiquette spécifique pour éviter les surprises.

Comment s'en remettre : Étiquetez les images avec une version ou un hachage de commit, par exemple monapp:1.4.2, et utilisez un pipeline CI/CD pour mettre à jour l'étiquette lorsqu'une nouvelle version est construite.

2. Placer les fichiers changeant fréquemment tôt dans le Dockerfile

Si vous copiez tout le code de l'application avant d'installer les dépendances, chaque modification de code invalide le cache de la couche de dépendances, rendant les builds lents et inefficaces.

Comment éviter : Séparez toujours l'installation des dépendances de la copie du code. Placez les opérations stables en premier, et mettez le COPY le plus fréquemment modifié près de la fin du Dockerfile.

3. Exécuter en tant que root dans le conteneur

De nombreux Dockerfiles ne spécifient pas de USER, donc le conteneur s'exécute en tant que root par défaut. Cela augmente le risque de sécurité : si un attaquant compromet l'application, il dispose des privilèges root dans le conteneur, ce qui peut parfois mener à une compromission de l'hôte.

Comment éviter : Créez un utilisateur non root et basculez vers lui :

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

Assurez-vous également que l'application n'a pas besoin de root pour se lier à des ports inférieurs à 1024 ou pour modifier des fichiers système.

4. Laisser des outils de débogage et des secrets dans l'image

Inclure des débogueurs, du code source ou des secrets dans les images de production est un risque de sécurité et gaspille de l'espace.

Comment éviter : Utilisez des builds multi-étapes pour ne conserver que les artefacts d'exécution. Utilisez .dockerignore pour exclure les fichiers sensibles. Ne codez jamais en dur des secrets ; utilisez des variables d'environnement ou des secrets Docker montés à l'exécution.

5. Ne pas nettoyer après l'installation de paquets

Les gestionnaires de paquets laissent des caches et des fichiers temporaires qui gonflent les images. Par exemple, apt-get install laisse /var/lib/apt/lists/* et éventuellement des fichiers .deb.

Comment éviter : Nettoyez dans la même couche RUN :

RUN apt-get update && apt-get install -y --no-install-recommends \
    un-paquet \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*

Pour Python, utilisez pip install --no-cache-dir ; pour Node, utilisez npm install --production ou npm ci --only=production.

Liste de contrôle opérationnelle pour la gestion des images

Utilisez cette liste de contrôle avant de pousser une image vers un registre ou de la déployer en production. Attribuez un responsable (par exemple, le développeur principal ou l'ingénieur DevOps) pour chaque élément, et révisez la liste mensuellement ou à chaque version.

ÉlémentCommande / VérificationRésultat attenduResponsable
L'image se construit avec succèsdocker build -t monapp:test .Code de sortie 0, aucune erreur dans la sortieDéveloppeur principal
La taille de l'image est acceptabledocker image inspect monapp:test --format='{{.Size}}'Moins de 200 Mo pour les microservices, ajuster le seuilIngénieur DevOps
Aucun secret dans l'imagetrivy image monapp:test ou docker scan monapp:testAucune vulnérabilité de haute gravité ni secret exposéÉquipe de sécurité
Utilisateur non root spécifiédocker inspect monapp:test --format='{{.Config.User}}'Non vide et différent de "root"Développeur principal
Condensat de l'image de base épingléLa ligne FROM du Dockerfile contient @sha256:...Condensat présent, pas seulement une étiquetteIngénieur DevOps
Le conteneur s'exécute et est saindocker run -d --name test monapp:test && docker ps --filter name=testLe conteneur démarre, le statut affiche Up (sain si une vérification de santé est définie)Ingénieur QA
Les données persistent après redémarragedocker rm -f test && docker run -d --name test monapp:test et vérifier les donnéesLes fichiers attendus sont toujours présentsIngénieur DevOps

Chaque élément doit être vérifié dans un environnement de préproduction avant la production. Consignez les résultats dans vos notes de version.

Conclusion

Comprendre l'architecture des images Docker transforme la conteneurisation d'une boîte noire en un processus contrôlé et prévisible. En maîtrisant les couches, la mise en cache et les stratégies de construction, vous pouvez réduire considérablement les temps de build, diminuer la taille des images, améliorer la sécurité et éviter les pièges les plus courants qui affectent les déploiements de conteneurs.

Commencez par appliquer une amélioration de ce guide : réorganisez un Dockerfile pour séparer l'installation des dépendances de la copie du code, ou épinglez un condensat d'image de base. Mesurez l'impact avec docker history et docker inspect. Au fur et à mesure que vous gagnez en confiance, adoptez les builds multi-étapes, l'analyse d'images et une liste de contrôle de révision.

Rappelez-vous que chaque image que vous construisez est un contrat : elle définit exactement ce qui s'exécutera en production. Une image bien conçue rend le reste de votre infrastructure plus simple et plus sûre.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO