Accueil

Docker en Production : BuildKit, Compose v2, Multi-stage et Sécurité

Devops
Difficulte: Advanced
18 min de lecture

Maîtrisez Docker en environnement de production avec BuildKit, les multi-stage builds, Compose v2, la sécurité des images et le monitoring. Guide complet et pragmatique.

Retour aux tutoriels

L'essentiel

  • BuildKit est le moteur de build par défaut depuis Docker 23.0, et ses cache mounts sont sa fonctionnalité la plus rentable : RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt réduit les rebuilds de 60 à 90 %, faisant tomber un pip install de 3 minutes à 10 secondes.
  • Ne passez jamais un secret par ARG ou ENV : ces valeurs restent lisibles via docker history et dans les métadonnées de l'image. Utilisez les secret mounts BuildKit — RUN --mount=type=secret,id=pip_conf côté Dockerfile, docker buildx build --secret id=pip_conf,src=... côté commande — qui n'écrivent le secret dans aucune layer.
  • Le choix de l'image de base dicte la taille et la surface d'attaque : pour une application Go, golang:1.22 pèse 850 Mo, alpine 15 Mo et scratch 8 Mo. En production, Distroless (ni shell ni gestionnaire de paquets) pour les langages interprétés, scratch pour les binaires statiques, Alpine quand il faut pouvoir déboguer dans le conteneur.
  • Avec Compose v2, un depends_on nu n'attend que la création du conteneur : utilisez condition: service_healthy pour attendre le healthcheck et condition: service_completed_successfully pour les jobs de migration. Définissez toujours deploy.resources.limits — sans limite mémoire, un conteneur peut déclencher l'OOM killer du noyau et faire tomber les autres.
  • Le durcissement production : read_only: true avec un tmpfs pour les écritures, no-new-privileges:true, cap_drop: ALL puis réajout minimal (NET_BIND_SERVICE), réseau internal: true pour la base de données, rotation des logs (max-size / max-file), et trivy image --severity HIGH,CRITICAL --exit-code 1 en étape bloquante du pipeline. Jamais de tag :latest en production : SHA de commit ou semver.
Prérequis
Ce tutoriel suppose une connaissance de base de Docker (images, conteneurs, Dockerfile). Si vous débutez, consultez d'abord le guide d'installation et configuration de Docker.

Docker en développement vs Docker en production

La majorité des équipes utilisent Docker en développement sans difficulté. Un docker compose up, quelques volumes montés, et tout fonctionne. Mais le passage en production révèle des problématiques bien différentes : temps de build, taille des images, sécurité, gestion des ressources, monitoring et fiabilité.

En développement, on tolère des images de 2 Go, des builds de 10 minutes et des conteneurs qui tournent en root. En production, chaque mégaoctet compte, chaque seconde de build impacte le cycle de déploiement, et chaque conteneur root est une faille potentielle.

Ce guide couvre les techniques et outils indispensables pour exploiter Docker de manière professionnelle : BuildKit pour des builds rapides et sécurisés, les multi-stage builds pour des images minimales, Compose v2 pour l'orchestration, et les bonnes pratiques de sécurité, monitoring et réseau.

BuildKit : le moteur de build nouvelle génération

BuildKit est le moteur de build par défaut depuis Docker 23.0 (janvier 2023). Il remplace le builder legacy et apporte des améliorations majeures en termes de performance et de sécurité. Si vous utilisez une version récente de Docker, BuildKit est déjà actif.

Activation et configuration

Vérifiez que BuildKit est bien activé sur votre système :

# Vérifier la version et le builder actif
docker buildx version
docker buildx ls

# Forcer BuildKit pour les versions anciennes
export DOCKER_BUILDKIT=1

# Ou dans /etc/docker/daemon.json
{
  "features": {
    "buildkit": true
  }
}

Créez un builder dédié avec des options avancées pour la production :

# Créer un builder avec un cache plus grand
docker buildx create --name production-builder \
  --driver docker-container \
  --config /etc/buildkitd.toml \
  --use

# Configuration buildkitd.toml
[worker.oci]
  max-parallelism = 4
  gc = true
  gckeepstorage = 10000  # 10 Go de cache
  [[worker.oci.gcpolicy]]
    keepBytes = 5000000000  # 5 Go minimum
    keepDuration = 604800   # 7 jours

Cache mounts : accélérer les builds

Les cache mounts sont la fonctionnalité la plus impactante de BuildKit. Ils permettent de persister les caches de gestionnaires de paquets entre les builds, éliminant les téléchargements répétitifs.

# syntax=docker/dockerfile:1

FROM python:3.12-slim AS builder

WORKDIR /app
COPY requirements.txt .

# Cache mount pour pip : les paquets téléchargés sont réutilisés
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-compile -r requirements.txt

# Équivalent pour Node.js
FROM node:20-alpine AS node-builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production

# Équivalent pour apt
FROM debian:bookworm-slim
RUN --mount=type=cache,target=/var/cache/apt \
    --mount=type=cache,target=/var/lib/apt/lists \
    apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates
Impact réel
Les cache mounts réduisent typiquement les rebuilds de 60 à 90%. Un pip install de 3 minutes passe à 10 secondes quand les paquets sont en cache.

Secrets mounts : injecter des credentials sans les exposer

Le problème classique : votre build a besoin d'un token pour accéder à un registre privé, un repo Git, ou une API. Avec l'ancien builder, le secret se retrouvait dans une layer de l'image. BuildKit résout ce problème avec les secret mounts.

