Comment réduire la taille des images Docker ?

On réduit la taille des images Docker en choisissant des bases légères, en ordonnant les couches pour profiter du cache, en utilisant des builds multi-étages et en nettoyant les dépendances. Je détaille 4 techniques concrètes et exemples pour gagner du temps au build et réduire des centaines de Mo.

Quelle image de base choisir

Je recommande de choisir par défaut une variante slim et de n’utiliser alpine que si vos dépendances sont compatibles avec musl; sinon préférer slim ou distroless pour la stabilité.

Les images « full », « slim » et « alpine » diffèrent par la distribution et les bibliothèques système embarquées.

FROM python:3.11

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

CMD [« python », « app.py »]

FROM python:3.11-slim

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

CMD [« python », « app.py »]

FROM python:3.11-alpine

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

CMD [« python », « app.py »]

Alpine est plus petite parce qu’elle utilise musl, une implémentation légère de la bibliothèque C (libc).

  • Explication de musl et risque : Musl est plus compact que glibc (GNU C Library), ce qui réduit la taille finale. Cependant, des extensions Python compilées en binaire (roues – wheels) ou des bindings C peuvent être incompatibles ou nécessiter une recompilation. Cela entraîne souvent l’ajout d’outils de compilation (gcc, make) qui annulent le gain de taille.
  • Pourquoi slim est un bon compromis : Slim conserve glibc, donc la majorité des paquets précompilés fonctionnent sans ajustement. Cela réduit les surprises en production et facilite la portabilité entre environnements.
  • Quand envisager distroless ou scratch : Ces images minimalistes contiennent uniquement l’exécutable et ses bibliothèques nécessaires, sans shell ni package manager. Elles réduisent la surface d’attaque et la taille, mais compliquent le debugging et exigent des builds multi-stage pour inclure seulement ce qui est nécessaire.

Recommandations pratiques : Tester localement l’installation des dépendances et l’exécution des tests unitaires avec l’image cible. Préférer slim pour la majorité des applications Python. Documenter le choix dans le README et conserver un Dockerfile de build séparé si vous utilisez distroless.

Type Simplicité Taille relative Compatibilité binaire Cas d’usage
Full Très simple à utiliser Plus grande Élevée Développement rapide, démos
Slim Bon compromis Moyenne Élevée Applications de production
Alpine Plus d’efforts Plus petite Variable (musl) Images très légères si compatible
Distroless / Scratch Complexe (debug difficile) Minimale Selon ce que vous incluez Production sécurisée, microservices

Comment ordonner les couches pour profiter du cache

Ordonnez les instructions du Dockerfile du moins susceptible de changer au plus susceptible (dépendances avant le code) pour maximiser la réutilisation du cache.

Ordonner les couches maximise la réutilisation du cache de Docker: les couches inchangées sont réutilisées et le rebuild est beaucoup plus rapide. Le principe est simple et s’applique surtout aux projets Python. Une liste d’exemples de séquence à suivre est fournie ci‑dessous.

Avant la liste, une courte explication: Chaque instruction Dockerfile crée une couche. Si une couche change, toutes les couches suivantes sont reconstruites. Placer les parties stables (dépendances, variables d’environnement) avant le code permet de réutiliser les couches quand on modifie juste le code applicatif.

  • Règle générale d’ordre des couches: Mettre d’abord les variables et métadonnées (ENV, ARG), puis copier les fichiers de dépendances (COPY requirements.txt), ensuite installer les dépendances (RUN pip install …), et enfin copier le code de l’application (COPY . .).
  • Erreur fréquente: Copier tout le contexte (COPY . .) avant RUN pip install invalide le cache dès qu’un fichier change (même un README) et force la réinstallation des paquets, ce qui coûte beaucoup de temps à chaque rebuild.
  • Usage de .dockerignore: Exclure les fichiers inutiles du contexte pour éviter d’invalider le cache (exemples de patterns à exclure: .git, venv, __pycache__, node_modules, *.pyc).

