DOCKER

Concernant les supports de cours

Supports de cours

Les cours sont maintenus et donnés par : https://www.alterway.fr/formation

Licence Creative Commons BY-SA 4.0
Licence Creative Commons BY-SA 4.0

DOCKER : Environnement de Formation & Méthodologie

Pédagogie & Déroulement de la Formation

┌─────────────────────────────────────────────────────────────┐
│                    APPRENDRE PAR LA PRATIQUE                │
│                                                             │
│   ┌───────────────────┐               ┌─────────────────┐   │
│   │  Cours & Concepts │ ────────────► │   Laboratoires  │   │
│   │   Architecturaux  │               │   & Challenges  │   │
│   └───────────────────┘               └─────────────────┘   │
│             ▲                                   │           │
│             └────────── Questions / Réponses ───┘           │
└─────────────────────────────────────────────────────────────┘
  • Principe fondamental : 70% de pratique en conditions réelles, 30% d'apports méthodologiques et architecturaux.
  • Rythme : Alternance continue entre présentations conceptuelles, démonstrations du formateur et ateliers autonomes.
  • Esprit d'équipe : Échange permanent, entraide et résolution collective des cas d'erreur.

Logistique de Session

  • Durée : 2 à 3 jours intensifs selon les parcours.
  • Pauses régulières : Toutes les 1h30 à 2h pour favoriser l'assimilation.
  • Supports et ressources :
  • Slides et cours versionnés sur Git.
  • Plateforme de laboratoires interactive : https://bit.ly/docker-labs (ou environnement cloud dédié).

