Plongez au coeur des instructions Dockerfile fondamentales : FROM, WORKDIR, COPY, RUN, EXPOSE et CMD. Apprenez leur syntaxe, leur rôle et comment les utiliser efficacement.
Introduction : les piliers de votre Dockerfile
Pour construire une image Docker personnalisée, vous utilisez un Dockerfile, qui est essentiellement une liste d'instructions. Si le nombre total d'instructions disponibles est conséquent, une poignée d'entre elles forment le socle de la quasi-totalité des Dockerfiles. Maîtriser ces commandes essentielles est la clé pour créer des images fonctionnelles, optimisées et maintenables.
Ces instructions fondamentales – `FROM`, `WORKDIR`, `COPY`, `RUN`, `EXPOSE`, et `CMD` – couvrent les besoins primaires de la construction d'une image : définir une base, organiser l'espace de travail, intégrer vos fichiers, exécuter des commandes de configuration, documenter les ports réseau et spécifier comment lancer votre application. Chacune joue un rôle précis et interagit avec les autres pour former l'environnement final.
Dans ce chapitre, nous allons examiner en détail chacune de ces six instructions cruciales. Nous explorerons leur syntaxe, leur fonction exacte, des exemples concrets d'utilisation, et les bonnes pratiques associées pour vous permettre de les manier avec confiance et efficacité dans vos propres Dockerfiles.
1. `FROM image[:tag|@digest]` : le point de départ
L'instruction `FROM` est quasiment toujours la première commande de votre Dockerfile (seule `ARG` peut la précéder dans certains cas). Elle est obligatoire car elle spécifie l'image de base sur laquelle votre propre image va être construite. Chaque image Docker est une superposition de couches, et `FROM` définit la couche (ou l'ensemble de couches) initiale.
Syntaxe : `FROM [:]` ou `FROM [@]`
: Le nom de l'image parente (ex: `ubuntu`, `python`, `nginx`, `node`).
[:] : Optionnel. Spécifie la version ou la variante de l'image de base (ex: `:22.04`, `:3.9-slim`, `:1.21-alpine`). Si omis, Docker utilise le tag `:latest` par défaut, ce qui est souvent déconseillé en production pour garantir la reproductibilité.
[@] : Optionnel. Permet de spécifier une image par son identifiant de contenu immuable (SHA256), garantissant une reproductibilité absolue.
Exemples :
# Utiliser la version 22.04 d'Ubuntu
FROM ubuntu:22.04
# Utiliser une version légère (slim) de Python 3.9
FROM python:3.9-slim
# Utiliser la dernière version "alpine" de Node.js
FROM node:alpine
# Utiliser une image par son digest (garantie d'immuabilité)
FROM ubuntu@sha256:aabed3296a3d4a0b4c488a28a2b5d6a284b41f170b3b5b11590f00d5208ae6ae
Rôle et bonnes pratiques : Le choix de l'image de base est crucial. Il détermine le système d'exploitation sous-jacent, les outils préinstallés, la taille initiale de votre image et sa surface d'attaque en termes de sécurité. Privilégiez les images de base minimalistes (comme les variantes `-alpine` ou `-slim`) pour réduire la taille et les vulnérabilités potentielles. Utilisez des tags spécifiques plutôt que `:latest`.
2. `WORKDIR /chemin/absolu` : définir le contexte d'exécution
L'instruction `WORKDIR` définit le répertoire de travail courant pour toutes les instructions suivantes dans le Dockerfile (`RUN`, `CMD`, `ENTRYPOINT`, `COPY`, `ADD`). Si le répertoire spécifié n'existe pas, `WORKDIR` le créera automatiquement (ainsi que les répertoires parents nécessaires).
FROM ubuntu
# Crée /app et s'y place
WORKDIR /app
# La commande suivante sera exécutée dans /app
RUN touch fichier1.txt
# Crée /app/data et s'y place
WORKDIR /app/data
# La commande suivante sera exécutée dans /app/data
RUN touch fichier2.txt
# Retourne dans /app
WORKDIR /app
# Copie un fichier local dans /app
COPY local.conf .
Rôle et bonnes pratiques : Utiliser `WORKDIR` est une bonne pratique pour structurer votre image et éviter d'avoir à spécifier des chemins complets dans chaque commande `RUN`, `COPY`, etc. Cela rend le Dockerfile plus lisible et moins sujet aux erreurs. Il est préférable d'utiliser des chemins absolus avec `WORKDIR`. Evitez les enchaînements comme `RUN cd /app && ...`, préférez `WORKDIR /app` suivi de `RUN ...`.
3. `COPY [--chown=:] ... ` : importer vos fichiers
L'instruction `COPY` permet de copier des fichiers ou des répertoires depuis le contexte de build (le répertoire où se trouve le Dockerfile et ses sous-répertoires, sur votre machine locale) vers le système de fichiers de l'image Docker en cours de construction.
Syntaxe : `COPY [--chown=:] ... `
: Chemin(s) vers le fichier ou répertoire dans le contexte de build. Peut utiliser des jokers (ex: `*.txt`).
: Chemin absolu dans l'image, ou chemin relatif au `WORKDIR` courant. Si la destination se termine par `/`, elle est interprétée comme un répertoire.
[--chown=:] : Optionnel. Permet de définir le propriétaire et le groupe des fichiers copiés dans l'image. Utile si vous n'exécutez pas en tant que root.
Exemples :
WORKDIR /app
# Copie le fichier config.yaml du contexte local vers /app/config.yaml dans l'image
COPY config.yaml .
# Copie tout le contenu du répertoire 'src' local vers /app/src dans l'image
COPY src/ ./src/
# Copie tous les fichiers .html locaux dans /app/static/html
COPY *.html ./static/html/
# Copie le fichier 'run.sh' et le rend appartenant à l'utilisateur 'appuser'
COPY --chown=appuser:appgroup run.sh .
Rôle et bonnes pratiques : `COPY` est l'instruction standard pour intégrer votre code source, vos fichiers de configuration, ou tout autre artefact nécessaire à votre application dans l'image. Préférez `COPY` à `ADD` pour les cas simples de copie de fichiers locaux ( `ADD` a des fonctionnalités supplémentaires comme l'extraction d'archives ou le téléchargement depuis des URL, ce qui peut être moins prévisible). Structurez vos `COPY` pour optimiser le cache : copiez d'abord les fichiers qui changent le moins souvent (ex: fichiers de dépendances) avant ceux qui changent fréquemment (ex: le code source).
4. `RUN ` ou `RUN ["executable", "param1", ...]` : exécuter des commandes
L'instruction `RUN` exécute une commande à l'intérieur d'une nouvelle couche de l'image pendant le processus de build. C'est l'outil principal pour installer des paquets logiciels, configurer l'environnement, compiler du code, créer des utilisateurs ou des répertoires, etc.
Syntaxe :
Forme "shell" : `RUN ` (exécutée via `/bin/sh -c` par défaut)
Forme "exec" : `RUN ["executable", "param1", "param2"]` (exécutée directement)
La forme shell est plus courante et permet d'utiliser les fonctionnalités du shell (pipes, variables d'environnement).Exemples :
# Forme shell: Mettre à jour les paquets et installer curl et git sur une base Debian/Ubuntu
RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/*
# Forme shell: Installer des dépendances Python
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Forme exec: Créer un utilisateur
RUN ["useradd", "-ms", "/bin/bash", "appuser"]
# Forme shell: Télécharger un fichier
RUN curl -o /tmp/fichier.zip https://example.com/fichier.zip && unzip /tmp/fichier.zip -d /opt && rm /tmp/fichier.zip
Rôle et bonnes pratiques : Chaque instruction `RUN` crée une nouvelle couche d'image. Pour optimiser la taille de l'image et la vitesse de build, chaînez les commandes logiquement liées avec `&&` au sein d'une seule instruction `RUN`. Pensez également à nettoyer les artefacts inutiles (caches de gestionnaires de paquets comme `/var/lib/apt/lists/*` ou fichiers temporaires) dans la même instruction `RUN` pour éviter qu'ils ne restent dans la couche.
5. `EXPOSE [/...]` : documenter les ports réseau
L'instruction `EXPOSE` informe Docker que le conteneur écoutera sur les ports réseau spécifiés lorsqu'il sera en cours d'exécution. C'est une forme de documentation pour les utilisateurs de l'image et peut être utilisée par certains outils d'orchestration ou de liaison de conteneurs.
Syntaxe : `EXPOSE ` ou `EXPOSE /` (le protocole peut être `tcp` (défaut) ou `udp`).Exemples :
# L'application écoute sur le port 8080 en TCP
EXPOSE 8080
# L'application écoute sur le port 80 TCP et 53 UDP
EXPOSE 80/tcp 53/udp
# ou simplement:
EXPOSE 80 53/udp
Rôle et bonnes pratiques : Il est crucial de comprendre que `EXPOSE` ne publie pas le port sur la machine hôte. C'est l'option `-p` (ou `-P`) de la commande `docker run` qui effectue la publication effective. `EXPOSE` sert principalement d'indication. Documentez toujours les ports standards sur lesquels votre application écoute à l'intérieur du conteneur.
6. `CMD ["executable", "param1", ...]` : la commande par défaut
L'instruction `CMD` définit la commande par défaut qui sera exécutée lorsqu'un conteneur est démarré à partir de votre image. Il ne peut y avoir qu'une seule instruction `CMD` effective dans un Dockerfile (la dernière rencontrée).
Syntaxe : Il existe trois formes :
Forme "exec" (préférée) : `CMD ["executable", "param1", "param2"]` (La commande est exécutée directement, sans passer par un shell).
Forme "shell" : `CMD command param1 param2` (La commande est exécutée via `/bin/sh -c`).
Forme "paramètres pour ENTRYPOINT" : `CMD ["param1", "param2"]` (Fournit des paramètres par défaut à une instruction `ENTRYPOINT` définie en forme exec).
Exemples :
# Forme exec: Lancer une application Python
CMD ["python", "app.py"]
# Forme exec: Lancer Nginx en avant-plan (mode standard pour conteneurs)
CMD ["nginx", "-g", "daemon off;"]
# Forme shell: Afficher une variable d'environnement (moins recommandé pour les services)
CMD echo "Bienvenue, \$USER_NAME"
# Forme paramètres pour ENTRYPOINT: Fournir un paramètre par défaut à l'exécutable 'ping'
# (Supposons qu'il y ait un ENTRYPOINT ["ping"] plus haut)
# CMD ["google.com"]
Rôle et bonnes pratiques : Le but principal de `CMD` est de fournir la commande d'exécution par défaut pour l'image. Un point clé est que la commande spécifiée dans `CMD` peut être facilement surchargée par l'utilisateur lors de l'exécution de `docker run`, simplement en ajoutant une commande à la fin (`docker run mon_image autre_commande`). Privilégiez la forme `exec` car elle évite les interprétations inattendues par le shell. Si vous voulez que votre image se comporte comme un exécutable principal dont seule la fin peut être modifiée (les arguments), utilisez plutôt `ENTRYPOINT` (souvent en combinaison avec `CMD` pour les arguments par défaut).