Pour optimiser les RUN: Utiliser pip –no-cache-dir pour éviter de stocker les wheels en cache dans l’image. Pour apt: apt-get install –no-install-recommends et nettoyer apt avec rm -rf /var/lib/apt/lists/* et apt-get clean. Combiner plusieurs commandes RUN en une seule ligne avec && pour réduire le nombre de couches et supprimer les fichiers temporaires dans la même couche.

Exemple de séquence recommandée:

COPY requirements.txt /app/
RUN pip install --no-cache-dir -r /app/requirements.txt
COPY . /app

Pour contrôler le cache: Utiliser ARG pour forcer un bust du cache volontairement (ARG CACHEBUST=1 puis docker build –build-arg CACHEBUST=$(date +%s)). ARG signifie build argument et permet d’invalider une couche quand nécessaire (par exemple après mise à jour massive des dépendances). Invalider le cache est utile lors de mises à jour de dépendances ou pour forcer une réinstallation complète.

Erreur courante Impact sur le cache Correction recommandée
COPY . . avant pip install Réinstalle toujours les dépendances → rebuild lent Copier requirements.txt puis pip install, puis COPY .
Pas de .dockerignore Contexte trop grand et invalidations fréquentes Ajouter .git, venv, __pycache__, node_modules, *.pyc
Multiples RUN sans nettoyage Images plus lourdes, plus de couches Combiner RUN, nettoyer caches (pip –no-cache-dir, rm -rf /var/lib/apt/lists/*)

Comment tirer parti des builds multi-étages

Utilisez un stage builder pour compiler/installer toutes les dépendances de build puis copiez uniquement les artefacts nécessaires dans une image finale propre et minimale.

Concept de base : Séparer la construction (builder) et l’exécution (runtime). Le stage builder contient tous les outils lourds (compilateurs, headers, pip wheel) nécessaires pour construire des extensions natives (wheels). Le stage runtime contient uniquement l’interpréteur Python et les artefacts compilés. Cette séparation réduit la taille, la surface d’attaque et le temps de push/pull.

Étapes pratiques pour un projet Python avec dépendances compilées :

  • Installer les outils de build uniquement dans le builder : Utiliser apt-get install –no-install-recommends build-essential gcc python3-dev et nettoyer immédiatement avec rm -rf /var/lib/apt/lists/* pour limiter l’image.
  • Construire des wheels : Utiliser pip wheel –no-deps -w /app/dist -r requirements.txt pour générer des wheels locaux évitant la compilation dans l’image finale. « Wheel » est un format binaire Python permettant une installation rapide sans compilation.
  • Transférer les dépendances : Trois stratégies possibles suivant vos contraintes :
    • Copier l’environnement virtuel complet (venv) du builder vers le runtime. Simple mais peut inclure fichiers inutiles.
    • Générer des wheels puis installer dans l’image finale avec pip install –no-deps /app/dist/*.whl –no-cache-dir. Méthode recommandée pour gérer les extensions natives proprement.
    • Utiliser pip install –target /app/lib -r requirements.txt dans le builder puis copier /app/lib vers le runtime. Utile si vous souhaitez isoler l’installation hors site-packages standard.
FROM python:3.11-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends build-essential gcc python3-dev git \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY pyproject.toml requirements.txt setup.cfg ./
COPY src/ ./src
RUN python -m pip install --upgrade pip setuptools wheel
RUN pip wheel --no-deps -w /app/dist -r requirements.txt

FROM python:3.11-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/dist /app/dist
RUN python -m pip install --no-deps --no-cache-dir /app/dist/*.whl
COPY --from=builder /app/src /app/src
ENV PYTHONPATH=/app
CMD ["python", "src/main.py"]

Commandes clés pour réduire l’empreinte : Toujours utiliser –no-install-recommends, nettoyer /var/lib/apt/lists/* après apt-get, et pip –no-cache-dir pour éviter les caches pip.

Étape Contenu Avantage
Builder Outils de compilation, sources, wheels (/app/dist) Gain de taille notable et isolation de la chaîne de build
Runtime Interpréteur minimal + wheels/artefacts installés Meilleure sécurité, image plus petite, push/pull plus rapides

Quelles optimisations complémentaires appliquer

Combinez nettoyage des caches, réduction des couches, activation de BuildKit et usage d’un .dockerignore pour obtenir des builds plus rapides et des images plus petites.

Quelques phrases pour cadrer les choix avant la checklist. BuildKit est le moteur de build récent de Docker qui active un meilleur parallélisme, un cache plus fin et des builds plus déterministes (voir https://docs.docker.com/develop/develop-images/build_enhancements/). Le fichier .dockerignore empêche d’envoyer des fichiers inutiles au démon Docker, ce qui réduit l’I/O et le contexte de build.

Avant la checklist, une brève explication utile. Fusionner des RUN réduit le nombre de couches (chaque instruction crée une couche). Le cache de build évite de tout reconstruire si rien n’a changé. Distroless et scratch retirent le système d’exploitation inutile pour gagner en taille, au prix d’outils de debug.

Checklist d’optimisations complémentaires (chaque item est suivi d’un exemple pratique ou d’une commande) :

  • Fusionner les RUN pour limiter les couches et supprimer les fichiers temporaires dans la même instruction. Exemple : combiner apt-get, cleans et rm -rf dans une seule RUN.
  • Nettoyage apt et pip. Utiliser rm -rf /var/lib/apt/lists/* et pip –no-cache-dir pour éviter de stocker les fichiers temporaires.
  • Utiliser –no-install-recommends pour apt-get afin d’éviter d’installer des paquets optionnels lourds.
  • Activer Docker BuildKit (export DOCKER_BUILDKIT=1) et utiliser le cache avec docker build –cache-from ou docker buildx build –cache-to/–cache-from pour accélérer CI.
  • Utiliser .dockerignore pour exclure les binaires, assets lourds, dossiers node_modules locaux, fichiers .git, etc.
  • Réduire la taille des artefacts copiés en ne faisant COPY que du répertoire de production (par ex. build/ ou dist/), pas du repo complet.
  • Sécurité et maintenance : préférer images patchées régulièrement, ne jamais stocker de secrets dans l’image (utiliser secrets manager), et scanner les images (Trivy, Clair).
  • Quand envisager distroless ou scratch : pour les binaires statiques Go/ Rust ou apps avec peu de dépendances runtime; gains de taille majeurs mais moins d’outils de debug.

Exemples de commandes :

export DOCKER_BUILDKIT=1
docker build -t monimage:latest .
docker buildx build --platform linux/amd64 \
  --cache-to=type=local,dest=./.buildx-cache \
  --cache-from=type=local,src=./.buildx-cache \
  -t monimage:latest .
RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential libssl-dev && \
    rm -rf /var/lib/apt/lists/* && \
    pip install --no-cache-dir -r requirements.txt && \
    rm -rf /root/.cache/pip
Optimisation Impact attendu (taille / vitesse) Risque / Complexité
Fusionner RUN Réduction de couches → tailles -5% à -30% Faible / simple
Nettoyage apt / pip Supprime caches → tailles -10% à -40% Faible / simple
–no-install-recommends Évite dépendances inutiles → tailles -5% à -50% Faible / vérifier fonctionnalités
BuildKit + cache Builds 2x+ plus rapides en pratique (dépend du projet) Moyen / config CI
.dockerignore Réduit contexte → builds plus rapides, moins d’I/O Faible / simple
Copier moins d’artefacts Taille fortement réduite selon contenu Moyen / attention chemins
Sécurité & maintenance Meilleure sécurité, moindre dette technique Moyen / process à maintenir
Distroless / scratch Gains majeurs (souvent -50%+) Élevé / debugging limité

Prêt à alléger et accélérer vos images Docker ?

En appliquant ces bonnes pratiques — choisir une base adaptée, ordonner les couches pour tirer parti du cache, isoler les outils de build via des builds multi-étages et nettoyer systématiquement les dépendances — vous réduisez souvent plusieurs centaines de mégaoctets et transformez des reconstructions longues en builds réactifs. Le bénéfice direct : déploiements plus rapides, économies de bande passante et CI plus agile pour votre équipe et votre business.

FAQ

  • Quelle différence entre slim et alpine pour Python ?
    Slim est basé sur Debian avec glibc : plus compatible et stable pour la plupart des paquets Python. Alpine est plus petite grâce à musl mais peut causer des problèmes de compilation ou d’incompatibilité binaire. Testez vos dépendances avant d’adopter alpine.
  • Pourquoi ordonner les couches dans le Dockerfile ?
    Docker met en cache chaque instruction. En plaçant les étapes les moins sujettes à changement (dépendances) avant le code application, on réutilise le cache et on évite de réinstaller les dépendances à chaque modification de code.
  • Quand utiliser un build multi-étages ?
    Quand vous avez besoin d’outils de compilation ou de dépendances lourdes au build (gcc, build-essential) mais que vous ne voulez pas les embarquer dans l’image finale. Le multi-stage permet d’inclure ces outils seulement pendant la compilation et de livrer une image propre.
  • Que fait Docker BuildKit et pourquoi l’activer ?
    BuildKit accélère les builds, permet un meilleur parallélisme, un cache plus fin et des fonctionnalités avancées (cache export/import, secrets en build). Activez-le (DOCKER_BUILDKIT=1) pour des builds CI/CD plus rapides et efficaces.
  • Comment réduire rapidement la taille d’une image existante ?
    Commencez par identifier les couches les plus lourdes (docker history), retirez les outils de build via un Dockerfile multi-étages, nettoyez caches apt/pip, utilisez une base plus légère et ajoutez un .dockerignore pour éviter d’envoyer des fichiers inutiles au daemon.

 

 

A propos de l’auteur

Franck Scandolera — expert & formateur en tracking server-side, Analytics Engineering, automatisation No/Low Code (n8n) et intégration de l’IA en entreprise. Responsable de l’agence webAnalyste et de l’organisme de formation Formations Analytics. Références : Logis Hôtel, Yelloh Village, BazarChic, Fédération Française de Football, Texdecor. Dispo pour aider les entreprises => contactez-moi.

Retour en haut