Skip to Content
CLIDockerfile

Les Dockerfiles bien écrits réduisent la taille des images, accélèrent les builds et renforcent la sécurité.

Multi-stage builds

Le pattern principal pour des images fines en production : la première étape compile ou installe les dépendances, la seconde ne conserve que l’artefact final.

# --- Build --- FROM maven:3.9-eclipse-temurin-21 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -B # --- Production --- FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]

L’image finale pèse ~180 Mo au lieu de ~1,3 Go.

Ordre des instructions et caching

Docker met en cache chaque couche. Plus une couche dépend de fichiers externes, plus le rebuild est lent.

# ✅ Bon ordre : copier les dépendances AVANT le code source COPY package*.json ./ RUN npm ci --only=production COPY . . # ❌ Mauvais ordre : tout rebuild à chaque modification de code COPY . . RUN npm ci --only=production

.dockerignore

Exclure ce qui n’est pas nécessaire dans l’image réduit la taille et évite de transmettre des secrets inutiles au daemon.

# .dockerignore node_modules .git target *.md .env Dockerfile

Ne pas mettre .env dans une image de production. Utiliser des variables d’environnement au runtime (docker run -e, docker-compose.yml, secrets Kubernetes) à la place.

Sécurité — utilisateur non-root

L’image par défaut s’exécute en root. Un conteneur compromis atteint le daemon Docker. Forcer un utilisateur non-root :

RUN addgroup -S app && adduser -S app -G app USER app

Vérifier que l’utilisateur a bien les droits d’écriture sur les répertoires nécessaires (/app, /tmp, les volumes montés).

Exemples par langage

Java / Spring Boot

# Multi-stage Spring Boot FROM maven:3.9-eclipse-temurin-21 AS build WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -B FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY --from=build /build/target/*.jar app.jar EXPOSE 8080 USER app ENTRYPOINT ["java", "-jar", "app.jar"]

Node.js

# Multi-stage Node.js FROM node:20 AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/package.json ./ EXPOSE 3000 USER node CMD ["node", "dist/server.js"]

Python

# Multi-stage Python FROM python:3.12 AS build WORKDIR /app COPY requirements.txt . RUN pip wheel --wheel-dir /wheels -r requirements.txt COPY src ./src FROM python:3.12-slim WORKDIR /app COPY --from=build /wheels /wheels RUN pip install --no-index --find-links=/wheels -r requirements.txt COPY --from=build /app/src ./src USER 1000 CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0"]

Pitfalls courants

PiègeCorrection
COPY . . avant RUN apt-get : pas de cache si le code changeCopier d’abord les fichiers de deps, construire, puis copier le reste
RUN rm -rf /var/lib/apt/lists/* dans la même couche que l’installCombiner dans un seul RUN pour garder le cache
Multi-stage sans AS nomObligatoire : AS builder doit avoir un nom référencé par --from=builder
ENTRYPOINT en format shell "java -jar app.jar"Préférer le format exec ["java", "-jar", "app.jar"] (signaux SIGTERM/SIGKILL)
$HOME dans RUN ne s’applique pas au USERVérifier les permissions après le USER avec RUN whoami

Vérification

# Taille de l'image docker images mon-app # Contenu de l'image (utilisateur, ports, environment) docker inspect mon-app | jq '.[0].Config' # Scanner de vulnérabilités (Docker Scout) docker scout cves mon-app # Vérifier que le conteneur tourne en non-root docker run --rm mon-app id

Voir aussi