Aller au contenu principal

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

AspectCMDENTRYPOINT
RemplaçableOui, facilementNon (sauf --entrypoint)
UsageParamètres par défautExécutable principal
CombinaisonPeut être des arguments pour ENTRYPOINTReç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-stageAvec multi-stage
Image 1.2 GoImage 50 Mo
Outils de build inclusSeulement l'exécutable
Surface d'attaque largeSurface 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é

InstructionDescription
FROMImage de base
WORKDIRRépertoire de travail
COPYCopier des fichiers
RUNExécuter une commande
ENVVariable d'environnement
ARGArgument de build
EXPOSEDocumenter un port
CMDCommande par défaut
ENTRYPOINTPoint d'entrée
USERChanger 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

  1. Créez un Dockerfile pour une application "Hello World" en Python
  2. Optimisez un Dockerfile en réordonnant les instructions
  3. Créez un multi-stage build pour une application Go
  4. Ajoutez un healthcheck à votre image

← Conteneurs Docker | Volumes Docker →