Création d’images Docker
La création d’images Docker est une étape essentielle pour construire des environnements fiables, reproductibles et adaptés aux besoins de chaque projet. Ces images seront ensuite utilisables par d'autres personnes. Au-delà de vous expliquer comment construire des images Docker personnalisées, ce chapitre passe en revue les bonnes pratiques pour optimiser les images. Nous verrons aussi comment publier une image sur Docker Hub et comment la récupérer, le tout via la ligne de commande Docker.
Sommaire
Qu'est-ce qu'un Dockerfile ?
Docker permet d'emballer une application et ses dépendances dans une image qui peut être déployée sur n'importe quelle machine exécutant Docker. Pour créer une image personnalisée, on utilise un Dockerfile, qui est un fichier texte décrivant étape par étape la manière dont l'image doit être construite.
Concrètement, un Dockerfile est un script contenant une liste d'instructions permettant de créer une image Docker personnalisée. Chaque instruction est exécutée successivement pour construire l'image, qui peut ensuite être utilisée pour démarrer des conteneurs.
Un Dockerfile suit une structure claire et comprend généralement :
- Une image de base sur laquelle s'appuyer.
- L'installation de logiciels ou de dépendances.
- La copie de fichiers nécessaires dans l'image.
- La configuration des ports, des variables d’environnement et du point d'entrée.
Nous allons voir ensemble la construction détaillée d'une image.
Un Dockerfile utilise des instructions spécifiques pour construire une image, dont voici les principales :
FROM: définir une image de base
L'instruction FROM spécifie l'image de base à utiliser. C'est le premier élément obligatoire d'un Dockerfile.
FROM ubuntu:22.04
Ici, l'image est basée sur Ubuntu 22.04, une distribution Linux légère et bien maintenue.
RUN: exécuter des commandes pendant la construction
RUN permet d'exécuter des commandes lors de la construction de l'image. Il est souvent utilisé pour installer des logiciels ou mettre à jour le système.
RUN apt update && apt install -y nginx
Cette commande met à jour les paquets et installe le serveur web Nginx.
COPY: copier des fichiers locaux dans l'image
COPY sert à transférer des fichiers du répertoire local (machine hôte) vers l'image en cours de construction.
COPY index.html /var/www/html/index.html
Cela permet de copier une page web index.html dans le dossier /var/www/html/ de l'image.
WORKDIR: définir le répertoire de travail
L'instruction WORKDIR définit le répertoire dans lequel les commandes suivantes seront exécutées.
WORKDIR /app
Tous les RUN, CMD et COPY suivants seront exécutés dans /app.
EXPOSE: ouvrir un port
EXPOSE indique le port que le conteneur utilisera pour la communication.
EXPOSE 80
Cela signifie que le conteneur utilisera le port 80 (généralement pour HTTP).
ENV: définir des variables d’environnement
ENV permet de définir des variables d'environnement accessibles dans le conteneur.
ENV APP_ENV=production
Cela configure une variable APP_ENV avec la valeur production.
CMDetENTRYPOINT: définir la commande de lancement
CMD spécifie la commande à exécuter par défaut lors du démarrage du conteneur, et ENTRYPOINT définit un exécutable principal et permet de passer des arguments dynamiques.
CMD ["nginx", "-g", "daemon off;"]
Cela démarre le serveur Nginx.
ENTRYPOINT ["python", "app.py"]
Cela force l'exécution de python app.py à chaque lancement du conteneur.
Découvrons ensemble un Dockerfile complet qui crée une image personnalisée avec Apache et une page d'accueil statique :
# Image de base
FROM php:8.2-apache
# Définition du répertoire de travail
WORKDIR /var/www/html
# Copier les fichiers de l'hôte vers l'image
COPY index.html index.html
# Exposer le port 80
EXPOSE 80
# Démarrer Apache
CMD ["apache2-foreground"]
Expliquons ces instructions :
FROM php:8.2-apache: utilise une image basée sur PHP 8.2 avec Apache préinstallé.WORKDIR /var/www/html: définit le dossier où seront copiés les fichiers de l'application.COPY index.html index.html: copie la page d'accueilindex.htmldans/var/www/html, qui est le répertoire web par défaut d'Apache.EXPOSE 80: indique que le conteneur écoutera sur le port 80.CMD ["apache2-foreground"]: démarre Apache en premier plan (mode foreground) pour que Docker puisse gérer son processus.
Construisons l'image personnalisée :
docker build -t mon-serveur-apache .
L'argument -t mon-serveur-apache assigne le nom mon-serveur-apache à l'image et . indique que le Dockerfile se trouve dans le répertoire courant.

Ensuite, nous vérifions que l'image a été correctement construite :
docker images

