RousselTMLEARNING

Conteneurisation

Bonnes pratiques Docker : images, exécution, sécurité et exploitation

Les bonnes pratiques Docker : Dockerfile et builds multi-étapes, images minimales et reproductibles, exécution sans privilèges, limites de ressources, secrets, analyse de vulnérabilités, Compose, journaux et nettoyage.

1. Construire de bonnes images #

Petites, reproductibles et sûres.

Vue d'ensemble #

Familles de bonnes pratiques Docker
Chaque famille est détaillée dans cet article.

Dockerfile optimisé #

Build multi-étapes
L'image finale ne contient ni compilateur ni code source.
# syntax=docker/dockerfile:1
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download      # couche de dépendances mise en cache
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/app ./cmd/app

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/app /app
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/app"]
# .dockerignore : réduire le contexte de build et éviter les fuites
.git
node_modules
*.log
.env
**/*.pem

Reproductibilité #

  • Épinglez les versions : python:3.12.7-slim, jamais latest ; idéalement le digest (image@sha256:...).
  • Un processus par conteneur, journaux sur stdout/stderr.
  • Regroupez les RUN liés et nettoyez dans la même couche (apt-get ... && rm -rf /var/lib/apt/lists/*).
  • Ordonnez du moins changeant (dépendances) au plus changeant (code) pour exploiter le cache.
docker buildx build --platform linux/amd64,linux/arm64 -t registre.exemple.fr/shop/api:1.4.2 --push .
docker image inspect nginx:1.27 --format '{{index .RepoDigests 0}}'      # récupérer le digest
docker history --no-trunc registre.exemple.fr/shop/api:1.4.2 | head       # tailles par couche
docker run --rm wagoodman/dive:latest registre.exemple.fr/shop/api:1.4.2   # explorer les couches

2. Exécuter avec le moindre privilège #

Réduire la surface d'attaque.

Un conteneur durci #

docker run -d --name api \
  --user 10001:10001 \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --cap-drop ALL --security-opt no-new-privileges:true \
  --memory 512m --memory-swap 512m --cpus 1.0 --pids-limit 200 \
  --restart unless-stopped \
  --health-cmd "wget -qO- http://localhost:8080/health || exit 1" --health-interval 30s \
  -p 127.0.0.1:8080:8080 \
  registre.exemple.fr/shop/api:1.4.2
OptionEffet
--userNe jamais exécuter en root
--read-onlySystème de fichiers en lecture seule (+ tmpfs si besoin)
--cap-drop ALLRetire toutes les capabilities Linux
no-new-privilegesInterdit l'élévation de privilèges
--memory / --cpusÉvite qu'un conteneur monopolise l'hôte
-p 127.0.0.1:...N'expose qu'en local derrière un reverse proxy
Erreur Courante : Ne montez jamais /var/run/docker.sock dans un conteneur applicatif et n'utilisez --privileged qu'en dernier recours : cela équivaut à donner root sur l'hôte.

Mode rootless #

dockerd-rootless-setuptool.sh install            # Docker sans privilèges root
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker info --format '{{.SecurityOptions}}'      # doit contenir rootless

3. Sécuriser la chaîne d'approvisionnement #

Secrets, scan et signature.

Secrets #

# ❌ À éviter : secret dans l'image
ENV DB_PASSWORD=secret
# ✅ Secrets de build (non conservés dans l'image)
docker build --secret id=npmrc,src=$HOME/.npmrc .
# Dockerfile : RUN --mount=type=secret,id=npmrc cp /run/secrets/npmrc .npmrc && npm ci
# ✅ Secrets à l'exécution (Compose / Swarm)
services:
  api:
    secrets: [db_password]
secrets:
  db_password: { file: ./secrets/db_password.txt }

Analyse et signature #

Analyse de vulnérabilités avec Trivy
Intégrez cette étape dans la CI et bloquez sur les vulnérabilités critiques.
trivy image --exit-code 1 --severity CRITICAL registre.exemple.fr/shop/api:1.4.2   # échoue la CI
trivy config .                                                              # Dockerfile / manifestes
cosign sign --key cosign.key registre.exemple.fr/shop/api:1.4.2             # signer
cosign verify --key cosign.pub registre.exemple.fr/shop/api:1.4.2           # vérifier avant déploiement
hadolint Dockerfile                                                         # linter de Dockerfile

4. Compose et exploitation #

Du poste de dev à la production.

Compose bien écrit #

services:
  api:
    image: registre.exemple.fr/shop/api:1.4.2
    env_file: [.env]
    depends_on: { db: { condition: service_healthy } }
    deploy: { resources: { limits: { cpus: "1", memory: 512M } } }
    logging: { driver: json-file, options: { max-size: "10m", max-file: "5" } }    # rotation des logs
    healthcheck: { test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"], interval: 30s, retries: 3 }
    networks: [backend]
  db:
    image: postgres:16.4
    volumes: [pgdata:/var/lib/postgresql/data]
    networks: [backend]
networks: { backend: { internal: true } }          # pas d'accès Internet direct pour la base
volumes: { pgdata: {} }
docker compose config                          # valider et afficher la configuration finale

Supervision et nettoyage #

docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
docker events --filter event=die --since 1h
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' api       # OOM ?
docker system df -v
docker system prune -af --filter "until=168h"       # supprimer ce qui est inutilisé depuis 7 jours
Bonne Pratique : Collectez les logs avec Alloy ou Fluent Bit plutôt qu'avec docker logs et exposez les métriques des conteneurs avec cAdvisor pour Prometheus.

Articles liés

Docker : vue d'ensemble, architecture et premiers pas

Découvrir Docker : conteneurs vs machines virtuelles, architecture client/daemon/registre, images et couches,…

Aide-mémoire Docker : Les commandes essentielles

Un guide pratique regroupant toutes les commandes indispensables pour manipuler les images, les conteneurs, l…

HAProxy comme Ingress Controller Kubernetes : Le Guide de Production

Découvrez comment utiliser HAProxy comme Ingress Controller dans Kubernetes. Architecture Direct-to-Pod, term…

Pour aller plus loin