Fabriquer son Dockerfile
Premier Dockerfile, instructions essentielles
On part du « hello world » et on monte progressivement.
Exemples comparés : PHP, Node, Python.
Avant de fabriquer : le vocabulaire
Trois mots à ne plus confondre
Un simple fichier texte nommé exactement « Dockerfile » (sans extension). Il contient les instructions pour construire ton image. Tu l'écris une fois, tu le versionnes dans Git.
Le produit du build. Une image est immuable, en lecture seule. Tu peux la stocker, l'envoyer sur Docker Hub, la partager. Pense « modèle » ou « plan ».
Une image qui tourne. Tu peux lancer 50 conteneurs depuis la même image, chacun avec son propre état (fichiers temporaires, processus, etc.).
Le « Hello World » de Docker
3 lignes, et tu as ton premier Dockerfile
Étape 1 : Le Dockerfile
# On part de l'image alpine (Linux mini, ~5 Mo) FROM alpine:3.19 # La commande lancée au démarrage CMD ["echo", "Hello depuis Docker !"]
C'est tout. Sauvegarde sous le nom exact « Dockerfile ».
Étape 2 : Construire et lancer
# Construire l'image (le point = répertoire courant) $ docker build -t hello-docker . # Lancer un conteneur depuis cette image $ docker run hello-docker Hello depuis Docker !
Astuce : commence toujours simple. Tu rajoutes des lignes une par une et tu rebuild à chaque fois.
Les instructions essentielles
Celles que tu vas écrire 95% du temps
| Instruction | Rôle | Exemple |
|---|---|---|
| FROM | Image de base à hériter | FROM node:20-alpine |
| WORKDIR | Définir le répertoire de travail | WORKDIR /app |
| COPY | Copier des fichiers de l'hôte vers l'image | COPY package.json . |
| RUN | Exécuter une commande pendant le build | RUN npm install |
| ENV | Définir une variable d'environnement | ENV NODE_ENV=production |
| EXPOSE | Documenter le port que l'app écoute | EXPOSE 3000 |
| CMD | Commande par défaut au démarrage | CMD ["node", "server.js"] |
| ENTRYPOINT | Point d'entrée fixe (avancé) | ENTRYPOINT ["./run.sh"] |
Note : les instructions sont en MAJUSCULES par convention (le parser les accepte aussi en minuscules).
Décortiquons un Dockerfile complet
Exemple Node.js - chaque ligne expliquée
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . ENV NODE_ENV=production EXPOSE 3000 CMD ["node", "server.js"]
- Image de base : Node 20 sur Alpine (~50 Mo)
- Tout ce qui suit se passe dans /app
- Copie SEULEMENT les fichiers de dépendances
- Installe les dépendances de prod uniquement
- Ensuite copie le code source (ordre important pour le cache !)
- Variable d'env lue par Express et autres
- Documente que l'app écoute sur 3000
- Au démarrage du conteneur : node server.js
Attention : l'ordre des lignes compte ! On y revient à la slide « cache des couches ».
Trois langages, trois Dockerfiles
Le squelette est toujours le même
FROM php:8.3-apache RUN docker-php-ext-install \ pdo pdo_mysql COPY ./src/ /var/www/html/ EXPOSE 80 CMD ["apache2-foreground"]
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . EXPOSE 3000 CMD ["node", "server.js"]
FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir \ -r requirements.txt COPY . . EXPOSE 5000 CMD ["python", "app.py"]
Patron commun : FROM (base) → WORKDIR (où) → COPY deps → RUN install → COPY code → EXPOSE → CMD. Le squelette est universel.
Trois pièges à éviter dès le début
Des paires d'instructions qui se ressemblent mais ne font pas pareil
Simple, prévisible : copie locale uniquement.
ADD https://... /appADD téléchargerait des URL et déballe les .tar automatiquement. Source de surprises.
→ Utilise COPY 99% du temps.
S'exécute pendant le build → fige le résultat dans l'image.
CMD ["node", "server.js"]S'exécute au démarrage du conteneur → l'app qui tourne.
→ Plusieurs RUN possibles, un seul CMD effectif.
Remplaçable au lancement : docker run img autre-cmd.
ENTRYPOINT ["./run.sh"]Toujours exécuté, pas remplaçable. Utile pour wrappers, sinon piège.
→ Reste sur CMD pour commencer.
Le cycle build → run en pratique
Ce que tu tapes vraiment dans le terminal
# 1. Construire (le point = Dockerfile dans le dossier courant) $ docker build -t mon-app:1.0 . # 2. Vérifier que l'image est bien créée $ docker images REPOSITORY TAG IMAGE ID SIZE mon-app 1.0 a3f8b2c1d9e0 142MB # 3. Lancer en arrière-plan, mapper le port $ docker run -d -p 8080:3000 --name mon-conteneur mon-app:1.0 # 4. Vérifier les logs $ docker logs mon-conteneur # 5. Modifier ton code, puis rebuild + relance $ docker stop mon-conteneur && docker rm mon-conteneur $ docker build -t mon-app:1.1 . && docker run -d -p 8080:3000 mon-app:1.1
Le tag (:1.0, :1.1, :latest) te sert à versionner tes images. Sans tag, Docker met :latest par défaut.
Erreurs typiques au premier Dockerfile
Les pièges qui font perdre une heure
Ce qu'il faut retenir
Dockerfile = recette texte. Image = produit du build (figé). Conteneur = instance vivante.
FROM → WORKDIR → COPY deps → RUN install → COPY code → EXPOSE → CMD. Marche pour PHP, Node, Python, Go, Ruby...
FROM, WORKDIR, COPY, RUN, ENV, EXPOSE, CMD, ENTRYPOINT. Le reste viendra avec l'expérience.
COPY (pas ADD), CMD (pas ENTRYPOINT au début), :alpine ou :slim comme base.
build -t nom:tag . / run -d -p hote:cont nom:tag / logs / stop / rm / rebuild. Itère vite.
Niveau 2 (intermédiaire) : optimisation des couches, multi-stage build, .dockerignore. Tes images vont passer de 1 Go à 100 Mo.