
Comment optimiser la taille des images Docker ?
La conteneurisation a transformé les méthodes de développement et de déploiement pour les équipes informatiques, en permettant de créer des environnements isolés proches de la production. Des outils comme Docker et Podman facilitent la création de ces environnements, indépendants des plateformes d’exploitation. Toutefois, bien que Docker soit facile à adopter, la conception d’images reste délicate. Une erreur fréquente et coûteuse est la taille excessive des images, une problématique qui, si elle n’est pas traitée, peut entraîner des inefficacités dans le stockage et le transfert.
Découvrez dans cet article les méthodes pour optimiser la taille des images Docker.
Pourquoi la taille des images Docker est un problème ?
Bien que les erreurs courantes comme la mauvaise gestion des droits ou les confusions autour des directives ENTRYPOINT
et CMD
soient souvent abordées, l’impact de la taille des images Docker reste méconnu et souvent sous-estimé.
En effet, les images volumineuses participent à la saturation de l’espace disque et ralentissent les déploiements en augmentant leur temps de transfert. Il est donc important d’adopter des stratégies efficaces pour réduire leur taille sans compromettre leur fonctionnement.
Comprendre la cause des images Docker trop lourdes
Lors de la finalisation d’une application, de nombreux langages de programmation nécessitent une étape de génération et/ou de packaging avant leur utilisation en production.
Lorsque l’application cible un environnement conteneurisé, il est fréquent d’intégrer ces étapes de génération à celle de l’image Docker. Par exemple, pour une application frontend React ou Angular, Node.js génère les fichiers statiques de l’application Web.
Cependant, un Dockerfile mal optimisé peut inclure des fichiers inutiles (code source, outils de génération) qui ne sont pas nécessaires en production et qui augmentent inutilement la taille de l’image finale.
Solution : utiliser les multi-stage builds dans vos Dockerfiles
Pourquoi utiliser les multi-stage builds ?
Les multi-stage builds permettent de diviser la génération de l’image en plusieurs étapes, chacune ayant accès aux artefacts des étapes précédentes. Cette approche optimise la taille des images en n’incluant que les éléments essentiels dans la version finale. Pour cela, nous nous appuyons sur la directive FROM
qui définit une nouvelle étape de génération.
Exemple de Dockerfile multi-stage pour Cppcheck
Pour illustrer ce principe, voici le Dockerfile permettant de générer une image contenant l’utilitaire cppcheck :
FROM fedora:41 as cppcheck_base # <1>
# Installation des paquets pour le "build" et le "run"
RUN dnf install -y pcre
FROM cppcheck_base as cppcheck_build # <2>
# Installation des paquets nécessaires à la génération
RUN dnf install -y @development-tools \
gcc-c++ \
cmake \
pcre-devel
# Récupération des sources
ARG CPPCHECK_VERSION=2.14.2
ADD https://github.com/danmar/cppcheck/archive/refs/tags/${CPPCHECK_VERSION}.tar.gz /build/
RUN tar -xzf /build/${CPPCHECK_VERSION}.tar.gz -C /build/ \
&& rm /build/${CPPCHECK_VERSION}.tar.gz
# Génération de cppcheck
RUN cmake -S /build/cppcheck-${CPPCHECK_VERSION}/ \
-B /build/release/ \
-DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=ON -DUSE_MATCHCOMPILER=ON \
-DCMAKE_INSTALL_PREFIX=/usr/ \
&& cmake --build /build/release/ \
# Déploiement des binaires
&& cmake --install /build/release/
FROM cppcheck_base # <3>
# Déploiement des fichiers générés (voir output "cmake --install")
COPY --from=cppcheck_build /usr/bin/cppcheck /usr/bin/
COPY --from=cppcheck_build /usr/share/Cppcheck /usr/share/Cppcheck/
# Installation des paquets pour le "run"
RUN dnf install -y python3 \
&& dnf clean all <4>
# Création de l'environnement de "run"
WORKDIR /opt/src/
CMD [ "cppcheck" ]
La première étape ajoute les éléments requis pour la génération et l’exécution. Ici, nous avons besoin de la librairie C pcre.
La seconde étape installe les éléments requis pour la génération, puis génère l’application cppcheck avec cmake.
La dernière étape construit l’image Docker finale en récupérant les livrables générés par la deuxième étape.
Certains script Python sont déployés sous /usr/share/Cppcheck
, nous installons donc les paquets nécessaires, puis nous nettoyons le cache de dnf pour libérer de l’espace disque.
Ce Dockerfile respecte deux principes pour minimiser la taille :
- Séparation des étapes de génération : Les étapes intermédiaires permettent de ne copier que les éléments nécessaires.
- Nettoyage des paquets : Les caches et paquets inutilisés sont supprimés pour limiter l’espace occupé.
Analyser la taille de l’image Docker optimisée
Vérifions le résultat de la génération en analysant la taille de cette image :
$ docker inspect -f '{{ .Size }}' lavoiedudev/cppcheck
287452681
Dans cet exemple, l’image Docker lavoiedudev/cppcheck
optimisée par une multi-stage build pèse environ 274 Mo, comparé à 1,09 Go pour une image générée en une seule étape. Ainsi, en utilisant cette approche nous pouvons réduire significativement la taille finale de nos images.
Autre méthode : le nettoyage manuel des images
Il est également possible de réduire la taille d’une image en ajoutant des instructions de nettoyage manuel. Cette méthode alternative consiste à supprimer les fichiers inutiles à l’exécution de l’image dans un conteneur.
Dans l’exemple précédent, cela se traduirait par :
- La suppression du code source ou des fichiers générés inutilisés ;
- La désinstallation des utilitaires de génération de l’application (headers C, compilateur gcc, cmake, etc.) ;
- Et enfin, le nettoyage du cache du gestionnaire de paquets systèmes.
Cette approche présente néanmoins des inconvénients :
- Oubli de fichiers : Dans des projets complexes, certains fichiers cachés peuvent rester présents dans l’image. C’est notamment le cas des dossiers de cache des gestionnaires de dépendances comme
~/.m2
et~/.npm
; - Augmentation du temps de génération : Les étapes de nettoyage allongent la durée de génération et augmentent la consommation CPU, ce qui implique un surcoût en temps et en énergie.
Réduction de l’empreinte carbone des images Docker
Les images Docker génèrent une empreinte environnementale due à leur stockage, leur transfert, et leur génération. En réduisant la taille des images, nous diminuons non seulement les coûts financiers, mais aussi l’impact carbone de nos opérations. C’est pourquoi optimiser un Dockerfile en suivant les bonnes pratiques, comme l’utilisation des multi-stage builds, contribue à une approche de développement plus responsable.
Conclusion
Réduire la taille des images Docker est essentiel pour un développement plus rapide, économique et respectueux de l’environnement. En adoptant les bonnes pratiques et en concevant des Dockerfiles optimisés, vous améliorerez la performance de vos déploiements tout en réduisant l’empreinte carbone de vos projets.