Aller au contenu

Optimiser la taille des images de container

Mise à jour :

Optimiser la taille des images de conteneurs est devenu une priorité pour les équipes DevOps cherchant à améliorer les performances de leurs applications, que ce soit dans des environnements Kuberntes, avec Podman, ou sous LXC. Une image de conteneur bien optimisée accélère non seulement les déploiements, mais réduit aussi l’utilisation des ressources, tant au niveau du stockage que de la mémoire. De plus, des images plus petites limitent la surface d’attaque, contribuant ainsi à renforcer la sécurité des applications.

Ce guide explore les stratégies universelles pour réduire la taille des images, indépendamment du moteur de conteneur utilisé.

Prérequis

Avant de vous lancer dans l’optimisation des images de conteneurs, il est indispensable de maîtriser la syntaxe des Dockerfile. Un Dockerfile bien structuré est la base pour créer des images optimisées. Pour acquérir une compréhension approfondie de la création et de l’organisation des Dockerfile, je vous recommande de consulter mon billet de blog détaillé sur le sujet. Il couvre les concepts essentiels et les meilleures pratiques pour écrire des Dockerfile efficaces et maintenables.

Vous pouvez lire cet article ici : Écrire un Dockerfile.

Évitez d’installer des outils inutiles

L’une des clés pour optimiser la taille de vos images de conteneurs est de limiter l’installation d’outils superflus. Chaque paquet installé ajoute non seulement du poids à l’image, mais peut aussi introduire des vulnérabilités. Par exemple, éviter d’inclure des utilitaires de débogage comme netcat ou curl dans vos images de production, à moins qu’ils ne soient absolument nécessaires. Une bonne pratique consiste à restreindre les installations aux seuls composants indispensables à l’exécution de l’application, en utilisant des commandes spécifiques pour gérer les dépendances.

Sur Alpine Linux, la commande apk permet de créer des paquets virtuels temporaires grâce à l’option --virtual. Cela permet de regrouper plusieurs dépendances sous un même nom pour les supprimer plus facilement après leur utilisation. Par exemple :

Terminal window
RUN apk add --no-cache --virtual .build-deps gcc make musl-dev \
&& make \
&& apk del .build-deps

Ce processus garantit que les outils de construction, souvent volumineux, sont retirés de l’image finale, minimisant ainsi son empreinte.

En résumé, moins votre image contiendra d’outils non essentiels, plus elle sera légère et sécurisée. Cette approche s’applique quel que soit le moteur de conteneur utilisé, qu’il s’agisse de Docker, Podman ou LXC.

Utilisation de l’option --no-install-recommends

Lorsque vous installez des paquets dans une image de conteneur, il est courant que le gestionnaire de paquets ajoute des dépendances supplémentaires jugées « recommandées », qui ne sont pas toujours nécessaires pour votre application. Cela peut entraîner une augmentation significative de la taille de l’image. Pour éviter cela, l’utilisation de l’option --no-install-recommends sur des distributions comme Debian ou Ubuntu est essentielle. Cette option garantit que seules les dépendances strictement nécessaires sont installées, excluant ainsi les paquets recommandés qui ne sont pas obligatoires.

Par exemple, lors de l’installation d’un serveur web comme Nginx dans une image Debian, la commande sans l’option --no-install-recommends pourrait installer des paquets supplémentaires non requis, ce qui gonflera inutilement la taille de l’image. Voici comment cette option peut être utilisée dans un Dockerfile :

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

Dans cet exemple, non seulement on installe Nginx sans les recommandations superflues, mais on nettoie également les listes de paquets pour réduire encore davantage la taille de l’image.

Pour les distributions basées sur Alpine, bien que la syntaxe soit différente, le concept reste le même. Utilisez l’option --no-cache pour éviter de conserver les caches d’installation qui augmentent inutilement la taille finale de l’image.

Ainsi, quelle que soit la distribution utilisée pour créer votre image de conteneur, l’emploi de cette approche garantit que vous ne transportez que le strict nécessaire, contribuant à une image plus légère et plus efficace.

