Aller au contenu principal
DockerConteneursFormation

Ton premier Dockerfile

30 min de lecture Apprendre Docker — Chapitre 3

Apprends à écrire un Dockerfile avec les instructions essentielles : FROM, COPY, RUN, CMD et docker build.

🎯 Objectif : À la fin de ce chapitre, tu sauras écrire un Dockerfile propre, comprendre le système de layers, et construire ta première image Docker prête pour la prod. ⏱️ Durée estimée : 45 minutes | Niveau : Intermédiaire

Tu sais lancer des conteneurs avec docker run. Mais en entreprise, personne ne travaille avec des images toutes faites. Tu dois construire les tiennes — adaptées à ton application, optimisées, reproductibles. C’est exactement ce que permet le Dockerfile : un fichier texte qui décrit, ligne par ligne, comment assembler une image Docker. Pas de magie, pas d’interface graphique. Juste du code déclaratif que tu versionnes avec ton projet.

Pourquoi le Dockerfile change tout

Sans Dockerfile, déployer une application ressemble à ça : un wiki obsolète avec 47 étapes manuelles, des “chez moi ça marche”, et un serveur de prod que personne n’ose toucher. Le Dockerfile élimine ce chaos.

Il apporte trois garanties fondamentales :

  • Reproductibilité — le même Dockerfile produit la même image, que ce soit sur ton laptop, en CI/CD ou en production
  • Traçabilité — chaque modification est versionnée dans Git, comme le reste du code
  • Automatisation — la CI peut builder, tester et pusher l’image sans intervention humaine

🔥 Cas réel : Une équipe fintech perdait 2 heures par déploiement avec des scripts bash fragiles. En passant au Dockerfile, le build est tombé à 3 minutes en CI, et les incidents de déploiement ont chuté de 80%. Le Dockerfile est devenu la source de vérité unique pour “comment on construit l’app”.

Comprendre le système de layers

Avant d’écrire ton premier Dockerfile, tu dois comprendre un concept central : les layers (couches). Chaque instruction (FROM, RUN, COPY…) crée une couche dans l’image finale. Ces couches sont empilées les unes sur les autres et — point crucial — mises en cache individuellement.

Concrètement, si tu modifies la ligne 5 de ton Dockerfile, Docker réutilise le cache des lignes 1 à 4 et ne reconstruit qu’à partir de la ligne 5. C’est ce qui rend les builds rapides… à condition d’ordonner tes instructions intelligemment.

💡 Tip DevOps : Place les instructions qui changent rarement en haut (installation de dépendances système, FROM) et celles qui changent souvent en bas (COPY du code source). Ton build passera de 2 minutes à 10 secondes grâce au cache.

🧠 À retenir : Pense aux layers comme les couches d’un sandwich. Si tu changes le fromage au milieu, tu dois refaire tout ce qui est au-dessus. Mais si tu changes la garniture du dessus, le reste reste intact.

Les commandes essentielles

Un Dockerfile se construit avec une poignée d’instructions. Voici celles que tu utiliseras dans 95% des cas.

FROM définit l’image de base. C’est toujours la première instruction. Utilise un tag précis pour garantir la reproductibilité :

FROM python:3.12-slim

WORKDIR fixe le répertoire de travail pour toutes les instructions suivantes. Oublie les RUN cd /app && ... à rallonge :

WORKDIR /app

COPY injecte des fichiers depuis ton projet dans l’image. L’astuce : copie d’abord les fichiers de dépendances, installe-les, puis copie le code. Ainsi, un changement de code ne déclenche pas la réinstallation des dépendances :

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

RUN exécute une commande pendant le build. Regroupe les commandes liées avec && pour limiter le nombre de layers :

