Dockerfile
| Fiche express | |
|---|---|
| Type | Fichier texte décrivant la construction reproductible d'une image Docker |
| Commande | docker build
|
| Concept clé | Chaque instruction crée une couche (layer), copy-on-write |
| Linter | hadolint |
| Voir aussi | Docker · Docker images · Docker containers |
Un Dockerfile est un fichier texte qui décrit, instruction par instruction, comment construire une image Docker : à partir de quelle image de base, quels paquets installer, quels fichiers copier, quelle commande exécuter au démarrage. C'est le mécanisme recommandé pour construire une image de façon reproductible et versionnable, par opposition à la construction manuelle via docker commit (voir Docker images).
Principe des couches
Chaque instruction du Dockerfile (à quelques exceptions près) crée une nouvelle couche (layer) dans l'image finale, en copy-on-write (COW) : une couche ne stocke que les différences par rapport à la couche précédente, et les couches sont partagées et mises en cache entre plusieurs images qui en dérivent. Cette mécanique a une conséquence directe sur l'écriture d'un Dockerfile : il est préférable de regrouper plusieurs commandes shell liées sur une seule instruction RUN (avec &&) plutôt que de les répartir sur plusieurs lignes, afin de limiter le nombre de couches et la consommation disque de l'image.
Construire une image
docker build -t myimage:v1.0.0 .
docker run -d --name c1 myimage:v1.0.0
docker logs c1
docker image inspect myimage:v1.0.0
docker exec -ti c1 bash
# utiliser un fichier autre que "Dockerfile" par défaut
docker build -t myimage:v1.0.0 -f Dockerfile2 .
Instructions courantes
| Instruction | Rôle |
|---|---|
FROM |
Image de base à partir de laquelle construire |
RUN |
Exécute une commande au moment du build (installation de paquets...), crée une couche |
COPY |
Copie des fichiers du contexte de build dans l'image — à préférer à ADD pour des fichiers locaux simples
|
ADD |
Comme COPY, mais gère en plus l'extraction automatique d'archives et la récupération d'URL distantes — à réserver à ces cas précis
|
WORKDIR |
Définit le répertoire de travail pour les instructions suivantes et pour l'exécution du conteneur |
USER |
Définit l'utilisateur sous lequel s'exécutent les instructions suivantes et le conteneur au démarrage |
EXPOSE |
Documente le(s) port(s) utilisé(s) par l'application (n'ouvre rien à lui seul, voir Docker network) |
ENTRYPOINT / CMD |
Commande exécutée au démarrage du conteneur |
LABEL |
Métadonnées clé/valeur sur l'image (l'instruction historique MAINTAINER est dépréciée au profit de LABEL)
|
Exemple, construction d'une image Apache avec un contenu de site statique :
FROM rhel
LABEL maintainer="admin@example.com"
# Mise à jour de l'image avec les derniers paquets
RUN yum update -y && yum clean all
# Installation du serveur web Apache
RUN yum install -y httpd && yum clean all
# Ajout de l'archive du site (extraite automatiquement par ADD)
ADD ./mysite.tar /tmp/
RUN mv /tmp/mysite/* /var/www/html
EXPOSE 80
ENTRYPOINT ["/usr/sbin/httpd", "-DFOREGROUND"]
Build multistage
Un Dockerfile peut contenir plusieurs instructions FROM successives : chaque bloc est une étape (stage), et une étape peut copier sélectivement des artefacts produits par une étape précédente (COPY --from=...). C'est le mécanisme recommandé pour séparer un environnement de build (compilateurs, dépendances de développement) de l'image finale exécutée en production, qui n'embarque alors que le strict résultat du build — réduisant significativement la taille de l'image.
docker build -t app:1.0.0 --build-arg TYPE=development .
Vérifier une image avant de builder dessus
Avant de choisir une image de base, il est utile d'en inspecter les spécifications publiées sur le registre :
docker manifest inspect python:3.9.16-slim-bullseye
Bonnes pratiques
- Choisir une image de base légère et minimaliste (Alpine, variantes slim...), envisager le multistage pour aller plus loin.
- Épingler précisément le tag de l'image de base, voire son digest, plutôt qu'un tag flottant.
- Nettoyer les caches de gestionnaire de paquets (APT, APK...) et les répertoires temporaires dans la même couche que l'installation.
- Grouper les instructions liées dans un seul
RUN(avec&&et\pour la lisibilité) afin de limiter le nombre de couches. - Utiliser un fichier
.dockerignorepour exclure du contexte de build les secrets et fichiers inutiles. - Préférer
COPYàADDsauf besoin explicite d'extraction d'archive ou de récupération distante. - Créer un utilisateur applicatif dédié plutôt que de tourner en root.
- Utiliser cet utilisateur via
USER, cohérent avec la commande lancée enCMD/ENTRYPOINT. - Supprimer explicitement les éléments installés qui ne sont plus nécessaires après le build.
- Éviter le tag
latest, qui rend le build non reproductible dans le temps. - Vérifier la provenance de l'image de base (registre de confiance, image activement maintenue, contenu des couches).
- Choisir consciemment la destination du
docker push: registre public ou privé. - Éviter le monolithe applicatif dans une seule image (base de données + application + fichiers statiques...) — une image par service.
- Passer le Dockerfile dans un linter comme hadolint :
docker run --rm -i hadolint/hadolint < Dockerfile
- Ne jamais intégrer de secrets en dur (mots de passe, clés, certificats) dans les couches de l'image.
- Variabiliser ce qui doit l'être (
ARG/ENV) plutôt que de dupliquer des Dockerfiles proches. - Éviter d'embarquer des outils de debug réseau bas niveau (telnet, tcpdump, netcat...) dans une image de production : surface d'attaque inutile.
- Standardiser les
LABEL(équipe propriétaire, langage, version...) pour faciliter l'exploitation à l'échelle. - Analyser les images à la recherche de vulnérabilités connues avec un scanner dédié (Clair, Trivy, Grype...) — à distinguer d'outils de détection d'anomalies à l'exécution comme Falco, qui adressent un besoin différent.
- Rester prudent avec les
COPYlarges (COPY . .) qui peuvent embarquer plus que prévu — combiné à.dockerignore. - Définir un
WORKDIRexplicite plutôt que de s'appuyer sur le répertoire par défaut. - Envisager systématiquement le multistage-build pour réduire la taille finale de l'image.
- Versionner les Dockerfiles comme du code, et les maintenir dans la durée au même titre que l'application qu'ils construisent.