Limitez le nombre de couches

Chaque instruction RUN, COPY, ou ADD dans un fichier de construction comme un Dockerfile crée une nouvelle couche dans l’image de conteneur. Plus vous avez de couches, plus l’image devient volumineuse et complexe. Pour réduire la taille de l’image, il est donc essentiel de combiner les commandes là où c’est possible.

Par exemple, au lieu de séparer les commandes RUN en plusieurs lignes, vous pouvez les combiner en une seule instruction :

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

Dans cet exemple, les commandes sont combinées en une seule instruction RUN, ce qui crée une seule couche au lieu de plusieurs. Cette pratique permet non seulement de réduire la taille finale de l’image, mais aussi de simplifier sa gestion et son analyse.

Une autre bonne pratique consiste à supprimer les fichiers temporaires ou les caches à la fin de la commande combinée pour s’assurer qu’ils ne sont pas inclus dans l’image finale. Ainsi, en regroupant les commandes et en nettoyant après chaque installation, vous obtenez une image de conteneur plus légère, ce qui améliore la vitesse de déploiement et réduit l’utilisation des ressources, peu importe le moteur de conteneur utilisé (Docker, Podman, etc.).

Cette technique est essentielle pour maintenir des images efficaces et éviter l’accumulation de données inutiles au fil des modifications apportées à vos conteneurs.

Utilisez .dockerignore

Un fichier .dockerignore joue un rôle important dans l’optimisation de vos images de conteneurs en excluant les fichiers et répertoires inutiles lors de la construction de l’image. Tout comme un fichier .gitignore, il empêche l’inclusion de fichiers temporaires, de configurations locales, ou de dépendances non essentielles dans l’image finale, ce qui permet de réduire sa taille de manière significative.

Par exemple, si votre projet contient des fichiers comme .env, node_modules, ou des dossiers de test, vous pouvez les exclure facilement avec un .dockerignore bien configuré :

Exemple :

# ignore all markdown files (md) beside all README*.md other than README-secret.md
*.md
!README*.md
README-secret.md

Cela garantit que ces fichiers ne seront pas copiés dans le contexte de construction, réduisant ainsi la taille de l’image et évitant les risques liés à la fuite d’informations sensibles. En ne copiant que les fichiers nécessaires, votre image devient non seulement plus légère, mais aussi plus sécurisée.

Privilégiez les images de base légères

L’une des méthodes les plus efficaces pour réduire la taille de vos images de conteneurs est de commencer par une image de base légère. Les distributions minimalistes comme Alpine Linux ou Wolfi-OS sont conçues pour fournir un environnement fonctionnel tout en restant extrêmement compactes. Par exemple, une image de base Alpine pèse environ 5,6 Mo, tandis qu’une image Ubuntu standard peut atteindre 80 Mo ou plus.

DISTRIBUTION /VERSION /TAILLE /
Debianbullseye-slim80MB
OracleLinux8246MB
Ubuntu21.0480 MB
Alpine3.145.6MB

Alpine Linux est particulièrement populaire pour la construction d’images Docker en raison de sa simplicité et de sa sécurité renforcée. Contrairement à des images plus complètes comme celles basées sur Debian ou Ubuntu, Alpine se concentre sur l’essentiel, en utilisant une bibliothèque C légère, musl, et BusyBox, une suite d’outils Unix réduits, optimisés pour la petite taille. Voici un exemple de démarrage avec Alpine dans un Dockerfile :

FROM alpine:3.18
RUN apk --no-cache add curl

Ce simple fichier construit une image de base Alpine avec curl installé, le tout dans un espace minimal. De plus, Alpine vous permet d’installer uniquement les packages nécessaires, avec une gestion fine des dépendances grâce à apk, ce qui vous aide à éviter le gonflement de l’image avec des bibliothèques inutilisées.

En fonction de vos besoins, vous pouvez également envisager d’utiliser Wolfi-OS, une distribution encore plus légère, ou d’autres alternatives minimalistes.

6. Utiliser le plus souvent possible le multi stage

