Dockerfile
Objectifs du chapitre
- Comprendre la syntaxe Dockerfile
- Maîtriser les instructions principales
- Optimiser vos images
- Appliquer les bonnes pratiques
1 - Qu'est-ce qu'un Dockerfile ?
Définition
Un Dockerfile est un fichier texte contenant les instructions pour construire une image Docker automatiquement.
# Exemple simple
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Processus de build
┌───────────────────────────────────────────────────┐
│ Dockerfile │
└─────────────────────┬────────────────────── ───────┘
│ docker build
▼
┌───────────────────────────────────────────────────┐
│ Docker Engine │
│ │
│ FROM → RUN → COPY → ... → CMD │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ Layer Layer Layer Layer │
└─────────────────────┬─────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────┐
│ Image Docker │
└───────────────────────────────────────────────────┘
2 - Instructions de base
FROM - Image de base
# Image officielle
FROM ubuntu:22.04
# Image Alpine (légère)
FROM node:18-alpine
# Image scratch (vide)
FROM scratch
# Multi-stage : nommer l'étape
FROM golang:1.21 AS builder
WORKDIR - Répertoire de travail
# Définir le répertoire de travail
WORKDIR /app
# Tous les chemins relatifs seront basés sur /app
COPY . . # Copie dans /app
RUN ls # Exécuté dans /app
COPY et ADD
# COPY - Copier des fichiers
COPY source destination
COPY package.json /app/
COPY . /app/
COPY --chown=user:group files /app/
# ADD - Copier avec fonctionnalités supplémentaires
ADD archive.tar.gz /app/ # Extrait automatiquement
ADD https://example.com/file /app/ # Télécharge depuis URL
Bonne pratique
Préférez COPY à ADD sauf si vous avez besoin de l'extraction automatique.
RUN - Exécuter des commandes
# Format shell
RUN apt-get update && apt-get install -y nginx
# Format exec (recommandé)
RUN ["apt-get", "update"]
# Commandes multiples (optimisé)
RUN apt-get update && \
apt-get install -y \
nginx \
curl \
vim && \
rm -rf /var/lib/apt/lists/*
3 - Instructions d'exécution
CMD - Commande par défaut
# Format exec (recommandé)
CMD ["node", "server.js"]
# Format shell
CMD node server.js
# Paramètres pour ENTRYPOINT
CMD ["--help"]
ENTRYPOINT - Point d'entrée
# Définit l'exécutable principal
ENTRYPOINT ["node"]
CMD ["server.js"]
# docker run mon-image → node server.js
# docker run mon-image app.js → node app.js
Différence CMD vs ENTRYPOINT
| Aspect | CMD | ENTRYPOINT |
|---|---|---|
| Remplaçable | Oui, facilement | Non (sauf --entrypoint) |
| Usage | Paramètres par défaut | Exécutable principal |
| Combinaison | Peut être des arguments pour ENTRYPOINT | Reçoit CMD comme arguments |
# Exemple combiné
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run mon-image → python app.py
# docker run mon-image test.py → python test.py
4 - Configuration
ENV - Variables d'environnement
# Définir des variables
ENV NODE_ENV=production
ENV APP_HOME=/app \
APP_PORT=3000
# Utiliser les variables
WORKDIR $APP_HOME
EXPOSE $APP_PORT
ARG - Arguments de build
# Argument avec valeur par défaut
ARG NODE_VERSION=18
# Utilisation
FROM node:${NODE_VERSION}-alpine
# Argument obligatoire
ARG API_KEY
# Passer des arguments au build
docker build --build-arg NODE_VERSION=20 .
docker build --build-arg API_KEY=secret .
EXPOSE - Documenter les ports
# Documenter les ports exposés
EXPOSE 80
EXPOSE 443
EXPOSE 3000/tcp
EXPOSE 5000/udp
Important
EXPOSE est informatif. Il faut toujours -p au runtime pour publier les ports.
5 - Instructions avancées
USER - Changer d'utilisateur
# Créer et utiliser un utilisateur non-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Revenir à root si nécessaire
USER root
VOLUME - Points de montage
# Déclarer un volume
VOLUME /data
VOLUME ["/data", "/logs"]
HEALTHCHECK - Vérification de santé
# Vérifier que l'application répond
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Désactiver le healthcheck
HEALTHCHECK NONE
LABEL - Métadonnées
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="Mon application web"
# Format OCI
LABEL org.opencontainers.image.source="https://github.com/user/repo"
6 - Multi-stage builds
Concept
Les multi-stage builds permettent de créer des images légères en séparant la construction de l'exécution.
# Étape 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Étape 2: Production
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]
Exemple Go
# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# Production stage
FROM scratch
COPY --from=builder /app/main /main
ENTRYPOINT ["/main"]
Avantages
| Sans multi-stage | Avec multi-stage |
|---|---|
| Image 1.2 Go | Image 50 Mo |
| Outils de build inclus | Seulement l'exécutable |
| Surface d'attaque large | Surface minimale |
7 - Construire une image
Commande docker build
# Build simple
docker build .
# Avec tag
docker build -t mon-app:v1 .
# Depuis un Dockerfile spécifique
docker build -f Dockerfile.prod -t mon-app:prod .
# Avec arguments
docker build --build-arg VERSION=1.0 -t mon-app:v1 .
# Sans cache
docker build --no-cache -t mon-app:v1 .
# Afficher la sortie des RUN
docker build --progress=plain -t mon-app:v1 .
Le fichier .dockerignore
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
Dockerfile*
docker-compose*
README.md
.DS_Store
*.log
coverage
.nyc_output
8 - Exemples complets
Application Node.js
FROM node:18-alpine
# Créer un utilisateur non-root
RUN addgroup -S nodejs && adduser -S nodejs -G nodejs
WORKDIR /app
# Copier les fichiers de dépendances
COPY package*.json ./
# Installer les dépendances
RUN npm ci --only=production
# Copier le code source
COPY --chown=nodejs:nodejs . .
# Changer d'utilisateur
USER nodejs
# Exposer le port
EXPOSE 3000
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Commande de démarrage
CMD ["node", "server.js"]
Application Python
FROM python:3.11-slim
# Variables d'environnement
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Installer les dépendances système
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# Installer les dépendances Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copier le code
COPY . .
# Utilisateur non-root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Application Java (multi-stage)
# Build stage
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
9 - Bonnes pratiques Dockerfile
Ordre des instructions
# ❌ Mauvais - Cache invalidé à chaque changement de code
FROM node:18
COPY . .
RUN npm install
# ✅ Bon - Dépendances en cache
FROM node:18
COPY package*.json ./
RUN npm install
COPY . .
Minimiser les couches
# ❌ Plusieurs couches
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean
# ✅ Une seule couche
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Utiliser des images légères
# ❌ Image lourde (1 Go+)
FROM ubuntu:22.04
# ✅ Image légère (~5 Mo)
FROM alpine:3.18
# ✅ Version slim (~100 Mo)
FROM python:3.11-slim
Sécurité
# ✅ Ne pas exécuter en root
USER appuser
# ✅ Copier uniquement le nécessaire
COPY --chown=appuser:appuser app.py /app/
# ✅ Utiliser des versions fixes
FROM node:18.19.0-alpine3.18
Résumé
| Instruction | Description |
|---|---|
FROM | Image de base |
WORKDIR | Répertoire de travail |
COPY | Copier des fichiers |
RUN | Exécuter une commande |
ENV | Variable d'environnement |
ARG | Argument de build |
EXPOSE | Documenter un port |
CMD | Commande par défaut |
ENTRYPOINT | Point d'entrée |
USER | Changer d'utilisateur |
Points clés
- Ordonnez les instructions pour optimiser le cache
- Utilisez des multi-stage builds pour des images légères
- N'exécutez pas en root
- Créez un fichier .dockerignore
Exercices pratiques
- Créez un Dockerfile pour une application "Hello World" en Python
- Optimisez un Dockerfile en réordonnant les instructions
- Créez un multi-stage build pour une application Go
- Ajoutez un healthcheck à votre image