Polarys
Docker & conteneurs/Images et Dockerfile

Images et Dockerfile

Écrire un Dockerfile

Écrire un Dockerfile

Un Dockerfile est la recette qui décrit comment construire votre image Docker. Chaque instruction crée un nouveau layer.

Instructions essentielles

# Image de base (point de départ)
FROM python:3.11-slim

# Variables d'environnement
ENV PYTHONUNBUFFERED=1
ENV APP_PORT=8000

# Répertoire de travail dans le conteneur
WORKDIR /app

# Copier les fichiers de dépendances EN PREMIER (optimisation du cache)
COPY requirements.txt .

# Exécuter une commande pendant le build
RUN pip install --no-cache-dir -r requirements.txt

# Copier le reste du code
COPY . .

# Exposer le port (documentation, pas une règle de pare-feu)
EXPOSE 8000

# Commande de démarrage du conteneur
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Construire et exécuter

# Construire l'image (. = contexte = répertoire courant)
docker build -t mon-app:v1.0 .

# Voir la progression et les layers
docker build -t mon-app:v1.0 . --progress=plain

# Lancer le conteneur basé sur l'image
docker run -d -p 8080:8000 --name mon-app mon-app:v1.0

# Accéder à l'app
curl http://localhost:8080

Optimiser le cache Docker

Docker met en cache chaque layer. Si un layer n'a pas changé depuis le dernier build, il est réutilisé. L'ordre des instructions est critique :

# ❌ Mauvais : COPY . . en premier invalide le cache à chaque changement de code
FROM python:3.11-slim
COPY . .
RUN pip install -r requirements.txt

# ✅ Bon : dépendances d'abord (changent rarement), code ensuite (change souvent)
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

Le fichier .dockerignore

# .dockerignore : exclut du contexte de build
__pycache__/
*.pyc
.git/
.env
node_modules/
*.log

Sans .dockerignore, COPY . . envoie TOUT le répertoire au daemon Docker, y compris .git et node_modules.

Cas concret : Dockerfile multi-stage

# Stage 1 : build (image complète avec outils de compilation)
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2 : production (image minimale, sans node_modules de dev)
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Résultat : image de production de ~30 Mo au lieu de ~1 Go avec tous les outils de build.

Erreurs fréquentes

  • RUN multiple au lieu de chaîner avec && : chaque RUN crée un layer. Chaîner (RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*) réduit la taille.
  • Oublier .dockerignore : envoyer .git ou node_modules au daemon ralentit le build et grossit le contexte.
  • Lancer en root : par défaut les conteneurs tournent en root. Ajouter USER appuser après avoir créé l'utilisateur.

À retenir

  • FROM → base. COPY → fichiers. RUN → commandes build. CMD → démarrage.
  • Mettre les dépendances AVANT le code source pour maximiser le cache.
  • .dockerignore = obligatoire pour un build propre.
  • Multi-stage builds → images de production minimales.

Quiz de validation

Quiz - 2 questions

1. Pourquoi copier requirements.txt avant COPY . . dans un Dockerfile Python ?

2. Quel est l'avantage principal des multi-stage builds ?

Suis ta progression

Crée un compte gratuit pour suivre ta progression et accéder à toutes les leçons.