Un développeur junior a réduit la taille d'une image Docker Python de 1,2 Go à 85 Mo, faisant passer le pipeline CI de 11 minutes à 90 secondes. Des images plus petites se téléchargent plus rapidement, coûtent moins cher en stockage et exposent moins de failles de sécurité.

Pourquoi la taille de l'image est importante

Chaque fois qu'un conteneur est récupéré (pulled), le registre transfère l'image entière. Une couche de 85 Mo arrive en quelques secondes ; une couche de 1,2 Go peut prendre plusieurs minutes sur un réseau modeste. Les agents de build doivent également télécharger et mettre en cache l'image complète, ce qui gonfle le temps de CI et les factures de stockage cloud. Chaque paquet supplémentaire est une vulnérabilité potentielle, donc réduire la base réduit la surface d'attaque.

D'où vient l'encombrement

  • Les outils de build tels que gcc restent dans l'image finale s'ils sont installés dans la même étape que l'exécution de l'application.
  • Les caches des gestionnaires de paquets (par ex. les caches apt ou pip) occupent de l'espace sur le disque et ne sont jamais nettoyés par défaut.
  • Chaque instruction RUN crée une nouvelle couche en lecture seule ; la duplication de fichiers entre les couches s'accumule.
  • Les images de base volumineuses comme ubuntu:latest sont livrées avec un OS complet, bien plus que ce dont un runtime Python minimal a besoin.

La réduction en trois étapes

Étape Image de base Approche de build Taille résultante
1 Python standard (complet) Étape unique, tous les outils présents 1,18 Go
2 python:slim Multi-stage : builder avec gcc, l'étape finale ne copie que les paquets compilés 210 Mo
3 python:alpine Multi-stage sur Alpine Linux, qui est lui-même minuscule 85 Mo

Étape 1 – la référence

En partant de l'image Python par défaut, le développeur a obtenu un artefact de 1,18 Go. L'image contenait toute la pile Debian, les en-têtes de développement et le cache pip.

Étape 2 – slim avec un builder

Le passage à python:slim a réduit l'empreinte de l'OS, mais les outils de build sont restés. L'ajout d'une étape de builder a permis d'installer, de compiler, puis de supprimer gcc, make et les autres dépendances de compilation. L'étape finale a utilisé COPY --from=builder pour ne récupérer que les wheels compilés et les fichiers d'exécution, faisant tomber la taille à 210 Mo.

Étape 3 – Alpine l'emporte

Alpine Linux repose sur musl libc et busybox. La répétition du modèle multi-stage sur Alpine a produit une image de 85 Mo — une réduction de 93 % par rapport à l'originale. Le développeur a noté que Kubernetes récupérait l'image en quelques secondes et que le job de CI se terminait en 90 secondes.

Impact concret

  • Coûts de stockage réduits – Le stockage du registre diminue.
  • Sécurité améliorée – Moins de paquets signifie moins de CVE à surveiller. L'image Alpine ne contient que le runtime Python et le code de l'application.
  • CI plus rapide – Le temps d'exécution du pipeline est passé de 11 minutes à 90 secondes.

Conseils pratiques pour réduire votre Dockerfile

  • Évitez les tags :latest ; choisissez des variantes :slim ou :alpine qui correspondent à vos besoins.
  • Utilisez des builds multi-étapes : une étape builder dédiée à la compilation, et une étape runtime qui ne reçoit que les artefacts dont vous avez réellement besoin.
  • Copiez de manière sélective avec COPY --from=builder /path/to/installed /path/in/final.
  • Ordonnez les commandes de sorte que l'installation des dépendances s'effectue avant la copie du code source ; cela maximise la mise en cache des couches.
  • Nettoyez explicitement les caches, par ex. rm -rf /var/lib/apt/lists/* ~/.cache/pip.

Si vous adoptez Alpine, ajoutez le paquet nss lorsque votre application rencontre des problèmes de résolution DNS. Lors de l'installation avec pip, préfixez votre PATH par /root/.local/bin afin que les scripts installés localement soient trouvés lors de l'exécution.

Mises en garde

La bibliothèque musl libc d'Alpine peut entrer en conflit avec les wheels binaires compilés pour glibc, provoquant des erreurs d'exécution. Dans ces cas, recompilez les wheels à l'intérieur du builder Alpine ou revenez à une base slim. Le paquet nss supplémentaire est un faible prix à payer pour la fiabilité du DNS.

Prochaines étapes

  • Analysez vos images existantes pour identifier les couches volumineuses qui pourraient faire l'objet d'une réécriture en multi-stage.
  • Surveillez les logs de CI pour les temps de téléchargement (upload/download) afin de détecter l'encombrement des images.
  • Gardez un œil sur les rapports de vulnérabilité pour la distribution de base que vous choisissez.

La leçon est claire : un Dockerfile discipliné, une base légère et une étape de builder peuvent réduire une image Python de plus d'un ordre de grandeur, offrant des gains de vitesse et de coût tangibles sans sacrifier les fonctionnalités.