05

🐳 Docker Deep Dive

Containers, Images & Orchestration Locale

"Avant Docker, on livrait du code en croisant les doigts. Aujourd'hui, on livre l'environnement d'exécution tout entier. L'excuse 'Ça marche sur ma machine' a définitivement disparu : on livre la machine."

🍰 Analogie : Le Gâteau Mille-Feuille

Une image Docker est un gâteau à étages (Layers). Chaque instruction RUN, COPY ajoute une couche. Si vous changez la cerise (code), on ne recuit pas tout le gâteau, on change juste la dernière couche. L'optimisation, c'est mettre les ingrédients lourds (OS, Deps) en bas, et les légers (Code) en haut.

📖 Docker : La fin de la "Matrix of Hell"

Avant Docker, déployer une application était un cauchemar de compatibilité (la Matrix of Hell). On devait s'assurer que l'App A (Python 2.7) ne cassait pas l'App B (Python 3.5) sur le même serveur. Docker a apporté l'Isolation Hermétique.

Le concept clé : "Build once, run anywhere". Le package contient tout : code, runtime, bibliothèques, variables d'environnement. Si ça marche sur votre laptop, ça marchera en prod.

💡 Le saviez-vous ? La naissance d'une révolution

En mars 2013, à la conférence PyCon, Solomon Hykes présente un projet interne de sa startup dotCloud : Docker. La démo de 5 minutes scotche l'assistance : lancer un container en moins d'une seconde. L'année d'après, dotCloud devient Docker Inc., et change le monde de l'IT pour toujours.

Aujourd'hui, Docker est l'unité de mesure de base du DevOps.

🏗️ A. Architecture Docker

💻
Docker CLI
Commandes utilisateur
⚙️
Docker Daemon
dockerd (Processus en arrière-plan)
📦
Image
Template read-only (Layers)
🚀
Container
Instance en cours d'exécution
☁️
Registry
Docker Hub, GHCR, ECR