Pour finir, nous exécutons un nouveau conteneur utilisant notre image :
docker run -d -p 80:80 --name serveur-web mon-serveur-apache
L'option -d exécute le conteneur en mode détaché, permettant ainsi de le lancer en arrière-plan sans bloquer le terminal. L'option -p 80:80 redirige le port 80 du conteneur, où Apache écoute par défaut, vers le port 80 de l'hôte, permettant ainsi d'accéder au serveur via http://localhost.
L'option --name serveur-web attribue un nom unique au conteneur, facilitant sa gestion via des commandes comme docker stop serveur-web pour l'arrêter ou docker logs serveur-web pour afficher les journaux. Enfin, mon-serveur-apache spécifie l'image utilisée pour créer le conteneur, qui doit avoir été construite préalablement avec la commande docker build évoquée précédemment.
On peut vérifier que le serveur fonctionne en accédant à http://localhost dans un navigateur (ou via curl comme ci-dessous).

Optimisation des images Docker (taille et performance)
L'optimisation des images Docker est un réel enjeu pour garantir des déploiements efficaces, rapides et économes en ressources. Une image trop volumineuse entraîne une consommation excessive d'espace disque, des téléchargements plus longs, et des temps de démarrage plus élevés. De plus, une mauvaise gestion des couches peut ralentir l'exécution des conteneurs et compliquer leur maintenance. Nous allons présenter les meilleures pratiques pour optimiser la taille et la performance des images Docker, avec des exemples concrets pour illustrer chaque point.
Image de base
L’image de base est le point de départ d’un Dockerfile. Certaines images sont volumineuses et contiennent de nombreux outils inutiles, tandis que d’autres sont conçues pour être minimalistes et plus légères.
Il est généralement préférable de partir d’une image légère (par exemple alpine ou une version -slim) et d’y ajouter uniquement les dépendances nécessaires dans le Dockerfile. Cela permet d’obtenir une image finale plus petite, plus rapide à télécharger, plus rapide à déployer et plus sécurisée, car elle expose moins de surface d’attaque.
Cependant, dans certains cas, il reste pertinent d’utiliser une image complète. Par exemple :
- Lorsqu’une application nécessite beaucoup de bibliothèques systèmes difficiles à installer manuellement
- Lors d’un développement ou d’un débogage, où une image complète offre immédiatement tous les outils utiles
- Pour certains langages où l’image complète inclut déjà des optimisations ou compilations longues (ex. images
pythonounodeavec outils de build préinstallés).
L’essentiel est donc de choisir une image adaptée au contexte : une image légère pour la production, et une image plus complète lorsque le besoin fonctionnel ou la facilité de développement le justifie.
Quelles sont les bonnes pratiques pour une image ?
Il faut privilégier les versions "slim" ou "alpine" :
debian:latest→ 22 Mo (debian:slimréduit à ~20 Mo)ubuntu:latest→ 77 Mo (ubuntu:minimalréduit à ~29 Mo)python:3.11→ 980 Mo (python:3.11-alpineréduit à ~24 Mo)
Comment optimiser ?
Voici un mauvais choix d'image :
FROM ubuntu:latest
RUN apt update && apt install -y python3
Le choix optimisé à considérer est :
FROM python:3.11-alpine
L'image alpine est directement prête à l'emploi avec Python et pèse 40 fois moins.
Nombre de couches
Chaque instruction RUN, COPY, ADD dans un Dockerfile crée une nouvelle couche dans l'image Docker. Plus il y a de couches, plus l'image est lourde et longue à télécharger. Les bonnes pratiques en la matière consistent à combiner plusieurs commandes en une seule pour éviter la création de couches inutiles et à supprimer les fichiers temporaires après installation pour libérer de l’espace.
Comment optimiser ?
Voici un mauvais usage :
FROM debian:slim
RUN apt update
RUN apt install -y nginx
RUN apt clean
Un meilleur usage serait plutôt :
FROM debian:slim
RUN apt update && apt install -y nginx && apt clean
De cette façon, notre image est plus compacte et plus rapide à construire.
Utilisation de .dockerignore
Lorsqu'on copie des fichiers avec COPY ou ADD, tous les fichiers présents dans le répertoire sont inclus par défaut. Il est essentiel d'exclure les fichiers inutiles comme les dossiers .git/, node_modules/, logs/, et le Dockerfile lui-même.
Exemple de fichier .dockerignore :
.git/
node_modules/
logs/
Dockerfile
Cela empêche Docker d'ajouter ces fichiers dans l'image et accélère le build.
Multi-stage build
Les multi-stage builds permettent de construire l'application dans une image temporaire contenant tous les outils nécessaires, puis de copier uniquement les fichiers utiles dans une image finale plus légère.
Comment optimiser ?
Dockerfile sans multi-stage (image lourde) :
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
Une optimisation avec multi-stage pourrait être :
# Étape 1 : construction de l'application
FROM node:18 as build
WORKDIR /app
COPY . .
RUN npm install && npm run build
# Étape 2 : image finale optimisée
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
CMD ["node", "./dist/server.js"]
Ainsi, notre image finale ne contient que le code compilé, réduisant sa taille de plusieurs centaines de Mo.
Utilisation de versions spécifiques
L'utilisation de latest pour une image de base peut causer des problèmes de cohérence car la version change au fil du temps. Cela peut entraîner des erreurs inattendues.
Un exemple à éviter :
FROM nginx:latest
Un meilleur usage serait plutôt :
FROM nginx:1.25.0
Cela garantit que l'image reste identique d'un build à l'autre.
Optimisation des fichiers de configuration
Certaines images intègrent par défaut des fichiers inutiles. Il est recommandé de supprimer ou d'alléger ces fichiers pour optimiser l'image.
Prenons l'exemple de l'optimisation de l'image Apache.
Un mauvais usage :
FROM php:8.2-apache
COPY index.php /var/www/html/
Un meilleur usage en supprimant les fichiers inutiles :
FROM php:8.2-apache
RUN rm -rf /var/www/html/*
COPY index.php /var/www/html/
L'image ne contient que les fichiers nécessaires.
Nettoyage des fichiers temporaires
Lors de l'installation de paquets avec RUN, il est recommandé de supprimer le cache immédiatement après l'installation.
Comment optimiser ?
Un mauvais usage :
FROM debian:slim
RUN apt update && apt install -y nginx
Un meilleur usage serait :
FROM debian:slim
RUN apt update && apt install -y nginx && rm -rf /var/lib/apt/lists/*
De cette manière, nous supprimons les fichiers temporaires pour réduire la taille de l'image.
Utiliser correctement ENTRYPOINT et CMD
L'instruction CMD est souvent utilisée pour exécuter une commande au démarrage du conteneur. Cependant, ENTRYPOINT est préférable lorsque le conteneur doit exécuter une seule tâche principale, avec des paramètres dynamiques.
Exemple : optimisation d'un conteneur Python.
Sans ENTRYPOINT :
CMD ["python", "app.py"]
Meilleure approche avec ENTRYPOINT :
ENTRYPOINT ["python", "app.py"]
L'ENTRYPOINT garantit que app.py est toujours exécuté et permet de passer des arguments en ligne de commande.
En résumé : en appliquant chacune de ces techniques, nous garantissons des images Docker plus légères, plus rapides à télécharger et à exécuter, et adaptées aux environnements de production.
Utilisation des commandes docker push, docker pull
Docker permet de créer, partager et déployer des applications conteneurisées à l'aide d’images. Comme nous avons déjà vu, les images sont construites localement avec docker build, puis stockées dans un registre distant avec docker push, et récupérées sur d'autres machines avec docker pull. Comprendre et maîtriser ces deux dernières commandes est essentiel pour gérer un flux de travail efficace et reproductible dans un environnement de développement et de production.
Nous considérons que nous construisons l'image suivante avec docker build :
# Utilisation d'une image de base légère avec Apache
FROM php:8.2-apache
# Définition du répertoire de travail
WORKDIR /var/www/html
# Copier les fichiers du projet dans l’image
COPY index.html index.html
# Exposer le port 80
EXPOSE 80
# Lancer Apache au démarrage du conteneur
CMD ["apache2-foreground"]
Ajoutons également un fichier index.html dans le même dossier :
<!DOCTYPE html>
<html>
<head>
<title>Mon serveur Apache</title>
</head>
<body>
<h1>Bienvenue sur mon serveur Apache avec Docker</h1>
</body>
</html>
Plaçons-nous dans le dossier contenant le Dockerfile et exécutons :
docker build -t mon-serveur-apache .

Publier une image sur Docker Hub
Une fois l'image construite, il est souvent nécessaire de la partager sur un registre Docker distant (comme Docker Hub ou un registre privé) afin de la rendre accessible à d’autres machines.
Avant de pousser notre image sur Docker Hub, connectons-nous avec notre nom d'utilisateur et mot de passe :
docker login
Nous considérons être déjà inscrits sur Docker Hub, donc posséder un compte.

Docker Hub exige que l'image soit préfixée par le nom d’utilisateur Docker Hub. Si votre compte Docker Hub est jeremy, nous utilisons :
docker tag mon-serveur-apache jeremy/mon-serveur-apache:latest
Cela crée une référence à l'image sous jeremy/mon-serveur-apache:latest.
Ensuite, nous poussons l'image vers Docker Hub :
docker push jeremy/mon-serveur-apache:latest
L'image est maintenant disponible publiquement (ou en privé, selon vos paramètres).

Récupérer une image avec docker pull
Notre image peut à présent être récupérée sur une autre machine avec docker pull :
docker pull jeremy/mon-serveur-apache:latest
Une fois l'image téléchargée, nous pouvons la vérifier avec :
docker images
Pour lancer un conteneur basé sur notre image téléchargée :
docker run -d -p 80:80 --name serveur-web jeremy/mon-serveur-apache
Conclusion
Vous pouvez profiter de Docker sans jamais créer vos propres images, en fonction de vos besoins et de vos missions. Néanmoins, maîtriser l’écriture et l’optimisation d’un Dockerfile vous permet de créer des images Docker sur mesure en fonction des besoins de projets, tout au long du cycle de développement et de déploiement.
Dans le prochain chapitre, nous parlerons des différents registres Docker, des versions et des tags d'images.