Prérequis Techniques

  • Connaissances attendues :
  • Maîtrise de base du terminal Linux (navigation, gestion des fichiers, droits, variables d'environnement).
  • Notions fondamentales de réseau (adresses IP, ports TCP/UDP, DNS, protocole HTTP).
  • Notions de développement ou d'administration système.

Votre Poste de Travail & Outils Recommandés

Pour tirer le meilleur parti de cette formation :

┌──────────────────────────────────────────────────────────────────┐
│                     LA BOÎTE À OUTILS DU CONTENEUR               │
├───────────────────────────────┬──────────────────────────────────┤
│ Docker Engine (v25+)          │ Moteur d'exécution & daemon      │
│ Docker CLI & Plugins          │ buildx, compose v2, scout        │
│ Éditeur de code (VS Code/etc) │ Extensions Docker & Dev Containers│
│ Shell moderne (Bash / Zsh)    │ Autocomplétion & alias rapides   │
└───────────────────────────────┴──────────────────────────────────┘

Installation Rapide & Autocomplétion

# Script d'installation automatisée officiel pour environnements Linux
curl -fsSL https://get.docker.com | sh

# Ajout de l'utilisateur courant au groupe docker (éviter sudo systématique)
sudo usermod -aG docker $USER
newgrp docker

# Activation de l'autocomplétion Zsh / Bash
source <(docker completion zsh)  # ou: source <(docker completion bash)

Vérification de l'Environnement

Exécutez la commande suivante pour valider votre installation :

docker version
Client: Docker Engine - Community
 Version:           29.7.2
 API version:       1.48
 Go version:        go1.23.6
 Git commit:        9b3c45a
 Built:             Wed Jan 28 10:00:00 2026
 OS/Arch:           linux/amd64

Server: Docker Engine - Community
 Engine:
  Version:          29.7.2
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.6
  Git commit:       8a2bc41
  Built:            Wed Jan 28 10:00:00 2026
  OS/Arch:          linux/amd64

DOCKER : De la Virtualisation aux Conteneurs

La Friction dans la Chaîne d'Approvisionnement Logiciel

Avant les conteneurs, le déploiement d'applications était une source constante de frictions :

   DÉVELOPPEMENT                        PRODUCTION
┌──────────────────┐               ┌──────────────────┐
│  "Ça marche sur  │               │ "C'est planté en │
│  ma machine !"   │ ────────────► │   production !"  │
│ (Node 20, macOS) │   LIVRAISON   │ (Node 16, RHEL 7)│
└──────────────────┘               └──────────────────┘
  • Différences d'OS, de bibliothèques système, de versions de runtimes.
  • Dépendances implicites non documentées dans les procédures manuelles.
  • Délais de mise en production mesurés en semaines, voire en mois.

Le Cauchemar du Déploiement : La "Matrix from Hell"

Sans standard d'empaquetage, chaque application doit être adaptée à chaque environnement :

┌─────────────────┬─────────────┬───────────┬────────────┬─────────────┐
│  APPLICATIONS   │ Machine Dev │ VM de Test│ Cloud AWS  │ Bare-Metal  │
├─────────────────┼─────────────┼───────────┼────────────┼─────────────┤
│ Static Web (PHP)│      ?      │     ?     │     ?      │      ?      │
│ Node.js / React │      ?      │     ?     │     ?      │      ?      │
│ Python / AI     │      ?      │     ?     │     ?      │      ?      │
│ Base PostgreSQL │      ?      │     ?     │     ?      │      ?      │
│ Redis / RabbitMQ│      ?      │     ?     │     ?      │      ?      │
└─────────────────┴─────────────┴───────────┴────────────┴─────────────┘
  NxM Combinaisons = NxM Problèmes de configuration et de dépendances

L'Analogie Révolutionnaire : Le Conteneur Intermodal

En 1956, Malcolm McLean invente le conteneur maritime standardisé (ISO) :

┌─────────────────────────────────────────────────────────────┐
│                LE STANDARD DU TRANSPORT PHYSIQUE            │
│  Peu importe la cargaison (voitures, café, électronique),    │
│  elle rentre dans un conteneur standardisé manipulable      │
│  par tous les bateaux, trains, camions et grues du monde.   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                 LE STANDARD DU LOGICIEL (DOCKER)            │
│  Peu importe le langage (Java, Go, Python, Rust, PHP),      │
│  le code et ses dépendances sont emballés dans un conteneur │
│  exécutable à l'identique sur n'importe quel serveur.       │
└─────────────────────────────────────────────────────────────┘

L'Évolution de l'Encapsulation Informatique

1. SERVEUR PHYSIQUE             2. MACHINE VIRTUELLE             3. CONTENEURS
┌──────────────────┐           ┌──────────────────┐           ┌──────────────────┐
│   Application    │           │ App 1  │  App 2  │           │ App 1  │  App 2  │
├──────────────────┤           ├────────┼─────────┤           ├────────┼─────────┤
│  OS Monolithique │           │Guest OS│Guest OS │           │ Bins / Libs isolés│
├──────────────────┤           ├────────┴─────────┤           ├──────────────────┤
│ Matériel Dédié   │           │   Hyperviseur    │           │  Docker Engine   │
│ (1 App / Serveur)│           ├──────────────────┤           ├──────────────────┤
│                  │           │   Host OS / KVM  │           │   Linux Kernel   │
│                  │           ├──────────────────┤           ├──────────────────┤
│                  │           │     Matériel     │           │     Matériel     │
└──────────────────┘           └──────────────────┘           └──────────────────┘
  Gaspillage 90% CPU             Isolation lourde              Léger, rapide, dense

Comparatif Technique : Machine Virtuelle vs Conteneur

Critère Machine Virtuelle (VM) Conteneur Docker
Niveau d'abstraction Matériel complet (Hardware) Noyau du système (OS Kernel)
Poids de l'image Plusieurs Go (Guest OS complet) Quelques Mo à quelques centaines de Mo
Temps de démarrage Plusieurs minutes Quelques millisecondes à secondes
Overhead CPU / RAM Consommation fixe allouée par VM Négligeable (processus natif sur l'hôte)
Densité par serveur 10 à 50 VMs par serveur Plusieurs centaines de conteneurs
Isolation Étanche (ring 0 / hyperviseur) Logique (Namespaces + Cgroups Linux)

La Règle d'Or : "Any App, Anywhere"

┌──────────────────────────────────────────────────────────────────┐
│           CONSTRUIRE           PARTAGER            EXÉCUTER      │
│            (Build)              (Ship)              (Run)        │
│                                                                  │
│         ┌───────────┐        ┌──────────┐        ┌───────────┐   │
│         │ Dockerfile│ ─────► │ Registry │ ─────► │ Production│   │
│         │   Source  │        │ (Hub/ACR)│        │ Kubernetes│   │
│         └───────────┘        └──────────┘        └───────────┘   │
└──────────────────────────────────────────────────────────────────┘
  • Développeur : Ne s'occupe que de son application et de ses dépendances.
  • Administrateur / Ops : Ne s'occupe que de fournir une plateforme Docker/Kubernetes stable.
  • Séparation nette des responsabilités (Separation of Concerns).

Bref Historique & Standardisation OCI

  • 2010 : Création de dotCloud (PaaS) par Solomon Hykes et Sébastien Pahl.
  • 2013 : Open source de Docker en Go → explosion mondiale de l'écosystème.
  • 2015 : Création de l'OCI (Open Container Initiative) sous l'égide de la Linux Foundation :
  • image-spec : Spécification universelle du format d'image.
  • runtime-spec : Spécification universelle d'exécution de conteneur (runc).
  • Aujourd'hui : Docker utilise BuildKit, containerd, et s'intègre nativement à Kubernetes.

Mini-Défi : VM vs Conteneur

Question : Quelle est la différence architecturale majeure entre une Machine Virtuelle et un Conteneur ?

  • A. Les conteneurs embarquent chacun leur propre noyau Linux complet.
  • B. Les conteneurs partagent le noyau Linux de l'hôte et isolent les processus applicatifs.
  • C. Les VMs consomment moins de mémoire vive que les conteneurs.
  • D. Un conteneur émule le BIOS et la carte mère du serveur hôte.

Mini-Défi : VM vs Conteneur

Question : Quelle est la différence architecturale majeure entre une Machine Virtuelle et un Conteneur ?

  • A. Les conteneurs embarquent chacun leur propre noyau Linux complet.
  • B. Les conteneurs partagent le noyau Linux de l'hôte et isolent les processus applicatifs.
  • C. Les VMs consomment moins de mémoire vive que les conteneurs.
  • D. Un conteneur émule le BIOS et la carte mère du serveur hôte.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  Contrairement à une VM qui démarre un Guest OS complet sur │
│  un hyperviseur, un conteneur est un simple processus hôte  │
│  isolé par les Namespaces et Cgroups du noyau Linux.        │
└─────────────────────────────────────────────────────────────┘

Mini-Défi : Le Standard OCI

Question : Que garantit le standard OCI (Open Container Initiative) dans l'écosystème moderne ?

  • A. Que Docker soit obligatoirement la seule plateforme autorisée.
  • B. Que les conteneurs puissent fonctionner sans aucun système d'exploitation.
  • C. Qu'une image construite avec Docker soit exécutable à l'identique avec containerd, Podman ou CRI-O.
  • D. Que le code source soit automatiquement chiffré en AES-256.

Mini-Défi : Le Standard OCI

Question : Que garantit le standard OCI (Open Container Initiative) dans l'écosystème moderne ?

  • A. Que Docker soit obligatoirement la seule plateforme autorisée.
  • B. Que les conteneurs puissent fonctionner sans aucun système d'exploitation.
  • C. Qu'une image construite avec Docker soit exécutable à l'identique avec containerd, Podman ou CRI-O.
  • D. Que le code source soit automatiquement chiffré en AES-256.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Grâce aux spécifications OCI (`image-spec` et              │
│  `runtime-spec`), les formats d'images et les runtimes bas  │
│  niveau (`runc`) sont universels et interopérables.         │
└─────────────────────────────────────────────────────────────┘

DOCKER : Architecture Interne & Fondations Linux

La Vérité Fondamentale : Un Conteneur n'Existe Pas !

Sur Linux, un conteneur n'est pas une entité matérielle ou une machine virtuelle.

Un conteneur est un simple processus Linux standard, exécuté directement par le processeur de l'hôte, mais isolé et contraint par des fonctionnalités natives du noyau Linux : 1. Les Namespaces (pour isoler ce que le processus peut voir). 2. Les Cgroups (pour limiter ce que le processus peut consommer). 3. Overlay2 / UnionFS (pour lui fournir son propre système de fichiers).

Les 7 Namespaces du Noyau Linux

Les Namespaces virtualisent les ressources globales de l'OS en espaces isolés :

┌──────────────────────────────────────────────────────────────────┐
│                LES 7 NAMESPACES LINUX DU CONTENEUR               │
├──────────────────┬───────────────────────────────────────────────┤
│ 1. PID Namespace │ Arborescence de processus isolée (PID 1 dédié)│
│ 2. NET Namespace │ Interfaces réseau (eth0, lo), tables IP/ports │
│ 3. MNT Namespace │ Points de montage et système de fichiers isolé│
│ 4. UTS Namespace │ Nom d'hôte (hostname) et nom de domaine       │
│ 5. IPC Namespace │ Communication inter-processus & mémoire part. │
│ 6. USER Namespace│ Mappage des UIDs/GIDs (root conteneur != host)│
│ 7. CGROUP NS     │ Masquage de la hiérarchie cgroup parente      │
└──────────────────┴───────────────────────────────────────────────┘

L'Isolation des Processus : PID Namespaces

Le même processus possède deux identifiants : un PID local vu dans le conteneur, et un PID réel sur l'hôte.

                    PARENT PID NAMESPACE (HÔTE LINUX)
┌──────────────────────────────────────────────────────────────────┐
│ PID 1 : /sbin/init (systemd)                                     │
│   └── PID 1042 : dockerd                                         │
│         └── PID 1120 : containerd                                │
│               ├── PID 2104 : containerd-shim (Conteneur Nginx)   │
│               │     └── PID 2180 : nginx: master process         │
│               └── PID 3240 : containerd-shim (Conteneur Python)  │
│                     └── PID 3310 : python app.py                 │
└───────────────────────────────────┬──────────────────────────────┘
                                    │
                                    ▼
       CONTENEUR NGINX (PID NS 1)        CONTENEUR PYTHON (PID NS 2)
      ┌──────────────────────────┐      ┌──────────────────────────┐
      │ PID 1 : nginx: master    │      │ PID 1 : python app.py    │
      │ PID 2 : nginx: worker    │      │ PID 2 : worker thread    │
      └──────────────────────────┘      └──────────────────────────┘

Control Groups (Cgroups v1 & v2) : Le Budget de Ressources

Les Cgroups garantissent qu'un conteneur ne peut pas saturer ou affamer la machine hôte (Noisy Neighbor problem) :

  • CPU : Partage relatif (--cpu-shares) ou plafond strict en millicores (--cpus=1.5).
  • Mémoire RAM : Limite stricte (--memory=512m). Si le conteneur dépasse ce seuil, le noyau déclenche l'OOM Killer (Out Of Memory - code sortie 137).
  • Entrées/Sorties (I/O) : Débit maximal de lecture/écriture disque (--device-read-bps).
  • Nombre de processus (PIDs) : Protection contre les fork bombs (--pids-limit=100).

Le Système de Fichiers : UnionFS & Driver Overlay2

Un conteneur combine plusieurs couches en lecture seule avec une couche supérieure en lecture/écriture :

┌─────────────────────────────────────────────────────────────┐
│ 4. MERGED VIEW (Ce que voit l'application dans le conteneur)│
├─────────────────────────────────────────────────────────────┤
│ 3. UPPERDIR (Lecture / Écriture - Couche du conteneur)      │ ── Modifiable
├─────────────────────────────────────────────────────────────┤
│ 2. LOWERDIR 2 (Couche Image - Configuration & Code)         │ ── Lecture Seule
├─────────────────────────────────────────────────────────────┤
│ 1. LOWERDIR 1 (Couche Image - Base OS Ubuntu / Alpine)      │ ── Lecture Seule
└─────────────────────────────────────────────────────────────┘

Le mécanisme de Copy-on-Write (CoW) : - Lors d'une lecture : les fichiers sont lus directement dans les couches de l'image (lowerdir). - Lors d'une écriture sur un fichier existant : le fichier est copié depuis l'image vers la couche supérieure (upperdir) avant modification.

Architecture Modulaire de Docker Engine

Docker n'est pas un bloc monolithique, mais une suite de composants spécialisés OCI :

┌─────────────────────────────────────────────────────────────┐
│ 1. CLIENT DOCKER (CLI) : `docker run`, `docker build`       │
└──────────────────────────────┬──────────────────────────────┘
                               │ Appel REST / Socket UNIX
                               ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. DOCKER DAEMON (`dockerd`) : API, Réseau, Volumes, Builds │
└──────────────────────────────┬──────────────────────────────┘
                               │ Interface gRPC
                               ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. CONTAINERD : Gestionnaire complet du cycle de vie OCI    │
└──────────────────────────────┬──────────────────────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            ▼                                     ▼
┌───────────────────────────────┐   ┌───────────────────────────┐
│ containerd-shim (Conteneur A) │   │ containerd-shim (Cont. B) │
├───────────────────────────────┤   ├───────────────────────────┤
│ runc (Instancie namespaces)   │   │ runc (Instancie namespaces│
├───────────────────────────────┤   ├───────────────────────────┤
│ Processus applicatif (App A)  │   │ Processus applicatif (App)│
└───────────────────────────────┘   └───────────────────────────┘
  • containerd-shim : Permet de redémarrer ou mettre à jour le démon Docker sans tuer les conteneurs en cours d'exécution (Live Restore).
  • runc : L'implémentation de référence OCI qui configure les appels noyau (clone, unshare, pivot_root).

Mini-Défi : Crash de Démon & Live Restore

Question : Que se passe-t-il si le démon dockerd crashe ou est redémarré avec l'option live-restore: true ?

  • A. Tous les conteneurs sont immédiatement tués et perdent leurs données.
  • B. Le système hôte redémarre automatiquement.
  • C. Grâce à containerd-shim, les processus applicatifs continuent de tourner normalement sans aucune coupure.
  • D. Les adresses IP de tous les conteneurs sont réinitialisées.

Mini-Défi : Crash de Démon & Live Restore

Question : Que se passe-t-il si le démon dockerd crashe ou est redémarré avec l'option live-restore: true ?

  • A. Tous les conteneurs sont immédiatement tués et perdent leurs données.
  • B. Le système hôte redémarre automatiquement.
  • C. Grâce à containerd-shim, les processus applicatifs continuent de tourner normalement sans aucune coupure.
  • D. Les adresses IP de tous les conteneurs sont réinitialisées.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Grâce à `containerd-shim`, les processus applicatifs       │
│  continuent de s'exécuter normalement sans aucune coupure ! │
│  Le démon `dockerd` se reconnecte simplement aux shims dès  │
│  son redémarrage.                                           │
└─────────────────────────────────────────────────────────────┘

DOCKER : Gestion & Cycle de Vie des Conteneurs

La Machine à États du Conteneur

Un conteneur traverse différents états tout au long de sa vie :

                    ┌─────────────┐
                    │  NON EXISTE │
                    └──────┬──────┘
             docker create │   ▲
             ou run        │   │ docker rm
                           ▼   │
┌─────────────────────────► CREATED ◄─────────────────────────┐
│                             │                               │
│                             │ docker start                  │
│                 docker stop │                               │
│                 ou kill     ▼                               │
│              ┌─────────── RUNNING ───────────┐              │
│              │              ▲                │              │
│  Fin du PID 1│  docker      │ docker         │ docker       │
│              │  unpause     │ pause          │ stop/kill    │
│              │              ▼                │              │
│              │           PAUSED              │              │
│              │                               │              │
│              ▼                               ▼              │
└─────────── EXITED ───────────────────────── DEAD ───────────┘

Le Cycle de Vie Quotidien en CLI

Docker utilise désormais la syntaxe unifiée orientée objets (docker container <action>) :

# 1. Créer et démarrer en arrière-plan (detached)
docker container run -d --name web-app -p 8080:80 nginx:alpine

# 2. Inspecter les conteneurs actifs
docker container ls  # ou: docker ps

# 3. Mettre en pause / Réactiver les processus (sans décharger la mémoire)
docker container pause web-app
docker container unpause web-app

# 4. Arrêter proprement (envoi SIGTERM puis SIGKILL après 10s)
docker container stop web-app

# 5. Supprimer le conteneur arrêté
docker container rm web-app

Les Modes d'Exécution : Interactif vs Détaché

        MODE INTERACTIF (-it)                       MODE DÉTACHÉ (-d)
┌──────────────────────────────────┐       ┌──────────────────────────────────┐
│ Terminal attaché au conteneur    │       │ Exécution en arrière-plan        │
│ Idéal pour déboguer, tester      │       │ Idéal pour serveurs web, APIs, DB│
│ Stoppe à la sortie du terminal   │       │ Reste actif tant que PID 1 tourne│
└──────────────────────────────────┘       └──────────────────────────────────┘
# Lancer un shell interactif éphémère (--rm supprime à la sortie)
docker container run -it --rm ubuntu:22.04 bash

# Lancer un serveur web pérenne en tâche de fond
docker container run -d --name api-server -p 3000:3000 node:20-alpine

Exécuter des Commandes dans un Conteneur en Cours d'Exécution

La commande docker exec permet d'injecter un nouveau processus dans les namespaces existants d'un conteneur :

# Ouvrir un shell d'administration dans un conteneur actif
docker container exec -it web-app sh

# Exécuter une commande ponctuelle sans ouvrir de session
docker container exec web-app nginx -t
docker container exec web-app cat /etc/hosts

Analyse des Logs & Débogage en Direct

# 1. Afficher les 50 dernières lignes de logs
docker container logs --tail 50 web-app

# 2. Suivre les logs en temps réel avec horodatage (Follow + Timestamps)
docker container logs -f -t web-app

# 3. Filtrer par date relative
docker container logs --since 15m web-app

Politiques de Redémarrage Automatique (--restart)

En production, un conteneur doit pouvoir se relancer automatiquement après un crash ou un redémarrage de la machine hôte :

┌───────────────────┬────────────────────────────────────────────────────────┐
│ POLITIQUE         │ COMPORTEMENT                                           │
├───────────────────┼────────────────────────────────────────────────────────┤
│ `no`              │ Jamais de redémarrage automatique (par défaut)         │
│ `on-failure[:max]`│ Redémarre uniquement si code retour != 0 (max N fois)  │
│ `always`          │ Redémarre toujours, y compris au reboot de l'hôte      │
│ `unless-stopped`  │ Comme always, sauf si stoppé manuellement par un admin │
└───────────────────┴────────────────────────────────────────────────────────┘
# Exemple recommandé en production :
docker container run -d --name db-prod --restart unless-stopped postgres:16

Le Défi du Processus PID 1 & l'Option --init

Dans un conteneur, le processus principal prend le PID 1 : - Il doit gérer la réception et propagation des signaux système (SIGTERM, SIGINT). - Il doit récolter les processus enfants terminés (Zombie processes / Init Reaping).

# Si votre application (ex: Node.js, Python, Java) gère mal les signaux :
# Utilisez l'option --init (injecte le mini-init 'tini' en PID 1)
docker container run -d --init --name node-app my-node-image:latest

Mini-Défi : Diagnostic d'Arrêt (Exit 137)

Incident SRE : Votre conteneur Java ou Node.js s'arrête brutalement avec le statut Exited (137). Quelle en est la cause principale ?

  • A. L'application a terminé son traitement avec succès (code 0).
  • B. Le fichier binaire ou la commande spécifiée dans CMD est introuvable.
  • C. Le conteneur a reçu un signal SIGKILL (128 + 9), déclenché par l'OOM Killer (dépassement du quota RAM).
  • D. Le port réseau demandé est déjà réservé sur la machine hôte.

Mini-Défi : Diagnostic d'Arrêt (Exit 137)

Incident SRE : Votre conteneur Java ou Node.js s'arrête brutalement avec le statut Exited (137). Quelle en est la cause principale ?

  • A. L'application a terminé son traitement avec succès (code 0).
  • B. Le fichier binaire ou la commande spécifiée dans CMD est introuvable.
  • C. Le conteneur a reçu un signal SIGKILL (128 + 9), déclenché par l'OOM Killer (dépassement du quota RAM).
  • D. Le port réseau demandé est déjà réservé sur la machine hôte.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Le code 137 correspond au signal SIGKILL (128 + 9).        │
│  Dans 95% des cas en production, il est émis par l'OOM      │
│  Killer du noyau Linux suite au dépassement de `--memory`.  │
└─────────────────────────────────────────────────────────────┘

Mini-Défi : Surveillance en Temps Réel

Question : Quelle commande native permet de suivre en direct la consommation CPU/RAM et les flux I/O de tous vos conteneurs ?

  • A. docker inspect --all
  • B. docker top
  • C. docker stats
  • D. docker system monitor

Mini-Défi : Surveillance en Temps Réel

Question : Quelle commande native permet de suivre en direct la consommation CPU/RAM et les flux I/O de tous vos conteneurs ?

  • A. docker inspect --all
  • B. docker top
  • C. docker stats
  • D. docker system monitor
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  `docker stats` affiche un tableau de bord dynamique en     │
│  temps réel (CPU %, RAM consommée / limite, I/O réseau).    │
└─────────────────────────────────────────────────────────────┘

DOCKER : Fondamentaux des Images & Registries

Qu'est-ce qu'une Image Docker ?

Une image est un modèle immuable et en lecture seule contenant tout le nécessaire pour exécuter une application :

┌─────────────────────────────────────────────────────────────┐
│                    ANATOMIE D'UNE IMAGE OCI                 │
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ 1. MANIFESTE JSON (Métadonnées, Architecture, OS)   │   │
│   ├─────────────────────────────────────────────────────┤   │
│   │ 2. CONFIG JSON (Variables ENV, ENTRYPOINT, Ports)   │   │
│   ├─────────────────────────────────────────────────────┤   │
│   │ 3. COUCHE 3 : Code source applicatif (/app)         │   │
│   ├─────────────────────────────────────────────────────┤   │
│   │ 2. COUCHE 2 : Dépendances et bibliothèques (pip/npm)│   │
│   ├─────────────────────────────────────────────────────┤   │
│   │ 1. COUCHE 1 : Système de base minimal (Debian/Alpine)│  │
│   └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
  • Chaque couche correspond à une empreinte cryptographique unique (SHA256 Content Addressable).
  • Si deux images partagent la même couche de base, elle n'est téléchargée et stockée qu'une seule fois.

Le Partage des Couches (Layer Sharing)

       IMAGE 1 (PYTHON APP)                       IMAGE 2 (NGINX APP)
┌─────────────────────────────────┐       ┌─────────────────────────────────┐
│ Layer 3 : app.py                │       │ Layer 3 : nginx.conf + html     │
├─────────────────────────────────┤       ├─────────────────────────────────┤
│ Layer 2 : python3 + pip         │       │ Layer 2 : nginx binary          │
├─────────────────────────────────┴───────┴─────────────────────────────────┤
│          Layer 1 PARTAGÉ : debian:12-slim (sha256:8a4b...)                │
│             (Téléchargé 1 seule fois, 0 Mo dupliqué !)                    │
└───────────────────────────────────────────────────────────────────────────┘
  • Économie drastique d'espace disque sur l'hôte et sur le registre.
  • Téléchargements quasi-instantanés lors des déploiements successifs.

Espaces de Noms (Namespaces) et Structure des Tags

Une image est identifiée selon sa portée dans les registres :

┌────────────────────────────┬──────────────────────────────────┬─────────────────────────────────┐
│ 1. ESPACE ROOT (Officiel)  │ 2. ORGANISATION / UTILISATEUR    │ 3. REGISTRE PRIVÉ COMPLET (FQDN)│
├────────────────────────────┼──────────────────────────────────┼─────────────────────────────────┤
│ `nginx:1.25-alpine`        │ `alterway/api-gateway:2.4.0`     │ `ghcr.io/alterway/auth:v1.0`    │
│ `postgres:16`              │ `herveleclerc/demo:latest`       │ `registry.gitlab.com/grp/app:1` │
│ `ubuntu:22.04`             │                                  │ `12345.dkr.ecr.eu-west-3...`    │
└────────────────────────────┴──────────────────────────────────┴─────────────────────────────────┘

Le Piège du Tag latest vs les Digests SHA256

⚠️ IMPORTANT : Le tag latest n'est PAS une version figée ! latest est simplement le tag appliqué par défaut lorsqu'aucun tag n'est spécifié. Il peut pointer vers une version différente d'un jour à l'autre.

# Mauvaise pratique en production (imprévisible / non reproductible) :
docker run -d my-app:latest

# Bonne pratique (Tag sémantique immuable) :
docker run -d my-app:1.4.2

# Pratique ultra-sécurisée (Digest cryptographique garanti infalsifiable) :
docker run -d my-app@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Commandes Clés de Manipulation des Images

# 1. Télécharger une image depuis un registre
docker image pull nginx:alpine

# 2. Lister les images locales et inspecter leur taille réelle
docker image ls

# 3. Créer un nouveau tag (alias) pour préparer la publication
docker image tag my-app:1.0 ghcr.io/my-org/my-app:1.0

# 4. Authentification et publication sur un registre distant
docker login ghcr.io -u <username>
docker image push ghcr.io/my-org/my-app:1.0

# 5. Inspecter l'historique et les couches d'une image
docker image history nginx:alpine

Sauvegarde & Transfert Hors-Ligne (save & load)

Pour les environnements déconnectés d'Internet (Air-Gapped / Datacenter sécurisé) :

# 1. Exporter une ou plusieurs images dans une archive tar
docker image save -o images-bundle.tar nginx:alpine postgres:16

# 2. Transférer le fichier tar vers le serveur cible (via SCP, clé USB sécurisée)
scp images-bundle.tar user@serveur-prod:/tmp/

# 3. Réimporter l'image dans le moteur Docker cible
docker image load -i /tmp/images-bundle.tar

Mini-Défi : Suppression & Partage de Couches

Question : Que se passe-t-il lorsque l'on supprime une image avec docker image rm my-app:1.0 sachant qu'elle partage des couches avec my-app:2.0 ?

  • A. Toutes les couches sont détruites et my-app:2.0 devient corrompue.
  • B. La commande est refusée tant que my-app:2.0 existe sur l'hôte.
  • C. Seul le tag 1.0 et ses couches spécifiques non partagées sont supprimés ; les couches communes restent préservées.
  • D. Toutes les images locales du registre sont réinitialisées.

Mini-Défi : Suppression & Partage de Couches

Question : Que se passe-t-il lorsque l'on supprime une image avec docker image rm my-app:1.0 sachant qu'elle partage des couches avec my-app:2.0 ?

  • A. Toutes les couches sont détruites et my-app:2.0 devient corrompue.
  • B. La commande est refusée tant que my-app:2.0 existe sur l'hôte.
  • C. Seul le tag 1.0 et ses couches spécifiques non partagées sont supprimés ; les couches communes restent préservées.
  • D. Toutes les images locales du registre sont réinitialisées.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Grâce au modèle en couches adressé par le contenu          │
│  (Content Addressable Storage / SHA256), Docker ne supprime │
│  que le pointeur de tag et les blobs orphelins.             │
└─────────────────────────────────────────────────────────────┘

DOCKER : Maîtrise du Dockerfile

Anatomie d'un Dockerfile Moderne

Le Dockerfile est le manifeste déclaratif automatisant la fabrication d'une image Docker :

# 1. Image de base officielle et épurée
FROM node:20-alpine

# 2. Définition du répertoire de travail (créé automatiquement)
WORKDIR /app

# 3. Variables d'environnement d'exécution
ENV NODE_ENV=production
ENV PORT=3000

# 4. Copie des descripteurs de dépendances en premier (Optimisation Cache)
COPY package*.json ./

# 5. Installation des dépendances de production
RUN npm ci --only=production && npm cache clean --force

# 6. Copie du reste du code source applicatif
COPY . .

# 7. Création et bascule sur un utilisateur non-root (Sécurité)
USER node

# 8. Documentation du port d'écoute
EXPOSE 3000

# 9. Sonde de santé intégrée au conteneur
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# 10. Commande d'exécution sous forme Exec
CMD ["node", "server.js"]

Le Guide des Instructions Fondamentales

┌────────────┬─────────────────────────────────────────────────────────────┐
│ INSTRUCTION│ RÔLE ET CAS D'USAGE RECOMMANDÉ                              │
├────────────┼─────────────────────────────────────────────────────────────┤
│ `FROM`     │ Initialise le build et sélectionne l'image de départ        │
│ `WORKDIR`  │ Définit le dossier d'exécution (évite les `cd /chemin &&`)  │
│ `COPY`     │ Copie des fichiers/dossiers locaux vers le conteneur        │
│ `ADD`      │ Comme COPY + extraction automatique de `.tar.gz` ou URLs    │
│ `RUN`      │ Exécute une commande pendant le build et crée une couche    │
│ `ENV`      │ Définit des variables d'environnement persistantes à l'exec │
│ `ARG`      │ Définit des variables temporaires utilisables au build seul │
│ `EXPOSE`   │ Documente les ports d'écoute (informatif, n'ouvre pas l'IP) │
│ `USER`     │ Définit l'UID/GID exécutant les commandes (évite root)      │
└────────────┴─────────────────────────────────────────────────────────────┘

Le Duel Crucial : ENTRYPOINT vs CMD

┌──────────────────────────────┬─────────────────────────────────────────────────┐
│ `CMD`                        │ Définit la commande ou les arguments par défaut.│
│                              │ Facilement SURCHARGÉ par `docker run <cmd>`.    │
├──────────────────────────────┼─────────────────────────────────────────────────┤
│ `ENTRYPOINT`                 │ Définit le binaire fixe et immuable du conteneur│
│                              │ Transforme le conteneur en exécutable CLI.      │
├──────────────────────────────┼─────────────────────────────────────────────────┤
│ `ENTRYPOINT` + `CMD` combinés│ ENTRYPOINT fixe le binaire, CMD fournit         │
│                              │ les arguments par défaut (surchargeables).      │
└──────────────────────────────┴─────────────────────────────────────────────────┘
# Exemple de synergie puissante :
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
  • Si exécuté avec docker run my-nginx → lance nginx -g "daemon off;"
  • Si exécuté avec docker run my-nginx -v → lance nginx -v (version)

Forme Exec vs Forme Shell : Attention aux Signaux !

┌──────────────────────────────────────┬──────────────────────────────────────┐
│ FORME EXEC (Recommandée)             │ FORME SHELL (À éviter)               │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ `CMD ["node", "app.js"]`             │ `CMD node app.js`                    │
│                                      │                                      │
│ - Lance le binaire directement.      │ - Lance en réalité `/bin/sh -c ...`. │
│ - `node` prend le PID 1.             │ - Le shell prend le PID 1.           │
│ - Reçoit directement les SIGTERM.    │ - Les SIGTERM ne sont pas transmis   │
│ - Arrêt propre et instantané.        │   à l'application (arrêt forcé 10s). │
└──────────────────────────────────────┴──────────────────────────────────────┘

Variables de Build (ARG) vs Variables d'Environnement (ENV)

                    ARG (Build Time)             ENV (Runtime)
┌───────────────────────────────────────┬───────────────────────────────────────┐
│ Accessible pendant `docker build`     │ Présente dans le conteneur en cours   │
│ Ne subsiste PAS dans l'image finale   │ Accessible par `process.env` / `$VAR` │
│ Ex: version d'un paquet, token build  │ Ex: `NODE_ENV=production`, `PORT=80`  │
└───────────────────────────────────────┴───────────────────────────────────────┘
# Déclaration :
ARG APP_VERSION=1.0.0
ENV ENVIRONMENT=production

# Passage lors du build :
# docker build --build-arg APP_VERSION=2.0.0 -t my-app .

La Sonde de Santé Native : HEALTHCHECK

Permet à Docker de surveiller si l'application répond réellement (et pas seulement si le processus est vivant) :

HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8080/api/health || exit 1
  • Statuts observables via docker ps :
  • (health: starting) : Période d'initialisation.
  • (healthy) : Sonde validée (code retour 0).
  • (unhealthy) : Échecs répétés → alertes et redémarrage possible via orchestrateur.

Mini-Défi : Forme Exec vs Forme Shell

Question : Pourquoi devez-vous toujours privilégier la syntaxe tableau JSON ["executable", "param"] pour vos instructions ENTRYPOINT et CMD ?

  • A. Parce que Docker refuse d'exécuter la syntaxe texte brut (shell form).
  • B. Pour que le binaire applicatif tourne directement en PID 1 et intercepte instantanément les signaux d'arrêt propres (SIGTERM).
  • C. Pour compresser automatiquement l'image lors du build.
  • D. Pour interdire toute modification des variables d'environnement.

Mini-Défi : Forme Exec vs Forme Shell

Question : Pourquoi devez-vous toujours privilégier la syntaxe tableau JSON ["executable", "param"] pour vos instructions ENTRYPOINT et CMD ?

  • A. Parce que Docker refuse d'exécuter la syntaxe texte brut (shell form).
  • B. Pour que le binaire applicatif tourne directement en PID 1 et intercepte instantanément les signaux d'arrêt propres (SIGTERM).
  • C. Pour compresser automatiquement l'image lors du build.
  • D. Pour interdire toute modification des variables d'environnement.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  La forme shell `CMD node server.js` lance en réalité       │
│  `/bin/sh -c` en PID 1, qui ne transmet pas le signal       │
│  SIGTERM à votre code Node, forçant un kill brutal (10s).   │
└─────────────────────────────────────────────────────────────┘

DOCKER : BuildKit & Docker Buildx (Fonctionnalités Modernes)

La Révolution du Moteur de Compilation : BuildKit

Historiquement, le moteur de build Docker exécutait les étapes de manière strictement séquentielle.

Depuis Docker 18.09+ et par défaut en Docker v23+, BuildKit est le moteur de compilation de nouvelle génération :

         BUILDER LEGACY (SÉQUENTIEL)                     BUILDKIT (GRAPHE DAG PARALLÈLE)
┌───────────────────────────────────────────┐       ┌───────────────────────────────────────────┐
│ Étape 1 : Base OS                         │       │              ┌──► Étape 2 (Frontend) ──┐  │
│   └── Étape 2 : Dépendances Frontend      │       │ Étape 1 (OS) ┤                         ├──►
│         └── Étape 3 : Compilation Backend │       │              └──► Étape 3 (Backend)  ──┘  │
│               └── Étape 4 : Assemblage    │       │             (Exécutées EN PARALLÈLE !)    │
└───────────────────────────────────────────┘       └───────────────────────────────────────────┘
  • Vitesse de compilation multipliée par 2x à 9x.
  • Graphe acyclique direct (DAG) : résout et exécute en parallèle toutes les étapes indépendantes.
  • Ignorance des étapes inutilisées : ne compile pas les étapes non requises par la cible demandée.

Docker Buildx : L'Outil Professionnel de Build

buildx est le plugin CLI officiel qui débloque toute la puissance de BuildKit :

# 1. Vérifier la présence de buildx
docker buildx version

# 2. Créer une instance de builder dédiée avec support multi-nœuds / conteneurisé
docker buildx create --name mon-builder --driver docker-container --use

# 3. Démarrer et inspecter l'instance
docker buildx inspect --bootstrap

Le Graal : Builds Multi-Architectures Natifs (AMD64 & ARM64)

Avec l'essor des serveurs Cloud Graviton (ARM64) et des puces Apple Silicon (M1/M2/M3), vos images doivent tourner sur toutes les architectures :

                                 ┌──► Image Linux AMD64 (Serveurs x86 Intel/AMD)
`docker buildx build` ───────────┼──► Image Linux ARM64 (Apple Silicon, AWS Graviton)
                                 └──► Image Linux ARMv7 (Raspberry Pi, IoT)
                                                 │
                                                 ▼
                                     MANIFESTE UNIQUE UNIFIÉ
                                 (`ghcr.io/org/app:latest`)
# Compiler et publier une image multi-architectures en une seule commande :
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/alterway/multiarch-demo:1.0.0 \
  --push .

Le registre héberge un "Fat Manifest" (Image Index) qui oriente automatiquement chaque machine vers le binaire adapté à son processeur.

Montages Avancés : Accélérer les Builds avec --mount=type=cache

Ne téléchargez plus jamais vos dépendances (npm, pip, maven, go) lors de chaque docker build :

# syntax=docker/dockerfile:1.7
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./

# Le dossier /root/.npm est persisté hors de l'image entre chaque build !
RUN --mount=type=cache,target=/root/.npm \
    npm ci --prefer-offline

COPY . .
RUN npm run build
  • Avantage 1 : Les builds suivants prennent quelques secondes au lieu de plusieurs minutes.
  • Avantage 2 : Les fichiers temporaires du cache npm ne sont jamais écrits dans les couches de l'image finale !

Sécurité Maximale : Injecter des Secrets avec --mount=type=secret

Ne risquez plus de fuiter des tokens GitHub, des clés privées ou des mots de passe dans les layers d'une image :

# syntax=docker/dockerfile:1.7
FROM alpine:3.19

# Le secret est monté temporairement en mémoire vive (RAM) et disparaît après la commande
RUN --mount=type=secret,id=github_token \
    TOKEN=$(cat /run/secrets/github_token) && \
    wget --header="Authorization: Bearer $TOKEN" https://api.internal/package.tar.gz
# Lancement de la compilation avec passage sécurisé du secret hôte :
docker buildx build --secret id=github_token,src=./token.txt -t secure-image .

Authentification Git Sécurisée avec --mount=type=ssh

Cloner des dépôts Git privés sans jamais copier votre clé privée SSH dans l'image :

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

# Utilise la socket de l'agent SSH de l'hôte
RUN --mount=type=ssh \
    git clone git@github.com:my-org/private-repo.git .
# Compilation en transmettant l'agent SSH local de la machine :
docker buildx build --ssh default -t go-app .

Mini-Défi : Secrets de Build avec BuildKit

Question : Quel est le bénéfice majeur de RUN --mount=type=secret par rapport à l'ancienne méthode ARG TOKEN=xyz ?

  • A. Il crypte le code source avec une clé RSA 4096.
  • B. Le secret n'est jamais stocké dans les métadonnées de l'image ni dans les couches intermédiaires (0 fuite dans l'historique).
  • C. Il force l'utilisateur à taper son mot de passe au lancement du conteneur.
  • D. Il remplace le besoin de variables d'environnement au runtime.

Mini-Défi : Secrets de Build avec BuildKit

Question : Quel est le bénéfice majeur de RUN --mount=type=secret par rapport à l'ancienne méthode ARG TOKEN=xyz ?

  • A. Il crypte le code source avec une clé RSA 4096.
  • B. Le secret n'est jamais stocké dans les métadonnées de l'image ni dans les couches intermédiaires (0 fuite dans l'historique).
  • C. Il force l'utilisateur à taper son mot de passe au lancement du conteneur.
  • D. Il remplace le besoin de variables d'environnement au runtime.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  Avec `ARG`, le secret reste visible dans `docker history`  │
│  et dans les métadonnées JSON de l'image. Avec le montage   │
│  `--mount=type=secret`, le secret existe uniquement en RAM  │
│  pendant la commande et n'est JAMAIS persisté.              │
└─────────────────────────────────────────────────────────────┘

DOCKER : Builds Multi-Étapes & Optimisation des Images

Le Pattern Multi-Stage Build : L'Élégance Absolue

Pour compiler un programme (Go, Java, Rust, TypeScript, C++), il faut des outils lourds (compilateurs, SDKs, Maven, NPM). Mais ces outils ne doivent jamais figurer en production !

Le Multi-Stage Build résout ce problème avec plusieurs instructions FROM dans un même Dockerfile :

# ==============================================================================
# ÉTAPE 1 : BUILDER (Lourd, contient le compilateur Go et les outils de build)
# ==============================================================================
FROM golang:1.22-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
# Compilation d'un binaire statique sans dépendance C (CGO_ENABLED=0)
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server .

# ==============================================================================
# ÉTAPE 2 : RUNTIME (Ultra-léger, contient UNIQUEMENT le binaire final)
# ==============================================================================
FROM alpine:3.19
WORKDIR /app

# Récupération chirurgicale du binaire depuis l'étape 'builder'
COPY --from=builder /app/server /app/server

USER 1000:1000
EXPOSE 8080
CMD ["/app/server"]

Résultats Spectaculaires : Étude de Cas

┌─────────────────────────────────┬──────────────────────┬────────────────────────┐
│ MÉTHODE DE FABRICATION          │ TAILLE DE L'IMAGE    │ VULNÉRABILITÉS (CVE)   │
├─────────────────────────────────┼──────────────────────┼────────────────────────┤
│ Build classique (Mono-stage)    │ 850 Mo (Go SDK + OS) │ 140+ CVEs détectées    │
│ Multi-stage avec Alpine         │ 18 Mo                │ 0 CVE critique         │
│ Multi-stage avec Scratch / Static│ 8 Mo                │ 0 CVE (Surface zéro)   │
└─────────────────────────────────┴──────────────────────┴────────────────────────┘

Choisir la Bonne Image de Base

┌───────────────────────┬───────────┬──────────────────────────────────────────────┐
│ IMAGE DE BASE         │ TAILLE    │ CAS D'USAGE RECOMMANDÉ                       │
├───────────────────────┼───────────┼──────────────────────────────────────────────┤
│ `debian:bookworm-slim`│ ~75 Mo    │ Applications avec dépendances C/glibc lourdes│
│ `alpine:3.19`         │ ~7 Mo     │ Standard léger avec gestionnaire de paquets  │
│ `gcr.io/distroless/*` │ ~20 Mo    │ Sécurité maximale (sans shell, sans package) │
│ `scratch`             │ 0 Mo      │ Binaire statique pur (Go, Rust, C)           │
└───────────────────────┴───────────┴──────────────────────────────────────────────┘

Cibles Multiples (--target) pour Dev, Test et Prod

Un seul Dockerfile peut gérer l'ensemble de la chaîne de développement :

FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

FROM base AS test
RUN npm ci
COPY . .
RUN npm test

FROM base AS production
RUN npm ci --only=production
COPY . .
USER node
CMD ["node", "index.js"]
# Lancer l'environnement de développement :
docker build --target development -t myapp:dev .

# Exécuter les tests unitaires en CI/CD :
docker build --target test -t myapp:test .

# Compiler l'image finale de production :
docker build --target production -t myapp:prod .

La Règle d'Or de l'Invalidation du Cache

Docker met en cache chaque couche. Dès qu'une instruction est modifiée, toutes les instructions suivantes sont recompilées sans cache :

       MAUVAISE PRATIQUE (CACHE CASSÉ À CHAQUE LIGNE DE CODE)
┌─────────────────────────────────────────────────────────────┐
│ COPY . .                     ── Dès qu'un fichier change    │
│ RUN npm install              ── npm réinstalle TOUT (5 min) │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
        BONNE PRATIQUE (CACHE RÉUTILISÉ À 99%)
┌─────────────────────────────────────────────────────────────┐
│ COPY package*.json ./        ── Mis en cache si package fixe │
│ RUN npm ci                   ── Exécuté UNIQUEMENT si modif │
│ COPY . .                     ── Seul le code source change  │
└─────────────────────────────────────────────────────────────┘

L'Indispensable Fichier .dockerignore

Tout comme .gitignore, le fichier .dockerignore évite d'envoyer des centaines de Mo inutiles ou des données sensibles dans le contexte de build :

# .dockerignore
.git
.gitignore
node_modules
dist
build
.env*
*.md
Dockerfile*
tests
.coverage
  • Accélère l'envoi du contexte au démon Docker (de 500 Mo à 2 Mo).
  • Évite de fuiter des secrets locaux (.env, clés SSH, certificats).

Nettoyage des Couches APT dans un Unique RUN

# Mauvaise pratique (3 couches, paquets temporaires conservés dans l'historique) :
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# Bonne pratique (1 seule couche propre et nettoyée) :
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
 && rm -rf /var/lib/apt/lists/*

Mini-Défi : Ordonnancement du Cache Docker

Question : Pourquoi devez-vous toujours copier package.json ou requirements.txt AVANT le code source applicatif ?

  • A. Parce que la syntaxe Dockerfile interdit d'exécuter RUN après COPY . ..
  • B. Pour crypter les dépendances avant de compiler l'application.
  • C. Pour réutiliser le cache de la couche d'installation (npm ci / pip install) tant que les dépendances ne changent pas.
  • D. Pour réduire automatiquement l'empreinte RAM du conteneur en production.

Mini-Défi : Ordonnancement du Cache Docker

Question : Pourquoi devez-vous toujours copier package.json ou requirements.txt AVANT le code source applicatif ?

  • A. Parce que la syntaxe Dockerfile interdit d'exécuter RUN après COPY . ..
  • B. Pour crypter les dépendances avant de compiler l'application.
  • C. Pour réutiliser le cache de la couche d'installation (npm ci / pip install) tant que les dépendances ne changent pas.
  • D. Pour réduire automatiquement l'empreinte RAM du conteneur en production.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Toute modification d'un fichier invalide le cache de       │
│  cette étape et de TOUTES les suivantes. En isolant les     │
│  dépendances, le `npm install` ne rejoue que si la liste    │
│  des paquets change, divisant le temps de build par 10 !    │
└─────────────────────────────────────────────────────────────┘

DOCKER : Gestion du Stockage Persistant & Volumes

L'Éphémérité des Conteneurs par Défaut

Par défaut, tout fichier créé à l'intérieur d'un conteneur est écrit dans sa couche supérieure en lecture/écriture (Upper Layer).

⚠️ Conséquence critique : - Lorsque le conteneur est supprimé (docker rm), toutes ses données sont définitivement perdues. - Les performances d'écriture sur l'UnionFS (Overlay2) sont moins rapides qu'un accès direct au disque. - Impossible de partager simplement des données entre plusieurs conteneurs.

Les 3 Types de Montages Docker

┌──────────────────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 1. VOLUMES NOMMÉS (Docker)   │ 2. BIND MOUNTS (Hôte direct) │ 3. TMPFS MOUNTS (RAM)        │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ Stockés dans l'espace géré   │ Montent un dossier ou fichier│ Stockés uniquement en        │
│ `/var/lib/docker/volumes/`   │ spécifique de votre machine  │ mémoire vive (RAM) de l'hôte │
│                              │                              │                              │
│ ✔ Haute performance         │ ✔ Idéal pour le code source  │ ✔ Ultra-rapide               │
│ ✔ Indépendants du conteneur  │   en développement (live-edit│ ✔ Données sensibles (jamais  │
│ ✔ Sauvegarde et migration    │ ✘ Dépendant de l'OS hôte     │   écrites sur le disque)     │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘

Syntaxe Moderne --mount vs Syntaxe Historique -v

Docker recommande la syntaxe explicite --mount (plus claire, plus stricte et standardisée avec Swarm) :

# 1. Montage d'un Volume Nommé (Production / Bases de données)
docker run -d --name db-postgres \
  --mount type=volume,source=pg_data,target=/var/lib/postgresql/data \
  postgres:16

# 2. Montage d'un Dossier Local / Bind Mount (Développement à chaud)
docker run -d --name dev-server -p 3000:3000 \
  --mount type=bind,source="$(pwd)/src",target=/app/src,readonly=false \
  node:20-alpine

# 3. Montage Temporaire en RAM (tmpfs)
docker run -d --name session-cache \
  --mount type=tmpfs,target=/tmp/cache,tmpfs-size=100m \
  redis:alpine

Cycle de Vie & Commandes des Volumes

# 1. Créer manuellement un volume
docker volume create db-prod-vol

# 2. Lister tous les volumes existants
docker volume ls

# 3. Inspecter le point de montage exact sur le système de fichiers hôte
docker volume inspect db-prod-vol

# 4. Supprimer un volume inutilisé
docker volume rm db-prod-vol

# 5. Nettoyer tous les volumes orphelins (libérer l'espace disque)
docker volume prune -f

Inspection d'un Volume : Trouver les Fichiers sur l'Hôte

docker volume inspect db-prod-vol
[
  {
    "CreatedAt": "2024-05-10T14:30:00Z",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/db-prod-vol/_data",
    "Name": "db-prod-vol",
    "Options": {},
    "Scope": "local"
  }
]

Sauvegarde & Restauration d'un Volume via un Conteneur Utilitaire

Comment sauvegarder le contenu d'un volume sans arrêter Docker ? En utilisant un conteneur temporaire éphémère :

# 1. SAUVEGARDE : Archiver le volume 'pg_data' dans 'backup.tar.gz'
docker run --rm \
  --mount type=volume,source=pg_data,target=/volume-data,readonly \
  --mount type=bind,source="$(pwd)",target=/backup \
  alpine tar czvf /backup/pg_data_backup.tar.gz -C /volume-data .

# 2. RESTAURATION : Restaurer l'archive dans un nouveau volume 'pg_data_restored'
docker volume create pg_data_restored
docker run --rm \
  --mount type=volume,source=pg_data_restored,target=/volume-data \
  --mount type=bind,source="$(pwd)",target=/backup,readonly \
  alpine tar xzvf /backup/pg_data_backup.tar.gz -C /volume-data

Problématiques de Droits & Permissions (UID/GID) avec les Bind Mounts

Lors d'un bind mount entre l'hôte et le conteneur, les permissions de fichiers Linux s'appliquent selon l'UID numérique : - Si l'utilisateur du conteneur est node (UID 1000) et que le dossier hôte appartient à root (UID 0), le conteneur recevra une erreur Permission Denied lors d'une écriture.

# Solution : Spécifier explicitement l'UID/GID lors de l'exécution
docker run -d --user "$(id -u):$(id -g)" \
  --mount type=bind,source="$(pwd)",target=/app \
  my-app:dev

Mini-Défi : Persistance des Volumes

Question : Si vous supprimez brutalement un conteneur avec docker rm -f my-postgres, qu'advient-il de son volume nommé monté (pg_data) ?

  • A. Le volume est automatiquement et définitivement détruit.
  • B. Le volume nommé reste totalement intact sur le disque et peut être immédiatement réattaché à un nouveau conteneur.
  • C. Les données sont vidées mais le dossier reste vide.
  • D. Le démon Docker refuse de redémarrer tant que le volume n'est pas purgé.

Mini-Défi : Persistance des Volumes

Question : Si vous supprimez brutalement un conteneur avec docker rm -f my-postgres, qu'advient-il de son volume nommé monté (pg_data) ?

  • A. Le volume est automatiquement et définitivement détruit.
  • B. Le volume nommé reste totalement intact sur le disque et peut être immédiatement réattaché à un nouveau conteneur.
  • C. Les données sont vidées mais le dossier reste vide.
  • D. Le démon Docker refuse de redémarrer tant que le volume n'est pas purgé.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  Les volumes sont des entités décorrélées du cycle de vie   │
│  du conteneur. Pour supprimer explicitement un volume,      │
│  il faut exécuter `docker volume rm <nom>` ou `docker rm -v`│
└─────────────────────────────────────────────────────────────┘

DOCKER : Réseau & Communication Inter-Conteneurs

Le Modèle Réseau des Conteneurs (CNM)

Docker implémente la spécification CNM (Container Network Model) composée de 3 abstractions clés :

┌────────────────────────────────────────────────────────────────────────┐
│ 1. SANDBOX  : Isole la pile réseau d'un conteneur (Linux Network NS)   │
│ 2. ENDPOINT : Interface réseau virtuelle (`veth`) connectant le conteneur│
│ 3. NETWORK  : Le commutateur virtuel (Bridge / Overlay) interconnecteur│
└────────────────────────────────────────────────────────────────────────┘
                           MACHINE HÔTE DOCKER
┌────────────────────────────────────────────────────────────────────────┐
│ CONTENEUR A (172.20.0.2)                     CONTENEUR B (172.20.0.3)  │
│   └── interface eth0 (vethA)                   └── interface eth0 (vethB)│
│            │                                            │              │
│            └───────────────────┬────────────────────────┘              │
│                                ▼                                       │
│                PONT RÉSEAU VIRTUEL : `my-bridge` (172.20.0.1)          │
│            (Serveur DNS intégré : 127.0.0.11 / Résolution par nom)     │
│                                │                                       │
│                                ▼ iptables (NAT / Forwarding)           │
│                   INTERFACE PHYSIQUE HÔTE (`eth0`)                     │
└────────────────────────────────────────────────────────────────────────┘

Les 5 Pilotes de Réseau Standards

┌────────────┬──────────────────────────────────────────────────────────────────┐
│ PILOTE     │ DESCRIPTION ET CAS D'USAGE RECOMMANDÉ                            │
├────────────┼──────────────────────────────────────────────────────────────────┤
│ `bridge`   │ Réseau privé virtuel local sur l'hôte (Le standard par défaut)   │
│ `host`     │ Supprime l'isolation réseau : le conteneur partage l'IP de l'hôte│
│ `none`     │ Conteneur totalement déconnecté (boucle locale `lo` seule)       │
│ `macvlan`  │ Assigne une adresse MAC physique et une IP réelle de votre réseau│
│ `overlay`  │ Réseau chiffré multi-hôtes pour les clusters (Swarm / Kubernetes)│
└────────────┴──────────────────────────────────────────────────────────────────┘

Bridge par Défaut (docker0) vs Bridge Personnalisé (User-Defined)

⚠️ DIFFÉRENCE FONDAMENTALE : - Bridge par défaut (docker0) : Pas de résolution DNS automatique par nom de conteneur (nécessitait l'ancienne option obsolète --link). - Bridge Personnalisé (user-defined bridge) : Résolution DNS interne automatique par nom de conteneur ou alias réseau !

# 1. Créer un réseau bridge dédié pour notre application
docker network create --driver bridge app-network

# 2. Lancer une base de données sur ce réseau
docker run -d --name database --network app-network redis:alpine

# 3. Lancer une API qui contacte la base DIRECTEMENT par son nom 'database'
docker run -d --name api --network app-network -p 8080:8080 my-api:latest
# Dans le code de l'API : redis://database:6379 fonctionne instantanément !

Publication et Redirection de Ports (-p)

Par défaut, un conteneur est accessible uniquement depuis les autres conteneurs du même réseau. Pour l'ouvrir à l'extérieur :

# 1. Ouvrir le port 80 du conteneur sur le port 8080 de TOUTES les interfaces de l'hôte
docker run -d -p 8080:80 nginx:alpine

# 2. Sécuriser : écouter UNIQUEMENT sur l'adresse locale (localhost)
docker run -d -p 127.0.0.1:8080:80 nginx:alpine

# 3. Spécifier le protocole UDP
docker run -d -p 53:53/udp dns-server:latest

# 4. Attribution dynamique d'un port aléatoire éphémère de l'hôte (-P)
docker run -d -P nginx:alpine
docker port <container_id>  # Affiche le port attribué (ex: 49153 -> 80)

Commandes de Gestion du Réseau

# 1. Lister tous les réseaux
docker network ls

# 2. Inspecter la configuration et la liste des conteneurs connectés avec leur IP
docker network inspect app-network

# 3. Connecter un conteneur existant à un deuxième réseau à chaud
docker network connect app-network web-app

# 4. Déconnecter un conteneur
docker network disconnect app-network web-app

# 5. Supprimer un réseau inutilisé
docker network rm app-network

Cloisonnement & Pare-Feu entre Conteneurs

Deux conteneurs placés sur des réseaux bridges distincts ne peuvent absolument pas communiquer entre eux, même s'ils tournent sur la même machine physique :

  RÉSEAU FRONTEND (172.18.0.0/16)             RÉSEAU BACKEND (172.19.0.0/16)
┌─────────────────────────────────┐         ┌─────────────────────────────────┐
│ [ Conteneur Web Nginx ]         │         │ [ Base de Données PostgreSQL ]  │
└────────────────┬────────────────┘         └────────────────┬────────────────┘
                 │                                           │
                 └─────────────── X BLOQUÉ X ────────────────┘

Pour permettre la communication : attacher uniquement le composant intermédiaire (ex: l'API backend) aux deux réseaux simultanément.

Mini-Défi : Résolution DNS Inter-Conteneurs

Question : Pourquoi deux conteneurs lancés sur le bridge par défaut (docker0) ne peuvent-ils pas se joindre via leur nom (curl http://mon-api) ?

  • A. Parce que les conteneurs partagent obligatoirement la même adresse MAC.
  • B. Parce que le protocole HTTP est bloqué par défaut sur docker0.
  • C. Parce que le serveur DNS interne de Docker (127.0.0.11) est activé UNIQUEMENT sur les réseaux personnalisés (User-Defined Bridges).
  • D. Parce qu'il faut obligatoirement installer Bind9 sur l'hôte.

Mini-Défi : Résolution DNS Inter-Conteneurs

Question : Pourquoi deux conteneurs lancés sur le bridge par défaut (docker0) ne peuvent-ils pas se joindre via leur nom (curl http://mon-api) ?

  • A. Parce que les conteneurs partagent obligatoirement la même adresse MAC.
  • B. Parce que le protocole HTTP est bloqué par défaut sur docker0.
  • C. Parce que le serveur DNS interne de Docker (127.0.0.11) est activé UNIQUEMENT sur les réseaux personnalisés (User-Defined Bridges).
  • D. Parce qu'il faut obligatoirement installer Bind9 sur l'hôte.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Sur `docker0`, la découverte DNS automatique par nom est   │
│  désactivée pour des raisons d'isolation historique.        │
│  Créez toujours un réseau dédié (`docker network create`) ! │
└─────────────────────────────────────────────────────────────┘

DOCKER : Orchestration Multi-Conteneurs avec Docker Compose v2

Pourquoi Docker Compose ?

Lancer une architecture moderne (Frontend + API + Base de Données + Redis) avec la CLI docker run devient vite ingérable : - Ordre de démarrage manuel des conteneurs. - Commandes verbeuses avec des dizaines d'arguments --network, -v, -p, -e. - Risque d'oubli ou d'incohérence entre les postes de l'équipe.

Docker Compose permet de décrire l'intégralité d'une pile applicative multi-conteneurs dans un unique fichier déclaratif compose.yaml et de piloter son cycle de vie en une seule commande.

La Spécification Compose v2 Moderne

Historiquement appelé docker-compose (en Python), Compose a été entièrement réécrit en Go et s'intègre nativement dans la CLI standard : docker compose (sans tiret).

# compose.yaml (La clé 'version:' est désormais obsolète et facultative !)
services:
  frontend:
    build:
      context: ./frontend
      target: production
    ports:
      - "80:80"
    depends_on:
      api:
        condition: service_healthy
    networks:
      - app-net

  api:
    build: ./backend
    environment:
      - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
      - REDIS_HOST=redis
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${DB_USER:-postgres}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
      POSTGRES_DB: ${DB_NAME:-appdb}
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - app-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres}"]
      interval: 5s
      timeout: 3s
      retries: 5

networks:
  app-net:
    driver: bridge

volumes:
  pgdata:

Dépendances Conditionnelles Avancées (depends_on)

Compose permet de synchroniser le démarrage réel en fonction de la disponibilité applicative :

┌─────────────────────────────────┬─────────────────────────────────────────────────┐
│ CONDITION                       │ COMPORTEMENT DU DÉMARRAGE                      │
├─────────────────────────────────┼─────────────────────────────────────────────────┤
│ `service_started`               │ Attend que le conteneur soit démarré (basique)  │
│ `service_healthy`               │ Attend que le `HEALTHCHECK` passe au vert       │
│ `service_completed_successfully`│ Attend qu'un conteneur de migration/init termine│
└─────────────────────────────────┴─────────────────────────────────────────────────┘

Gestion des Variables d'Environnement & Fichier .env

Compose charge automatiquement le fichier .env situé dans le même répertoire :

# .env
DB_USER=appuser
DB_PASSWORD=SuperSecr3tPassword!
DB_NAME=production_db
APP_PORT=8080

Dans compose.yaml, vous pouvez injecter avec des valeurs par défaut de secours : PORT: ${APP_PORT:-3000}.

Les Commandes Quotidiennes Compose v2

# 1. Démarrer toute l'infrastructure en arrière-plan (avec rebuild si nécessaire)
docker compose up -d --build

# 2. Inspecter l'état des services et sondes de santé
docker compose ps

# 3. Suivre les logs de tous les services ou d'un service spécifique
docker compose logs -f api

# 4. Exécuter une commande dans un service actif
docker compose exec db psql -U appuser -d appdb

# 5. Mettre à l'échelle un composant sans modifier le YAML
docker compose up -d --scale api=3

# 6. Stopper et détruire les conteneurs et réseaux (ajouter -v pour purger les volumes)
docker compose down -v

La Nouveauté Phare : Compose Watch (develop.watch)

Compose v2 intègre le synchronisme automatique de fichiers à chaud en cours de développement (Hot Reload) :

services:
  web:
    build: .
    develop:
      watch:
        # Synchronise le code HTML/JS instantanément sans redémarrer le conteneur
        - action: sync
          path: ./src
          target: /app/src
        # Reconstruit l'image et redémarre si package.json est modifié
        - action: rebuild
          path: ./package.json
# Lancer Compose en mode surveillance continue :
docker compose watch

Mini-Défi : Arrêt & Nettoyage Compose

Question : Quelle est la différence fondamentale entre la commande docker compose stop et docker compose down ?

  • A. stop détruit les volumes de base de données ; down sauvegarde les fichiers.
  • B. stop arrête simplement les processus sans détruire les conteneurs ; down supprime les conteneurs, les réseaux créés et les liens virtuels.
  • C. down désinstalle Docker Engine du serveur.
  • D. Ce sont deux commandes strictement synonymes.

Mini-Défi : Arrêt & Nettoyage Compose

Question : Quelle est la différence fondamentale entre la commande docker compose stop et docker compose down ?

  • A. stop détruit les volumes de base de données ; down sauvegarde les fichiers.
  • B. stop arrête simplement les processus sans détruire les conteneurs ; down supprime les conteneurs, les réseaux créés et les liens virtuels.
  • C. down désinstalle Docker Engine du serveur.
  • D. Ce sont deux commandes strictement synonymes.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  `docker compose stop` laisse les conteneurs intacts prêts  │
│  à redémarrer via `start`. `docker compose down` fait place │
│  nette en détruisant les conteneurs et réseaux de la pile.  │
│  (Les volumes sont préservés sauf si l'option `-v` est mise)│
└─────────────────────────────────────────────────────────────┘

DOCKER : Sécurité, Isolation & Durcissement

Le Modèle des 4C Appliqué aux Conteneurs

La sécurité Cloud Native repose sur le principe de défense en profondeur (Defense in Depth) :

┌───────────────────────────────────────────────────────────┐
│               1. CLOUD / HÔTE (Host Security)             │
│   ┌───────────────────────────────────────────────────┐   │
│   │           2. DÉMON DOCKER (Docker Engine)         │   │
│   │   ┌───────────────────────────────────────────┐   │   │
│   │   │         3. CONTENEUR (Runtime Hardening)  │   │   │
│   │   │   ┌───────────────────────────────────┐   │   │   │
│   │   │   │          4. CODE & DÉPENDANCES    │   │   │   │
│   │   │   └───────────────────────────────────┘   │   │   │
│   │   └───────────────────────────────────────────┘   │   │
│   └───────────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────────┘
  • Hôte : Durcissement Linux, mises à jour kernel, pare-feu UFW/iptables, chiffrement disques.
  • Démon : Socket UNIX protégée (/var/run/docker.sock non exposée), mode Rootless, TLS mutuel pour l'API.
  • Conteneur : Utilisateur non-root, drop des Linux Capabilities, profils Seccomp & AppArmor, système de fichiers en lecture seule.
  • Code : Scan des dépendances (SCA), analyse statique (SAST), gestion des secrets hors de l'image.

Règle d'Or N°1 : Ne Jamais Exécuter en tant que root !

Par défaut, l'utilisateur à l'intérieur du conteneur est root (UID 0). En cas d'évasion de conteneur (Container Escape), l'attaquant devient root sur l'hôte !

# Dans le Dockerfile :
RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -s /bin/sh -m appuser

USER 1001:1001
# Ou en surcharge lors du lancement :
docker run -d --user 1001:1001 --name safe-app my-image:latest

Le Mode Docker Rootless : Éliminer les Privilèges

Le mode Rootless permet d'exécuter à la fois le démon Docker et les conteneurs sous un utilisateur standard sans aucun droit sudo :

       DOCKER CLASSIQUE (ROOT)                        DOCKER ROOTLESS (NON-ROOT)
┌──────────────────────────────────────┐       ┌──────────────────────────────────────┐
│ `dockerd` tourne en tant que `root`  │       │ `dockerd` tourne sous l'UID 1000     │
│ Faille conteneur = Compromission Hôte│       │ Faille conteneur = Restreinte au user│
└──────────────────────────────────────┘       └──────────────────────────────────────┘
# Installation du mode rootless par utilisateur :
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock

Réduire les Privilèges : Les Linux Capabilities

Par défaut, le noyau Linux accorde environ 14 capacités aux conteneurs (sur les 40 existantes). La bonne pratique consiste à tout supprimer, puis réautoriser au cas par cas :

# 1. Dropper TOUTES les capacités, et autoriser UNIQUEMENT la liaison aux ports < 1024 :
docker run -d \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  -p 80:80 \
  nginx:alpine

Système de Fichiers Immuable : --read-only

Empêcher un malware d'écrire ou de modifier des binaires dans le conteneur en rendant le système de fichiers en lecture seule :

# Système en lecture seule + montage de /tmp éphémère en mémoire RAM
docker run -d \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --tmpfs /var/run:rw,noexec,nosuid \
  my-app:prod

Les Profils Seccomp & AppArmor

  • Seccomp (Secure Computing Mode) : Filtre les appels système Linux (syscalls). Le profil par défaut de Docker bloque plus de 44 appels système dangereux (reboot, ptrace, mount, kexec_load).
  • AppArmor / SELinux : Contrôle d'accès obligatoire (MAC) restreignant l'accès aux fichiers sensibles /proc et /sys.

Scan de Vulnérabilités Intégré avec Docker Scout & Trivy

Détecter les failles de sécurité (CVEs) directement avant le déploiement :

# 1. Analyser une image avec Docker Scout
docker scout quickview nginx:latest
docker scout cves nginx:latest

# 2. Scanner avec Trivy (outil open-source incontournable)
trivy image --severity HIGH,CRITICAL my-app:latest

Audit Automatique : Le Docker CIS Benchmark

Vérifier la conformité de votre hôte et démon Docker selon les recommandations du Center for Internet Security (CIS) :

# Lancer l'audit de sécurité automatisé :
docker run --rm --net host --pid host --userns host --cap-add audit_control \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /etc:/etc:ro --label docker_bench_security \
  docker/docker-bench-security

Mini-Défi : Durcissement Ultime en Production

Question : Quelle combinaison de drapeaux docker run applique le principe du moindre privilège pour un conteneur web ?

  • A. --privileged --net=host --user 0:0
  • B. --cap-add=ALL --restart always
  • C. --read-only --cap-drop=ALL --cap-add=NET_BIND_SERVICE --user 1000:1000 --security-opt no-new-privileges:true
  • D. -v /:/host -it ubuntu bash

Mini-Défi : Durcissement Ultime en Production

Question : Quelle combinaison de drapeaux docker run applique le principe du moindre privilège pour un conteneur web ?

  • A. --privileged --net=host --user 0:0
  • B. --cap-add=ALL --restart always
  • C. --read-only --cap-drop=ALL --cap-add=NET_BIND_SERVICE --user 1000:1000 --security-opt no-new-privileges:true
  • D. -v /:/host -it ubuntu bash
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Cette combinaison verrouille le système en lecture seule,  │
│  supprime toutes les capacités Linux sauf le bind du port,  │
│  tourne sans droits root et bloque l'escalade de privilèges.│
└─────────────────────────────────────────────────────────────┘

DOCKER : Exploitation, Monitoring & Dépannage

Méthodologie Systématique de Diagnostic d'un Incident

Quand un conteneur refuse de fonctionner, suivez cet arbre d'investigation :

┌─────────────────────────────────────────────────────────────┐
│ 1. ÉTAT DU CONTENEUR : `docker ps -a`                       │
│    └── Le conteneur tourne-t-il ou est-il arrêté (Exited) ? │
├─────────────────────────────────────────────────────────────┤
│ 2. CODE DE SORTIE : `docker inspect <id> --format '{{...}}'`│
│    ├── Exit 0   : Fin normale du script                     │
│    ├── Exit 1   : Erreur applicative interne                │
│    ├── Exit 127 : Commande ou binaire introuvable           │
│    ├── Exit 137 : Tué par OOM Killer (Manque de RAM)        │
│    └── Exit 139 : Segmentation Fault                        │
├─────────────────────────────────────────────────────────────┤
│ 3. JOURNAUX D'ERREURS : `docker logs --tail 100 <id>`       │
│    └── Traceback, exceptions, port déjà réservé             │
├─────────────────────────────────────────────────────────────┤
│ 4. SONDES DE SANTÉ : `docker inspect <id>`                  │
│    └── Le HEALTHCHECK échoue-t-il ? (Statut unhealthy)      │
└─────────────────────────────────────────────────────────────┘

Requêtes Expertes avec docker inspect & Go Templates

Ne parcourez plus des fichiers JSON de 500 lignes :

# 1. Extraire l'adresse IP d'un conteneur
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web-app

# 2. Vérifier le code d'erreur et la raison de l'arrêt
docker inspect --format 'ExitCode: {{.State.ExitCode}} | OOMKilled: {{.State.OOMKilled}}' web-app

# 3. Extraire les points de montage réels sur le disque hôte
docker inspect --format '{{range .Mounts}}{{.Source}} -> {{.Destination}} ({{.Type}}){{"\n"}}{{end}}' web-app

# 4. Formater en JSON élégant avec jq
docker inspect web-app | jq '.[0].State'

Gestion & Rotation des Logs de Conteneurs

Par défaut, le pilote json-file stocke les logs indéfiniment sur le disque hôte jusqu'à saturation (/var/lib/docker/containers/*/*-json.log).

Configurez une politique globale de rotation dans /etc/docker/daemon.json :

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  }
}
# Recharger la configuration à chaud sans couper les conteneurs :
sudo systemctl reload docker

Maintenance du Disque : Éviter le "Disk Full"

# 1. Analyser l'espace disque consommé par catégorie (Images, Containers, Volumes, Cache)
docker system df -v

# 2. Nettoyer les ressources non utilisées (conteneurs arrêtés, réseaux orphelins)
docker system prune

# 3. Nettoyage TOTAL (Images non utilisées + Build cache + Volumes orphelins)
docker system prune -a --volumes -f

Surveillance des Performances en Temps Réel (docker stats)

# Tableau de bord live de la consommation CPU, RAM, I/O Disque et Réseau :
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
NAME        CPU %     MEM USAGE / LIMIT     NET I/O       BLOCK I/O
api-server  0.45%     128.4MiB / 1.952GiB   1.2MB / 450KB 0B / 12KB
postgres-db 1.20%     240.1MiB / 1.952GiB   850KB / 2.1MB 4.1MB / 80MB
redis       0.05%     14.2MiB / 512MiB      320KB / 120KB 0B / 0B

Export des Métriques Docker vers Prometheus

Activez le point d'accès natif aux métriques dans /etc/docker/daemon.json :

{
  "metrics-addr": "127.0.0.1:9323",
  "experimental": true
}

Prometheus peut désormais moissonner (scrape) les compteurs de conteneurs, les allocations mémoires et les temps de réponse du moteur Docker !

Mini-Défi : Diagnostic d'un Crash Éclair

Question : Votre conteneur démarre et s'arrête instantanément avec un code Exit 137. Quel est le réflexe de diagnostic immédiat ?

  • A. Réinstaller le moteur Docker depuis zéro.
  • B. Vérifier docker inspect <nom> pour vérifier si la propriété OOMKilled est à true (dépassement du quota de mémoire RAM).
  • C. Changer le nom du conteneur avec --name.
  • D. Purger la totalité des volumes de l'hôte.

Mini-Défi : Diagnostic d'un Crash Éclair

Question : Votre conteneur démarre et s'arrête instantanément avec un code Exit 137. Quel est le réflexe de diagnostic immédiat ?

  • A. Réinstaller le moteur Docker depuis zéro.
  • B. Vérifier docker inspect <nom> pour vérifier si la propriété OOMKilled est à true (dépassement du quota de mémoire RAM).
  • C. Changer le nom du conteneur avec --name.
  • D. Purger la totalité des volumes de l'hôte.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : B                          │
│  La commande `docker inspect --format '{{.State.OOMKilled}}'│
│  <nom>` permet de confirmer instantanément si le noyau Linux│
│  a tué le processus suite à une saturation de la RAM.       │
└─────────────────────────────────────────────────────────────┘

DOCKER : Vers l'Orchestration Multi-Nœuds (Swarm & Kubernetes)

Le Besoin d'Orchestration : Sortir du Serveur Unique

Sur un serveur unique, si la machine tombe en panne, tous vos conteneurs sont indisponibles.

L'Orchestration regroupe un ensemble de machines physiques ou virtuelles (Cluster) pour former un supercalculateur unifié, capable de : - Répartir automatiquement les conteneurs sur les nœuds disponibles (Scheduling). - Remplacer instantanément un conteneur ou un nœud tombé en panne (Self-Healing). - Mettre à l'échelle dynamiquement (Scaling horizontal). - Mettre à jour les applications sans interruption de service (Rolling Updates).

Architecture Interne de Docker Swarm

Docker Swarm est le moteur d'orchestration natif intégré directement dans Docker Engine :

                    GROUPE DE CONSENSUS RAFT (MANAGERS)
┌────────────────────────────────────────────────────────────────────────┐
│ Manager Leader ◄─────────► Manager 2 ◄─────────► Manager 3             │
│ (API + Scheduler + Store Raft chiffré + Autorité de Certification TLS) │
└───────────────────────────────────┬────────────────────────────────────┘
                                    │ Communication mTLS (port 2377)
             ┌──────────────────────┼──────────────────────┐
             ▼                      ▼                      ▼
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ WORKER NODE 1        │ │ WORKER NODE 2        │ │ WORKER NODE 3        │
│ [ Task: App replica1]│ │ [ Task: App replica2]│ │ [ Task: App replica3]│
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
  • Tasks : L'unité atomique d'exécution (1 tâche = 1 conteneur).
  • Services : Définition de l'état désiré (replicas: 3, image, ports, contraintes de placement).

Le Réseau Overlay & le Routing Mesh

Swarm intègre un mécanisme de distribution du trafic réseau sur l'ensemble du cluster :

                            REQUÊTE HTTP : PORT 80
                                       │
                                       ▼
┌────────────────────────────────────────────────────────────────────────┐
│                    SWARM ROUTING MESH (IPVS + VXLAN)                   │
│  Le port publié 80 est ouvert sur TOUS les nœuds du cluster,           │
│  même ceux qui n'exécutent AUCUN conteneur de l'application !          │
└──────────────────┬───────────────────┬───────────────────┬─────────────┘
                   │                   │                   │
                   ▼                   ▼                   ▼
             [ Nœud 1 : App ]    [ Nœud 2 : App ]    [ Nœud 3 : Vide ]
                                                           │ (Redirige vers
                                                           └── Nœud 1 ou 2)

Déploiement de Stacks Déclaratives

Swarm réutilise la syntaxe de compose.yaml enrichie de la section deploy :

# stack.yaml
services:
  web:
    image: my-app:1.2.0
    ports:
      - "80:80"
    deploy:
      replicas: 5
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.role == worker
# 1. Initialiser le cluster Swarm sur le premier manager
docker swarm init

# 2. Déployer toute la pile applicative
docker stack deploy -c stack.yaml production-app

# 3. Suivre le déploiement des tâches
docker stack ps production-app

Gestion des Secrets dans Swarm

Swarm intègre un coffre-fort sécurisé : - Les secrets sont stockés chiffrés au repos dans le datastore Raft des managers. - Transmis aux workers exclusivement via TLS mutuel. - Montés uniquement en mémoire vive (tmpfs) dans /run/secrets/<nom_secret> des conteneurs autorisés.

# Création d'un secret Swarm
echo "super-password-123" | docker secret create db_root_pwd -

Quand Passer de Docker Swarm à Kubernetes ?

Fonctionnalité Docker Swarm Kubernetes (K8s)
Complexité d'installation Immédiate (docker swarm init) Moyenne à élevée (Cloud managé EKS/GKE/AKS recommandé)
Écosystème & Communauté Réduit / Stable Standard mondial de l'industrie (CNCF)
Autoscaling (HPA / VPA) Manuel Automatique selon CPU, RAM ou métriques métier
Stockage & CSI Local / NFS basique Pilotes CSI avancés, snapshots, PVC dynamiques
GitOps & Ingress Moderne Limité ArgoCD, Flux, Traefik, Cilium eBPF, Gateway API
Gestion Fine des Droits Basique RBAC granulaire, OIDC, Pod Security Standards

Mini-Défi : Résilience d'un Cluster Swarm

Question : Que se passe-t-il si un nœud Worker hébergeant 2 répliques de conteneurs s'éteint brutalement dans un cluster Swarm ?

  • A. Tout le cluster Swarm s'arrête instantanément.
  • B. Les conteneurs sont définitivement perdus et ne redémarrent jamais.
  • C. Les managers détectent la perte du nœud (Heartbeat) et replanifient immédiatement les 2 conteneurs sur les autres nœuds sains du cluster.
  • D. Une simple notification par email est envoyée sans action corrective.

Mini-Défi : Résilience d'un Cluster Swarm

Question : Que se passe-t-il si un nœud Worker hébergeant 2 répliques de conteneurs s'éteint brutalement dans un cluster Swarm ?

  • A. Tout le cluster Swarm s'arrête instantanément.
  • B. Les conteneurs sont définitivement perdus et ne redémarrent jamais.
  • C. Les managers détectent la perte du nœud (Heartbeat) et replanifient immédiatement les 2 conteneurs sur les autres nœuds sains du cluster.
  • D. Une simple notification par email est envoyée sans action corrective.
┌─────────────────────────────────────────────────────────────┐
│                        RÉPONSE : C                          │
│  Le groupe de consensus Raft (Managers) détecte la rupture  │
│  du Heartbeat et ordonne au scheduler d'instancier 2        │
│  nouvelles tâches pour restaurer l'état désiré (Replicas).  │
└─────────────────────────────────────────────────────────────┘

DOCKER : Synthèse & Bonnes Pratiques de Production

Les 10 Commandements du Conteneur en Production

┌────────────────────────────────────────────────────────────────────────┐
│               LE DÉCALOGUE DU DÉVELOPPEUR & DE L'OPS                   │
├────────────────────────────────────────────────────────────────────────┤
│ 1. Utiliser des images de base minimales et officielles (Alpine/Slim)  │
│ 2. Ne JAMAIS exécuter en tant que root : déclarer `USER <non-root>`    │
│ 3. Toujours figer les versions : bannir le tag `latest` en production  │
│ 4. Déclarer systématiquement un `.dockerignore` complet                │
│ 5. Adopter le pattern Multi-Stage Build pour séparer build et runtime  │
│ 6. Intégrer une sonde de santé explicite via `HEALTHCHECK`             │
│ 7. Fixer des plafonds stricts de CPU et de RAM (`--memory`, `--cpus`)  │
│ 8. Configurer la rotation automatique des logs dans `daemon.json`      │
│ 9. Ne JAMAIS intégrer de secrets ou de tokens dans le Dockerfile       │
│ 10. Scanner régulièrement les vulnérabilités avec Docker Scout / Trivy │
└────────────────────────────────────────────────────────────────────────┘

Synthèse de l'Outillage Moderne

┌───────────────────────┬────────────────────────────────────────────────────────┐
│ OUTIL                 │ RÔLE ET CAS D'USAGE PRINCIPAL                          │
├───────────────────────┼────────────────────────────────────────────────────────┤
│ `docker container`    │ Pilotage unifié du cycle de vie des conteneurs isolés  │
│ `docker buildx`       │ Compilation ultra-rapide multi-arch (BuildKit, cache)  │
│ `docker compose`      │ Orchestration déclarative multi-services en local & CI │
│ `docker scout`        │ Analyse de vulnérabilités et recommandations de base   │
│ `trivy`               │ Scanner de sécurité et de conformité open source       │
└───────────────────────┴────────────────────────────────────────────────────────┘

Vers la Suite du Parcours : Kubernetes & GitOps

Vous maîtrisez désormais les briques fondamentales de la conteneurisation !

┌───────────────────┐       ┌──────────────────────┐       ┌───────────────────┐
│ DOCKER FONDATIONS │ ────► │ KUBERNETES EXPERT    │ ────► │ GITOPS & ARGOCD   │
│ (Images, Run,     │       │ (Pods, Deployments,  │       │ (CI/CD Automatisée│
│  Volumes, Compose)│       │  Services, Ingress)  │       │  Infrastructure)  │
└───────────────────┘       └──────────────────────┘       └───────────────────┘
  • Certifications recommandées :
  • DCA : Docker Certified Associate
  • CKAD : Certified Kubernetes Application Developer
  • CKA : Certified Kubernetes Administrator
  • CKS : Certified Kubernetes Security Specialist

Ressources & Documentation de Référence

  • Documentation Officielle Docker : https://docs.docker.com/
  • Docker Hub : https://hub.docker.com/
  • Spécifications OCI : https://opencontainers.org/
  • Awesome Docker Security : https://github.com/veggiemonk/awesome-docker
  • Benchmark CIS Docker : https://www.cisecurity.org/benchmark/docker