# syntax=docker/dockerfile:1

FROM python:3.12-slim

# Le secret est monté en lecture seule, jamais stocké dans une layer
RUN --mount=type=secret,id=pip_conf,target=/etc/pip.conf \
    pip install -r requirements.txt

# Accès à un repo privé via SSH
RUN --mount=type=ssh \
    git clone [email protected]:private/repo.git
# Passer le secret au build
docker buildx build \
  --secret id=pip_conf,src=$HOME/.pip/pip.conf \
  --ssh default=$SSH_AUTH_SOCK \
  -t monapp:latest .
Attention
N'utilisez jamais ARG ou ENV pour passer des secrets. Ces valeurs sont visibles avec docker history et stockées dans les métadonnées de l'image.

Builds parallèles et cache distribué

BuildKit parallélise automatiquement les stages indépendants d'un Dockerfile. Combiné avec un cache distribué via un registry, les builds deviennent rapides même sur des runners CI éphémères.

# Build avec cache distribué via un registry
docker buildx build \
  --cache-from type=registry,ref=registry.example.com/monapp:cache \
  --cache-to type=registry,ref=registry.example.com/monapp:cache,mode=max \
  -t monapp:latest \
  --push .

# Cache local pour les builds fréquents
docker buildx build \
  --cache-from type=local,src=/tmp/buildcache \
  --cache-to type=local,dest=/tmp/buildcache,mode=max \
  -t monapp:latest .

Le mode max exporte toutes les layers intermédiaires, pas uniquement celles de l'image finale. Cela maximise les hits de cache pour les stages de build.

Contenu Premium

Ce tutoriel avancé est réservé aux membres premium.

9,90€ / mois
  • Tous les tutoriels avancés
  • Nouveaux contenus chaque semaine
  • Suivi de progression
  • Annulation à tout moment
MR

Écrit par

Morgann Riu

Expert en cybersécurité et administration Linux. Je partage mes connaissances à travers des tutoriels gratuits et des formations pour aider les administrateurs systèmes et développeurs à sécuriser leurs infrastructures.

Questions fréquentes

Quelle est la différence entre BuildKit et le build Docker classique ?
BuildKit est le moteur de build nouvelle génération intégré à Docker depuis la version 23.0. Il apporte la parallélisation automatique des stages indépendants, les cache mounts pour accélérer les builds (pip, npm, apt), les secret mounts pour injecter des identifiants sans les stocker dans les layers, et un système de cache distribué via des registries. Un build classique exécute les instructions séquentiellement et ne dispose pas de ces optimisations.
Quelle est la différence entre Docker Compose v1 et v2 ?
Compose v2 est intégré directement comme plugin Docker CLI (docker compose sans tiret). Il est écrit en Go au lieu de Python, ce qui le rend plus rapide. Il prend en charge les profiles pour activer des services par environnement, les conditions de healthcheck dans depends_on, les directives deploy pour limiter les ressources, et le watch mode pour le rechargement automatique en développement. Compose v1 (docker-compose avec tiret) est déprécié depuis juillet 2023.
Faut-il utiliser Alpine, Distroless ou scratch comme image de base ?
Alpine est un bon compromis général : léger (environ 5 Mo), il dispose d'un shell et d'un gestionnaire de paquets pour le débogage. Distroless de Google est idéal pour la production à forte contrainte de sécurité : pas de shell, pas de gestionnaire de paquets, surface d'attaque minimale. Scratch est réservé aux binaires statiques (Go, Rust) compilés sans dépendance système. En production, privilégiez Distroless pour les applications interprétées et scratch pour les binaires statiques.
Comment intégrer le scan de vulnérabilités Docker dans une pipeline CI/CD ?
Intégrez Trivy directement dans votre pipeline CI comme étape bloquante après le build de l'image. Configurez un seuil de sévérité (par exemple --severity HIGH,CRITICAL --exit-code 1) pour bloquer le déploiement si des vulnérabilités critiques sont détectées. Combinez le scan d'image avec le scan de fichiers de configuration (trivy config .) pour détecter les Dockerfiles mal configurés. Planifiez aussi des scans réguliers sur les images déjà déployées, car de nouvelles CVE sont publiées quotidiennement.
Quel gain de taille obtient-on avec les multi-stage builds ?
Les gains varient selon le langage et les dépendances, mais ils sont généralement considérables. Une application Go peut passer de plus de 800 Mo (image avec SDK complet) à moins de 10 Mo (binaire statique sur scratch). Une application Node.js passe typiquement de 1 Go à 150-200 Mo en séparant les dépendances de build des dépendances de runtime. Une application Python peut être réduite de 900 Mo à 200 Mo en éliminant les outils de compilation.
Comment gérer les logs Docker en production ?
Évitez le driver json-file par défaut sans rotation, car il peut saturer le disque. Configurez la rotation avec max-size et max-file dans le daemon.json, ou préférez le driver journald pour une intégration systemd native. Pour une stack de production distribuée, utilisez fluentd ou fluent-bit pour centraliser les logs vers Elasticsearch, Loki ou un service cloud. Vos applications doivent écrire sur stdout/stderr pour que Docker capture correctement les sorties.

Partager ce tutoriel

Cet article vous a plu ?

Cet article vous a-t-il été utile ?

Merci pour votre retour !

Commentaires

Recommandé pour vous

Article de fond sur le sujet

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.