Image vs Container

  • Image = Recette (figée, versionnée, read-only)
  • Container = Gâteau (instance vivante et éphémère de l'image)
  • 1 Image → N Containers identiques

🥊 B. Machine Virtuelle (VM) vs Conteneur

🖥️ Machine Virtuelle (VM)

  • Isolation forte : Matérielle (Hyperviseur).
  • OS Complet : Chaque VM embarque son propre système d'exploitation (Guest OS).
  • Poids : Lourd (plusieurs Gigaoctets).
  • Démarrage : Lent (quelques minutes).
  • Cas d'usage : Exécuter des OS différents, isolation de sécurité maximale.

🐳 Conteneur (Docker)

  • Isolation logique : Processus (Namespaces, Cgroups).
  • Partage kernel : Tous les conteneurs partagent l'OS de l'hôte (Host OS).
  • Poids : Très léger (quelques Mégaoctets).
  • Démarrage : Instantané (quelques millisecondes/secondes).
  • Cas d'usage : Microservices, CI/CD, scalabilité rapide, standardisation.

📜 B. Instructions Dockerfile

Instruction Rôle Exemple
FROM Image de base (obligatoire en 1er) FROM node:20-alpine
WORKDIR Définit le dossier de travail WORKDIR /app
COPY Copie fichiers locaux → image COPY package*.json ./
RUN Exécute une commande (Build Time) RUN npm install
ENV Définit une variable d'environnement ENV NODE_ENV=production
EXPOSE Documente le port (ne l'ouvre pas !) EXPOSE 3000
CMD Commande par défaut au run CMD ["node", "server.js"]
ENTRYPOINT Point d'entrée fixe (CMD = arguments) ENTRYPOINT ["python"]
Dockerfile Multi-Stage (Node.js)
# --- Build Stage ---
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# --- Run Stage (Distroless) ---
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
CMD ["dist/main.js"]

Best Practices (2025)

  • Multi-Stage : Compile dans une image "grosse", run dans une "minuscule"
  • .dockerignore : Exclure node_modules, .git
  • USER : Ne jamais run en root (USER 1001)
  • Layers : Mettre les deps avant le code (cache)

🎼 C. Docker Compose (Multi-Services)

docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432
    depends_on:
      - db

  db:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret

volumes:
  pgdata:

Concepts Clés

  • services : Chaque conteneur est un service
  • ports : Mappage Hôte → Container
  • volumes : Persistence des données
  • depends_on : Ordre de démarrage
💡 Commande : docker compose up -d pour lancer en background.

⌨️ D. Commandes CLI Essentielles

Commande Action
docker build -t myapp . Construire une image depuis le Dockerfile
docker run -d -p 8080:80 nginx Lancer un container en arrière-plan
docker ps Lister les containers actifs
docker logs -f <id> Suivre les logs en temps réel
docker exec -it <id> sh Ouvrir un shell dans le container
docker stop <id> Arrêter un container
docker rm <id> Supprimer un container arrêté
docker system prune -a ⚠️ Nettoyer tout (images, containers, volumes orphans)

🛑 Troubleshooting : Le Container quitte immédiatement (Exit 0)

  • Symptôme : Vous lancez docker run -d ubuntu, mais docker ps ne montre rien. docker ps -a montre que le container est "Exited (0)".
  • Diagnostic : Un container Docker ne reste en vie que tant que son processus principal (PID 1) tourne. L'image de base Ubuntu démarre avec "/bin/bash" par défaut. Sans terminal interactif attaché, bash se termine aussitôt, et donc le container s'arrête.
  • Remède : Pour garder un container en vie "pour rien", ajoutez une tâche bloquante comme tail -f /dev/null ou utilisez docker run -dit ubuntu pour l'attacher à un TTY interactif en arrière-plan.

⚡ E. Modern Build (BuildKit)

Depuis Docker 18.09, BuildKit est le moteur de build par défaut. Il permet des builds parallélisés, plus sûrs et plus rapides grâce au cache intelligent.

Dockerfile (BuildKit Syntax)
# Syntax directive (facultatif mais recommandé)
# syntax=docker/dockerfile:1

FROM node:20-alpine
WORKDIR /app

# Mount Cache : Accélère npm ci x10
RUN --mount=type=cache,target=/root/.npm \
    npm ci

# Mount Secret : Clé SSH sécurisée
RUN --mount=type=secret,id=ssh_key \
    git clone git@github.com:priv/repo.git

CMD ["node", "app.js"]
🚀 Pourquoi BuildKit ?
  • Concurrent Build : Si 2 étapes ne dépendent pas l'une de l'autre, elles se lancent EN MÊME TEMPS.
  • Secrets : Les secrets montés ne sont JAMAIS persistés dans l'image finale (contrairement à COPY).
  • Cache Mounts : Garde le dossier node_modules ou .m2 entre les builds.

🛡️ F. Sécurité & Optimisation

🧹

Distroless

Images Google contenant uniquement l'app et ses deps runtime. Pas de Shell, pas de package manager, pas de ls. Surface d'attaque minimale.

🔍

Scanning (Trivy)

Scanner les CVEs (failles connues) avant de push.
trivy image myapp:latest

👤

Rootless

Ne JAMAIS faire tourner en root.
Ajouter USER node ou USER 1001 à la fin du Dockerfile.

🌐 G. Docker Context

Comment contrôler un Docker sur un serveur distant (prod) depuis son laptop sans ouvrir le port 2375 (dangereux) ?
Réponse : Docker Context over SSH.

Terminal
$ docker context create remote-prod --docker "host=ssh://admin@192.168.1.50"
$ docker context use remote-prod
$ docker ps
# Affiche les containers du serveur PROD, sécurisé via SSH !

📊 Cas d'étude : Spotify - L'Autonomie à l'échelle

500+ Équipes de dev
20k+ Services par jour

Challenge : Comment permettre à 500 équipes de déployer sans qu'une équipe "Ops" centrale ne devienne un goulot d'étranglement ?

Solution : Golden Paths.

  • Spotify fournit des "Golden Paths" (Templates Dockerfile + Pipeline CI/CD pré-approuvés).
  • Si une équipe utilise le Golden Path Docker, elle déploie en prod sans validation humaine.
  • Si elle veut faire du custom, elle doit gérer sa propre sécurité et astreinte.

Résultat : Docker a permis de standardiser l'unité de déploiement, rendant l'autonomie possible.

🖥️ Machines Virtuelles (VM)

  • Isolation Fort : Noyau OS dédié. Plus sécurisé.
  • Lourd : Plusieurs Go par VM. Consomme beaucoup de RAM.
  • Lent : Minutes pour démarrer.
  • Cas d'usage : Isolation totale, OS différents sur un même hôte.

🐳 Containers (Docker)

  • Efficace : Partage le noyau de l'hôte. Quelques Mo.
  • Rapide : Millisecondes pour démarrer.
  • Portabilité : Garanti le fonctionnement identique partout.
  • Cas d'usage : Microservices, CI/CD, Cloud Native apps.

📊 Cas d'étude : Migration Monolithe → Microservices

6 mois Cycle release (Avant)
1 sem Cycle release (Après)
-60% Coût infra

Contexte : Une banque traditionnelle gérait son application de trading avec un monolithe J2EE. Les déploiements étaient si risqués qu'ils n'avaient lieu que deux fois par an.

Transformation :

  1. Containérisation : Migration graduelle vers Docker.
  2. Découpage : Séparation du front, de l'auth et du moteur de prix en micro-services Docker.
  3. Auto-scaling : Utilisation de Docker Swarm (puis K8s) pour gérer les pics de charge.

Résultat : Temps de mise sur le marché divisé par 24. Coût de maintenance réduit grâce à l'optimisation des ressources Docker.

🔄 À Retenir - Docker Deep Dive

  • Image vs Container : L'image est le plan, le container est la maison.
  • Isolation : Les containers partagent le kernel mais sont isolés.
  • Dockerfile : Automatisez la création d'images. Optimisez les layers.
  • Persistence : Utilisez les Volumes pour les données (DB, Logs).