Le multi-stage est une fonctionnalité intéressante introduite avec la version 17.05 de Docker. Il est possible de décrire plusieurs étapes dans un même fichier Dockerfile, Chaque stage commençant par une instruction FROM. Je vous conseille de nommer chaque stage en ajoutant AS <nom du stage>, cela permet d’y faire référence dans un autre stage. Il est possible de copier une partie du système de fichier d’un stage avec l’instruction COPY en utilisant le paramètre --from=<nom du stage>. La première application possible est de construire l’image en trois stages :

  • Le premier décrit la partie commune des deux autres
  • Le second permet de construire votre application
  • Le troisième permet de ne copier que le résultat de la construction du second. Par exemple pour les images python utilisant pip, qui consomme énormément de place lors de la compilation des packages, il est possible de garder le résultat de la compilation en utilisant un environnement virtuel.

Un exemple : Création d’une image Ansible-Lint

Une image non Optimisée

FROM alpine:3.10.2
RUN apk update && apk upgrade
RUN apk add --no-cache python3 openssl
RUN apk add --no-cache --virtual .build-deps python3-dev gcc ca-certificates libffi-dev openssl-dev build-base
RUN pip3 install --no-cache-dir --upgrade pip ansible
RUN apk del .build-deps

On contrôle la taille de ‘image

Terminal window
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ansible 0.1 5e43ab54b9a0 56 seconds ago 393MB

La même Optimisée

FROM alpine:3.10.2 as base
RUN apk update && apk --no-cache upgrade && apk add --no-cache python3 openssl
FROM base as builder
RUN apk add --no-cache --virtual .build-deps python3-dev gcc ca-certificates libffi-dev openssl-dev build-base
RUN python3 -m venv /opt/venv
Make sure we use the virtualenv:
ENV PATH="/opt/venv/bin:$PATH"
RUN pip3 install --no-cache-dir ansible
FROM base
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

On contrôle :

Terminal window
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ansible 0.2 304de2e8b235 51 seconds ago 165MB

On voit que l’image ne fait plus que 165MB contre 393MB pour la version non optimisée.

Cette approche est applicable à d’autres langages et plateformes, permettant d’optimiser les images de conteneurs pour la production, peu importe que vous utilisiez Docker, Podman ou un autre moteur de conteneurisation, voir un orchestrateur tel que Kubernetes.

Analysez vos images

Analyser vos images de conteneurs est une étape importante pour identifier les opportunités d’optimisation. Des outils comme Dive vous permettent de visualiser chaque couche de votre image et de comprendre exactement ce qui contribue à sa taille. Dive vous aide à repérer les fichiers ou les couches redondantes, à voir où l’espace est gaspillé et à prendre des décisions éclairées pour réduire l’image.

Par exemple, après avoir construit une image, vous pouvez exécuter Dive pour analyser les couches :

Terminal window
dive <nom_de_l_image>

Dive affichera une vue interactive où chaque couche est détaillée, avec la possibilité de voir quels fichiers ont été ajoutés ou modifiés à chaque étape. Cela vous permet d’identifier les couches inutiles ou surdimensionnées, qui peuvent être consolidées ou retirées dans une prochaine version de votre Dockerfile.

dive

En appliquant ces analyses, vous pouvez affiner votre processus de construction d’images pour obtenir des conteneurs plus légers et performants. Ces optimisations contribuent directement à des déploiements plus rapides et à une utilisation plus efficace des ressources dans vos environnements de production.

Conclusion

Optimiser la taille des images de conteneurs est une démarche indispensable pour améliorer l’efficacité de vos déploiements, réduire les temps de démarrage et diminuer l’utilisation des ressources. En appliquant les techniques décrites, telles que la réduction des couches, l’utilisation d’images de base légères, le multi-stage build et l’analyse approfondie avec des outils comme Dive, vous pouvez créer des images plus petites, plus rapides et plus sécurisées. Peu importe le moteur de conteneur utilisé, ces pratiques permettent d’obtenir des résultats optimaux dans vos environnements de production.