E-NO Logo
EN FR
Docker build cache 9 Min Read

Dépannage du cache de build Docker pour accélérer vos pipelines CI : guide pratique

calendar_today Published: 2026-07-08
update Last Updated: 2026-07-08
analytics SEO Efficiency: 97%
Technical guide illustration for Dépannage du cache de build Docker pour accélérer vos pipelines CI : guide pratique.

Introduction

Des builds Docker lents rongent le budget CI et masquent les échecs réels derrière des files d'attente interminables. Le gain le plus rapide vient d'une compréhension précise de la réutilisation des couches (layer caching) par Docker, puis d'un agencement du Dockerfile et de la CI pour atteindre ce cache de manière prédictible. Ce guide explique comment diagnostiquer les ratés de cache, ordonner les couches, utiliser les BuildKit cache mounts, fiabiliser l'installation des dépendances, persister le cache entre exécutions CI, tester localement avec Docker Compose et déployer sans surprise.

Vue d'ensemble du workflow

Utilisez cette séquence de bout en bout pour des changements sûrs et mesurables :

  1. Baseline
  • Chronométrez un build à froid et un rebuild sans changement.
  • Conservez la sortie couche-par-couche pour comparaison.
  1. Activer BuildKit
  • Activez BuildKit localement et en CI.
  • Basculez en logs "plain" pour la visibilité.
  1. Corriger l'ordre des couches et le contexte
  • Placez d'abord les couches stables.
  • Ajoutez un .dockerignore strict pour éviter les invalidations bruyantes.
  1. Ajouter des cache mounts pour les dépendances
  • Utilisez RUN --mount=type=cache pour persister les caches d'outils.
  1. Persister le cache entre exécutions CI
  • Utilisez docker buildx avec --cache-to et --cache-from.
  1. Dépanner les restes
  • Appliquez une checklist pour dénicher les ratés courants.
  1. Piloter localement, puis déployer
  • Validez performances et justesse sur un service avant généralisation.

Comment fonctionne le Docker build cache

Chaque instruction Dockerfile crée une couche. Docker peut réutiliser une couche si :

  • Le texte de l'instruction est identique.
  • Les build args et l'environnement influençant cette instruction sont identiques.
  • Tous les fichiers lus depuis le contexte par cette instruction sont inchangés.

Implications clés :

  • COPY/ADD sont très sensibles au cache : modifier un seul fichier référencé invalide cette couche et toutes les suivantes.
  • L'ordre des instructions compte : si une couche échoue, toutes les couches après reconstruisent.
  • Un .dockerignore strict réduit les ratés de cache en excluant logs, builds locaux, venvs, node_modules, coverage, tmp et autres bruits.

Exemple .dockerignore :

