Découvrez l'anatomie d'un Dockerfile simple. Apprenez l'ordre des instructions clés comme FROM, WORKDIR, COPY, RUN, EXPOSE, CMD pour créer vos images Docker.
Introduction : le plan de votre image Docker
Le Dockerfile est le coeur de la création d'images personnalisées dans Docker. Il s'agit d'un fichier texte sans extension nommé littéralement `Dockerfile` qui contient une série d'instructions ordonnées. Chaque instruction indique au moteur Docker une action à réaliser pour assembler l'image, couche par couche. Comprendre la structure de base de ce fichier est essentiel pour écrire des Dockerfiles clairs, efficaces et maintenables.
Pensez au Dockerfile comme à un script ou une recette de cuisine. L'ordre des instructions est primordial, car Docker exécute ces commandes séquentiellement. Chaque instruction (à quelques exceptions près comme `FROM`, `MAINTAINER`, `EXPOSE`) crée une nouvelle couche dans l'image finale. Cette architecture en couches est fondamentale pour l'efficacité de Docker, notamment pour le partage d'images et l'utilisation du cache de build.
Bien qu'un Dockerfile puisse devenir complexe, sa structure fondamentale reste relativement simple et suit une logique claire : définir une base, configurer l'environnement, ajouter le code ou les fichiers nécessaires, et spécifier comment lancer l'application. Nous allons maintenant décomposer la structure typique d'un Dockerfile simple.
Les composants fondamentaux d'un Dockerfile
Un Dockerfile simple s'articule généralement autour de quelques instructions clés, apparaissant souvent dans un ordre logique, même si ce n'est pas strictement obligatoire pour toutes les instructions :
1. `FROM [:]` : C'est presque toujours la première instruction. Elle définit l'image de base sur laquelle votre propre image sera construite. Toute image Docker commence par une image parente (sauf l'image spéciale `scratch`). Le choix de l'image de base influence la taille, la sécurité et les outils disponibles dans votre image finale.
2. Instructions de métadonnées (optionnelles mais recommandées) :
`LABEL = ...` : Permet d'ajouter des métadonnées à votre image (mainteneur, version, description, etc.). Exemple : `LABEL maintainer="CertiQuizz " version="1.0"`.
`ARG [=]` : Définit une variable utilisable uniquement pendant le processus de build (non persistante dans l'image finale). Peut être définie avant `FROM`.
`ENV = ...` : Définit une variable d'environnement persistante dans l'image et donc disponible pour les conteneurs qui en seront issus.
3. Instructions de configuration et d'installation :
`WORKDIR /chemin/absolu` : Définit le répertoire de travail par défaut pour les instructions suivantes (`RUN`, `CMD`, `ENTRYPOINT`, `COPY`, `ADD`). C'est une bonne pratique pour éviter de longs chemins répétitifs et organiser le contenu de l'image.
`RUN ` : Exécute une commande dans le contexte de l'image (par exemple, installer des paquets avec `apt-get install`, `apk add` ou `pip install`, créer des répertoires, etc.). Chaque `RUN` crée une nouvelle couche.
4. Instruction de copie de fichiers :
`COPY ` : Copie des fichiers ou des répertoires depuis le contexte de build (votre machine locale) vers le système de fichiers de l'image. C'est l'instruction privilégiée pour ajouter votre code applicatif ou vos fichiers de configuration.
5. Instruction d'exécution par défaut :
`EXPOSE [/...]` : Informe Docker que le conteneur écoutera sur les ports spécifiés au moment de l'exécution. C'est une documentation ; la publication effective se fait avec `-p` dans `docker run`.
`CMD ["executable", "param1", "param2"]` ou `ENTRYPOINT ["executable", "param1", "param2"]` : Définit la commande qui sera exécutée par défaut lorsque le conteneur démarrera. `CMD` peut être facilement surchargée lors du `docker run`, tandis que `ENTRYPOINT` définit un exécutable principal plus difficile à surcharger (souvent utilisé pour créer des images qui se comportent comme des exécutables). Un Dockerfile doit contenir au moins l'une de ces deux instructions pour être utile en tant que service. La dernière instruction `CMD` ou `ENTRYPOINT` est celle qui prend effet.
6. Commentaires : Les lignes commençant par `#` sont des commentaires et sont ignorées par le builder Docker. Utilisez-les généreusement pour expliquer les choix faits dans votre Dockerfile.
Exemple concret de structure simple
Voyons comment ces éléments s'agencent dans un exemple concret. Imaginons un Dockerfile pour une petite application Python (fichier `app.py`) utilisant le framework Flask et nécessitant un fichier `requirements.txt` :
# 1. Image de base
FROM python:3.9-slim
# 2. Metadonnées (optionnel)
LABEL maintainer="Votre Nom "
# 3. Définir le répertoire de travail
WORKDIR /app
# 4. Copier le fichier des dépendances
# (Copier avant le reste pour profiter du cache si les dépendances ne changent pas)
COPY requirements.txt .
# 5. Installer les dépendances
RUN pip install --no-cache-dir -r requirements.txt
# 6. Copier le reste de l'application
COPY . .
# 7. Exposer le port utilisé par Flask (par défaut 5000)
EXPOSE 5000
# 8. Commande de démarrage de l'application
CMD ["python", "app.py"]
Dans cet exemple, la structure est claire : on part de Python, on définit où l'on travaille (`/app`), on installe les dépendances (isolé pour le cache), on copie le code, on documente le port, et on définit comment lancer l'application. Cet ordre logique facilite la lecture et optimise l'utilisation du cache de build.
Comprendre cette structure de base est la première étape pour écrire des Dockerfiles efficaces. Même pour des applications plus complexes, les principes restent les mêmes : organiser logiquement les instructions pour construire l'environnement requis, couche par couche.