RUN apt-get update && apt-get install -y \
    curl ca-certificates \
    && rm -rf /var/lib/apt/lists/*

EXPOSE documente le port d’écoute (c’est une métadonnée, pas une règle réseau — il faut toujours -p au docker run).

CMD définit la commande lancée au démarrage du conteneur. Utilise toujours la forme tableau JSON pour une bonne gestion des signaux UNIX :

EXPOSE 8000
CMD ["python", "app.py"]

⚠️ Attention : Ne confonds pas CMD et ENTRYPOINT. CMD est remplaçable par l’utilisateur (docker run mon-app autre-commande). ENTRYPOINT est fixe. En pratique, CMD suffit dans la majorité des cas. Réserve ENTRYPOINT aux images qui se comportent comme des binaires (CLI tools, agents).

Cas concret : conteneuriser une API Flask

Mettons tout ensemble. Tu travailles sur une API Flask pour un service interne. Voici le Dockerfile complet, commenté :

FROM python:3.12-slim

# Créer un utilisateur non-root (sécurité)
RUN addgroup --system app && adduser --system --ingroup app app

WORKDIR /app

# Dépendances d'abord (cache Docker)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Code source ensuite
COPY --chown=app:app . .

# Passer en utilisateur non-root
USER app

EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:create_app()"]

Pour builder et lancer cette image :

docker build -t api-interne:1.0 .
docker run -d -p 8000:8000 --name api api-interne:1.0
curl http://localhost:8000/health

🔥 Cas réel : Ce pattern (dépendances avant code + utilisateur non-root) est le standard dans la plupart des entreprises. Un rebuild après modification du code Python prend 2-3 secondes grâce au cache des dépendances. Sans cette séparation, chaque changement de code force la réinstallation de tous les packages — plusieurs minutes à chaque itération.

💡 Tip DevOps : Ajoute un fichier .dockerignore à la racine de ton projet pour exclure __pycache__, .git, node_modules, .env et les fichiers de test. Ça réduit le build context envoyé au daemon Docker et évite de leaker des secrets dans l’image.

Les pièges qui font perdre des heures

1. Le cache invalidé trop tôt. Si tu fais COPY . . avant RUN pip install, le moindre changement dans ton code invalide le cache des dépendances. Résultat : 2 minutes de build au lieu de 5 secondes. Copie toujours les fichiers de dépendances en premier.

2. Le nettoyage dans un layer séparé. Un classique :

RUN apt-get update && apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

Le rm dans le deuxième RUN ne récupère aucun espace — les fichiers existent toujours dans le layer précédent. Regroupe tout dans un seul RUN avec &&.

⚠️ Attention : Chaque layer est immuable. Supprimer un fichier dans un layer ultérieur ne fait que le masquer — il reste dans l’image et alourdit le tout. C’est comme graver un DVD puis coller du scotch sur une piste : les données sont toujours là.

3. Tourner en root. Par défaut, le conteneur tourne en root. En cas de faille applicative, l’attaquant a les pleins pouvoirs. Ajoute systématiquement un USER non-root après l’installation des dépendances.

4. Utiliser latest comme tag. FROM node:latest aujourd’hui te donne Node 22. Dans 6 mois, ce sera Node 24 avec des breaking changes. Fixe toujours la version : FROM node:22-alpine.

🧠 À retenir : Un bon Dockerfile suit quatre règles — tag fixe, dépendances avant code, nettoyage dans le même layer, utilisateur non-root. Si tu respectes ces quatre points, tu es déjà au-dessus de 80% des images en production.

Exercice : construis ton image

Crée un dossier docker-exercice/ avec deux fichiers :

Un app.py minimaliste :

from http.server import HTTPServer, BaseHTTPRequestHandler

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"Hello from my Dockerfile!")

HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()

Puis écris un Dockerfile qui :

  1. Part de python:3.12-slim
  2. Crée un utilisateur non-root
  3. Copie app.py dans /app
  4. Expose le port 8080
  5. Lance l’application avec CMD

Build l’image, lance le conteneur, et vérifie avec curl http://localhost:8080. Bonus : vérifie que le conteneur ne tourne pas en root avec docker exec <container> whoami.


➡️ La suite : Dans le prochain chapitre, on passe aux multi-stage builds pour produire des images ultra-légères et sécurisées. On continue ! 🚀

🖥️ Pratique sur ton propre serveur

Pour suivre Apprendre Docker en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.

Obtenir 200$

Articles liés