.git
**/__pycache__/
**/*.pyc
node_modules/
.env
build/
dist/
.coverage
coverage/
tmp/
.DS_Store

Ordonner les couches pour une réutilisation maximale

Organisez votre Dockerfile pour que le travail stable précède le travail volatil :

  • Stables : image de base, paquets OS, runtimes de langage
  • Semi-stables : installation de dépendances gouvernée par des lockfiles
  • Volatiles : code source applicatif

Modèle général :

# syntax=docker/dockerfile:1.7
FROM python:3.11-slim AS app

# 1) Dépendances système d'abord
RUN apt-get update \
 && apt-get install -y --no-install-recommends build-essential \
 && rm -rf /var/lib/apt/lists/*

# 2) Copier uniquement les lockfiles puis installer les deps
WORKDIR /app
COPY requirements.txt ./
# L'installation des deps vient ici (voir cache mount ci-dessous)

# 3) Copier le reste après l'installation des deps
COPY . .

# 4) Commande finale
CMD ["python", "-m", "my_service"]

Pourquoi cela aide : éditer un fichier source n'impose pas de réinstaller les dépendances, car la couche de dépendances ne dépend que de requirements.txt.

À éviter :

  • Ne placez pas des ARG ou ENV volatils avant des couches lourdes.
  • Ne faites pas COPY de tout le dépôt avant d'installer des dépendances guidées par des lockfiles.
  • Gardez les RUN déterministes (pinner les versions, trier les listes de paquets).

BuildKit cache mounts utiles

BuildKit permet de persister les caches d'outils sans les intégrer à l'image finale. Activez-le et utilisez --mount=type=cache.

Activer BuildKit et les logs "plain" :

export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain

Exemple Python avec cache pip wheels :

# syntax=docker/dockerfile:1.7
FROM python:3.11-slim AS app
WORKDIR /app

RUN --mount=type=cache, target=/root/.cache/pip \
    pip install --upgrade pip

COPY requirements.txt ./
RUN --mount=type=cache, target=/root/.cache/pip \
    pip install -r requirements.txt

COPY . .
CMD ["python", "-m", "my_service"]

Exemple Node.js avec cache npm :

# syntax=docker/dockerfile:1.7
FROM node:20-alpine AS app
WORKDIR /app

COPY package*.json ./
RUN --mount=type=cache, target=/root/.npm \
    npm ci --prefer-offline --no-audit --no-fund

COPY . .
CMD ["node", "server.js"]

Exemple Go avec cache des modules :

# syntax=docker/dockerfile:1.7
FROM golang:1.22-alpine AS builder
WORKDIR /src

COPY go.mod go.sum ./
RUN --mount=type=cache, target=/go/pkg/mod \
    go mod download

COPY . .
RUN --mount=type=cache, target=/go/pkg/mod \
    CGO_ENABLED=0 go build -o /out/app ./cmd/app

FROM gcr.io/distroless/base
COPY --from=builder /out/app /app
ENTRYPOINT ["/app"]

Exemple cache APT :

# syntax=docker/dockerfile:1.7
FROM debian: stable-slim
RUN --mount=type=cache, target=/var/cache/apt, sharing=locked \
    --mount=type=cache, target=/var/lib/apt, sharing=locked \
    apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*

Notes :

  • Le cache mount persiste entre builds sur la même machine. Pour persister entre machines CI, exportez/importeZ le cache via buildx (section suivante).
  • Alignez les répertoires de cache sur les valeurs par défaut des outils (pip: /root/.cache/pip, npm: /root/.npm, Go: /go/pkg/mod).

Modèles d'installation des dépendances

Règles à suivre pour une réutilisation fiable :

  • Utilisez toujours des lockfiles (requirements.txt, poetry.lock, Pipfile.lock, package-lock.json, yarn.lock, go.sum). Pinner les versions.
  • COPY uniquement les lockfiles au départ, installez les dépendances, puis COPY le reste du code.
  • Privilégiez des commandes propres et cache-friendly : npm ci pour Node.js ; pip avec cache de wheels ; go mod download avant build.
  • Évitez les téléchargements réseau dans les couches tardives. Placez-les tôt, dans des couches cacheables.
  • En multi-stage, gardez les dépendances de build dans l'étape builder et ne copiez que les artefacts dans l'étape finale.

Exemple multi-stage pour un front React derrière une API Python :

# syntax=docker/dockerfile:1.7
FROM node:20-alpine AS ui
WORKDIR /ui
COPY ui/package*.json ./
RUN --mount=type=cache, target=/root/.npm npm ci --prefer-offline
COPY ui/. .
RUN npm run build

FROM python:3.11-slim AS api
WORKDIR /app
COPY requirements.txt ./
RUN --mount=type=cache, target=/root/.cache/pip pip install -r requirements.txt
COPY . .
COPY --from=ui /ui/build ./static
CMD ["python", "-m", "api"]

Accélérations CI avec registry cache

Utilisez docker buildx pour persister le cache entre exécutions CI (CI speedups) :

Initialiser buildx localement (souvent déjà fait sur les runners CI) :

docker buildx create --use --name ci-builder || docker buildx use ci-builder
docker buildx inspect --bootstrap

Build avec export de cache vers un registry :

# Build et push de l'image et du cache
IMAGE=registry.example.com/my/app:$(git rev-parse --short HEAD)
CACHE_REF=registry.example.com/my/app: buildcache

docker buildx build \
  --progress=plain \
  --push \
  --tag "$IMAGE" \
  --cache-to type=registry, ref=$CACHE_REF,mode=max \
  --cache-from type=registry, ref=$CACHE_REF \
  -f Dockerfile .

Notes :

  • mode=max stocke plus de métadonnées pour une meilleure réutilisation.
  • Gardez CACHE_REF stable pour un repo/service donné.
  • Récupérez cache-from avant le build afin que les runners distants en bénéficient immédiatement.

Docker Compose pour tests locaux (cible de build stage) :

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: app
    environment:
      - PYTHONUNBUFFERED=1
    ports:
      - "8080:8080"
    command: ["python", "-m", "my_service"]

Checklist de dépannage

Parcourez cette liste quand une étape ne se met pas en cache :

  1. Activer les logs "plain"
export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain

Vérifiez quelles étapes affichent CACHED et lesquelles relancent.

  1. Vérifier la taille et le bruit du contexte
  • Lancez : docker buildx build . --progress=plain 2>&1 | head -n 50
  • Assurez-vous que .dockerignore exclut : node_modules, venv, artefacts de build, logs, tmp, .git (sauf besoin explicite).
  1. Vérifier l'ordre des COPY
  • COPY des lockfiles d'abord ; installation ; COPY du code ensuite.
  • Si vous faites COPY . avant l'installation des deps, chaque changement de code casse le cache de la couche deps.
  1. Stabiliser ARG/ENV
  • Les build args qui varient par commit doivent être placés après les couches lourdes.
  • Évitez les RUN non déterministes (timestamps, seeds aléatoires, curl sans version épinglée).
  1. Aligner les cache mounts
  • Confirmez le chemin effectivement utilisé par l'outil (pip: /root/.cache/pip, npm: /root/.npm).
  1. Surveiller la dérive d'image de base
  • Si le digest FROM change souvent, les couches deps sautent. Envisagez un pin sur digest.
  1. Multi-stage et cache-from
  • En multi-stage, vérifiez que cache-from pointe vers la bonne image ou le bon nom de stage déjà poussé.
  1. Reproduire sur une machine propre
  • Faites un pull du cache-from, puis build. Si l'échec persiste, les clés d'entrée du cache changent entre exécutions.

Plan pilote local

Choisissez un service aux builds lents et réalisez une expérience ciblée :

Objectif : Réduire de 40%+ le temps de rebuild sans modifier le comportement en exécution.

Périmètre : Uniquement le cache d'installation des dépendances (ex. wheels pip ou cache npm) et le réordonnancement des couches autour des lockfiles.

Étapes :

  1. Baseline
  • Chronométrez deux builds consécutifs :
time docker build -t pilot: baseline .
time docker build -t pilot: rebuild .

Conservez le second temps : c'est votre baseline de cache hit.

  1. Activer BuildKit et réordonner les couches
  • Ajoutez "# syntax=docker/dockerfile:1.7"
  • Exportez DOCKER_BUILDKIT=1 et BUILDKIT_PROGRESS=plain
  • COPY lockfiles d'abord ; installez deps ; puis COPY du code.
  1. Ajouter des cache mounts
  • Pour pip : --mount=type=cache, target=/root/.cache/pip
  • Pour npm : --mount=type=cache, target=/root/.npm
  1. Rebuild et mesure
time docker build -t pilot: optimized .
time docker build -t pilot: optimized2 .

Attendez-vous à un second build optimisé bien plus rapide.

  1. Vérification fonctionnelle avec Docker Compose
docker compose up --build
# Testez rapidement le service (smoke test)
  1. Option : tester le cache soutenu par registry
  • Poussez avec --cache-to et rebuild sur un runner frais avec --cache-from.

Critères de sortie :

  • Temps de rebuild réduit de 40%+
  • Le service démarre et passe un smoke test basique

Si l'essai réussit, appliquez le schéma aux autres services.

Conclusion

Vous pouvez réduire fortement les temps de build CI en combinant quatre pratiques : un ordonnancement correct des couches, des BuildKit cache mounts pour les dépendances, un .dockerignore strict et un cache soutenu par registry pour la réutilisation entre runners. Commencez par un petit pilote local, mesurez les gains, puis déployez progressivement la stratégie. Cette approche fournit des CI speedups mesurables, des Dockerfile examples reproductibles, et un chemin clair pour éviter les production pitfalls liés au cache tout en conservant des docker commands et un Docker Compose simples pour les tests locaux.

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL