# Fichier: python_cheats/cheatsheets/docker.txt
# Cheatsheet Docker - Guide Complet de Débutant à Expert


================================================================================
        DOCKER POUR GRANDS DÉBUTANTS - GUIDE ULTRA-DÉTAILLÉ
        Sections 1 & 2 : Concepts Fondamentaux et Installation
================================================================================


================================================================================
[OK] SECTION 1 : QU'EST-CE QUE DOCKER ? - CONCEPTS FONDAMENTAUX
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION POUR DÉBUTANTS                           ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QU'EST-CE QUE DOCKER ?                                                   │
└──────────────────────────────────────────────────────────────────────────┘

COMMENT le décrire simplement ?
  Docker est un outil qui permet d'empaqueter une application avec TOUT ce 
  dont elle a besoin pour fonctionner (code, bibliothèques, configuration) 
  dans une "boîte" appelée CONTENEUR.

POURQUOI cette définition ?
  - Imaginez un déménagement : vous mettez vos affaires dans des cartons
  - Chaque carton est fermé, étiqueté, et transportable partout
  - Docker fait pareil avec les applications informatiques

QUAND utiliser cette définition ?
  - Quand on vous demande "C'est quoi Docker ?"
  - Pour expliquer à un non-technicien
  - Dans un CV ou une présentation


┌──────────────────────────────────────────────────────────────────────────┐
│ LE PROBLÈME QUE DOCKER RÉSOUT                                            │
└──────────────────────────────────────────────────────────────────────────┘

COMMENT ça se passait AVANT Docker ?

  SITUATION 1 : L'équipe de développement
  ---------------------------------------
  [PERSONNE][CODE] Marc (développeur) : "Mon code marche !"
     - Python 3.11 installé
     - Bibliothèque Flask version 2.3
     - Base de données PostgreSQL 15
     - Système : Windows 11

  [PERSONNE][CODE] Sarah (développeuse) : "Moi ça bug..."
     - Python 3.9 installé
     - Flask 2.0 (ancienne version)
     - PostgreSQL 13
     - Système : Mac OS

  [ECRAN] Serveur de production : Encore différent !
     - Python 3.8
     - Flask 1.9
     - PostgreSQL 12
     - Système : Linux Ubuntu

  RÉSULTAT : Le code ne marche pas pareil partout ! [!]


  SITUATION 2 : Les conflits d'installation
  ------------------------------------------
  Projet A : Besoin de Python 3.11
  Projet B : Besoin de Python 3.8
  
  PROBLÈME : On ne peut pas avoir 2 versions en même temps facilement !
  (Sauf avec des outils compliqués comme virtualenv, conda, etc.)


COMMENT Docker RÉSOUT ce problème ?

  AVEC Docker :
  -------------
  1. Vous créez UN conteneur avec Python 3.11 + Flask 2.3 + PostgreSQL 15
  2. Ce conteneur fonctionne EXACTEMENT PAREIL sur :
     - L'ordinateur de Marc (Windows)
     - L'ordinateur de Sarah (Mac)
     - Le serveur de production (Linux)
  
  3. Vous pouvez avoir PLUSIEURS conteneurs en parallèle :
     - Conteneur 1 : Python 3.11 pour projet A
     - Conteneur 2 : Python 3.8 pour projet B
     - Ils ne se gênent PAS !


POURQUOI c'est révolutionnaire ?

  AVANT                              AVEC DOCKER
  ─────────────────────────────────────────────────────────────
  [X] Configuration manuelle          [OK] Configuration automatique
  [X] "Ça marche sur mon PC"          [OK] "Ça marche partout"
  [X] Installation compliquée         [OK] Une commande suffit
  [X] Conflits de versions            [OK] Isolation complète
  [X] Documentation obsolète          [OK] Code = documentation
  [X] Onboarding : 1-2 jours          [OK] Onboarding : 10 minutes


QUAND utiliser Docker ?

  [OK] OUI, utilisez Docker pour :
     - Développer une application web
     - Tester du code sur différents environnements
     - Déployer en production
     - Partager un projet avec votre équipe
     - Apprendre une nouvelle technologie sans "polluer" votre PC
     - Créer un portfolio de projets

  [X] NON, Docker n'est pas nécessaire pour :
     - Un script Python simple de 10 lignes
     - Une application de bureau classique
     - Un site web HTML/CSS statique basique
     - Un projet scolaire ultra-simple


╔══════════════════════════════════════════════════════════════════════════╗
║                    ANALOGIES POUR COMPRENDRE                             ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ ANALOGIE 1 : Le CONTENEUR = Boîte à lunch complète                       │
└──────────────────────────────────────────────────────────────────────────┘

COMMENT ça marche ?

  Imaginez une boîte à lunch Tupperware :
  
  [BENTO_BOX] BOÎTE À LUNCH                    [PACKAGE] CONTENEUR DOCKER
  ─────────────────────────────────────────────────────────────
  • Contient nourriture              • Contient application
  • Couvercle fermé hermétiquement   • Environnement isolé
  • Transportable partout            • Portable sur tout système
  • Garde fraîcheur                  • Garde configuration
  • Réutilisable                     • Redémarrable à l'infini
  • Empilable                        • Plusieurs en parallèle

POURQUOI cette analogie fonctionne ?

  - ISOLATION : Votre sandwich ne touche pas celui du collègue
    -> Dans Docker, vos apps ne s'interfèrent pas

  - PORTABILITÉ : Vous emmenez la boîte au travail, pique-nique, voyage
    -> Docker fonctionne sur Windows, Mac, Linux, Cloud

  - STANDARDISATION : Toutes les boîtes Tupperware ont même format
    -> Tous les conteneurs Docker suivent le même standard

  - REPRODUCTIBILITÉ : Même recette = même repas à chaque fois
    -> Même Dockerfile = même conteneur partout

QUAND utiliser cette analogie ?
  - Pour expliquer Docker à quelqu'un qui n'est pas informaticien
  - En entretien d'embauche pour montrer votre pédagogie
  - Dans une présentation non-technique


┌──────────────────────────────────────────────────────────────────────────┐
│ ANALOGIE 2 : L'IMAGE = Recette de cuisine                                │
└──────────────────────────────────────────────────────────────────────────┘

COMMENT ça marche ?

  [GUIDE] RECETTE DE GÂTEAU               [FRAME_WITH_PICTURE] IMAGE DOCKER
  ─────────────────────────────────────────────────────────────
  • Instructions écrites             • Fichier avec instructions
  • Liste d'ingrédients              • Liste de logiciels/libs
  • Étapes numérotées                • Commandes ordonnées
  • Partageable (livre recettes)     • Partageable (Docker Hub)
  • Réutilisable à l'infini          • Créer ∞ conteneurs
  • Ne se mange pas                  • Ne s'exécute pas
  • Template pour faire des gâteaux  • Template pour créer conteneurs

POURQUOI cette analogie est puissante ?

  RECETTE -> GÂTEAUX                  IMAGE -> CONTENEURS
  ─────────────────────────────────────────────────────────────
  1 recette -> 10 gâteaux             1 image -> 10 conteneurs
  
  Exemple :
  • Recette "Brownies"               • Image "python:3.11"
  • Vous faites 5 brownies           • Vous lancez 5 conteneurs
  • Tous identiques                  • Tous identiques
  • À partir de LA MÊME recette      • À partir de LA MÊME image

QUAND cette distinction est-elle importante ?

  CONFUSION FRÉQUENTE DÉBUTANT :
  "Je supprime l'image, mon application va crasher !"
  
  NON ! C'est comme :
  "Je brûle le livre de recettes, mes gâteaux vont disparaître !"
  
  Vos conteneurs (gâteaux) continuent d'exister même si vous 
  supprimez l'image (recette).


┌──────────────────────────────────────────────────────────────────────────┐
│ ANALOGIE 3 : Le DOCKERFILE = Liste de courses + Instructions             │
└──────────────────────────────────────────────────────────────────────────┘

COMMENT ça marche ?

  [NOTE] RECETTE ÉCRITE                  [FICHIER] DOCKERFILE
  ─────────────────────────────────────────────────────────────
  1. Prenez 3 œufs                   FROM python:3.11
  2. Ajoutez 200g de farine          RUN apt-get install curl
  3. Mélangez 100g de sucre          COPY app.py /app/
  4. Enfournez 180° pendant 30min    CMD ["python", "app.py"]

POURQUOI c'est pratique ?

  SANS DOCKERFILE (manuel) :
  1. Installer Python
  2. Installer Flask
  3. Copier le code
  4. Installer PostgreSQL
  5. Configurer...
  -> 30 minutes à 2 heures de travail manuel !
  -> Si erreur : recommencer tout !

  AVEC DOCKERFILE (automatique) :
  1. Écrire le Dockerfile (5 minutes)
  2. Taper : docker build .
  -> Docker fait TOUT automatiquement !
  -> Reproductible à l'infini

QUAND écrire un Dockerfile ?
  - Dès que votre projet dépasse 1 fichier
  - Quand vous voulez le partager avec d'autres
  - Pour le déployer en production
  - Pour documenter les dépendances


╔══════════════════════════════════════════════════════════════════════════╗
║                         CONCEPTS CLÉS                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 1 : L'IMAGE (l'emballage)                                        │
└──────────────────────────────────────────────────────────────────────────┘

QU'EST-CE QUE C'EST exactement ?

  DÉFINITION TECHNIQUE :
  Une image Docker est un fichier en lecture seule qui contient :
  - Un système d'exploitation minimal (souvent Linux)
  - Votre code source
  - Toutes les dépendances (bibliothèques, outils)
  - La configuration nécessaire

  DÉFINITION SIMPLE :
  C'est un fichier ZIP géant avec TOUT ce qu'il faut pour faire 
  tourner votre application.

COMMENT ça se présente ?

  Vous verrez des noms comme :
  • ubuntu:22.04
  • python:3.11
  • nginx:alpine
  • postgres:15
  • node:18-slim

  FORMAT : nom:version
           └─┘ └──────┘
         logiciel  tag (version spécifique)

POURQUOI plusieurs versions ?

  python:3.11          -> Python 3.11 complet (900 MB)
  python:3.11-slim     -> Version allégée (150 MB)
  python:3.11-alpine   -> Version ultra-légère (50 MB)

  QUAND utiliser laquelle ?
  - Développement : version complète (plus d'outils)
  - Production : slim ou alpine (plus rapide, plus sûr)

D'OÙ viennent les images ?

  DOCKER HUB : https://hub.docker.com
  C'est comme un "App Store" pour Docker :
  
  • Images OFFICIELLES : python, node, nginx, postgres
    -> Maintenues par les éditeurs officiels
    -> Testées et sécurisées
    -> À privilégier TOUJOURS

  • Images COMMUNAUTAIRES : créées par des utilisateurs
    -> Vérifiez la popularité (nombre de downloads)
    -> Lisez les commentaires
    -> Prudence sur la sécurité

COMMENT télécharger une image ?

  Commande : docker pull nom_image
  
  Exemple :
  docker pull python:3.11
  
  Ce qui se passe :
  1. Docker contacte Docker Hub
  2. Télécharge l'image couche par couche
  3. Vérifie l'intégrité
  4. Stocke dans votre disque dur local
  
  Taille typique : 50 MB à 2 GB selon l'image


┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 2 : LE CONTENEUR (l'application en marche)                       │
└──────────────────────────────────────────────────────────────────────────┘

QU'EST-CE QUE C'EST ?

  DÉFINITION TECHNIQUE :
  Un conteneur est une instance en cours d'exécution d'une image.

  DÉFINITION SIMPLE :
  Si l'image est un programme.exe, le conteneur est le programme 
  OUVERT et qui tourne.

  ANALOGIE :
  Image = Fichier .docx sur votre disque
  Conteneur = Document Word ouvert à l'écran

COMMENT ça fonctionne ?

  1 IMAGE -> PLUSIEURS CONTENEURS possibles
  
  Exemple :
  Image "nginx"  ->  Conteneur 1 : Site A (port 80)
                 ->  Conteneur 2 : Site B (port 8080)
                 ->  Conteneur 3 : Site C (port 8090)
  
  Les 3 conteneurs sont INDÉPENDANTS et ISOLÉS.

POURQUOI c'est léger et rapide ?

  MACHINE VIRTUELLE          CONTENEUR DOCKER
  ─────────────────────────────────────────────────
  • Démarre en 2-5 minutes   • Démarre en 1-2 secondes
  • Pèse 2-20 GB             • Pèse 50-500 MB
  • OS complet dupliqué      • Partage l'OS de l'hôte
  • Beaucoup de RAM          • Peu de RAM
  
  COMMENT c'est possible ?
  Docker ne crée PAS un nouvel OS. Il utilise l'OS de votre 
  ordinateur (Linux, Mac ou Windows) mais isole les processus.

QUAND un conteneur s'arrête-t-il ?

  Un conteneur s'arrête quand :
  [OK] Son processus principal se termine
  [OK] Vous tapez : docker stop nom_conteneur
  [OK] Une erreur fatale se produit
  [OK] Vous éteignez Docker

  IMPORTANT : Les données dans un conteneur sont PERDUES 
  quand il s'arrête (sauf si vous utilisez des volumes - 
  voir plus loin).


┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 3 : DOCKER HUB (le magasin d'images)                             │
└──────────────────────────────────────────────────────────────────────────┘

QU'EST-CE QUE C'EST ?

  URL : https://hub.docker.com
  
  C'est une bibliothèque en ligne gratuite avec des MILLIERS 
  d'images prêtes à l'emploi.

COMMENT naviguer Docker Hub ?

  RECHERCHE :
  1. Allez sur hub.docker.com
  2. Tapez "python" dans la barre de recherche
  3. Vous verrez : 
     - Image officielle "python"
     - Nombre de téléchargements : 1 milliard+
     - Différents tags disponibles

  CHOISIR UNE IMAGE :
  [OK] Badge "Official Image" : Prioritaire !
  [OK] Beaucoup de downloads : Signe de confiance
  [OK] Dernière mise à jour récente : Moins de 6 mois
  [OK] Documentation complète : README détaillé

POURQUOI utiliser Docker Hub ?

  Au lieu de :
  [X] Installer manuellement PostgreSQL (30 minutes)
  [X] Configurer les paramètres (15 minutes)
  [X] Déboguer les erreurs (1 heure)
  
  Vous faites :
  [OK] docker pull postgres (1 minute)
  [OK] docker run postgres (10 secondes)
  [OK] C'est prêt !

QUAND utiliser Docker Hub ?

  TOUJOURS pour commencer un projet :
  - Besoin de Python ? docker pull python
  - Besoin de Node.js ? docker pull node
  - Besoin d'une base de données ? docker pull postgres
  - Besoin de Redis ? docker pull redis

  Ne réinventez JAMAIS la roue !


┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 4 : DOCKERFILE (la recette de fabrication)                       │
└──────────────────────────────────────────────────────────────────────────┘

QU'EST-CE QUE C'EST ?

  Un Dockerfile est un fichier TEXTE qui contient les instructions 
  pour créer une image Docker personnalisée.

  NOM DU FICHIER : Exactement "Dockerfile" (sans extension)
  EMPLACEMENT : À la racine de votre projet

COMMENT ça ressemble ?

  Exemple ultra-simple :
  
  FROM python:3.11
  COPY app.py /app/
  WORKDIR /app
  CMD ["python", "app.py"]

  Explication ligne par ligne :
  
  FROM python:3.11
  └─ "Commence avec l'image Python 3.11"
  
  COPY app.py /app/
  └─ "Copie mon fichier app.py dans le dossier /app du conteneur"

  VOLUME ["/app/data"]
  └─ [ATTENTION] “Déclare que /app/data est un point de montage pour un volume.A éviter car si
  aucun volume n’est fourni (ni volume nommé, ni bind mount), alors Docker :
  crée un volume anonyme (sans nom explicite)”  
  
  WORKDIR /app
  └─ "Place-toi dans le dossier /app"
  
  CMD ["python", "app.py"]
  └─ "Lance la commande : python app.py"

  1. Exemple docker-compose
  volumes:
      - app_data:/app/data   # Ceci est un VOLUME NOMMÉ

   2. Exemple en ligne de commande
   docker run -v /chemin/hote:/chemin/conteneur nom_image   # Ceci est un BIND MOUNT

   NB: On peux utiliser un bind mount dans un docker-compose.yml. Même si la syntaxe la plus courante pour docker-compose utilise des volumes nommés, rien n’empêche de monter un dossier précis de ton PC vers un conteneur.

   3. Exemple
   volumes:
      # Bind mount : dossier local ./data -> /app/data dans le conteneur
      - ./data:/app/data  # Ceci est un BIND MOUNT

   [ATTENTION] Attention aux bind mounts
   Le chemin hôte doit exister, sinon Docker le crée vide.
   Peut poser problème sur Windows avec les permissions.
   Pas recommandé pour la production (sauf si tu sais ce que tu fais).
   Idéal pour dev / tests rapides.


POURQUOI c'est puissant ?

  AVANT DOCKERFILE :
  Vous devez dire à chaque collègue :
  "Installe Python 3.11, puis Flask, puis copie le code, 
  puis configure, puis lance..."
  -> Instructions verbales [X]
  -> Erreurs humaines [X]
  -> Temps perdu [X]

  AVEC DOCKERFILE :
  Vous partagez 1 fichier. N'importe qui tape :
  docker build .
  -> Automatisé [OK]
  -> Reproductible [OK]
  -> Documenté [OK]

QUAND créer un Dockerfile ?

  [OK] OUI :
  - Vous voulez déployer votre app
  - Vous travaillez en équipe
  - Votre projet a des dépendances complexes

  [ATTENTION] PAS NÉCESSAIRE :
  - Vous utilisez une image toute faite (ex: docker pull python)
  - Vous testez rapidement quelque chose
  - Projet ultra-simple sans dépendances


┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 5 : VOLUME (le stockage persistant)                              │
└──────────────────────────────────────────────────────────────────────────┘

QUEL PROBLÈME ÇA RÉSOUT ?

  PROBLÈME :
  Vous créez un conteneur avec une base de données.
  Vous ajoutez des données.
  Vous arrêtez le conteneur.
  Vous le redémarrez...
  [!] Toutes vos données ont DISPARU !

  POURQUOI ?
  Les conteneurs sont ÉPHÉMÈRES par défaut.
  Quand ils s'arrêtent, tout ce qui était dedans est PERDU.

COMMENT les volumes résolvent ça ?

  Un volume est un dossier PARTAGÉ entre :
  - Votre ordinateur (hôte)
  - Le conteneur

  Schéma :
  
  VOTRE PC              CONTENEUR
  ───────────           ──────────
  /data  <-──────────->  /app/data
  │
  └─ Les fichiers ici sont SYNCHRONISÉS
     et SURVIVENT à l'arrêt du conteneur

COMMENT créer un volume ?

  Commande simple :
  docker run -v /chemin/hote:/chemin/conteneur nom_image
  
  Exemple concret :
  docker run -v /Users/moi/data:/data postgres
  
  Résultat :
  - PostgreSQL stocke ses données dans /data du conteneur
  - Ce dossier pointe vers /Users/moi/data sur votre Mac
  - Les données restent même si vous supprimez le conteneur

QUAND utiliser des volumes ?

  [OK] TOUJOURS pour :
  - Bases de données (PostgreSQL, MongoDB, MySQL)
  - Fichiers uploadés par utilisateurs
  - Logs qu'on veut garder
  - Configuration qu'on veut modifier en direct

  [X] PAS NÉCESSAIRE pour :
  - Code source (on utilise COPY dans Dockerfile)
  - Dépendances (gérées par l'image)
  - Conteneurs temporaires de test


┌──────────────────────────────────────────────────────────────────────────┐
│ CONCEPT 6 : RÉSEAU (la communication entre conteneurs)                   │
└──────────────────────────────────────────────────────────────────────────┘

QUEL PROBLÈME ÇA RÉSOUT ?

  SITUATION :
  Vous avez 2 conteneurs :
  1. Conteneur "web" : Votre application Flask
  2. Conteneur "db" : Votre base de données PostgreSQL
  
  PROBLÈME :
  Comment "web" peut-il parler à "db" ? [REFLEXION]

COMMENT les réseaux Docker résolvent ça ?

  Docker crée un "réseau virtuel" où les conteneurs 
  peuvent communiquer entre eux PAR LEUR NOM.

  Exemple :
  
  Conteneur "web"  ──->  Conteneur "db"
  
  Dans le code de "web", vous écrivez :
  DATABASE_URL = "postgresql://db:5432/mydb"
                                ^
                         Nom du conteneur !
  
  Docker résout automatiquement "db" vers l'IP du conteneur.

COMMENT créer un réseau ?

  Méthode 1 : Réseau automatique avec docker-compose (plus simple)
  Méthode 2 : Réseau manuel
  
  docker network create monreseau
  docker run --network monreseau --name web myapp
  docker run --network monreseau --name db postgres

QUAND s'en préoccuper ?

  [ATTENTION] DÉBUTANT : Ne vous cassez pas la tête au début
  Docker crée un réseau par défaut qui marche dans 90% des cas.

  [OK] INTERMÉDIAIRE : Apprenez quand vous avez plusieurs 
  conteneurs qui doivent communiquer.


Méthode 1 : Réseau automatique avec docker-compose

   Docker-compose crée automatiquement un réseau pour tous les services d’un même fichier compose.

   version: "3.9"

   services:
   web:
      image: my_flask_app
      container_name: web
      ports:
         - "5000:5000"
      depends_on:
         - db
      # Le réseau est créé automatiquement par compose
      # Le conteneur 'web' pourra contacter 'db' par son nom

   db:
      image: postgres:15
      container_name: db
      environment:
         POSTGRES_USER: admin
         POSTGRES_PASSWORD: admin123
         POSTGRES_DB: mydb

Méthode 2 : Réseau manuel avec docker run

   Si tu utilises docker run seul, tu dois créer et assigner le réseau manuellement.

   1. Créer le réseau :
      docker network create monreseau

   2. Lancer la base de données sur ce réseau :
      docker run -d \
         --network monreseau \
         --name db \
         -e POSTGRES_USER=admin \
         -e POSTGRES_PASSWORD=admin123 \
         -e POSTGRES_DB=mydb \
         postgres:15

   3. Lancer l’application Flask sur le même réseau :
      docker run -d \
      --network monreseau \
      --name web \
      -p 5000:5000 \
      my_flask_app



╔══════════════════════════════════════════════════════════════════════════╗
║          COMPARAISON : MACHINE VIRTUELLE vs DOCKER                       ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ TABLEAU COMPARATIF DÉTAILLÉ                                              │
└──────────────────────────────────────────────────────────────────────────┘

CRITÈRE                MACHINE VIRTUELLE        DOCKER CONTENEUR
─────────────────────────────────────────────────────────────────────────
TAILLE                 2-20 GB                  50-500 MB
                       [X] Très lourd            [OK] Léger

DÉMARRAGE              2-5 minutes              1-2 secondes
                       [X] Lent comme PC         [OK] Instantané

MÉMOIRE RAM            512 MB - 8 GB minimum    10-100 MB typique
                       [X] Gourmand              [OK] Économe

SYSTÈME D'EXPLOIT.     OS complet dupliqué      Partage l'OS hôte
                       [X] Overhead important    [OK] Efficace

ISOLATION              Complète (hardware)      Processus
                       [OK] Très sûr              [OK] Assez sûr

PORTABILITÉ            Fichier .vmdk/ova        Image Docker
                       [ATTENTION] Limité                [OK] Universel

NOMBRE POSSIBLE        2-10 max sur PC          50-100+ possible
                       [X] Limité par RAM        [OK] Scalable

PERFORMANCE            80-90% du natif          95-99% du natif
                       [ATTENTION] Overhead              [OK] Quasi-natif


QUAND utiliser une VM au lieu de Docker ?

  [OK] MACHINE VIRTUELLE si :
  - Vous voulez un OS graphique complet (Windows avec interface)
  - Vous testez des systèmes d'exploitation différents
  - Vous avez besoin d'isolation matérielle totale
  - Vous utilisez des logiciels avec licence liée au système
  - Sécurité maximale requise (banking, crypto)

  [OK] DOCKER si :
  - Vous développez des applications web/mobiles
  - Vous voulez déployer en production
  - Vous travaillez en équipe
  - Vous voulez rapidité et légèreté
  - Vous faites du DevOps/CI-CD


POURQUOI Docker est-il plus rapide ?

  MACHINE VIRTUELLE :
  ┌─────────────────────────────────────┐
  │       Votre PC (Windows/Mac)        │
  │  ┌───────────────────────────────┐  │
  │  │    Hypervisor (VirtualBox)    │  │
  │  │  ┌─────────────────────────┐  │  │
  │  │  │   OS Invité (Linux)     │  │  │
  │  │  │  ┌───────────────────┐  │  │  │
  │  │  │  │  Votre App        │  │  │  │
  │  │  │  └───────────────────┘  │  │  │
  │  │  └─────────────────────────┘  │  │
  │  └───────────────────────────────┘  │
  └─────────────────────────────────────┘
  
  -> 4 couches d'abstraction
  -> OS complet dupliqué
  -> Lent et lourd

  DOCKER :
  ┌─────────────────────────────────────┐
  │       Votre PC (Windows/Mac)        │
  │  ┌───────────────────────────────┐  │
  │  │     Docker Engine             │  │
  │  │  ┌─────────────────────────┐  │  │
  │  │  │  Votre App              │  │  │
  │  │  └─────────────────────────┘  │  │
  │  └───────────────────────────────┘  │
  └─────────────────────────────────────┘
  
  -> 2 couches seulement
  -> Partage l'OS
  -> Rapide et léger


╔══════════════════════════════════════════════════════════════════════════╗
║               WORKFLOW TYPIQUE POUR UN DÉBUTANT                          ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ SCÉNARIO : Votre premier conteneur Python                                │
└──────────────────────────────────────────────────────────────────────────┘

ÉTAPE 1 : Télécharger une image
───────────────────────────────

COMMANDE :
docker pull python:3.11

QUE SE PASSE-T-IL ?
1. Docker contacte Docker Hub (hub.docker.com)
2. Vérifie que l'image "python:3.11" existe
3. Télécharge l'image couche par couche :
   
   a1b2c3d4: Downloading [>                ] 1.5 MB/50 MB
   e5f6g7h8: Downloading [=>               ] 5 MB/150 MB
   ...
   
4. Vérifie l'intégrité (checksum)
5. Stocke l'image sur votre disque :
   Windows : C:\ProgramData\docker\
   Mac : ~/Library/Containers/com.docker.docker/
   Linux : /var/lib/docker/

DURÉE : 1-3 minutes (selon votre connexion)
TAILLE : ~900 MB pour python:3.11 complet

COMMENT vérifier que c'est bien téléchargé ?
docker images

Vous verrez :
REPOSITORY   TAG    IMAGE ID      CREATED       SIZE
python       3.11   a1b2c3d4e5f6  2 weeks ago   920MB


ÉTAPE 2 : Créer et démarrer un conteneur
─────────────────────────────────────────

COMMANDE :
docker run -it python:3.11

DÉCORTIQUONS LA COMMANDE :
docker       -> Appel au programme Docker
run          -> "Lance un conteneur"
-it          -> -i = interactif (vous pouvez taper des commandes)
               -t = terminal (affiche un terminal propre)
python:3.11  -> Nom de l'image à utiliser

QUE SE PASSE-T-IL ?
1. Docker cherche l'image "python:3.11" localement
2. Crée un nouveau conteneur à partir de cette image
3. Démarre le conteneur
4. Vous donne accès au terminal Python :
   
   Python 3.11.0 (main, Oct 24 2022, 18:26:48)
   [GCC 8.3.0] on linux
   Type "help", "copyright", "credits" or "license" for more info.
   >>> 

DURÉE : 1-2 secondes [RAPIDE]

VOUS ÊTES MAINTENANT "DANS" LE CONTENEUR !

Vous pouvez taper du code Python :
>>> print("Hello Docker!")
Hello Docker!
>>> import sys
>>> sys.version
'3.11.0 (main, Oct 24 2022, 18:26:48) [GCC 8.3.0]'

C'est magique ! Vous avez Python 3.11 qui tourne sans l'avoir 
installé "pour de vrai" sur votre PC.


ÉTAPE 3 : Sortir du conteneur
──────────────────────────────

Pour quitter, tapez :
>>> exit()

OU appuyez sur : Ctrl+D

Vous revenez à votre terminal normal.

LE CONTENEUR S'ARRÊTE automatiquement quand vous sortez.


ÉTAPE 4 : Vérifier ce qui s'est passé
──────────────────────────────────────

COMMANDE :
docker ps -a

Vous verrez :
CONTAINER ID   IMAGE         COMMAND      CREATED         STATUS
a1b2c3d4e5f6   python:3.11   "python3"    2 minutes ago   Exited (0)

EXPLICATION :
- CONTAINER ID : Identifiant unique (comme un numéro de série)
- IMAGE : L'image utilisée
- COMMAND : La commande lancée automatiquement
- CREATED : Quand le conteneur a été créé
- STATUS : État actuel (Exited = arrêté)


RÉSUMÉ DU WORKFLOW :
┌─────────────────────────────────────────────────────────────┐
│  1. docker pull    -> Télécharge l'image                     │
│  2. docker run     -> Crée et lance le conteneur             │
│  3. Vous travaillez dans le conteneur                       │
│  4. exit           -> Quitte et arrête le conteneur          │
│  5. docker ps -a   -> Vérifie l'historique                   │
└─────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║                  EXEMPLE CONCRET : APPLICATION FLASK                     ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ COMPARAISON : Méthode Traditionnelle vs Docker                           │
└──────────────────────────────────────────────────────────────────────────┘

SCÉNARIO :
Vous voulez lancer une application web Flask simple.

═══════════════════════════════════════════════════════════════════════════
  MÉTHODE TRADITIONNELLE (Sans Docker)
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Installer Python (15-30 minutes)
──────────────────────────────────────────
1. Aller sur python.org
2. Télécharger Python 3.11 pour votre OS
3. Installer (suivre l'assistant)
4. Configurer PATH
5. Vérifier l'installation

[ATTENTION] PROBLÈMES POSSIBLES :
- "Python n'est pas reconnu comme commande"
- Conflit avec autre version de Python
- Certificat SSL qui plante


ÉTAPE 2 : Créer environnement virtuel (5-10 minutes)
─────────────────────────────────────────────────────
python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate     # Windows

[ATTENTION] PROBLÈMES POSSIBLES :
- "venv n'est pas installé"
- Problèmes de permissions
- Script bloqué par politique d'exécution (Windows)


ÉTAPE 3 : Installer Flask (5 minutes)
──────────────────────────────────────
pip install flask

[ATTENTION] PROBLÈMES POSSIBLES :
- "pip n'est pas reconnu"
- Problème de réseau
- Conflit de versions avec autres packages


ÉTAPE 4 : Créer l'application (5 minutes)
──────────────────────────────────────────
# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run(debug=True)


ÉTAPE 5 : Lancer l'app (1 minute)
──────────────────────────────────
python app.py

[ATTENTION] PROBLÈMES POSSIBLES :
- Port déjà utilisé
- Firewall bloque
- Module introuvable


ÉTAPE 6 : Partager avec collègue (30+ minutes)
───────────────────────────────────────────────
1. Expliquer les étapes 1-5 par écrit
2. Votre collègue suit les étapes
3. Déboguer ses erreurs spécifiques à son OS
4. Recommencer jusqu'à ce que ça marche

TOTAL : 1h à 2h pour tout le monde
[X] Frustrant
[X] Erreurs fréquentes
[X] Non reproductible


═══════════════════════════════════════════════════════════════════════════
  MÉTHODE DOCKER
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Créer un Dockerfile (2 minutes)
──────────────────────────────────────────
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]


ÉTAPE 2 : Créer requirements.txt (1 minute)
────────────────────────────────────────────
# requirements.txt
Flask==2.3.0


ÉTAPE 3 : Créer l'application (même code)
──────────────────────────────────────────
# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)


ÉTAPE 4 : Build l'image (2 minutes)
────────────────────────────────────
docker build -t monapp .

1⃣ Décomposition de la commande
   docker build : Indique à Docker que l’on veut construire une image 
                  à partir d’un Dockerfile.
   -t monapp : Taguer l’image avec un nom lisible (monapp). Facultatif : 
               tu peux aussi faire -t monapp:1.0 pour ajouter une version.
   . : Contexte de build -> le dossier actuel où Docker va chercher le Dockerfile 
         et tous les fichiers nécessaires (ex : app.py, requirements.txt).

2⃣ Ce que Docker fait exactement
   QUE SE PASSE-T-IL ?
   1. Docker lit le Dockerfile ligne par ligne
   2. Télécharge Python 3.11-slim si pas déjà là
   3. Crée un dossier /app
   4. Copie requirements.txt
   5. Installe Flask automatiquement
   6. Copie tout votre code
   7. Configure la commande de démarrage
   -> TOUT EST AUTOMATIQUE !

3⃣ Effet concret
   À la fin, tu as une image Docker locale nommée monapp.
   Cette image contient :
   Python 3.11
   Flask installé
   Ton code complet
   La commande pour démarrer l’application

4⃣ Pourquoi utiliser -t ?
   Le tag rend l’image facile à référencer dans docker run :
   docker run -p 5000:5000 monapp
   Sans -t, Docker donnerait un ID numérique incompréhensible.
   Avec un tag, tu peux partager et versionner tes images facilement.


ÉTAPE 5 : Lancer l'app (10 secondes)
─────────────────────────────────────
docker run -p 5000:5000 monapp

Ouvrez : http://localhost:5000
Vous voyez : "Hello World!" [OK]


ÉTAPE 6 : Partager avec collègue (5 minutes)
─────────────────────────────────────────────
1. Vous partagez 3 fichiers :
   - Dockerfile
   - app.py
   - requirements.txt

2. Votre collègue tape :
   docker build -t monapp .
   docker run -p 5000:5000 monapp

3. ÇA MARCHE. Immédiatement.

TOTAL : 10 minutes pour tout le monde
[OK] Rapide
[OK] Zéro erreur
[OK] Reproductible à 100%


POURQUOI c'est si différent ?

TRADITIONNEL                      DOCKER
────────────────────────────────────────────────────────────
Installation manuelle             Automatisée
Dépendant de l'OS                 Indépendant de l'OS
Configuration verbale             Configuration en code
Erreurs humaines fréquentes       Reproductible
Documentation obsolète            Code = documentation
Onboarding : 1-2 jours            Onboarding : 10 minutes


╔══════════════════════════════════════════════════════════════════════════╗
║                      VOCABULAIRE ESSENTIEL                               ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ Les 12 Commandes à Connaître Absolument                                  │
└──────────────────────────────────────────────────────────────────────────┘

1. BUILD - Construire une image
   ────────────────────────────
   QUOI : Crée une image à partir d'un Dockerfile
   QUAND : Après avoir écrit/modifié un Dockerfile
   COMMENT : docker build -t nom_image .
   
   ANALOGIE : Compiler un programme
   
   Exemple :
   docker build -t monapp:v1 .
   
   Ce qui se passe :
   - Lit le Dockerfile
   - Exécute chaque instruction
   - Crée l'image finale
   - La nomme "monapp:v1"


2. RUN - Créer et démarrer un conteneur
   ─────────────────────────────────────
   QUOI : Lance un conteneur depuis une image
   QUAND : Pour démarrer une application
   COMMENT : docker run [options] nom_image
   
   ANALOGIE : Double-cliquer sur un .exe
   
   Options courantes :
   -d               -> Détaché (en arrière-plan)
   -it              -> Interactif avec terminal
   -p 8080:80       -> Mapper le port
   --name monnom    -> Donner un nom
   --rm             -> Supprimer après arrêt
   
   Exemples :
   docker run -d nginx
   docker run -it ubuntu bash
   docker run -p 8080:80 --name web nginx

1⃣ Syntaxe : -p PORT_HOTE:PORT_CONTENEUR
   -p <port_de_ta_machine>:<port_du_conteneur>
   Cela signifie :
      PORT_HÔTE (8080) -> le port sur TON PC
      PORT_CONTENEUR (80) -> le port exposé À L’INTÉRIEUR du conteneur

2⃣ Pourquoi faut-il mapper les ports ?
   Un conteneur est isolé du système et il a ses propres ports internes.

   Exemple :
      Un serveur web Nginx tourne dans un conteneur sur le port 80 (port par défaut d’un serveur web).
      Mais ce port n’est PAS visible depuis ta machine.
      Donc si tu tapes :
         http://localhost:80
      -> tu n’atteins pas le conteneur.
      Pour que ton PC puisse communiquer avec le conteneur, tu dois faire un pont (mapping) 
      entre un port de ta machine et un port interne du conteneur.

3⃣ Donc : Pourquoi 8080:80 ?
   -> 80 = port du serveur dans le conteneur
   (nginx, apache, flask, fastapi, etc.)

   -> 8080 = port sur ta machine
   Celui que toi tu vas utiliser dans le navigateur.

4⃣ Exemple concret
   docker run -p 8080:80 nginx
   
   Effets :
      Nginx écoute dans le conteneur sur port 80
      Ton navigateur va se connecter à localhost:8080
      Docker redirige automatiquement vers 80 dans le conteneur
      -> Et tu vois la page d'accueil Nginx.

DOCKERFILE --(docker build)--> IMAGE --(docker run)--> CONTAINER

3. PULL - Télécharger une image
   ─────────────────────────────
   QUOI : Télécharge une image depuis Docker Hub
   QUAND : Avant d'utiliser une image qu'on n'a pas
   COMMENT : docker pull nom_image:tag
   
   ANALOGIE : Télécharger une app depuis l'App Store
   
   Exemples :
   docker pull ubuntu
   docker pull python:3.11
   docker pull nginx:alpine


4. PUSH - Envoyer une image
   ──────────────────────────
   QUOI : Envoie votre image sur Docker Hub
   QUAND : Pour partager votre image publiquement
   COMMENT : docker push username/nom_image:tag
   
   ANALOGIE : Publier une app sur l'App Store
   
   Prérequis : docker login
   
   Exemple :
   docker tag monapp username/monapp:v1
   docker push username/monapp:v1


5. START / STOP - Gérer un conteneur existant
   ───────────────────────────────────────────
   QUOI : Démarre/arrête un conteneur déjà créé
   QUAND : Pour redémarrer sans recréer
   COMMENT : docker start/stop nom_ou_id
   
   ANALOGIE : Mettre un programme en pause/reprendre
   
   Exemples :
   docker stop monconteneur
   docker start monconteneur
   docker restart monconteneur


6. PS - Voir les conteneurs
   ─────────────────────────
   QUOI : Liste les conteneurs qui tournent
   QUAND : Pour surveiller ce qui est actif
   COMMENT : docker ps [options]
   
   ANALOGIE : Gestionnaire des tâches Windows
   
   Options :
   (rien)    -> Conteneurs actifs seulement
   -a        -> Tous les conteneurs
   -q        -> IDs seulement
   
   Exemples :
   docker ps
   docker ps -a
   docker ps -q


7. IMAGES - Voir les images
   ─────────────────────────
   QUOI : Liste toutes les images téléchargées
   QUAND : Pour voir ce qu'on a localement
   COMMENT : docker images
   
   ANALOGIE : Liste des programmes installés
   
   Affiche :
   REPOSITORY   TAG      IMAGE ID      CREATED      SIZE
   python       3.11     a1b2c3d4      2 weeks ago  920MB
   nginx        alpine   e5f6g7h8      1 month ago  23MB


8. RM - Supprimer un conteneur
   ────────────────────────────
   QUOI : Supprime un conteneur arrêté
   QUAND : Pour libérer de l'espace
   COMMENT : docker rm nom_ou_id
   
   ANALOGIE : Désinstaller un programme
   
   Options :
   -f    -> Forcer (même si actif)
   
   Exemples :
   docker rm monconteneur
   docker rm -f monconteneur
   docker rm $(docker ps -aq)  # Tous les conteneurs


9. RMI - Supprimer une image
   ──────────────────────────
   QUOI : Supprime une image
   QUAND : Pour libérer beaucoup d'espace
   COMMENT : docker rmi nom_image:tag
   
   ANALOGIE : Supprimer un fichier d'installation .exe
   
   [ATTENTION] ATTENTION : Ne peut pas supprimer si un conteneur l'utilise
   
   Exemples :
   docker rmi nginx
   docker rmi -f image_id


10. LOGS - Voir les logs
    ────────────────────
    QUOI : Affiche ce qu'un conteneur a affiché
    QUAND : Pour déboguer ou surveiller
    COMMENT : docker logs nom_ou_id
    
    ANALOGIE : Console de débogage
    
    Options :
    -f          -> Suivre en temps réel (tail -f)
    --tail 50   -> 50 dernières lignes
    
    Exemples :
    docker logs monconteneur
    docker logs -f monconteneur


11. EXEC - Exécuter dans un conteneur actif
    ────────────────────────────────────────
    QUOI : Lance une commande dans un conteneur en cours
    QUAND : Pour inspecter ou déboguer
    COMMENT : docker exec [options] nom commande
    
    ANALOGIE : Ouvrir la console développeur dans un navigateur
    
    Exemples :
    docker exec monconteneur ls /app
    docker exec -it monconteneur bash
    docker exec monconteneur python script.py


12. INSPECT - Inspecter en détail
    ──────────────────────────────
    QUOI : Affiche toutes les infos sur un conteneur/image
    QUAND : Pour déboguer ou comprendre la config
    COMMENT : docker inspect nom_ou_id
    
    ANALOGIE : Propriétés d'un fichier (clic droit)
    
    Retour : JSON avec TOUT
    - Configuration réseau
    - Variables d'environnement
    - Volumes montés
    - État
    - etc.


╔══════════════════════════════════════════════════════════════════════════╗
║                    PREMIERS PAS APRÈS INSTALLATION                       ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ Test 1 : Vérifier que Docker fonctionne                                  │
└──────────────────────────────────────────────────────────────────────────┘

COMMANDE :
docker --version

RÉSULTAT ATTENDU :
Docker version 24.0.6, build ed223bc

SI ERREUR "docker n'est pas reconnu" :
-> Docker n'est pas installé ou pas dans le PATH
-> Redémarrez votre terminal
-> Sur Windows : Relancez Docker Desktop


COMMANDE :
docker run hello-world

QUE SE PASSE-T-IL ?
1. Docker cherche l'image "hello-world" localement
2. Ne la trouve pas
3. La télécharge depuis Docker Hub
4. Crée un conteneur
5. Le lance
6. Affiche un message de bienvenue
7. S'arrête automatiquement

RÉSULTAT ATTENDU :
Hello from Docker!
This message shows that your installation appears to be working correctly.
[...]

[OK] SI VOUS VOYEZ ÇA : Docker fonctionne parfaitement !


┌──────────────────────────────────────────────────────────────────────────┐
│ Test 2 : Télécharger votre première "vraie" image                        │
└──────────────────────────────────────────────────────────────────────────┘

COMMANDE :
docker pull ubuntu

QUE SE PASSE-T-IL ?
Vous verrez quelque chose comme :

Using default tag: latest
latest: Pulling from library/ubuntu
445a6a12be2b: Downloading [====>                ] 5.2MB/29.5MB
445a6a12be2b: Pull complete
Digest: sha256:...
Status: Downloaded newer image for ubuntu:latest

EXPLICATION :
- "Using default tag: latest" -> Pas de version spécifiée, prend la dernière
- "445a6a12be2b: Downloading" -> Télécharge couche par couche
- "Pull complete" -> Téléchargement terminé
- "Status: Downloaded" -> Image stockée localement

DURÉE : 30 secondes à 2 minutes

VÉRIFICATION :
docker images

Vous verrez :
REPOSITORY   TAG      IMAGE ID       CREATED      SIZE
ubuntu       latest   3b418d7b466a   2 weeks ago  77.8MB


┌──────────────────────────────────────────────────────────────────────────┐
│ Test 3 : Lancer Ubuntu interactif                                        │
└──────────────────────────────────────────────────────────────────────────┘

COMMANDE :
docker run -it ubuntu bash

RÉSULTAT :
Votre prompt change :
root@a1b2c3d4e5f6:/#

VOUS ÊTES DANS UBUNTU !

Essayez des commandes Linux :

# Où suis-je ?
pwd
-> /

# Quels fichiers ?
ls
-> bin  boot  dev  etc  home  lib  ...

# Qui suis-je ?
whoami
-> root

# Quelle version d'Ubuntu ?
cat /etc/os-release
-> Ubuntu 22.04.3 LTS

C'est magique ! Vous avez Ubuntu qui tourne, et vous êtes 
"root" (administrateur) sans rien installer sur votre PC !


POUR SORTIR :
exit

OU : Ctrl+D

Vous revenez à votre terminal normal.


┌──────────────────────────────────────────────────────────────────────────┐
│ Test 4 : Voir ce qui tourne                                              │
└──────────────────────────────────────────────────────────────────────────┘

COMMANDE :
docker ps

SI RIEN NE TOURNE :
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
(vide)

C'est normal ! Tous vos conteneurs se sont arrêtés.


COMMANDE :
docker ps -a

VOUS VERREZ L'HISTORIQUE :
CONTAINER ID   IMAGE         COMMAND       CREATED         STATUS
a1b2c3d4e5f6   ubuntu        "bash"        2 minutes ago   Exited (0)
f7g8h9i0j1k2   hello-world   "/hello"      5 minutes ago   Exited (0)

EXPLICATION :
- CONTAINER ID : Identifiant court (les 12 premiers caractères)
- IMAGE : Image utilisée
- COMMAND : Commande lancée automatiquement
- CREATED : Quand le conteneur a été créé
- STATUS : Exited (0) = Terminé sans erreur
           Exited (1) = Terminé avec erreur
           Up 2 minutes = Actif depuis 2 minutes


╔══════════════════════════════════════════════════════════════════════════╗
║                   ERREURS COURANTES DE DÉBUTANT                          ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ ERREUR 1 : "Cannot connect to Docker daemon"                             │
└──────────────────────────────────────────────────────────────────────────┘

MESSAGE COMPLET :
Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?

POURQUOI ça arrive ?
Docker fonctionne en 2 parties :
1. Docker CLI (le programme que vous appelez)
2. Docker Engine/Daemon (le service qui tourne en arrière-plan)

Le CLI essaie de parler au Daemon, mais ne le trouve pas.

COMMENT résoudre ?

SUR WINDOWS / MAC :
-> Lancez Docker Desktop
-> Attendez que l'icône Docker (la baleine) soit stable
-> Réessayez votre commande

SUR LINUX :
-> Démarrez le service :
  sudo systemctl start docker
-> Vérifiez qu'il tourne :
  sudo systemctl status docker
-> Activez au démarrage :
  sudo systemctl enable docker

QUAND ça arrive ?
- Premier lancement après installation
- Après un redémarrage de PC
- Si Docker Desktop a crashé


┌──────────────────────────────────────────────────────────────────────────┐
│ ERREUR 2 : "Permission denied"                                           │
└──────────────────────────────────────────────────────────────────────────┘

MESSAGE COMPLET :
Got permission denied while trying to connect to the Docker daemon socket

POURQUOI ça arrive ?
Sur Linux, Docker nécessite des permissions root par défaut.
Sur Linux, Docker fonctionne grâce à un service système spécial appelé Docker daemon (dockerd).
Ce service écoute sur un socket :
/var/run/docker.sock
Que contient réellement /var/run/docker.sock ?
C’est un fichier spécial représentant un socket UNIX, utilisé pour communiquer avec Docker.
Quand vous tapez : docker ps
la commande se connecte à : /var/run/docker.sock -> dockerd
Si vous n'avez pas les droits, vous ne pouvez pas envoyer les commandes -> d'où permission denied.
Ce fichier est protégé, et seuls les utilisateurs ayant les permissions nécessaires peuvent communiquer avec Docker.
Par défaut :
seul root a accès au daemon Docker
les utilisateurs normaux (votre compte Linux) ne peuvent PAS lancer de commandes Docker

POURQUOI Docker exige des permissions root ?
Parce que Docker permet :
d’accéder au réseau de l’hôte
de manipuler des fichiers système
d’exécuter du code isolé dans des conteneurs
de binder des ports (80, 443, etc.)
d’accéder au kernel (cgroups, namespaces)
Ce sont des opérations sensibles, donc réservées à root ou à un groupe spécial.

COMMENT résoudre ?

SOLUTION TEMPORAIRE (NON RECOMMANDÉE)
Utiliser sudo devant chaque commande :
sudo docker run hello-world
sudo docker ps
sudo docker build .

Ça fonctionne…
Mais ce n’est pas pratique, ni recommandé à long terme.

SOLUTION PERMANENTE (recommandée) :
1. Ajoutez votre utilisateur au groupe docker :
   sudo usermod -aG docker $USER

Explication :
usermod -> modifie un utilisateur
-aG docker -> ajoute à un groupe sans retirer les autres
$USER -> votre nom d’utilisateur

2. Déconnectez-vous et reconnectez-vous
   OU tapez : newgrp docker

3. Vérifiez :
   docker run hello-world

QUAND ça arrive ?
- Uniquement sur Linux
- Après installation fraîche
- Si vous n'avez pas configuré les permissions


┌──────────────────────────────────────────────────────────────────────────┐
│ ERREUR 3 : "Port is already allocated"                                   │
└──────────────────────────────────────────────────────────────────────────┘

MESSAGE COMPLET :
Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already
in use.

POURQUOI ça arrive ?
Vous essayez de lancer un conteneur sur le port 80, mais ce port 
est déjà utilisé par :
- Un autre conteneur Docker
- Un serveur web (Apache, Nginx) déjà installé
- Une autre application

COMMENT résoudre ?

OPTION 1 : Utiliser un autre port
docker run -p 8080:80 nginx
              └──┘
           Port différent sur votre PC

OPTION 2 : Trouver et arrêter ce qui utilise le port

SUR LINUX/MAC :
   Voir qui occupe le port 80 :
   sudo lsof -i :80
   Vous verrez quelque chose comme : 
   nginx   1234   root   80
   Alors vous pouvez le stopper :
   sudo kill -9 PID    # Dans ce cas PID = 1234

SUR WINDOWS :
   Voir le processus qui utilise le port 80 :
   netstat -ano | findstr :80
   Résultat :
   TCP 0.0.0.0:80   ...   PID: 9482
   Tuer ce processus :
   taskkill /PID 9482 /F

OPTION 3 : Arrêter le conteneur existant
   Voir les conteneurs :
   docker ps
   Supprimer celui qui prend le port :
   docker stop nom_du_conteneur_ou_id

QUAND ça arrive ?
- Vous lancez plusieurs conteneurs web
- Vous avez déjà un serveur local qui tourne
- Vous oubliez qu'un conteneur tourne déjà


┌──────────────────────────────────────────────────────────────────────────┐
│ ERREUR 4 : "No space left on device"                                     │
└──────────────────────────────────────────────────────────────────────────┘

MESSAGE COMPLET :
no space left on device

POURQUOI ça arrive ?
Docker stocke BEAUCOUP de données :
- Images : 100 MB à 5 GB chacune
- Conteneurs arrêtés : gardent leurs données
- Volumes : peuvent grossir indéfiniment
- Logs : s'accumulent
- Build cache : prend de la place

Après quelques mois : facilement 20-50 GB utilisés !

COMMENT résoudre ?

NETTOYAGE LÉGER (sans risque) :
docker system prune
-> Supprime conteneurs arrêtés, réseaux non utilisés, images pendantes
-> Aucun risque pour les conteneurs actifs

NETTOYAGE MOYEN (récupère beaucoup d’espace)
docker system prune -a
-> Supprime images non utilisées (même pas par un conteneur actif), 
   conteneurs arrêtés, réseaux inutilisés, build cache.
-> Vous devrez re-télécharger les images plus tard

NETTOYAGE COMPLET (radical, attention !) :
docker system prune -a --volumes
-> Supprime TOUT ce qui n'est pas en cours d'utilisation
-> Vous devrez re-télécharger les images plus tard

NETTOYAGE CIBLÉ :
Supprimer seulement les conteneurs arrêtés:
docker container prune  # Conteneurs arrêtés

Supprimer seulement les images inutilisées:
docker image prune -a   # Images non utilisées

Supprimer les volumes orphelins:
docker volume prune     # Volumes orphelins

Supprimer le cache de build:
docker builder prune

VOIR L'UTILISATION :
docker system df

Affiche :
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          15        3         5.2GB     3.8GB (73%)
Containers      20        1         1.5GB     1.4GB (93%)
Local Volumes   8         2         850MB     650MB (76%)
Build Cache     45        0         2.1GB     2.1GB (100%)

QUAND faire le nettoyage ?
- Une fois par mois minimum
- Quand votre disque est plein
- Avant de travailler sur un gros projet


┌──────────────────────────────────────────────────────────────────────────┐
│ ERREUR 5 : Le conteneur s'arrête immédiatement                           │
└──────────────────────────────────────────────────────────────────────────┘

SITUATION :
docker run ubuntu
-> La commande se termine, rien ne se passe

docker ps
-> Aucun conteneur actif

docker ps -a
-> Le conteneur est "Exited" immédiatement

POURQUOI ça arrive ?
Un conteneur tourne SEULEMENT tant que son processus principal est actif.

Ubuntu sans commande = rien à faire = arrêt immédiat

ANALOGIE :
Imaginez ouvrir Notepad et fermer tout de suite.
Le programme n'a rien à faire, il se ferme.

COMMENT résoudre ?

OPTION 1 : Lancer avec un terminal interactif
docker run -it ubuntu bash
-> Le bash garde le conteneur vivant

OPTION 2 : Lancer en arrière-plan avec un processus continu (un faux processus "infini")
docker run -d ubuntu tail -f /dev/null
-> tail -f /dev/null = ne fait rien… mais ne s’arrête jamais.
-> tail garde le conteneur vivant indéfiniment

OPTION 3 : Utiliser une image avec un serveur
docker run -d nginx
-> Nginx tourne en continu automatiquement
Pourquoi Nginx reste actif ?
Parce que son processus principal est : nginx -g "daemon off;"
-> un serveur web qui tourne en continu
-> Docker ne ferme pas le conteneur

QUAND ça arrive ?
- Vous utilisez des images "de base" sans serveur
- Vous oubliez le -it pour les sessions interactives
- Votre application crash immédiatement (bug dans le code)


================================================================================
[OK] SECTION 2 : INSTALLATION DOCKER
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║                        AVANT DE COMMENCER                                ║
╚══════════════════════════════════════════════════════════════════════════╝

POURQUOI cette section est importante ?

Une installation correcte de Docker est CRUCIALE.
Une mauvaise installation = problèmes constants.

OBJECTIF : À la fin de cette section, vous aurez :
[OK] Docker installé correctement
[OK] Docker qui démarre automatiquement
[OK] Permissions configurées (Linux)
[OK] Testé que tout fonctionne


COMMENT choisir quelle méthode ?

┌─────────────────────────────────────────────────────────────────────────┐
│ VOTRE SYSTÈME          MÉTHODE RECOMMANDÉE                              │
├─────────────────────────────────────────────────────────────────────────┤
│ Windows 10/11          Docker Desktop (GUI facile)                      │
│ Mac (Intel ou M1/M2)   Docker Desktop (GUI facile)                      │
│ Ubuntu/Debian          Docker Engine (ligne de commande)                │
│ Fedora/RHEL            Docker Engine (ligne de commande)                │
│ Serveur Linux          Docker Engine (pas d'interface graphique)        │
└─────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║                    INSTALLATION SUR LINUX (UBUNTU/DEBIAN)                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 1 : Désinstaller les anciennes versions                            │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Si vous avez déjà essayé d'installer Docker avant, il faut nettoyer 
pour éviter les conflits.

COMMANDE :
sudo apt-get remove docker docker-engine docker.io containerd runc

QUE SE PASSE-T-IL ?
- Cherche les anciennes versions
- Les désinstalle si elles existent
- Si rien n'est installé : message "Unable to locate package" (normal)

QUAND sauter cette étape ?
Si c'est votre toute première installation de Docker.


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 2 : Installer les dépendances nécessaires                          │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Docker a besoin d'outils spéciaux pour :
- Télécharger des paquets via HTTPS
- Vérifier les signatures cryptographiques
- Gérer les certificats

COMMANDE :
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release

EXPLICATION DÉTAILLÉE :

sudo apt-get update
└─ Met à jour la liste des paquets disponibles
   POURQUOI ? Pour être sûr d'installer les dernières versions
   DURÉE : 10-30 secondes

ca-certificates
└─ Certificats pour connexions HTTPS sécurisées
   POURQUOI ? Docker Hub utilise HTTPS

curl
└─ Outil pour télécharger des fichiers depuis Internet
   POURQUOI ? Pour télécharger la clé GPG de Docker

gnupg
└─ Outil de cryptographie
   POURQUOI ? Pour vérifier que les paquets viennent bien de Docker

lsb-release
└─ Détecte votre version d'Ubuntu/Debian
   POURQUOI ? Docker a besoin de savoir quelle version vous avez

QUE SE PASSE-T-IL ?
Vous verrez :
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
  ca-certificates curl gnupg lsb-release
[...]
Setting up ca-certificates (20230311)
[...]
Done.

DURÉE TOTALE : 1-2 minutes


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 3 : Ajouter la clé GPG officielle de Docker                        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
C'est une mesure de SÉCURITÉ.

ANALOGIE :
Quand vous téléchargez un fichier, comment être sûr qu'il vient 
vraiment de Docker et pas d'un pirate ?
-> Docker signe ses paquets avec une "clé GPG"
-> Vous téléchargez la clé publique officielle
-> Votre système vérifie automatiquement les signatures

COMMANDE 1 : Créer le dossier pour les clés
sudo mkdir -p /etc/apt/keyrings

EXPLICATION :
mkdir -p    -> Crée le dossier (et parents si nécessaire)
/etc/apt/keyrings -> Emplacement standard pour les clés

COMMANDE 2 : Télécharger et installer la clé
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

DÉCORTIQUONS :

curl -fsSL https://download.docker.com/linux/ubuntu/gpg
  │    │││
  │    ││└─ Follow redirects (suit les redirections)
  │    │└── Silent (pas de barre de progression)
  │    └─── Show errors (affiche les erreurs)
  └──────── Fail on server errors
  
  -> Télécharge la clé GPG depuis le serveur officiel Docker

| (pipe)
  -> Envoie le résultat à la commande suivante

sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
     │    │         │
     │    │         └─ Output (fichier de sortie)
     │    └─────────── Décode la clé (format binaire)
     └──────────────── Programme GPG (cryptographie)

QUE SE PASSE-T-IL ?
- curl télécharge la clé (quelques ko)
- gpg la convertit en format utilisable
- La clé est sauvegardée dans /etc/apt/keyrings/

AUCUN MESSAGE si tout va bien (c'est normal !).

SI ERREUR :
"curl: command not found" -> Retournez à l'étape 2


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 4 : Ajouter le repository Docker                                   │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Ubuntu ne connaît pas Docker par défaut.
Vous devez lui dire : "Va chercher Docker sur download.docker.com"

COMMANDE (ATTENTION : une seule ligne, très longue) :
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

C'est compliqué ! DÉCORTIQUONS :

echo "..."
└─ Affiche du texte

deb
└─ Début d'une ligne de repository Debian/Ubuntu

[arch=$(dpkg --print-architecture)]
      └─ Détecte votre architecture (amd64, arm64, etc.)
   POURQUOI ? Télécharger la bonne version pour votre processeur

signed-by=/etc/apt/keyrings/docker.gpg
└─ Utilise la clé GPG qu'on a téléchargée
   POURQUOI ? Vérifier que les paquets sont authentiques

https://download.docker.com/linux/ubuntu
└─ URL du repository Docker

$(lsb_release -cs)
└─ Détecte votre version Ubuntu (focal, jammy, etc.)
   POURQUOI ? Télécharger la version compatible

stable
└─ Canal stable (pas beta ou test)

| sudo tee /etc/apt/sources.list.d/docker.list
└─ Écrit dans le fichier de configuration APT

> /dev/null
└─ Supprime l'affichage en double

QUE SE PASSE-T-IL ?
Ça crée un fichier : /etc/apt/sources.list.d/docker.list
Contenu du fichier :
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable

Maintenant Ubuntu sait où chercher Docker !


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 5 : Installer Docker Engine                                        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
On a préparé le terrain, maintenant on installe !

COMMANDE 1 : Mettre à jour la liste des paquets
sudo apt-get update

POURQUOI ?
Pour qu'Ubuntu découvre les paquets Docker disponibles.

Vous verrez :
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Get:2 https://download.docker.com/linux/ubuntu jammy InRelease [48.8 kB]
Get:3 https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages [...]
                                       ^
                              Docker est découvert !

COMMANDE 2 : Installer Docker et ses composants
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

EXPLICATION DES COMPOSANTS :

docker-ce
└─ Docker Community Edition (le moteur principal)
   C'est le cœur de Docker

docker-ce-cli
└─ Command Line Interface (l'outil en ligne de commande)
   C'est ce que vous utilisez quand vous tapez "docker ..."

containerd.io
└─ Container runtime (gère l'exécution des conteneurs)
   Travaille en coulisses avec docker-ce

docker-buildx-plugin
└─ Outil de build avancé
   Pour construire des images multi-architecture

docker-compose-plugin
└─ Docker Compose (orchestration de conteneurs)
   Pour lancer plusieurs conteneurs ensemble

QUE SE PASSE-T-IL ?
Vous verrez :
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  containerd.io docker-buildx-plugin docker-ce docker-ce-cli
  docker-compose-plugin
[...]
Setting up docker-ce (5:24.0.6-1~ubuntu.22.04~jammy)
[...]
Processing triggers for systemd (249.11-0ubuntu3.11)
Done.

DURÉE : 2-5 minutes
TAILLE : ~150-200 MB à télécharger


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 6 : Vérifier l'installation                                        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Pour s'assurer que tout est installé correctement.

TEST 1 : Vérifier la version
────────────────────────────
COMMANDE :
docker --version

RÉSULTAT ATTENDU :
Docker version 24.0.6, build ed223bc

[OK] Si vous voyez ça : Docker est installé !
[X] Si "command not found" : problème d'installation


TEST 2 : Lancer le conteneur de test
──────────────────────────────────────
COMMANDE :
sudo docker run hello-world

POURQUOI sudo ?
Sur Linux, Docker nécessite des permissions root par défaut.
On va corriger ça à l'étape suivante.

QUE SE PASSE-T-IL ?
1. Docker cherche l'image "hello-world" localement
2. Ne la trouve pas
3. La télécharge depuis Docker Hub
4. Crée un conteneur
5. Le lance
6. Affiche :

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:4f53e2564790c8e7856ec08e384732aa38dc43c52f02952483e3f003afbf23db
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

[...]

[OK] SI VOUS VOYEZ CE MESSAGE : INSTALLATION RÉUSSIE ! [BRAVO]


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 7 : Utiliser Docker sans sudo (IMPORTANT)                          │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Taper "sudo" à chaque fois est :
[X] Pénible
[X] Dangereux (trop de pouvoir)
[X] Lent

COMMENT ça marche ?
Linux a des "groupes" d'utilisateurs avec différentes permissions.
Il existe un groupe "docker" qui a le droit d'utiliser Docker.
On va ajouter votre utilisateur à ce groupe.

COMMANDE 1 : Créer le groupe docker (si pas déjà créé)
sudo groupadd docker

RÉSULTAT :
groupadd: group 'docker' already exists
-> C'est normal ! Le groupe existe déjà depuis l'installation.

COMMANDE 2 : Ajouter votre utilisateur au groupe
sudo usermod -aG docker $USER

DÉCORTIQUONS :
sudo       -> Exécute en tant que root
usermod    -> Modifie un utilisateur
-aG        -> -a = append (ajoute)
             -G = group (à un groupe)
docker     -> Nom du groupe
$USER      -> Variable = votre nom d'utilisateur actuel

QUE SE PASSE-T-IL ?
Ça modifie le fichier /etc/group pour ajouter :
docker:x:999:votre_nom_utilisateur

COMMANDE 3 : Activer le changement
newgrp docker

OU : Déconnectez-vous et reconnectez-vous

POURQUOI ?
Linux charge les groupes au login.
Pour que le changement soit pris en compte, il faut :
- Soit utiliser "newgrp" (temporaire, session actuelle)
- Soit se déconnecter/reconnecter (permanent)

COMMANDE 4 : Tester sans sudo
docker run hello-world

[OK] SI ÇA MARCHE SANS sudo : Parfait !
[X] SI "Permission denied" : Déconnectez-vous et reconnectez-vous


┌──────────────────────────────────────────────────────────────────────────┐
│ ÉTAPE 8 : Démarrer Docker automatiquement au boot                        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI cette étape ?
Par défaut, Docker ne démarre pas automatiquement quand vous 
allumez votre PC. Vous devrez le lancer manuellement à chaque fois.

C'est pénible ! On va configurer le démarrage automatique.

COMMANDE :
sudo systemctl enable docker.service
sudo systemctl enable containerd.service

EXPLICATION :

systemctl
└─ Outil de gestion des services Linux (systemd)

enable
└─ Active le démarrage automatique

docker.service
└─ Le service Docker Engine

containerd.service
└─ Le service containerd (nécessaire pour Docker)

QUE SE PASSE-T-IL ?
Vous verrez :
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service -> /lib/systemd/system/docker.service.
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service -> /lib/systemd/system/containerd.service.

VÉRIFICATION :
sudo systemctl status docker

RÉSULTAT ATTENDU :
[BLACK_CIRCLE] docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-01-15 10:30:25 UTC; 2min ago
                       ^
                 C'est "enabled" !

QUAND sauter cette étape ?
- Jamais ! C'est très pratique.
- Sauf si vous êtes sur un serveur et voulez contrôler manuellement.


╔══════════════════════════════════════════════════════════════════════════╗
║                   INSTALLATION SUR FEDORA/RHEL/CENTOS                    ║
╚══════════════════════════════════════════════════════════════════════════╝

POURQUOI une section séparée ?
Fedora/RHEL/CentOS utilisent DNF/YUM au lieu d'APT.
Les commandes sont légèrement différentes.

┌──────────────────────────────────────────────────────────────────────────┐
│ Installation complète                                                    │
└──────────────────────────────────────────────────────────────────────────┘

COMMANDE 1 : Installer les dépendances
sudo dnf -y install dnf-plugins-core

COMMANDE 2 : Ajouter le repository Docker
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo

COMMANDE 3 : Installer Docker
sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

COMMANDE 4 : Démarrer Docker
sudo systemctl start docker
sudo systemctl enable docker

COMMANDE 5 : Ajouter utilisateur au groupe
sudo usermod -aG docker $USER
newgrp docker

COMMANDE 6 : Tester
docker run hello-world

[OK] Si ça marche : Installation réussie !


╔══════════════════════════════════════════════════════════════════════════╗
║                        INSTALLATION SUR MAC                              ║
╚══════════════════════════════════════════════════════════════════════════╝

Sur Mac, l'installation est BEAUCOUP plus simple grâce à Docker Desktop.

┌──────────────────────────────────────────────────────────────────────────┐
│ MÉTHODE 1 : Docker Desktop (RECOMMANDÉE)                                │
└──────────────────────────────────────────────────────────────────────────┘

ÉTAPE 1 : Télécharger Docker Desktop
─────────────────────────────────────

1. Allez sur : https://www.docker.com/products/docker-desktop

2. Cliquez sur "Download for Mac"

3. Choisissez votre processeur :
   - Mac Intel : Docker Desktop for Mac with Intel chip
   - Mac M1/M2/M3 : Docker Desktop for Mac with Apple silicon

COMMENT savoir quel Mac vous avez ?
Cliquez sur  (pomme) -> About This Mac
- Chip: Apple M1/M2/M3 -> Apple silicon
- Processor: Intel Core -> Intel

TAILLE DU TÉLÉCHARGEMENT : ~500 MB
DURÉE : 2-10 minutes selon votre connexion


ÉTAPE 2 : Installer Docker Desktop
───────────────────────────────────

1. Double-cliquez sur Docker.dmg téléchargé

2. Glissez l'icône Docker dans Applications
   
   ┌─────────────────────────────────────────┐
   │                                         │
   │     [WHALE] Docker.app                       │
   │                                         │
   │              ->                          │
   │                                         │
   │     [DOSSIER] Applications                     │
   │                                         │
   └─────────────────────────────────────────┘

3. Ouvrez Applications et double-cliquez sur Docker

4. Mac vous demande : "Docker is an app downloaded from the Internet"
   -> Cliquez "Open"

5. Docker vous demande votre mot de passe
   -> C'est normal (il doit installer des composants système)
   -> Tapez votre mot de passe

6. Docker démarre :
   - Vous voyez une baleine [WHALE] dans la barre de menu en haut
   - Elle "bouge" pendant le démarrage
   - Quand elle est stable : Docker est prêt !

DURÉE : 1-2 minutes


ÉTAPE 3 : Configuration initiale
─────────────────────────────────

Au premier lancement, Docker Desktop vous montre un tutorial.

Vous pouvez :
[OK] Suivre le tutorial (recommandé pour débutants)
[BLACK_RIGHT-POINTING_DOUBLE_TRIANGLE_WITH_VERTICAL_BAR] Skip (si vous lisez déjà ce guide)

PARAMÈTRES IMPORTANTS :
Cliquez sur [CONFIG] (Settings) dans Docker Desktop

General :
[OK] Start Docker Desktop when you log in
   -> Docker démarre automatiquement

Resources :
-> Allouez de la mémoire (4 GB minimum, 8 GB recommandé)
-> Choisissez le nombre de CPUs (2-4)


ÉTAPE 4 : Vérifier l'installation
──────────────────────────────────

Ouvrez Terminal (Applications -> Utilities -> Terminal)

COMMANDE :
docker --version

RÉSULTAT ATTENDU :
Docker version 24.0.6, build ed223bc

COMMANDE :
docker run hello-world

RÉSULTAT ATTENDU :
Hello from Docker!
[...]

[OK] SI VOUS VOYEZ ÇA : INSTALLATION RÉUSSIE ! [BRAVO]


┌──────────────────────────────────────────────────────────────────────────┐
│ MÉTHODE 2 : Homebrew (ALTERNATIVE)                                       │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI utiliser Homebrew ?
Si vous préférez la ligne de commande et utilisez déjà Homebrew.

PRÉREQUIS : Avoir Homebrew installé
Si pas installé : /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

COMMANDE :
brew install --cask docker

QUE SE PASSE-T-IL ?
Homebrew télécharge et installe Docker Desktop automatiquement.

Ensuite :
1. Lancez Docker depuis Applications
2. Suivez les mêmes étapes que la Méthode 1

QUAND utiliser cette méthode ?
[OK] Vous êtes à l'aise avec Homebrew
[OK] Vous voulez mettre à jour Docker via "brew upgrade"
[X] Vous êtes débutant -> Privilégiez Méthode 1


╔══════════════════════════════════════════════════════════════════════════╗
║                      INSTALLATION SUR WINDOWS                            ║
╚══════════════════════════════════════════════════════════════════════════╝

Sur Windows, Docker nécessite WSL 2 (Windows Subsystem for Linux).

┌──────────────────────────────────────────────────────────────────────────┐
│ PRÉREQUIS : Activer WSL 2 (Windows Subsystem for Linux)                 │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI WSL 2 ?
Docker utilise des technologies Linux.
Sur Windows, Docker tourne dans un environnement Linux virtuel (WSL 2).

QUI PEUT UTILISER WSL 2 ?
[OK] Windows 10 version 2004+ (Build 19041+)
[OK] Windows 11 (toutes versions)

COMMENT vérifier votre version ?
1. Tapez : Windows + R
2. Tapez : winver
3. Regardez la ligne "Version" et "OS Build"

Exemple :
Version 22H2 (OS Build 22621.1000)
         ^             ^
      OK (>2004)   OK (>19041)


ÉTAPE 1 : Installer WSL 2
─────────────────────────

Ouvrez PowerShell en ADMINISTRATEUR :
1. Tapez "PowerShell" dans la recherche Windows
2. Clic droit -> "Run as administrator"

COMMANDE :
wsl --install

QUE SE PASSE-T-IL ?
WSL télécharge et installe :
- Le système WSL
- Une distribution Ubuntu par défaut
- Tous les composants nécessaires

Vous verrez :
Installing: Windows Subsystem for Linux
Installing: Ubuntu
[...]
The requested operation is successful. Changes will not be effective until the system is rebooted.

[ATTENTION] IMPORTANT : REDÉMARREZ votre PC !

Après le redémarrage :
- Ubuntu se lance automatiquement
- Créez un utilisateur Linux :
  Enter new UNIX username: votre_nom
  New password: *******
  Retype new password: *******

DURÉE : 5-10 minutes + redémarrage


ÉTAPE 2 : Définir WSL 2 par défaut
───────────────────────────────────

COMMANDE (dans PowerShell) :
wsl --set-default-version 2

VÉRIFICATION :
wsl --list --verbose

RÉSULTAT ATTENDU :
  NAME      STATE           VERSION
* Ubuntu    Running         2
                            ^
                      Doit être "2"

SI VERSION = 1 :
wsl --set-version Ubuntu 2


┌──────────────────────────────────────────────────────────────────────────┐
│ Installation de Docker Desktop sur Windows                               │
└──────────────────────────────────────────────────────────────────────────┘

ÉTAPE 1 : Télécharger Docker Desktop
─────────────────────────────────────

1. Allez sur : https://www.docker.com/products/docker-desktop

2. Cliquez sur "Download for Windows"

TAILLE : ~500 MB
DURÉE : 2-10 minutes selon votre connexion


ÉTAPE 2 : Installer
────────────────────

1. Double-cliquez sur "Docker Desktop Installer.exe"

2. Configuration recommandée :
   [OK] Use WSL 2 instead of Hyper-V (coché par défaut)
   [OK] Add shortcut to desktop

3. Cliquez "Ok"

4. Installation en cours...
   - Extraction des fichiers
   - Installation des composants
   - Configuration de WSL 2

DURÉE : 3-5 minutes

5. À la fin : "Installation succeeded"
   -> Cliquez "Close and restart"

[ATTENTION] IMPORTANT : Redémarrez Windows !


ÉTAPE 3 : Premier lancement
────────────────────────────

Après le redémarrage :

1. Docker Desktop se lance automatiquement
   (Si non : cherchez "Docker Desktop" dans le menu Démarrer)

2. Acceptez les termes de service

3. Docker démarre :
   - Vous voyez une baleine [WHALE] dans la barre des tâches
   - Elle change d'apparence pendant le démarrage
   - Quand le texte dit "Docker Desktop is running" : c'est prêt !

4. Tutorial optionnel :
   [OK] Suivez-le si débutant
   [BLACK_RIGHT-POINTING_DOUBLE_TRIANGLE_WITH_VERTICAL_BAR] Skip si vous lisez ce guide


ÉTAPE 4 : Configuration WSL 2
──────────────────────────────

Docker Desktop -> [CONFIG] Settings -> Resources -> WSL Integration

[OK] Enable integration with my default WSL distro
[OK] Ubuntu (coché)

Cliquez "Apply & Restart"


ÉTAPE 5 : Vérifier l'installation
──────────────────────────────────

Ouvrez PowerShell ou CMD :

COMMANDE :
docker --version

RÉSULTAT ATTENDU :
Docker version 24.0.6, build ed223bc

COMMANDE :
docker run hello-world

RÉSULTAT ATTENDU :
Hello from Docker!
[...]

[OK] SI VOUS VOYEZ ÇA : INSTALLATION RÉUSSIE ! [BRAVO]


┌──────────────────────────────────────────────────────────────────────────┐
│ PROBLÈMES COURANTS SUR WINDOWS                                           │
└──────────────────────────────────────────────────────────────────────────┘

PROBLÈME 1 : "WSL 2 installation is incomplete"
───────────────────────────────────────────────

SOLUTION :
1. Téléchargez le package WSL 2 kernel update :
   https://aka.ms/wsl2kernel

2. Installez-le

3. Redémarrez Docker Desktop


PROBLÈME 2 : "Hardware assisted virtualization is not available"
─────────────────────────────────────────────────────────────────

CAUSE : La virtualisation n'est pas activée dans le BIOS

SOLUTION :
1. Redémarrez votre PC
2. Entrez dans le BIOS (touche Del, F2, F10 selon PC)
3. Cherchez :
   - Intel : "Intel VT-x" ou "Virtualization Technology"
   - AMD : "AMD-V" ou "SVM Mode"
4. Activez-le (Enabled)
5. Sauvegardez et redémarrez


PROBLÈME 3 : Docker Desktop ne démarre pas
───────────────────────────────────────────

SOLUTIONS :
1. Vérifiez que WSL 2 fonctionne :
   wsl --list --verbose

2. Redémarrez le service WSL :
   wsl --shutdown
   (Puis relancez Docker Desktop)

3. Réinitialisez Docker :
   Docker Desktop -> [CONFIG] Settings -> Troubleshoot -> Reset to factory defaults


╔══════════════════════════════════════════════════════════════════════════╗
║                   VÉRIFICATION FINALE (TOUS SYSTÈMES)                    ║
╚══════════════════════════════════════════════════════════════════════════╝

Après l'installation sur n'importe quel système, faites ces tests :

┌──────────────────────────────────────────────────────────────────────────┐
│ TEST COMPLET DE FONCTIONNEMENT                                           │
└──────────────────────────────────────────────────────────────────────────┘

TEST 1 : Version Docker
───────────────────────
docker --version
docker compose version

[OK] ATTENDU :
Docker version 24.0.6, build ed223bc
Docker Compose version v2.21.0


TEST 2 : Info système
─────────────────────
docker info

[OK] ATTENDU :
Client:
 Version:    24.0.6
 Context:    default
 [...]
Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 [...]


TEST 3 : Hello World
────────────────────
docker run hello-world

[OK] ATTENDU :
Hello from Docker!
This message shows that your installation appears to be working correctly.


TEST 4 : Conteneur interactif
──────────────────────────────
docker run -it alpine sh

Vous entrez dans un conteneur Alpine Linux.
Tapez :
/ # echo "Docker fonctionne !"
Docker fonctionne !
/ # exit


TEST 5 : Conteneur web (Nginx)
───────────────────────────────
docker run -d -p 8080:80 --name test-nginx nginx

Ouvrez votre navigateur : http://localhost:8080

[OK] ATTENDU : Page "Welcome to nginx!"

Nettoyage :
docker stop test-nginx
docker rm test-nginx


TEST 6 : Docker Compose
───────────────────────
Créez un fichier docker-compose.yml :

version: '3.8'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

Lancez :
docker compose up -d

Vérifiez : http://localhost:8080

Arrêtez :
docker compose down

SI TOUS LES TESTS PASSENT : DOCKER EST PARFAITEMENT INSTALLÉ !


[OK] COMMANDES DE BASE - DÉBUTANT


================================================================================
               DOCKER - COMMANDES DE BASE POUR DÉBUTANTS
                    Guide Détaillé avec Exemples Pratiques
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION À CETTE SECTION                          ║
╚══════════════════════════════════════════════════════════════════════════╝

OBJECTIF :
Maîtriser les commandes essentielles pour utiliser Docker au quotidien.

PRÉREQUIS :
[OK] Docker installé et fonctionnel
[OK] Compréhension des concepts (image, conteneur, volume)

CE QUE VOUS ALLEZ APPRENDRE :
1. Comment obtenir des informations sur Docker
2. Comment gérer les images (télécharger, lister, supprimer)
3. Comment gérer les conteneurs (créer, démarrer, arrêter, supprimer)
4. Comment surveiller et déboguer

STRUCTURE DU GUIDE :
Pour chaque commande, vous trouverez :
• QUOI : Ce que fait la commande
• POURQUOI : Quand l'utiliser
• COMMENT : Syntaxe et options
• EXEMPLES : Cas pratiques avec explications
• RÉSULTAT : Ce que vous verrez à l'écran
• PIÈGES : Erreurs courantes à éviter


================================================================================
[OK] SECTION 1 : INFORMATIONS SYSTÈME
================================================================================

Ces commandes vous permettent de vérifier que Docker fonctionne et d'obtenir
des informations sur votre installation.

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker --version                                             ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche la version de Docker installée                           │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Vérifier que Docker est bien installé
• Connaître votre version (important pour la compatibilité)
• Dépanner des problèmes liés à la version
• Documenter votre environnement

COMMENT l'utiliser ?

SYNTAXE :
docker --version

AUCUNE option nécessaire - c'est la commande la plus simple !


EXEMPLE 1 : Vérification simple
────────────────────────────────

COMMANDE :
docker --version

RÉSULTAT ATTENDU :
Docker version 24.0.6, build ed223bc

EXPLICATION :
┌────────────────────┬──────────────────────────────────────┐
│ Docker version     │ Le logiciel "Docker"                 │
│ 24.0.6             │ Version majeure.mineure.patch        │
│ build ed223bc      │ Identifiant du build (commit Git)    │
└────────────────────┴──────────────────────────────────────┘

VERSION DÉCODÉE :
24     -> Version majeure (changements importants)
0      -> Version mineure (nouvelles fonctionnalités)
6      -> Patch (corrections de bugs)


EXEMPLE 2 : Vérifier avant installation de plugin
──────────────────────────────────────────────────

CONTEXTE : Vous voulez installer un plugin nécessitant Docker 23+

COMMANDE :
docker --version

SI RÉSULTAT : Docker version 24.0.6
[OK] OK ! Vous pouvez installer le plugin

SI RÉSULTAT : Docker version 20.10.24
[X] Version trop ancienne, mise à jour nécessaire


QUAND l'utiliser ?
[OK] Première commande après installation
[OK] Avant d'installer des outils qui dépendent de Docker
[OK] Quand vous demandez de l'aide (mentionnez toujours votre version)
[OK] Pour vérifier si une mise à jour est disponible

AU LIEU DE : Ouvrir l'interface Docker Desktop pour voir la version


PIÈGES À ÉVITER :
[X] ERREUR : "docker: command not found"
   -> Docker n'est pas installé ou pas dans le PATH
   -> SOLUTION : Vérifiez l'installation, redémarrez le terminal

[X] ERREUR : "Cannot connect to the Docker daemon"
   -> Docker est installé mais le service ne tourne pas
   -> SOLUTION : Lancez Docker Desktop ou sudo systemctl start docker


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker version                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche les détails complets client ET serveur                    │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir la version du client (CLI) et du serveur (Engine)
• Vérifier l'architecture (amd64, arm64)
• Connaître le système d'exploitation du serveur
• Déboguer des problèmes de compatibilité

COMMENT l'utiliser ?

SYNTAXE :
docker version


EXEMPLE 1 : Affichage complet
──────────────────────────────

COMMANDE :
docker version

RÉSULTAT ATTENDU :
Client:
 Version:           24.0.6
 API version:       1.43
 Go version:        go1.20.7
 Git commit:        ed223bc
 Built:             Mon Sep  4 12:32:16 2023
 OS/Arch:           darwin/arm64
 Context:           default

Server: Docker Desktop 4.24.0 (122432)
 Engine:
  Version:          24.0.6
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.7
  Git commit:       1a79695
  Built:            Mon Sep  4 12:31:36 2023
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.22
  GitCommit:        8165feabfdfe38c65b599c4993d227328c231fca
 runc:
  Version:          1.1.8
  GitCommit:        v1.1.8-0-g82f18fe
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

EXPLICATION DÉTAILLÉE :

┌────────────────────────────────────────────────────────────────────────┐
│ SECTION CLIENT (ce que vous utilisez)                                  │
├────────────────────────────────────────────────────────────────────────┤
│ Version: 24.0.6        -> Version de la CLI (ligne de commande)         │
│ API version: 1.43      -> Version de l'API qu'elle utilise              │
│ OS/Arch: darwin/arm64  -> Votre système (Mac/Apple Silicon ici)         │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ SECTION SERVER (le moteur Docker)                                      │
├────────────────────────────────────────────────────────────────────────┤
│ Version: 24.0.6        -> Version du Docker Engine                      │
│ OS/Arch: linux/arm64   -> Docker tourne dans Linux (via VM sur Mac)     │
│ containerd: 1.6.22     -> Gestionnaire de conteneurs                    │
│ runc: 1.1.8            -> Runtime bas niveau                            │
└────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Vérifier compatibilité client-serveur
──────────────────────────────────────────────────

CONTEXTE : Vous avez mis à jour Docker Desktop mais pas la CLI

COMMANDE :
docker version

RÉSULTAT PROBLÉMATIQUE :
Client:
 API version:       1.41

Server:
 API version:       1.43 (minimum version 1.12)
                    ^
            Versions différentes !

SIGNIFICATION :
[ATTENTION] Le client et le serveur ont des versions API différentes
[ATTENTION] Certaines fonctionnalités récentes ne marcheront pas

SOLUTION :
Mettez à jour le client : brew upgrade docker (Mac)
                         apt-get update && apt-get upgrade docker-ce (Linux)


QUAND l'utiliser ?
[OK] Pour diagnostiquer des problèmes de connexion
[OK] Quand une commande ne marche pas (vérifier compatibilité API)
[OK] Pour signaler un bug (inclure docker version dans le rapport)
[OK] Pour vérifier l'architecture (amd64 vs arm64)

AU LIEU DE : docker --version (qui ne montre que le client)


DIFFÉRENCE avec docker --version :

docker --version          ->  Version courte, client seulement
docker version            ->  Version longue, client + serveur + composants


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker info                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche TOUTES les informations sur votre installation Docker     │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir combien de conteneurs/images vous avez
• Vérifier l'espace disque utilisé
• Connaître la configuration réseau
• Déboguer des problèmes complexes
• Documenter votre environnement pour support

COMMENT l'utiliser ?

SYNTAXE :
docker info


EXEMPLE 1 : Vue d'ensemble complète
────────────────────────────────────

COMMANDE :
docker info

RÉSULTAT ATTENDU (extrait) :
Client:
 Version:    24.0.6
 Context:    default
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
  compose: Docker Compose (Docker Inc.)

Server:
 Containers: 5
  Running: 2
  Paused: 0
  Stopped: 3
 Images: 12
 Server Version: 24.0.6
 Storage Driver: overlay2
  Backing Filesystem: extfs
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Security Options:
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.4.16-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: aarch64
 CPUs: 5
 Total Memory: 7.765GiB
 Name: docker-desktop
 ID: abcd1234-5678-90ef-ghij-klmnopqrstuv
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Experimental: false
 Live Restore Enabled: false

EXPLICATION DES SECTIONS IMPORTANTES :

┌────────────────────────────────────────────────────────────────────────┐
│ STATISTIQUES D'UTILISATION                                             │
├────────────────────────────────────────────────────────────────────────┤
│ Containers: 5          -> Vous avez 5 conteneurs au total               │
│   Running: 2           -> 2 sont actifs en ce moment                    │
│   Stopped: 3           -> 3 sont arrêtés                                │
│ Images: 12             -> 12 images téléchargées sur votre disque       │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ CONFIGURATION SYSTÈME                                                  │
├────────────────────────────────────────────────────────────────────────┤
│ Storage Driver: overlay2   -> Comment Docker stocke les données         │
│ Logging Driver: json-file  -> Format des logs                           │
│ Cgroup Driver: cgroupfs    -> Gestion des ressources                    │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ RESSOURCES DISPONIBLES                                                 │
├────────────────────────────────────────────────────────────────────────┤
│ CPUs: 5                -> 5 cœurs CPU disponibles pour Docker           │
│ Total Memory: 7.765GiB -> 7.7 GB de RAM allouée                         │
│ Architecture: aarch64  -> Processeur ARM (M1/M2 Mac ici)                │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ CHEMINS ET CONFIGURATION                                               │
├────────────────────────────────────────────────────────────────────────┤
│ Docker Root Dir: /var/lib/docker -> Où Docker stocke tout               │
│ Registry: index.docker.io        -> Docker Hub par défaut               │
└────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Vérifier l'espace disque utilisé
─────────────────────────────────────────────

CONTEXTE : Votre disque est presque plein

COMMANDE :
docker info | grep -A 5 "Images"

RÉSULTAT :
Images: 47
 Stopped: 23

INTERPRÉTATION :
[ATTENTION] 47 images ! C'est beaucoup (5-20 GB probablement)
[ATTENTION] 23 conteneurs arrêtés qui prennent de la place

ACTION :
docker system prune -a  # Nettoyer


EXEMPLE 3 : Diagnostiquer problème de mémoire
──────────────────────────────────────────────

CONTEXTE : Vos conteneurs sont lents

COMMANDE :
docker info | grep Memory

RÉSULTAT :
Total Memory: 2GiB

PROBLÈME IDENTIFIÉ :
[X] Seulement 2 GB de RAM allouée à Docker
[X] C'est trop peu pour plusieurs conteneurs

SOLUTION (Docker Desktop) :
Settings -> Resources -> Memory -> Augmenter à 4-8 GB


QUAND l'utiliser ?
[OK] Après installation (vérifier la configuration)
[OK] Quand Docker est lent (vérifier ressources)
[OK] Avant de demander de l'aide (inclure docker info dans votre rapport)
[OK] Pour surveiller l'utilisation (combien de conteneurs actifs)
[OK] Pour vérifier les plugins installés

AU LIEU DE : Chercher dans plusieurs menus de Docker Desktop


PIÈGES À ÉVITER :
[ATTENTION] Sortie TRÈS longue (plus de 100 lignes)
   SOLUTION : Utilisez grep pour filtrer
   docker info | grep "Containers"
   docker info | grep "Images"
   docker info | grep "Memory"

[ATTENTION] Beaucoup d'infos techniques incompréhensibles
   SOLUTION : Concentrez-vous sur :
   - Containers (combien)
   - Images (combien)
   - CPUs et Memory (ressources)
   - Docker Root Dir (où sont les données)


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker --help et docker COMMAND --help                       ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche l'aide intégrée pour les commandes Docker                 │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Découvrir toutes les commandes disponibles
• Voir les options d'une commande spécifique
• Ne pas avoir à chercher sur Google
• Aide toujours à jour avec votre version

COMMENT l'utiliser ?

SYNTAXE :
docker --help                    # Liste toutes les commandes
docker COMMAND --help            # Aide pour une commande spécifique


EXEMPLE 1 : Liste de toutes les commandes
──────────────────────────────────────────

COMMANDE :
docker --help

RÉSULTAT (extrait) :
Usage:  docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

Common Commands:
  run         Create and run a new container from an image
  exec        Execute a command in a running container
  ps          List containers
  build       Build an image from a Dockerfile
  pull        Download an image from a registry
  push        Upload an image to a registry
  images      List images
  login       Log in to a registry
  logout      Log out from a registry
  search      Search Docker Hub for images
  version     Show the Docker version information
  info        Display system-wide information

Management Commands:
  container   Manage containers
  image       Manage images
  network     Manage networks
  volume      Manage volumes
  system      Manage Docker

[...]

COMMENT lire cette aide ?

STRUCTURE :
docker [OPTIONS] COMMAND
       └────┘   └───────┘
      Options   Commande
      globales  spécifique

CATÉGORIES :
• Common Commands    -> Les plus utilisées (à connaître !)
• Management Commands -> Gestion avancée (groupées par type)


EXEMPLE 2 : Aide sur une commande spécifique (docker run)
──────────────────────────────────────────────────────────

COMMANDE :
docker run --help

RÉSULTAT (extrait) :
Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Create and run a new container from an image

Options:
  -d, --detach           Run container in background
  -e, --env list         Set environment variables
  -i, --interactive      Keep STDIN open
  -t, --tty             Allocate a pseudo-TTY
  -p, --publish list     Publish a container's port(s)
  -v, --volume list      Bind mount a volume
      --name string      Assign a name to the container
      --rm              Remove container when it exits
  -w, --workdir string   Working directory inside container

[...]

COMMENT lire cette aide ?

FORMAT DES OPTIONS :
-d, --detach
└┘  └──────┘
│   Version longue (--detach)
│
Version courte (-d)

Les deux sont équivalentes :
docker run -d nginx   ≡   docker run --detach nginx


EXEMPLE 3 : Chercher une option spécifique
───────────────────────────────────────────

CONTEXTE : Vous voulez savoir comment mapper un port

COMMANDE :
docker run --help | grep port

RÉSULTAT :
  -p, --publish list     Publish a container's port(s) to the host
  -P, --publish-all      Publish all exposed ports to random ports

DÉCOUVERTE :
[OK] -p pour mapper un port spécifique
[OK] -P (majuscule) pour mapper tous les ports automatiquement


EXEMPLE 4 : Aide sur une sous-commande (docker container)
──────────────────────────────────────────────────────────

COMMANDE :
docker container --help

RÉSULTAT :
Usage:  docker container COMMAND

Manage containers

Commands:
  attach      Attach to a running container
  commit      Create a new image from a container's changes
  cp          Copy files/folders between container and local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories
  exec        Execute a command in a running container
  export      Export a container's filesystem as a tar archive
  inspect     Display detailed information on one or more containers
  kill        Kill one or more running containers
  logs        Fetch the logs of a container
  ls          List containers
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping
  prune       Remove all stopped containers
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  run         Create and run a new container
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage
  stop        Stop one or more running containers
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  wait        Block until one or more containers stop

UTILITÉ :
Voir TOUTES les opérations possibles sur les conteneurs.


QUAND l'utiliser ?
[OK] Vous oubliez une option (exemple : comment suivre les logs ?)
[OK] Vous découvrez Docker (explorer les commandes disponibles)
[OK] Vous voulez essayer une nouvelle commande
[OK] Hors ligne (pas besoin d'Internet)

AU LIEU DE : Chercher sur Google ou dans la documentation


ASTUCES PRO :

1. Combiner avec grep pour chercher
   docker run --help | grep volume
   docker ps --help | grep filter

2. Lire la description complète
   docker run --help | less    # Navigation avec flèches, q pour quitter

3. Chaîner les aides
   docker --help                    # Liste générale
   docker container --help          # Commandes container
   docker container run --help      # Options de run


================================================================================
[OK] SECTION 2 : GESTION DES IMAGES
================================================================================

Les images sont les "templates" pour créer des conteneurs.
Cette section couvre toutes les opérations sur les images.

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker search                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Recherche des images sur Docker Hub depuis la ligne de commande  │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Trouver une image sans ouvrir le navigateur
• Voir rapidement les images populaires
• Vérifier qu'une image existe avant de la télécharger
• Comparer plusieurs images similaires

COMMENT l'utiliser ?

SYNTAXE :
docker search TERME_RECHERCHE [OPTIONS]

OPTIONS UTILES :
--limit N          -> Limiter à N résultats (défaut: 25)
--filter KEY=VALUE -> Filtrer les résultats
--no-trunc         -> Afficher descriptions complètes


EXEMPLE 1 : Recherche basique
──────────────────────────────

COMMANDE :
docker search ubuntu

RÉSULTAT :
NAME                       DESCRIPTION                     STARS   OFFICIAL
ubuntu                     Ubuntu is a Debian-based...     16234   [OK]
ubuntu/nginx               Nginx, a high-performance...    103     
websphere-liberty          WebSphere Liberty multi-...     296     [OK]
ubuntu/mysql               MySQL open source fast...       58      
neurodebian                NeuroDebian provides...         101     [OK]
[...]

EXPLICATION DES COLONNES :

NAME        -> Nom de l'image (format: utilisateur/nom ou juste nom)
DESCRIPTION -> Description courte (tronquée)
STARS       -> Nombre d'étoiles (popularité)
OFFICIAL    -> [OK] = image officielle (IMPORTANT !)


COMMENT interpréter ?

IMAGE OFFICIELLE :
NAME: ubuntu
OFFICIAL: [OK]
STARS: 16234
-> [OK] Fiable, maintenue par Canonical, à privilégier

IMAGE UTILISATEUR :
NAME: ubuntu/nginx
OFFICIAL: (vide)
STARS: 103
-> [ATTENTION] Créée par un utilisateur, vérifier la réputation


EXEMPLE 2 : Limiter le nombre de résultats
───────────────────────────────────────────

COMMANDE :
docker search python --limit 5

RÉSULTAT :
NAME                DESCRIPTION                          STARS   OFFICIAL
python              Python is an interpreted...          8952    [OK]
pypy                PyPy is a fast...                    363     [OK]
circleci/python     Python is an interpreted...          60      
nikolaik/python-nodejs  Python with Node.js            83      
arm32v7/python      Python is an interpreted...          73      

UTILITÉ :
Voir rapidement les TOP résultats sans être noyé.


EXEMPLE 3 : Filtrer par images officielles seulement
─────────────────────────────────────────────────────

COMMANDE :
docker search nginx --filter is-official=true

RÉSULTAT :
NAME      DESCRIPTION                              STARS   OFFICIAL
nginx     Official build of Nginx.                 19456   [OK]

UTILITÉ :
[VERROUILLE] Sécurité ! Ne voir QUE les images officielles.


EXEMPLE 4 : Filtrer par popularité minimale
────────────────────────────────────────────

COMMANDE :
docker search postgres --filter stars=1000

RÉSULTAT :
NAME        DESCRIPTION                            STARS   OFFICIAL
postgres    The PostgreSQL object-relational...    13421   [OK]

SIGNIFICATION :
Seulement les images avec 1000+ étoiles = très populaires et fiables.


QUAND l'utiliser ?
[OK] Découvrir quelle image utiliser pour un projet
[OK] Vérifier l'existence d'une image avant docker pull
[OK] Comparer la popularité de plusieurs images
[OK] Travailler en ligne de commande (pas d'interface graphique)

AU LIEU DE : Aller sur hub.docker.com (mais le site a plus d'infos)


PIÈGES À ÉVITER :

[X] NE PAS se fier uniquement aux STARS
   TOUJOURS vérifier [OK] dans OFFICIAL

[X] NE PAS prendre la première image trouvée
   Exemple : "docker search mysql"
   -> mysql (officielle) vs mysql/mysql-server (moins maintenue)
   -> Prenez l'officielle !

[X] Descriptions tronquées difficiles à lire
   SOLUTION : Allez sur hub.docker.com pour description complète
   OU utilisez --no-trunc :
   docker search python --no-trunc --limit 3


RÉSUMÉ RECHERCHE D'IMAGES :

WORKFLOW RECOMMANDÉ :
1. docker search nom --filter is-official=true --limit 5
2. Si trouvé -> Utiliser l'image officielle
3. Si pas trouvé -> Chercher sur hub.docker.com pour plus d'infos
4. Vérifier toujours : popularité (STARS) + officielle ([OK])


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker pull                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Télécharge une image depuis Docker Hub sur votre machine         │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Télécharger une image avant de l'utiliser
• Avoir une image disponible hors ligne
• Mettre à jour une image existante
• Préparer plusieurs images en avance

COMMENT l'utiliser ?

SYNTAXE :
docker pull [OPTIONS] NOM[:TAG|@DIGEST]

FORMAT DU NOM :
nom_image               -> Dernière version (latest)
nom_image:tag           -> Version spécifique
nom_image@sha256:...    -> Version exacte par hash


EXEMPLE 1 : Télécharger dernière version
─────────────────────────────────────────

COMMANDE :
docker pull ubuntu

CE QUI SE PASSE :
1. Docker contacte hub.docker.com
2. Cherche l'image "ubuntu" avec tag "latest" (par défaut)
3. Télécharge couche par couche
4. Vérifie l'intégrité
5. Stocke localement

RÉSULTAT :
Using default tag: latest
latest: Pulling from library/ubuntu
445a6a12be2b: Downloading [=========>                ] 12.5MB/29.5MB
445a6a12be2b: Pull complete
Digest: sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

EXPLICATION LIGNE PAR LIGNE :

Using default tag: latest
└─ Pas de tag spécifié, Docker utilise "latest" automatiquement

latest: Pulling from library/ubuntu
└─ Téléchargement depuis le repository officiel

445a6a12be2b: Downloading [=========>] 12.5MB/29.5MB
└─ ID de la couche + barre de progression

445a6a12be2b: Pull complete
└─ Cette couche est téléchargée et vérifiée

Digest: sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21
└─ Empreinte cryptographique (garantit l'authenticité)

Status: Downloaded newer image for ubuntu:latest
└─ Image sauvegardée localement

docker.io/library/ubuntu:latest
└─ Nom complet de l'image téléchargée

DURÉE : 10 secondes à 3 minutes selon la taille et votre connexion


EXEMPLE 2 : Télécharger version spécifique
───────────────────────────────────────────

COMMANDE :
docker pull ubuntu:22.04

RÉSULTAT :
22.04: Pulling from library/ubuntu
445a6a12be2b: Already exists   <- Couche déjà présente localement !
Digest: sha256:...
Status: Downloaded newer image for ubuntu:22.04

MAGIE DE DOCKER :
Les couches communes entre images sont RÉUTILISÉES !
Si vous avez déjà ubuntu:latest, télécharger ubuntu:22.04 sera rapide.

TAILLE ÉCONOMISÉE :
TAILLE ÉCONOMISÉE :
- ubuntu:latest seul : 77 MB
- ubuntu:22.04 seul : 77 MB
- ubuntu:latest + ubuntu:22.04 ensemble : 80 MB (pas 154 MB !)
  └─ Docker partage les couches communes

POURQUOI utiliser un tag spécifique ?

[X] MAUVAIS (en production) :
docker pull python
-> Télécharge "latest" qui peut changer demain
-> Votre code peut casser avec la nouvelle version

[OK] BON (en production) :
docker pull python:3.11.5
-> Version fixe, reproductible
-> Votre code marchera toujours pareil


EXEMPLE 3 : Télécharger version allégée
────────────────────────────────────────

COMMANDE :
docker pull python:3.11-slim

RÉSULTAT :
3.11-slim: Pulling from library/python
[...]
Status: Downloaded newer image for python:3.11-slim

COMPARAISON DES TAILLES :

TAG              TAILLE    USAGE
─────────────────────────────────────────────────────────────
python:3.11      920 MB    Développement, débogage
python:3.11-slim 150 MB    Production (recommandé)
python:3.11-alpine 50 MB   Production ultra-optimisée

QUAND utiliser quoi ?
[OK] Débutant -> slim (bon compromis)
[OK] Production -> slim ou alpine
[OK] Apprentissage -> version complète (plus d'outils)


EXEMPLE 4 : Télécharger depuis un registry privé
─────────────────────────────────────────────────

CONTEXTE : Votre entreprise a un registry Docker privé

COMMANDE :
docker pull registry.entreprise.com/mon-app:v1.2.3
                └──────────────────────┘
                Registry personnalisé

PRÉREQUIS :
docker login registry.entreprise.com

FORMAT COMPLET :
[REGISTRY/]NOM[:TAG]
└─────────┘ └─┘ └─┘
Optionnel  Obligatoire  Optionnel (défaut: latest)


EXEMPLE 5 : Mettre à jour une image existante
──────────────────────────────────────────────

SITUATION : Vous avez téléchargé nginx il y a 3 mois

COMMANDE :
docker pull nginx

RÉSULTAT 1 (si mise à jour disponible) :
Using default tag: latest
latest: Pulling from library/nginx
a1b2c3d4e5f6: Pull complete   <- Nouvelles couches
[...]
Status: Downloaded newer image for nginx:latest

RÉSULTAT 2 (si déjà à jour) :
Using default tag: latest
latest: Pulling from library/nginx
Digest: sha256:...
Status: Image is up to date for nginx:latest
         └──────────────────────────────┘
         Aucun téléchargement nécessaire


QUAND l'utiliser ?
[OK] Avant de lancer un nouveau projet (avoir les images nécessaires)
[OK] Pour travailler hors ligne (télécharger en avance)
[OK] Pour mettre à jour vos images (sécurité)
[OK] Dans un script de déploiement
[OK] Avant docker run (optionnel, run télécharge automatiquement)

AU LIEU DE : docker run télécharge automatiquement si absent


PIÈGES À ÉVITER :

[X] Oublier le tag en production
   docker pull python  # Version changeante
   [OK] docker pull python:3.11.5  # Version fixe

[X] Télécharger des images trop grosses
   docker pull ubuntu  # 77 MB [OK]
   docker pull ubuntu:20.04  # 72 MB [OK]
   docker pull une-image-custom  # 3 GB [X] Vérifiez la taille !

[X] Ne jamais mettre à jour
   Images vieilles = failles de sécurité
   [OK] Mettez à jour mensuellement : docker pull nom_image

[X] Problème réseau
   ERROR : error pulling image configuration: connection refused
   SOLUTION : Vérifiez votre connexion Internet


ASTUCE PRO : Voir la taille avant de télécharger

Allez sur hub.docker.com et cherchez l'image.
La taille est affichée pour chaque tag.

Exemple : python:3.11-slim -> Compressed Size: 49.8 MB


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker images                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Liste toutes les images Docker présentes sur votre machine       │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir ce qui est téléchargé localement
• Vérifier la taille des images (surveillance disque)
• Trouver l'ID d'une image
• Inventorier vos images avant nettoyage

COMMENT l'utiliser ?

SYNTAXE :
docker images [OPTIONS]

ALIAS ÉQUIVALENTS :
docker images     ≡  docker image ls

OPTIONS UTILES :
-a              -> Toutes les images (incluant intermédiaires)
-q              -> Seulement les IDs
--filter        -> Filtrer les résultats
--format        -> Format personnalisé
--digests       -> Afficher les digests


EXEMPLE 1 : Liste simple
────────────────────────

COMMANDE :
docker images

RÉSULTAT :
REPOSITORY       TAG          IMAGE ID       CREATED        SIZE
python           3.11         a1b2c3d4e5f6   2 weeks ago    920MB
python           3.11-slim    f6e5d4c3b2a1   2 weeks ago    150MB
nginx            alpine       1a2b3c4d5e6f   3 weeks ago    23MB
ubuntu           22.04        9f8e7d6c5b4a   1 month ago    77MB
postgres         15           4d5e6f7a8b9c   1 month ago    379MB
redis            7            c3b2a1f0e9d8   2 months ago   117MB

EXPLICATION DES COLONNES :

┌──────────────────────────────────────────────────────────────────────────┐
│ REPOSITORY                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ Nom de l'image (sans le tag)                                            │
│ Exemple : python, nginx, mon-app                                        │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ TAG                                                                      │
├──────────────────────────────────────────────────────────────────────────┤
│ Version ou variante de l'image                                          │
│ Exemples : 3.11, latest, alpine, slim                                   │
│ Une même image peut avoir plusieurs tags                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ IMAGE ID                                                                 │
├──────────────────────────────────────────────────────────────────────────┤
│ Identifiant unique de l'image (hash court)                              │
│ Format : 12 premiers caractères d'un SHA256                             │
│ Utilisation : référencer l'image sans ambiguïté                         │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CREATED                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ Quand l'image a été CONSTRUITE (pas téléchargée !)                      │
│ Relatif : "2 weeks ago"                                                 │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ SIZE                                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ Taille totale de l'image                                                │
│ [ATTENTION] Attention : tailles partagées entre images !                         │
│ Exemple : 3 images de 500 MB ≠ 1.5 GB réel                             │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Calculer l'espace disque réel
──────────────────────────────────────────

PROBLÈME : Les tailles affichées sont trompeuses !

COMMANDE pour voir l'espace RÉEL :
docker system df

RÉSULTAT :
TYPE            TOTAL   ACTIVE   SIZE      RECLAIMABLE
Images          15      3        3.2GB     2.1GB (65%)
Containers      8       2        450MB     380MB (84%)
Local Volumes   5       1        850MB     600MB (70%)

EXPLICATION :
- Vous avez 15 images
- Taille totale : 3.2 GB (pas la somme des SIZE !)
- 2.1 GB peuvent être libérés (images inutilisées)

POURQUOI cette différence ?
Docker PARTAGE les couches communes entre images.

EXEMPLE CONCRET :
ubuntu:20.04      77 MB
ubuntu:22.04      77 MB
ubuntu:latest     77 MB
─────────────────────────
Total affiché:   231 MB
Total réel:      ~90 MB (couches partagées)


EXEMPLE 3 : Voir toutes les images (incluant intermédiaires)
─────────────────────────────────────────────────────────────

COMMANDE :
docker images -a

RÉSULTAT :
REPOSITORY       TAG          IMAGE ID       CREATED        SIZE
python           3.11         a1b2c3d4e5f6   2 weeks ago    920MB
<none>           <none>       b2c3d4e5f6a7   2 weeks ago    850MB
<none>           <none>       c3d4e5f6a7b8   2 weeks ago    780MB
nginx            alpine       1a2b3c4d5e6f   3 weeks ago    23MB

IMAGES <none> <none> :
Ce sont des "dangling images" (images pendantes).
Elles apparaissent quand :
- Vous reconstruisez une image avec le même nom
- Un build échoue partiellement
- Vous supprimez un tag mais pas l'image

CE SONT DES DÉCHETS -> À nettoyer !

COMMANDE NETTOYAGE :
docker image prune


EXEMPLE 4 : Lister seulement les IDs
─────────────────────────────────────

COMMANDE :
docker images -q

RÉSULTAT :
a1b2c3d4e5f6
f6e5d4c3b2a1
1a2b3c4d5e6f
9f8e7d6c5b4a
4d5e6f7a8b9c

UTILITÉ : Pour les scripts !

EXEMPLE PRATIQUE : Supprimer toutes les images
docker rmi $(docker images -q)
           └──────────────────┘
           Liste de tous les IDs


EXEMPLE 5 : Filtrer par nom
───────────────────────────

COMMANDE :
docker images python

RÉSULTAT :
REPOSITORY   TAG          IMAGE ID       CREATED        SIZE
python       3.11         a1b2c3d4e5f6   2 weeks ago    920MB
python       3.11-slim    f6e5d4c3b2a1   2 weeks ago    150MB
python       3.10         e5f6a7b8c9d0   3 weeks ago    915MB

Seulement les images Python !


EXEMPLE 6 : Filtrer avec --filter
──────────────────────────────────

COMMANDE : Images créées avant une certaine date
docker images --filter "before=nginx:alpine"

COMMANDE : Images créées après
docker images --filter "since=ubuntu:22.04"

COMMANDE : Images "dangling" (à supprimer)
docker images --filter "dangling=true"

RÉSULTAT :
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
<none>       <none>    b2c3d4e5f6a7   2 weeks ago    850MB
<none>       <none>    c3d4e5f6a7b8   2 weeks ago    780MB

Ce sont des images orphelines -> À nettoyer


EXEMPLE 7 : Format personnalisé
────────────────────────────────

COMMANDE :
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

RÉSULTAT :
REPOSITORY   TAG          SIZE
python       3.11         920MB
python       3.11-slim    150MB
nginx        alpine       23MB

Plus lisible ! Seulement les colonnes importantes.

AUTRE FORMAT UTILE :
docker images --format "{{.Repository}}:{{.Tag}} ({{.Size}})"

RÉSULTAT :
python:3.11 (920MB)
python:3.11-slim (150MB)
nginx:alpine (23MB)


QUAND l'utiliser ?
[OK] Tous les jours (vérifier ce qui est installé)
[OK] Avant un nettoyage (voir ce qui prend de la place)
[OK] Pour trouver l'ID d'une image
[OK] Pour vérifier qu'une image est bien téléchargée
[OK] Pour inventorier votre environnement

AU LIEU DE : Ouvrir Docker Desktop pour voir les images


PIÈGES À ÉVITER :

[X] Confondre CREATED avec "quand j'ai téléchargé"
   CREATED = quand Docker Inc. a construit l'image
   Pas quand vous l'avez pull

[X] Additionner les SIZE pour calculer l'espace total
   [OK] Utilisez : docker system df

[X] Ignorer les images <none>
   Elles prennent de la place !
   [OK] Nettoyez : docker image prune


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker rmi                                                   ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Supprime une ou plusieurs images de votre machine                │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Libérer de l'espace disque
• Supprimer des images obsolètes
• Nettoyer après des tests
• Forcer le re-téléchargement d'une image

COMMENT l'utiliser ?

SYNTAXE :
docker rmi [OPTIONS] IMAGE [IMAGE...]

ALIAS ÉQUIVALENTS :
docker rmi nom          ≡  docker image rm nom

OPTIONS UTILES :
-f, --force     -> Forcer la suppression (même si utilisée)
--no-prune      -> Ne pas supprimer les images parentes


EXEMPLE 1 : Supprimer une image par nom
────────────────────────────────────────

COMMANDE :
docker rmi nginx:alpine

RÉSULTAT :
Untagged: nginx:alpine
Untagged: nginx@sha256:4c0fdaa8b6341bfdeca5f18f7837462c80cff90527ee35ef185571e1c327beac
Deleted: sha256:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b
Deleted: sha256:3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d

EXPLICATION :

Untagged: nginx:alpine
└─ Le tag "nginx:alpine" est supprimé

Deleted: sha256:...
└─ Les couches de l'image sont supprimées du disque

ESPACE LIBÉRÉ : 23 MB (dans ce cas)


EXEMPLE 2 : Supprimer par IMAGE ID
───────────────────────────────────

CONTEXTE : Vous avez l'ID mais pas le nom

COMMANDE :
docker rmi 1a2b3c4d5e6f

RÉSULTAT : Identique à l'exemple 1

ASTUCE : Pas besoin de taper tout l'ID !
docker rmi 1a2b    # Les 4-6 premiers caractères suffisent
                   # (tant que c'est unique)


EXEMPLE 3 : Supprimer plusieurs images
───────────────────────────────────────

COMMANDE :
docker rmi nginx:alpine ubuntu:22.04 python:3.11-slim

RÉSULTAT :
Untagged: nginx:alpine
[...]
Untagged: ubuntu:22.04
[...]
Untagged: python:3.11-slim
[...]

Les 3 images sont supprimées en une seule commande !


EXEMPLE 4 : Erreur - Image utilisée par un conteneur
─────────────────────────────────────────────────────

COMMANDE :
docker rmi nginx

ERREUR :
Error response from daemon: conflict: unable to remove repository reference "nginx" (must force) - container a1b2c3d4 is using its referenced image 5e6f7a8b9c0d

TRADUCTION :
[X] Impossible de supprimer nginx
[X] Un conteneur (ID: a1b2c3d4) l'utilise

SOLUTION 1 : Supprimer le conteneur d'abord
docker ps -a                    # Trouver le conteneur
docker rm a1b2c3d4              # Supprimer le conteneur
docker rmi nginx                # Maintenant ça marche !

SOLUTION 2 : Forcer (ATTENTION, dangereux !)
docker rmi -f nginx
[ATTENTION] Le conteneur peut crasher si vous faites ça


EXEMPLE 5 : Supprimer toutes les images d'un repository
────────────────────────────────────────────────────────

CONTEXTE : Vous avez python:3.11, python:3.11-slim, python:3.10

COMMANDE :
docker rmi $(docker images python -q)
           └──────────────────────┘
           Liste tous les IDs des images Python

RÉSULTAT :
Toutes les images Python sont supprimées !

DÉCOMPOSITION :
1. docker images python -q  -> Liste les IDs : a1b2c3 f6e5d4 e5f6a7
2. docker rmi a1b2c3 f6e5d4 e5f6a7  -> Supprime les 3


EXEMPLE 6 : Supprimer toutes les images (DANGEREUX !)
──────────────────────────────────────────────────────

COMMANDE :
docker rmi $(docker images -q)

RÉSULTAT :
TOUTES vos images sont supprimées !

[ATTENTION] ATTENTION : 
- Vous devrez tout re-télécharger
- Peut prendre des heures pour tout récupérer
- Utilisez seulement si vous voulez repartir de zéro

ALTERNATIVE PLUS SÛRE :
docker image prune -a     # Supprime seulement les images inutilisées


EXEMPLE 7 : Image partagée entre plusieurs tags
────────────────────────────────────────────────

SITUATION : Même IMAGE ID pour plusieurs tags

COMMANDE :
docker images

RÉSULTAT :
REPOSITORY   TAG      IMAGE ID       CREATED        SIZE
ubuntu       latest   9f8e7d6c5b4a   1 month ago    77MB
ubuntu       22.04    9f8e7d6c5b4a   1 month ago    77MB
                      └──────────────┘
                      Même IMAGE ID !

C'est la MÊME image avec 2 tags différents.

COMMANDE :
docker rmi ubuntu:latest

RÉSULTAT :
Untagged: ubuntu:latest
(L'image n'est PAS supprimée, juste le tag)

docker images
REPOSITORY   TAG      IMAGE ID       CREATED        SIZE
ubuntu       22.04    9f8e7d6c5b4a   1 month ago    77MB

L'image existe encore avec le tag "22.04" !

Pour VRAIMENT supprimer :
docker rmi ubuntu:22.04
-> Là, l'image est supprimée (plus aucun tag)


QUAND l'utiliser ?
[OK] Disque presque plein
[OK] Après tests (supprimer images temporaires)
[OK] Nettoyage mensuel (vieilles versions)
[OK] Avant de re-pull une image (forcer mise à jour)

AU LIEU DE : docker image prune (qui est plus sûr)


PIÈGES À ÉVITER :

[X] Supprimer une image utilisée
   -> Vérifiez avant : docker ps -a

[X] Supprimer toutes les images par accident
   -> Double-check avant : docker rmi $(docker images -q)

[X] Oublier que plusieurs tags peuvent pointer vers la même image
   -> Vérifiez les IMAGE ID


TABLEAU RÉCAPITULATIF :

COMMANDE                          RÉSULTAT
────────────────────────────────────────────────────────────────
docker rmi nginx                  Supprime nginx:latest
docker rmi nginx:alpine           Supprime nginx:alpine
docker rmi abc123                 Supprime par ID
docker rmi -f nginx               Force même si utilisée ([ATTENTION])
docker rmi nginx ubuntu python    Supprime 3 images
docker rmi $(docker images -q)    Supprime TOUT ([ATTENTION][ATTENTION][ATTENTION])


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker image prune                                           ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Supprime les images "dangling" (orphelines) automatiquement      │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Nettoyer automatiquement les images inutiles
• Libérer de l'espace en toute sécurité
• Plus simple et sûr que docker rmi manuel
• Maintenance régulière

COMMENT l'utiliser ?

SYNTAXE :
docker image prune [OPTIONS]

OPTIONS UTILES :
-a, --all       -> Supprimer TOUTES les images non utilisées (pas que dangling)
-f, --force     -> Ne pas demander confirmation


QU'EST-CE QU'UNE IMAGE "DANGLING" ?

DÉFINITION :
Image sans tag ET sans conteneur associé.

COMMENT ça arrive ?

SITUATION 1 : Rebuild d'une image
─────────────────────────────────
1. Vous avez : mon-app:latest (ID: abc123)
2. Vous rebuild : docker build -t mon-app:latest .
3. Nouvelle image : mon-app:latest (ID: def456)
4. Ancienne image : <none>:<none> (ID: abc123)  <- DANGLING

SITUATION 2 : Suppression d'un tag
───────────────────────────────────
1. Vous avez : nginx:test
2. Vous supprimez le tag mais pas l'image
3. Image reste : <none>:<none>  <- DANGLING

SITUATION 3 : Build interrompu
──────────────────────────────
1. Build échoue à mi-chemin
2. Couches partielles restent : <none>:<none>  <- DANGLING


EXEMPLE 1 : Nettoyage basique
──────────────────────────────

COMMANDE :
docker image prune

RÉSULTAT :
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y

Deleted Images:
untagged: sha256:b2c3d4e5f6a7...
deleted: sha256:c3d4e5f6a7b8...
deleted: sha256:d4e5f6a7b8c9...

Total reclaimed space: 1.2GB

EXPLICATION :
- Docker demande confirmation (sécurité)
- Supprime les images <none>:<none>
- Affiche l'espace libéré

QUAND DOCKER VOUS DEMANDE DE CONFIRMER :
Tapez : y puis Enter
Pour annuler : n ou Ctrl+C


EXEMPLE 2 : Sans confirmation (scripts)
────────────────────────────────────────

COMMANDE :
docker image prune -f

RÉSULTAT :
Deleted Images:
[...]
Total reclaimed space: 850MB

Pas de question posée, suppression immédiate !

UTILITÉ : Dans des scripts automatisés
#!/bin/bash
docker image prune -f
echo "Nettoyage terminé"


EXEMPLE 3 : Supprimer TOUTES les images non utilisées
──────────────────────────────────────────────────────

COMMANDE :
docker image prune -a

RÉSULTAT :
WARNING! This will remove all images without at least one container associated.
Are you sure you want to continue? [y/N] y

Deleted Images:
untagged: python:3.10
untagged: nginx:1.24
deleted: sha256:...
[...]

Total reclaimed space: 3.5GB

[ATTENTION] ATTENTION : Option -a est AGRESSIVE !

DIFFÉRENCE :
docker image prune      -> Supprime images <none>:<none>
docker image prune -a   -> Supprime TOUTES images non utilisées par conteneur

EXEMPLE CONCRET :

AVANT :
REPOSITORY   TAG    IMAGE ID      CONTENEUR ASSOCIÉ
python       3.11   abc123        Oui (conteneur actif)
python       3.10   def456        Non
nginx        alpine ghi789        Non
<none>       <none> jkl012        Non

APRÈS docker image prune :
python       3.11   abc123        Oui
python       3.10   def456        Non  <- Gardée (a un tag)
nginx        alpine ghi789        Non  <- Gardée (a un tag)

APRÈS docker image prune -a :
python       3.11   abc123        Oui  <- Seule gardée !


EXEMPLE 4 : Filtrer par ancienneté
───────────────────────────────────

COMMANDE :
docker image prune -a --filter "until=24h"

SIGNIFICATION :
Supprime les images non utilisées créées il y a plus de 24h.

AUTRES FILTRES UTILES :
--filter "until=72h"        -> Plus de 3 jours
--filter "until=2023-01-01" -> Avant le 1er janvier 2023
--filter "label=test"       -> Images avec label "test"


EXEMPLE 5 : Voir ce qui sera supprimé SANS supprimer
─────────────────────────────────────────────────────

ASTUCE : Utilisez docker images pour simuler

COMMANDE :
docker images --filter "dangling=true"

RÉSULTAT :
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
<none>       <none>    b2c3d4e5f6a7   2 weeks ago    850MB
<none>       <none>    c3d4e5f6a7b8   2 weeks ago    780MB

Ce sont les images qui seront supprimées par docker image prune !

MAINTENANT, vous pouvez décider :
- Si OK -> docker image prune
- Si vous voulez garder quelque chose -> Ne pas lancer


QUAND l'utiliser ?
[OK] Toutes les semaines (maintenance)
[OK] Après plusieurs builds/tests
[OK] Quand le disque est plein
[OK] Avant de partir en vacances (nettoyer proprement)

AU LIEU DE : Supprimer manuellement avec docker rmi


TABLEAU COMPARATIF :

COMMANDE                    QUI EST SUPPRIMÉ ?
─────────────────────────────────────────────────────────────────────
docker image prune          Images <none>:<none> seulement
docker image prune -a       Toutes images sans conteneur associé
docker rmi nom              Image spécifique (manuelle)
docker rmi $(docker...)     Toutes images (DANGEREUX)

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker inspect (IMAGES)                                      ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche TOUTES les informations techniques d'une image en JSON   │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir la configuration complète d'une image
• Comprendre comment une image a été construite
• Déboguer des problèmes
• Extraire des informations spécifiques (variables, ports, volumes)
• Vérifier la provenance et l'authenticité

COMMENT l'utiliser ?

SYNTAXE :
docker inspect [OPTIONS] IMAGE [IMAGE...]

ALIAS ÉQUIVALENTS :
docker inspect ubuntu   ≡   docker image inspect ubuntu

OPTIONS UTILES :
--format '{{.Config.Cmd}}'    -> Extraire une info spécifique
-f '{{json .Config}}'         -> Format JSON pour une section


EXEMPLE 1 : Inspection complète
────────────────────────────────

COMMANDE :
docker inspect ubuntu

RÉSULTAT (extrait simplifié) :
[
    {
        "Id": "sha256:3b418d7b466a23f5d1f9b91e8d70e34e7f7c5b3e8c6f8d7e6d5c4b3a2f1e0d9c",
        "RepoTags": [
            "ubuntu:latest"
        ],
        "RepoDigests": [
            "ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21"
        ],
        "Created": "2023-09-28T18:28:45.123456789Z",
        "Container": "abc123def456...",
        "ContainerConfig": {
            "Hostname": "abc123def456",
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"/bin/bash\"]"
            ]
        },
        "DockerVersion": "20.10.23",
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 77829510,
        "VirtualSize": 77829510,
        "GraphDriver": {
            "Data": {
                "MergedDir": "/var/lib/docker/overlay2/.../merged",
                "UpperDir": "/var/lib/docker/overlay2/.../diff",
                "WorkDir": "/var/lib/docker/overlay2/.../work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:445a6a12be2be54b4da18d7c77d4a41bc4f5",
                "sha256:8b15606a9e3e430cb7ba739f34e75"
            ]
        },
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "sha256:...",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "Labels": {
                "org.opencontainers.image.version": "22.04"
            }
        }
    }
]

EXPLICATION DES SECTIONS IMPORTANTES :

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. IDENTIFICATION                                                        │
├──────────────────────────────────────────────────────────────────────────┤
│ "Id": "sha256:3b418d..."                                                 │
│   -> Identifiant unique complet (SHA256) de l'image                      │
│                                                                          │
│ "RepoTags": ["ubuntu:latest"]                                           │
│   -> Nom et tag de l'image                                               │
│                                                                          │
│ "RepoDigests": ["ubuntu@sha256:..."]                                    │
│   -> Digest (signature) pour vérifier l'authenticité                     │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 2. MÉTADONNÉES                                                           │
├──────────────────────────────────────────────────────────────────────────┤
│ "Created": "2023-09-28T18:28:45..."                                      │
│   -> Quand l'image a été créée (format ISO 8601)                         │
│                                                                          │
│ "DockerVersion": "20.10.23"                                              │
│   -> Version de Docker utilisée pour construire l'image                  │
│                                                                          │
│ "Architecture": "amd64"                                                  │
│   -> Architecture du processeur (amd64, arm64, etc.)                     │
│                                                                          │
│ "Os": "linux"                                                            │
│   -> Système d'exploitation de base                                      │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 3. TAILLE                                                                │
├──────────────────────────────────────────────────────────────────────────┤
│ "Size": 77829510                                                         │
│   -> Taille en bytes (77829510 = ~74 MB)                                 │
│                                                                          │
│ CONVERSION :                                                             │
│   77829510 bytes ÷ 1024 ÷ 1024 = 74.2 MB                               │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 4. SYSTÈME DE FICHIERS                                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ "GraphDriver": {                                                         │
│     "Name": "overlay2",                                                  │
│     "Data": { ... }                                                      │
│ }                                                                        │
│   -> Comment Docker stocke les données (overlay2 = moderne et rapide)    │
│                                                                          │
│ "RootFS": {                                                              │
│     "Type": "layers",                                                    │
│     "Layers": ["sha256:...", "sha256:..."]                              │
│ }                                                                        │
│   -> Liste des couches (layers) qui composent l'image                    │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 5. CONFIGURATION (LA PLUS IMPORTANTE !)                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ "Config": {                                                              │
│     "Env": [                                                             │
│         "PATH=/usr/local/sbin:..."                                       │
│     ],                                                                   │
│     -> Variables d'environnement par défaut                              │
│                                                                          │
│     "Cmd": ["/bin/bash"],                                                │
│     -> Commande exécutée par défaut au démarrage                         │
│                                                                          │
│     "WorkingDir": "",                                                    │
│     -> Répertoire de travail par défaut                                  │
│                                                                          │
│     "Entrypoint": null,                                                  │
│     -> Point d'entrée (si défini)                                        │
│                                                                          │
│     "ExposedPorts": { "80/tcp": {} },                                    │
│     -> Ports exposés (pour les apps web)                                 │
│                                                                          │
│     "Volumes": { "/data": {} },                                          │
│     -> Points de montage pour volumes                                    │
│                                                                          │
│     "Labels": {                                                          │
│         "version": "22.04"                                               │
│     }                                                                    │
│     -> Métadonnées personnalisées                                        │
│ }                                                                        │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Extraire une information spécifique
────────────────────────────────────────────────

PROBLÈME : Le JSON complet est illisible (100-500 lignes !)

SOLUTION : Utiliser --format pour extraire ce qui nous intéresse

COMMANDE : Voir la commande par défaut
docker inspect --format='{{.Config.Cmd}}' ubuntu

RÉSULTAT :
[/bin/bash]

Beaucoup plus lisible !


COMMANDE : Voir les variables d'environnement
docker inspect --format='{{.Config.Env}}' python:3.11

RÉSULTAT :
[PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LANG=C.UTF-8 GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D PYTHON_VERSION=3.11.5]


COMMANDE : Voir l'architecture
docker inspect --format='{{.Architecture}}' nginx

RÉSULTAT :
amd64


COMMANDE : Voir la taille
docker inspect --format='{{.Size}}' ubuntu

RÉSULTAT :
77829510

Pour avoir en MB :
docker inspect --format='{{.Size}}' ubuntu | awk '{print $1/1024/1024 " MB"}'
RÉSULTAT : 74.2 MB


COMMANDE : Voir les ports exposés
docker inspect --format='{{.Config.ExposedPorts}}' nginx

RÉSULTAT :
map[80/tcp:{}]

Signification : Nginx expose le port 80 en TCP


COMMANDE : Voir tous les labels
docker inspect --format='{{json .Config.Labels}}' ubuntu | jq

RÉSULTAT (avec jq pour formatter) :
{
  "org.opencontainers.image.ref.name": "ubuntu",
  "org.opencontainers.image.version": "22.04"
}


EXEMPLE 3 : Comparer deux images
─────────────────────────────────

CONTEXTE : Vous hésitez entre python:3.11 et python:3.11-slim

COMMANDE : Comparer les tailles
docker inspect --format='{{.RepoTags}} {{.Size}}' python:3.11
docker inspect --format='{{.RepoTags}} {{.Size}}' python:3.11-slim

RÉSULTAT :
[python:3.11] 967262267       -> 922 MB
[python:3.11-slim] 157234789  -> 150 MB

Différence : 772 MB ! Slim est 6x plus léger.


COMMANDE : Comparer les commandes par défaut
docker inspect --format='{{.Config.Cmd}}' python:3.11
docker inspect --format='{{.Config.Cmd}}' python:3.11-slim

RÉSULTAT :
[python3]
[python3]

Identiques ! Les deux lancent python3 par défaut.


COMMANDE : Comparer le nombre de layers
docker inspect --format='{{len .RootFS.Layers}}' python:3.11
docker inspect --format='{{len .RootFS.Layers}}' python:3.11-slim

RÉSULTAT :
12 layers
5 layers

Slim a moins de couches = plus simple, plus rapide.


EXEMPLE 4 : Vérifier la provenance (sécurité)
──────────────────────────────────────────────

CONTEXTE : Vous voulez être sûr que l'image vient bien de Docker Hub officiel

COMMANDE : Voir le digest (signature)
docker inspect --format='{{.RepoDigests}}' ubuntu

RÉSULTAT :
[ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21]

VÉRIFICATION :
Allez sur hub.docker.com/r/library/ubuntu
Cherchez l'onglet "Tags"
Comparez le SHA256

[OK] Si identique : Image authentique
[X] Si différent : Image modifiée ou fausse (DANGER !)


EXEMPLE 5 : Déboguer une image qui ne démarre pas
──────────────────────────────────────────────────

PROBLÈME : Vous lancez un conteneur, il s'arrête immédiatement

COMMANDE : Voir quelle commande est exécutée
docker inspect --format='{{.Config.Cmd}}' mon-image

RÉSULTAT :
[]

AH HA ! Aucune commande par défaut !
C'est pour ça que le conteneur s'arrête tout de suite.

SOLUTION :
docker run mon-image /bin/bash  # Spécifier une commande


COMMANDE : Voir s'il y a un ENTRYPOINT
docker inspect --format='{{.Config.Entrypoint}}' mon-image

RÉSULTAT :
[/docker-entrypoint.sh]

Maintenant on sait que c'est ce script qui s'exécute !


EXEMPLE 6 : Inspecter plusieurs images à la fois
─────────────────────────────────────────────────

COMMANDE :
docker inspect ubuntu nginx python:3.11

RÉSULTAT :
[
    { ... ubuntu ... },
    { ... nginx ... },
    { ... python:3.11 ... }
]

Un tableau JSON avec les 3 images !


QUAND l'utiliser ?
[OK] Déboguer pourquoi un conteneur ne démarre pas
[OK] Comprendre comment une image est configurée
[OK] Vérifier la sécurité (digest, provenance)
[OK] Extraire des infos pour scripts (taille, architecture)
[OK] Comparer des images similaires
[OK] Documentation (avant de déployer en production)

AU LIEU DE : Deviner la configuration ou chercher la doc


PIÈGES À ÉVITER :

[X] JSON trop long et illisible
   [OK] Utilisez --format pour extraire ce qui vous intéresse

[X] Format de --format complexe
   ASTUCE : Utilisez jq pour parser le JSON
   docker inspect ubuntu | jq '.[] | .Config'

[X] Confondre inspect d'image et de conteneur
   docker inspect ubuntu        -> Image
   docker inspect mon_conteneur -> Conteneur
   (Syntaxe identique mais contenu différent !)


FORMATS UTILES À RETENIR :

# Commande par défaut
--format='{{.Config.Cmd}}'

# Variables d'environnement
--format='{{.Config.Env}}'

# Ports exposés
--format='{{.Config.ExposedPorts}}'

# Taille
--format='{{.Size}}'

# Architecture
--format='{{.Architecture}}'

# Nombre de layers
--format='{{len .RootFS.Layers}}'

# Tout le Config en JSON
--format='{{json .Config}}' | jq


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker history                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche l'historique de construction d'une image (layers)        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Comprendre comment une image a été construite
• Voir la taille de chaque couche (layer)
• Identifier quelle instruction prend le plus de place
• Optimiser vos propres Dockerfiles
• Déboguer des problèmes de taille

COMMENT l'utiliser ?

SYNTAXE :
docker history [OPTIONS] IMAGE

OPTIONS UTILES :
--no-trunc      -> Afficher les commandes complètes (non tronquées)
-q, --quiet     -> Afficher seulement les IDs
--format        -> Format personnalisé


QU'EST-CE QU'UN "LAYER" (COUCHE) ?

CONCEPT CLÉ :
Une image Docker est composée de COUCHES empilées.
Chaque instruction dans un Dockerfile crée une nouvelle couche.

EXEMPLE DE DOCKERFILE :
FROM ubuntu           <- Layer 1
RUN apt-get update    <- Layer 2
RUN apt-get install   <- Layer 3
COPY app.py /app/     <- Layer 4
CMD ["python", "app"] <- Layer 5 (metadata seulement)

ANALOGIE :
Comme un mille-feuille [BIRTHDAY_CAKE]
- Chaque couche de pâte = un layer
- Empilées les unes sur les autres
- Le gâteau final = l'image complète


EXEMPLE 1 : Historique d'Ubuntu
────────────────────────────────

COMMANDE :
docker history ubuntu

RÉSULTAT :
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
3b418d7b466a   4 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      4 weeks ago   /bin/sh -c #(nop) ADD file:3b4e59bca326...      77.8MB    
<missing>      4 weeks ago   /bin/sh -c #(nop)  LABEL org.opencontain...    0B        
<missing>      4 weeks ago   /bin/sh -c #(nop)  LABEL org.opencontain...    0B        
<missing>      4 weeks ago   /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH    0B        
<missing>      4 weeks ago   /bin/sh -c #(nop)  ARG RELEASE                 0B        

EXPLICATION DES COLONNES :

┌──────────────────────────────────────────────────────────────────────────┐
│ IMAGE                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ 3b418d7b466a   -> ID de la couche (layer)                                │
│ <missing>      -> Couche intermédiaire (normale)                         │
│                                                                          │
│ POURQUOI <missing> ?                                                     │
│ Les couches intermédiaires n'ont pas d'IMAGE ID propre.                 │
│ Seule la couche finale (la plus récente) a un ID.                       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CREATED                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ Quand cette couche a été créée                                          │
│ Format relatif : "4 weeks ago"                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CREATED BY                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ La commande Dockerfile qui a créé cette couche                          │
│ Tronquée par défaut (utilisez --no-trunc pour voir en entier)          │
│                                                                          │
│ /bin/sh -c #(nop) CMD ["/bin/bash"]                                     │
│            └────┘                                                        │
│            "nop" = no operation (métadonnée seulement)                  │
│                                                                          │
│ /bin/sh -c apt-get install nginx                                        │
│ └─────────┘                                                              │
│ Commande réelle exécutée                                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ SIZE                                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ Taille ajoutée par cette couche                                         │
│                                                                          │
│ 0B       -> Instruction de métadonnées (CMD, LABEL, ARG, etc.)          │
│ 77.8MB   -> Instruction qui ajoute des fichiers (ADD, COPY, RUN)        │
│                                                                          │
│ TAILLE TOTALE = Somme de toutes les couches                            │
└──────────────────────────────────────────────────────────────────────────┘


LECTURE DE BAS EN HAUT :
L'historique se lit de BAS en HAUT (ordre chronologique de construction).

ORDRE DE CONSTRUCTION :
1. (Ligne du bas)   ARG RELEASE
2. ^                ARG LAUNCHPAD_BUILD_ARCH
3. ^                LABEL org...
4. ^                LABEL org...
5. ^                ADD file (77.8MB)  <- Grosse couche !
6. (Ligne du haut)  CMD ["/bin/bash"]


EXEMPLE 2 : Historique complet avec --no-trunc
───────────────────────────────────────────────

COMMANDE :
docker history nginx --no-trunc

RÉSULTAT :
IMAGE          CREATED       CREATED BY                                                                                           SIZE      COMMENT
5e6f7a8b9c0d   2 weeks ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon off;"]                                                0B        
<missing>      2 weeks ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT                                                               0B        
<missing>      2 weeks ago   /bin/sh -c #(nop)  EXPOSE 80                                                                        0B        
<missing>      2 weeks ago   /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entrypoint.sh"]                                            0B        
<missing>      2 weeks ago   /bin/sh -c #(nop) COPY file:9e3b2b63db9f8fc7de7049a5b0f6f7a5a6b8c9d7e8f6g5h4i3j2k1 in /            1.2kB     
<missing>      2 weeks ago   /bin/sh -c set -x && apt-get update && apt-get install --no-install-recommends nginx && rm -rf ... 63.7MB    
<missing>      2 weeks ago   /bin/sh -c #(nop)  ENV NGINX_VERSION=1.25.2                                                         0B        
<missing>      2 weeks ago   /bin/sh -c #(nop)  LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"            0B        

MAINTENANT on voit TOUT !

ANALYSE :
1. Base Debian + metadata (0B)
2. ENV NGINX_VERSION (0B)
3. Installation de nginx -> 63.7MB  <- GROSSE COUCHE !
4. Copy du script entrypoint -> 1.2kB
5. ENTRYPOINT, EXPOSE, STOPSIGNAL, CMD -> 0B (metadata)

TAILLE TOTALE : ~64 MB


EXEMPLE 3 : Identifier les couches qui prennent de la place
────────────────────────────────────────────────────────────

CONTEXTE : Vous avez construit une image de 2 GB et vous voulez savoir pourquoi

COMMANDE :
docker history mon-app

RÉSULTAT :
IMAGE          CREATED         CREATED BY                    SIZE      
abc123         5 minutes ago   CMD ["python" "app.py"]       0B        
<missing>      5 minutes ago   COPY . /app                   1.5GB     <- PROBLÈME ICI !
<missing>      5 minutes ago   RUN pip install -r req...     450MB     
<missing>      5 minutes ago   COPY requirements.txt         1.2kB     
<missing>      5 minutes ago   WORKDIR /app                  0B        
<missing>      10 minutes ago  FROM python:3.11              920MB     

ANALYSE :
[OK] python:3.11 -> 920 MB (normal)
[OK] pip install -> 450 MB (normal pour beaucoup de dépendances)
[X] COPY . /app -> 1.5 GB (!!! ÉNORME !)

PROBLÈME IDENTIFIÉ :
Vous copiez TOUT le dossier, incluant probablement :
- node_modules/ (500 MB)
- .git/ (300 MB)
- venv/ (400 MB)
- __pycache__/ (100 MB)
- Autres fichiers inutiles (200 MB)

SOLUTION :
Créer un .dockerignore :
node_modules
.git
venv
__pycache__
*.pyc
.env

Après correction :
COPY . /app -> 15 MB  [OK] (100x plus léger !)


EXEMPLE 4 : Comparer deux versions d'image
───────────────────────────────────────────

CONTEXTE : Comparer python:3.11 vs python:3.11-slim

COMMANDE :
docker history python:3.11 | head -n 5
docker history python:3.11-slim | head -n 5

RÉSULTAT python:3.11 :
IMAGE          CREATED       SIZE      
a1b2c3d4       2 weeks ago   0B        
<missing>      2 weeks ago   5.2MB     
<missing>      2 weeks ago   43MB      
<missing>      2 weeks ago   234MB     
<missing>      2 weeks ago   124MB     

RÉSULTAT python:3.11-slim :
IMAGE          CREATED       SIZE      
f6e5d4c3       2 weeks ago   0B        
<missing>      2 weeks ago   3.1MB     
<missing>      2 weeks ago   12MB      
<missing>      2 weeks ago   89MB      

OBSERVATION :
Slim a beaucoup moins de couches et des couches plus petites !


COMMANDE : Compter le nombre de couches
docker history python:3.11 | wc -l
docker history python:3.11-slim | wc -l

RÉSULTAT :
12   <- python:3.11
5    <- python:3.11-slim

Slim = moins de couches = plus simple = plus rapide


EXEMPLE 5 : Format personnalisé
────────────────────────────────

COMMANDE : Seulement les tailles
docker history --format "{{.Size}}" ubuntu

RÉSULTAT :
0B
77.8MB
0B
0B
0B
0B

COMMANDE : Taille + commande
docker history --format "{{.Size}}\t{{.CreatedBy}}" nginx | head -n 5

RÉSULTAT :
0B      /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon off;"]
0B      /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT
0B      /bin/sh -c #(nop)  EXPOSE 80
0B      /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entrypoint.sh"]
63.7MB  /bin/sh -c set -x && apt-get update...


EXEMPLE 6 : Somme des tailles (script)
───────────────────────────────────────

CONTEXTE : Calculer la taille totale réelle

COMMANDE (Linux/Mac) :
docker history python:3.11 --format "{{.Size}}" | \
  grep -v 0B | \
  sed 's/MB//g' | \
  awk '{sum+=$1} END {print sum " MB"}'

RÉSULTAT :
920 MB

Correspond à la taille affichée par docker images !


QUAND l'utiliser ?
[OK] Votre image est trop grosse (optimisation)
[OK] Comprendre comment une image officielle est construite (apprendre)
[OK] Déboguer un build qui échoue (voir où ça plante)
[OK] Comparer des images (laquelle est la plus simple ?)
[OK] Documentation (expliquer comment l'image est faite)

AU LIEU DE : Deviner ou lire le Dockerfile (si disponible)


PIÈGES À ÉVITER :

[X] Lire de haut en bas
   [OK] Lisez de BAS en HAUT (ordre chronologique)

[X] S'inquiéter des <missing>
   C'est NORMAL pour les couches intermédiaires

[X] Ignorer les grosses couches
   Ce sont elles qu'il faut optimiser !

[X] Comparer SIZE avec docker images
   docker images = taille après compression/déduplication
   docker history = taille réelle de chaque couche


ASTUCES D'OPTIMISATION :

Si vous voyez :
RUN apt-get update       -> 50 MB
RUN apt-get install ...  -> 100 MB
RUN apt-get clean        -> -30 MB (mais la couche précédente reste !)

TOTAL : 120 MB

MIEUX :
RUN apt-get update && \
    apt-get install ... && \
    apt-get clean

TOTAL : 70 MB (tout dans la même couche !)


RÉSUMÉ :

COMMANDE              UTILITÉ
────────────────────────────────────────────────────────────────
docker history img    Voir toutes les couches
docker history --no-trunc img   Commandes complètes
docker history img | head -n 5  5 premières couches
docker history -q img   Seulement les IDs


================================================================================
[OK] SECTION 3 : GESTION DES CONTENEURS - OPÉRATIONS DE BASE
================================================================================

Les conteneurs sont les instances en cours d'exécution de vos images.
Cette section couvre toutes les opérations fondamentales sur les conteneurs.

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker run                                                   ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Crée ET démarre un nouveau conteneur à partir d'une image        │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Lancer une application dans Docker
• Tester une image
• Démarrer un service (base de données, serveur web)
• Exécuter du code isolé de votre système
• C'est LA commande la plus importante de Docker !

COMMENT l'utiliser ?

SYNTAXE :
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

WORKFLOW DE docker run :
1. Cherche l'image localement
2. Si absente -> Télécharge depuis Docker Hub
3. Crée un nouveau conteneur
4. Démarre le conteneur
5. Exécute la commande spécifiée (ou CMD par défaut)


OPTIONS ESSENTIELLES (À CONNAÎTRE ABSOLUMENT) :

┌──────────────────────────────────────────────────────────────────────────┐
│ -d, --detach                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ Lance le conteneur en arrière-plan (détaché)                             │
│ QUAND : Services qui tournent en continu (web, DB)                       │
│ EXEMPLE : docker run -d nginx                                            │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ -it                                                                      │
├──────────────────────────────────────────────────────────────────────────┤
│ -i = interactif (garde STDIN ouvert)                                     │
│ -t = pseudo-terminal (affiche un terminal propre)                        │
│ QUAND : Sessions interactives (bash, python shell)                       │
│ EXEMPLE : docker run -it ubuntu bash                                     │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ --name NOM                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ Donne un nom au conteneur (sinon nom aléatoire)                          │
│ QUAND : Toujours ! Pour identifier facilement                            │
│ EXEMPLE : docker run --name myweb nginx                                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ --rm                                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ Supprime automatiquement le conteneur quand il s'arrête                  │
│ QUAND : Tests rapides, commandes one-shot                                │
│ EXEMPLE : docker run --rm ubuntu echo "Hello"                            │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ -p HOST_PORT:CONTAINER_PORT                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ Mappe un port du conteneur vers votre machine                            │
│ QUAND : Applications web, API, bases de données                          │
│ EXEMPLE : docker run -p 8080:80 nginx                                    │
│           Accès : http://localhost:8080                                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ -v HOST_PATH:CONTAINER_PATH                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ Monte un volume (partage dossier hôte <-> conteneur)                       │
│ QUAND : Données persistantes, développement                              │
│ EXEMPLE : docker run -v /data:/app/data postgres                         │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ -e KEY=VALUE                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ Définit une variable d'environnement                                     │
│ QUAND : Configuration (DB password, API keys, etc.)                      │
│ EXEMPLE : docker run -e POSTGRES_PASSWORD=secret postgres                │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 1 : Lancement le plus simple
─────────────────────────────────────

COMMANDE :
docker run ubuntu

QUE SE PASSE-T-IL ?
1. Docker cherche "ubuntu" localement
2. Si absent : Downloading...
3. Crée un conteneur
4. Lance la commande par défaut (/bin/bash)
5. Bash démarre mais n'a rien à faire
6. Le conteneur s'arrête immédiatement

RÉSULTAT :
(Rien ne s'affiche, le conteneur démarre et s'arrête)

VÉRIFICATION :
docker ps -a

CONTAINER ID   IMAGE    COMMAND       CREATED         STATUS
a1b2c3d4e5f6   ubuntu   "/bin/bash"   5 seconds ago   Exited (0)

POURQUOI il s'arrête ?
Sans -it, bash n'a pas de terminal -> Il quitte tout de suite.


EXEMPLE 2 : Session interactive
────────────────────────────────

COMMANDE :
docker run -it ubuntu bash

QUE SE PASSE-T-IL ?
1. Crée le conteneur
2. Lance bash
3. Vous donne accès au terminal

RÉSULTAT :
root@a1b2c3d4e5f6:/#
                  ^
            Vous êtes "dans" Ubuntu !

Vous pouvez maintenant :
root@a1b2c3d4e5f6:/# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

root@a1b2c3d4e5f6:/# whoami
root

root@a1b2c3d4e5f6:/# python3 --version
bash: python3: command not found  (Ubuntu minimal)

POUR SORTIR :
root@a1b2c3d4e5f6:/# exit
OU : Ctrl+D

Le conteneur s'arrête quand vous sortez.


EXEMPLE 3 : Conteneur en arrière-plan (daemon)
───────────────────────────────────────────────

COMMANDE :
docker run -d nginx

QUE SE PASSE-T-IL ?
1. Télécharge nginx (si absent)
2. Crée le conteneur
3. Lance nginx en arrière-plan
4. Retourne l'ID du conteneur

RÉSULTAT :
f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5

ID complet du conteneur (64 caractères)

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   COMMAND                  STATUS         PORTS
f6e5d4c3b2a1   nginx   "nginx -g 'daemon ..."   Up 10 seconds  80/tcp

Nginx tourne en arrière-plan ! [OK]

MAIS : Pas accessible de l'extérieur (pas de port mappé)


EXEMPLE 4 : Serveur web accessible
───────────────────────────────────

COMMANDE :
docker run -d -p 8080:80 nginx

EXPLICATION :
-d           -> En arrière-plan
-p 8080:80   -> Port 8080 de votre PC -> Port 80 du conteneur
nginx        -> Image à utiliser

RÉSULTAT :
a1b2c3d4e5f6...

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   PORTS                  STATUS
a1b2c3d4e5f6   nginx   0.0.0.0:8080->80/tcp   Up 5 seconds
                       └────────────────┘
                       8080 (hôte) -> 80 (conteneur)

ACCÈS :
Ouvrez votre navigateur : http://localhost:8080

Vous verrez : "Welcome to nginx!" [BRAVO]

SCHÉMA :
┌──────────────────────┐
│  Votre Ordinateur    │
│                      │
│  Navigateur          │
│      v               │
│  localhost:8080 ───────┐
└──────────────────────┘ │
                         │
        ┌───────────────-┘
        v
┌──────────────────────┐
│  Conteneur Docker    │
│                      │
│  Nginx port 80       │
│  (serveur web)       │
└──────────────────────┘


EXEMPLE 5 : Avec un nom personnalisé
─────────────────────────────────────

COMMANDE :
docker run -d -p 8080:80 --name mon-site-web nginx

AVANTAGE :
Maintenant vous pouvez référencer par nom au lieu de l'ID !

docker stop mon-site-web       (au lieu de docker stop a1b2c3d4)
docker logs mon-site-web       (au lieu de docker logs a1b2c3d4)
docker rm mon-site-web         (au lieu de docker rm a1b2c3d4)

ERREUR COURANTE :
docker run --name mon-site-web nginx
docker run --name mon-site-web nginx  # 2ème fois

ERREUR :
docker: Error response from daemon: Conflict. The container name "/mon-site-web" is already in use

SOLUTION :
1. Supprimer l'ancien : docker rm mon-site-web
2. Ou utiliser --rm : docker run --rm --name test nginx


EXEMPLE 6 : Commande one-shot avec --rm
────────────────────────────────────────

COMMANDE :
docker run --rm ubuntu echo "Hello Docker!"

QUE SE PASSE-T-IL ?
1. Crée conteneur Ubuntu
2. Exécute : echo "Hello Docker!"
3. Affiche : Hello Docker!
4. Le conteneur s'arrête
5. --rm le supprime automatiquement

RÉSULTAT :
Hello Docker!

VÉRIFICATION :
docker ps -a
(Le conteneur n'apparaît pas, il a été supprimé)

UTILITÉ :
Tests rapides sans polluer avec des conteneurs arrêtés.

AUTRES EXEMPLES :
docker run --rm python:3.11 python -c "print(2+2)"
-> 4

docker run --rm alpine cat /etc/os-release
-> Affiche les infos système Alpine


EXEMPLE 7 : Base de données avec variables d'environnement
────────────────────────────────────────────────────────────

COMMANDE :
docker run -d \
  --name ma-db \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=monsecret \
  -e POSTGRES_USER=admin \
  -e POSTGRES_DB=mabase \
  postgres:15

EXPLICATION :
-d                            -> En arrière-plan
--name ma-db                  -> Nom du conteneur
-p 5432:5432                  -> Mapper le port PostgreSQL
-e POSTGRES_PASSWORD=monsecret -> Définir le mot de passe
-e POSTGRES_USER=admin        -> Définir l'utilisateur
-e POSTGRES_DB=mabase         -> Créer une base "mabase"
postgres:15                   -> Image PostgreSQL version 15

RÉSULTAT :
PostgreSQL démarre avec votre configuration !

CONNEXION (depuis votre PC) :
psql -h localhost -U admin -d mabase
Password: monsecret

Vous êtes connecté à la base ! [BRAVO]


EXEMPLE 8 : Volume pour données persistantes
──────────────────────────────────────────────

PROBLÈME : Si vous supprimez le conteneur postgres, toutes les données sont perdues !

SOLUTION : Utiliser un volume

COMMANDE :
docker run -d \
  --name ma-db \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15

EXPLICATION :
-v postgres-data:/var/lib/postgresql/data
   └────────────┘ └───────────────────────┘
   Nom du volume  Chemin dans le conteneur

MAINTENANT :
1. Les données sont dans le volume "postgres-data"
2. Vous pouvez supprimer le conteneur
3. Les données survivent !

TEST :
# Créer une table
docker exec -it ma-db psql -U postgres -c "CREATE TABLE test(id INT);"

# Supprimer le conteneur
docker stop ma-db
docker rm ma-db

# Recréer avec le même volume
docker run -d --name ma-db -v postgres-data:/var/lib/postgresql/data postgres:15

# La table existe encore !
docker exec -it ma-db psql -U postgres -c "\dt"
-> La table "test" est toujours là ! [OK]


EXEMPLE 9 : Développement avec bind mount
──────────────────────────────────────────

CONTEXTE : Développer une app Flask, voir les changements en temps réel

STRUCTURE :
mon-projet/
  ├── app.py
  └── requirements.txt

COMMANDE (Linux/Mac) :
docker run -it --rm \
  -p 5000:5000 \
  -v $(pwd):/app \
  -w /app \
  python:3.11 \
  bash -c "pip install flask && python app.py"

EXPLICATION :
-v $(pwd):/app     -> Monte le dossier actuel dans /app du conteneur
-w /app            -> Définit /app comme répertoire de travail
bash -c "..."      -> Exécute une série de commandes

RÉSULTAT :
Vous pouvez modifier app.py sur votre PC, les changements sont visibles dans le conteneur !

WINDOWS :
docker run -it --rm -p 5000:5000 -v "%cd%":/app -w /app python:3.11 bash


EXEMPLE 10 : Plusieurs ports
─────────────────────────────

COMMANDE :
docker run -d \
  -p 3000:3000 \
  -p 8080:80 \
  --name multi-ports \
  mon-app

RÉSULTAT :
Port 3000 du conteneur -> Port 3000 de votre PC
Port 80 du conteneur -> Port 8080 de votre PC


EXEMPLE 11 : Override de la commande par défaut
────────────────────────────────────────────────

L'image python:3.11 lance "python3" par défaut.
Mais vous pouvez override !

COMMANDE :
docker run -it python:3.11 bash

Maintenant vous avez bash au lieu de python !

AUTRE EXEMPLE :
docker run ubuntu ls /usr/bin
-> Liste le contenu de /usr/bin au lieu de lancer bash


EXEMPLE 12 : Mode lecture seule (sécurité)
───────────────────────────────────────────

COMMANDE :
docker run --read-only --tmpfs /tmp nginx

RÉSULTAT :
Le système de fichiers est en lecture seule.
Seul /tmp est modifiable (en RAM).

UTILITÉ : Sécurité maximale en production


QUAND utiliser quelles options ?

SCÉNARIO                          COMMANDE
────────────────────────────────────────────────────────────────────
Test rapide                       docker run --rm image
Session interactive               docker run -it image bash
Service permanent (web, DB)       docker run -d -p 8080:80 image
Développement                     docker run -it --rm -v $(pwd):/app
Production                        docker run -d -p 80:80 --name app --restart always


ERREURS COURANTES :

[X] Oublier -d pour un service
   docker run nginx
   -> Bloque votre terminal
   [OK] docker run -d nginx

[X] Oublier -it pour du code interactif
   docker run python:3.11
   -> S'arrête tout de suite
   [OK] docker run -it python:3.11

[X] Mapper sur un port déjà utilisé
   docker run -p 80:80 nginx
   -> ERROR: port is already allocated
   [OK] docker run -p 8080:80 nginx

[X] Oublier --rm pour les tests
   -> Des dizaines de conteneurs arrêtés s'accumulent
   [OK] docker run --rm pour les tests


RÉSUMÉ DES OPTIONS ESSENTIELLES :

OPTION          QUOI                    QUAND
──────────────────────────────────────────────────────────────────
-d              Background              Services (web, DB)
-it             Interactif              Shell, Python REPL
-p 8080:80      Mapper port             Applications web
-v /data:/app   Volume                  Données persistantes
-e KEY=VAL      Variable env            Configuration
--name nom      Nommer                  Toujours !
--rm            Auto-supprimer          Tests rapides
-w /app         Working dir             Développement


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker ps                                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Liste les conteneurs qui tournent (ou tous avec -a)               │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir ce qui tourne actuellement
• Trouver l'ID ou le nom d'un conteneur
• Vérifier les ports mappés
• Surveiller l'état des conteneurs
• Commande quotidienne essentielle !

COMMENT l'utiliser ?

SYNTAXE :
docker ps [OPTIONS]

ALIAS ÉQUIVALENTS :
docker ps   ≡   docker container ls

OPTIONS UTILES :
-a, --all           -> Tous les conteneurs (actifs + arrêtés)
-q, --quiet         -> Seulement les IDs
-l, --latest        -> Le dernier conteneur créé
-n N                -> Les N derniers conteneurs
--filter            -> Filtrer les résultats
--format            -> Format personnalisé
-s, --size          -> Afficher les tailles


EXEMPLE 1 : Liste des conteneurs actifs
────────────────────────────────────────

COMMANDE :
docker ps

RÉSULTAT :
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                  NAMES
f6e5d4c3b2a1   nginx     "nginx -g 'daemon of…"   5 minutes ago   Up 5 minutes   0.0.0.0:8080->80/tcp   mon-site-web
a1b2c3d4e5f6   postgres  "docker-entrypoint.s…"   10 minutes ago  Up 10 minutes  5432/tcp               ma-db

EXPLICATION DES COLONNES :

┌──────────────────────────────────────────────────────────────────────────┐
│ CONTAINER ID                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ f6e5d4c3b2a1  -> Identifiant court (12 premiers caractères)               │
│                                                                          │
│ UTILITÉ : Pour les commandes                                             │
│ docker stop f6e5d4c3b2a1                                                 │
│ OU juste : docker stop f6e5  (début suffit si unique)                    │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ IMAGE                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ nginx  -> Image utilisée pour créer ce conteneur                          │
│                                                                          │
│ Peut inclure le tag : nginx:alpine, python:3.11                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ COMMAND                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ "nginx -g 'daemon of…"  -> Commande en cours (tronquée)                   │
│                                                                          │
│ C'est le processus principal du conteneur                                │
│ Si ce processus s'arrête -> Le conteneur s'arrête                         │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CREATED                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ 5 minutes ago  -> Quand le conteneur a été créé                           │
│                                                                          │
│ Format relatif (humain) : "2 hours ago", "3 days ago"                    │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ STATUS                                                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ Up 5 minutes  -> Actif depuis 5 minutes                                   │
│                                                                          │
│ AUTRES STATUTS POSSIBLES :                                               │
│ - Up 2 hours           -> Actif                                           │
│ - Up 10 seconds (health: starting)  -> Démarrage en cours                 │
│ - Restarting           -> Redémarrage automatique                         │
│ - Paused               -> En pause                                        │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ PORTS                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ 0.0.0.0:8080->80/tcp  -> Mapping de ports                                 │
│ └─────┘ └──┘  └┘                                                         │
│ Toutes  Port  Proto                                                      │
│ IP      hôte  (TCP)                                                      │
│         v                                                                │
│      Port 80 du conteneur                                                │
│                                                                          │
│ SIGNIFICATION :                                                          │
│ http://localhost:8080  ->  Port 80 du conteneur                           │
│                                                                          │
│ Si vide ou juste "5432/tcp" : Port exposé mais pas mappé                 │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ NAMES                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ mon-site-web  -> Nom du conteneur                                         │
│                                                                          │
│ Si --name pas spécifié : Nom aléatoire généré                            │
│ Exemples : "hungry_darwin", "jovial_tesla", "nostalgic_turing"           │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Tous les conteneurs (actifs + arrêtés)
───────────────────────────────────────────────────

COMMANDE :
docker ps -a

RÉSULTAT :
CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS                     NAMES
f6e5d4c3b2a1   nginx     "nginx..."  5 minutes ago    Up 5 minutes               mon-site-web
a1b2c3d4e5f6   ubuntu    "bash"      10 minutes ago   Exited (0) 2 minutes ago   test-ubuntu
b2c3d4e5f6a7   python    "python3"   1 hour ago       Exited (1) 1 hour ago      mon-script

NOUVEAUX STATUTS :

Exited (0) 2 minutes ago
       └┘
    Code de sortie (0 = succès, >0 = erreur)

Exited (1) 1 hour ago
       └┘
    Erreur !


EXEMPLE 3 : Seulement les IDs (pour scripts)
─────────────────────────────────────────────

COMMANDE :
docker ps -q

RÉSULTAT :
f6e5d4c3b2a1
a1b2c3d4e5f6

UTILITÉ : Scripts automatisés

EXEMPLE PRATIQUE : Arrêter tous les conteneurs
docker stop $(docker ps -q)

EXEMPLE : Supprimer tous les conteneurs arrêtés
docker rm $(docker ps -aq --filter "status=exited")


EXEMPLE 4 : Le dernier conteneur créé
──────────────────────────────────────

COMMANDE :
docker ps -l

RÉSULTAT :
CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS
f6e5d4c3b2a1   nginx     "nginx..."  5 seconds ago    Up 4 seconds

Le dernier lancé, même s'il est arrêté !

UTILITÉ : Voir rapidement ce que vous venez de lancer


EXEMPLE 5 : Les N derniers conteneurs
──────────────────────────────────────

COMMANDE :
docker ps -n 3

RÉSULTAT :
Les 3 derniers conteneurs créés (actifs ou non)


EXEMPLE 6 : Filtrer par statut
───────────────────────────────

COMMANDE : Seulement les conteneurs arrêtés
docker ps --filter "status=exited"

COMMANDE : Seulement ceux qui tournent
docker ps --filter "status=running"

COMMANDE : Ceux qui redémarrent
docker ps --filter "status=restarting"

COMMANDE : Ceux en pause
docker ps --filter "status=paused"


EXEMPLE 7 : Filtrer par nom
────────────────────────────

COMMANDE :
docker ps --filter "name=web"

RÉSULTAT :
Tous les conteneurs dont le nom contient "web"
- mon-site-web [OK]
- web-server [OK]
- backend-api [X]


EXEMPLE 8 : Filtrer par image
──────────────────────────────

COMMANDE :
docker ps --filter "ancestor=nginx"

RÉSULTAT :
Tous les conteneurs créés depuis l'image nginx


EXEMPLE 9 : Avec les tailles
─────────────────────────────

COMMANDE :
docker ps -s

RÉSULTAT :
CONTAINER ID   IMAGE   ...   SIZE
f6e5d4c3b2a1   nginx   ...   1.2kB (virtual 142MB)
                              └───┘         └──────┘
                              Taille        Taille de l'image
                              modifiée      + modifications

EXPLICATION :
- virtual 142MB : Taille de l'image de base
- 1.2kB : Données ajoutées/modifiées dans ce conteneur

TAILLE RÉELLE sur disque ≈ 142MB (l'image) + 1.2kB (changements)


EXEMPLE 10 : Format personnalisé
─────────────────────────────────

COMMANDE : Seulement les infos essentielles
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

RÉSULTAT :
NAMES           STATUS         PORTS
mon-site-web    Up 5 minutes   0.0.0.0:8080->80/tcp
ma-db           Up 10 minutes  5432/tcp

Plus lisible !

AUTRE FORMAT UTILE :
docker ps --format "{{.Names}} ({{.Image}})"

RÉSULTAT :
mon-site-web (nginx)
ma-db (postgres)


QUAND l'utiliser ?
[OK] Tous les jours (plusieurs fois par jour !)
[OK] Pour vérifier qu'un conteneur tourne
[OK] Pour trouver l'ID d'un conteneur
[OK] Pour voir les ports mappés
[OK] Avant d'arrêter/supprimer des conteneurs

AU LIEU DE : Ouvrir Docker Desktop


PIÈGES À ÉVITER :

[X] Oublier -a et ne pas voir les conteneurs arrêtés
   docker ps  -> Conteneurs actifs seulement
   [OK] docker ps -a  -> Tous

PIÈGES À ÉVITER (suite) :

[X] Confondre CREATED et STATUS
   CREATED : Quand le conteneur a été créé
   STATUS : Depuis combien de temps il tourne
   
   Exemple :
   CREATED: 10 minutes ago    -> Créé il y a 10 minutes
   STATUS: Up 2 minutes       -> Mais actif seulement depuis 2 minutes
                              -> Donc arrêté pendant 8 minutes entre les deux

[X] Ne pas comprendre les codes de sortie
   Exited (0)   -> Succès [OK]
   Exited (1)   -> Erreur générique [X]
   Exited (2)   -> Mauvaise utilisation de commande [X]
   Exited (137) -> Tué par SIGKILL (kill -9) [X]
   Exited (143) -> Tué par SIGTERM (arrêt normal)


TABLEAU RÉCAPITULATIF docker ps :

COMMANDE                              RÉSULTAT
──────────────────────────────────────────────────────────────────────
docker ps                             Conteneurs actifs
docker ps -a                          Tous (actifs + arrêtés)
docker ps -l                          Dernier créé
docker ps -n 5                        5 derniers créés
docker ps -q                          IDs seulement
docker ps -s                          Avec tailles
docker ps --filter "status=exited"    Seulement arrêtés
docker ps --filter "name=web"         Nom contient "web"
docker ps --filter "ancestor=nginx"   Créés depuis nginx
docker ps --format "{{.Names}}"       Format personnalisé


EXEMPLES PRATIQUES AVANCÉS :

EXEMPLE 11 : Compter les conteneurs actifs
──────────────────────────────────────────

COMMANDE :
docker ps -q | wc -l

RÉSULTAT :
5

Vous avez 5 conteneurs actifs.


EXEMPLE 12 : Trouver quel conteneur utilise un port
────────────────────────────────────────────────────

COMMANDE :
docker ps --filter "publish=8080"

RÉSULTAT :
CONTAINER ID   IMAGE   PORTS                  NAMES
f6e5d4c3b2a1   nginx   0.0.0.0:8080->80/tcp   mon-site-web

C'est "mon-site-web" qui utilise le port 8080 !


EXEMPLE 13 : Exporter la liste en CSV (pour Excel)
───────────────────────────────────────────────────

COMMANDE :
docker ps --format "{{.Names}},{{.Image}},{{.Status}},{{.Ports}}" > conteneurs.csv

Maintenant vous pouvez ouvrir conteneurs.csv dans Excel !


EXEMPLE 14 : Surveiller en temps réel (watch)
──────────────────────────────────────────────

COMMANDE (Linux/Mac) :
watch -n 2 'docker ps'

RÉSULTAT :
La liste se rafraîchit toutes les 2 secondes.
Parfait pour surveiller des démarrages/arrêts !

Pour sortir : Ctrl+C


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker start                                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Démarre un conteneur qui est arrêté                               │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Redémarrer un conteneur sans le recréer
• Plus rapide que docker run (pas de recréation)
• Garde toutes les données et configuration
• Utilisation quotidienne pour services réguliers

COMMENT l'utiliser ?

SYNTAXE :
docker start [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS UTILES :
-a, --attach        -> Attacher STDOUT/STDERR (voir les logs)
-i, --interactive   -> Attacher STDIN (mode interactif)


DIFFÉRENCE FONDAMENTALE : docker run vs docker start

┌──────────────────────────────────────────────────────────────────────────┐
│ docker run                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • CRÉE un NOUVEAU conteneur                                              │
│ • À partir d'une IMAGE                                                   │
│ • Le démarre immédiatement                                               │
│ • Chaque run = nouveau conteneur                                         │
│                                                                          │
│ ANALOGIE : Créer un nouveau document Word                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker start                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ • Démarre un conteneur EXISTANT                                          │
│ • Qui était arrêté                                                       │
│ • Garde tout son historique/données                                      │
│ • Un conteneur = peut être start/stop plusieurs fois                     │
│                                                                          │
│ ANALOGIE : Rouvrir un document Word existant                             │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 1 : Démarrage simple
────────────────────────────

CONTEXTE : Vous avez arrêté votre serveur web nginx

COMMANDE :
docker ps -a

RÉSULTAT :
CONTAINER ID   IMAGE   STATUS                    NAMES
f6e5d4c3b2a1   nginx   Exited (0) 5 minutes ago  mon-site-web

Le conteneur existe mais est arrêté.

COMMANDE :
docker start mon-site-web

RÉSULTAT :
mon-site-web

Le conteneur redémarre !

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   STATUS        NAMES
f6e5d4c3b2a1   nginx   Up 3 seconds  mon-site-web

[OK] Il tourne à nouveau !

ACCÈS :
http://localhost:8080 fonctionne à nouveau !


EXEMPLE 2 : Démarrer par ID
────────────────────────────

COMMANDE :
docker start f6e5d4c3b2a1

OU (ID court suffit) :
docker start f6e5

Même résultat !


EXEMPLE 3 : Démarrer plusieurs conteneurs
──────────────────────────────────────────

COMMANDE :
docker start mon-site-web ma-db mon-cache

RÉSULTAT :
mon-site-web
ma-db
mon-cache

Les 3 démarrent en même temps !


EXEMPLE 4 : Démarrer et attacher les logs (-a)
───────────────────────────────────────────────

COMMANDE :
docker start -a mon-site-web

RÉSULTAT :
Le conteneur démarre ET vous voyez les logs en direct :

/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/15 10:30:25 [notice] 1#1: start worker process 29
[...]

Utile pour déboguer !

Pour détacher : Ctrl+C (mais le conteneur continue de tourner)


EXEMPLE 5 : Mode interactif (-ai)
──────────────────────────────────

CONTEXTE : Vous aviez lancé un conteneur Ubuntu interactif, vous l'avez quitté

COMMANDE :
docker start -ai test-ubuntu

RÉSULTAT :
root@a1b2c3d4e5f6:/#

Vous êtes de retour dans le bash !

IMPORTANT : Sans -ai, le conteneur démarrerait puis s'arrêterait immédiatement
(car bash sans terminal = rien à faire = arrêt)


EXEMPLE 6 : Tous les conteneurs arrêtés
────────────────────────────────────────

COMMANDE :
docker start $(docker ps -aq --filter "status=exited")

EXPLICATION :
docker ps -aq --filter "status=exited"  -> Liste IDs des conteneurs arrêtés
docker start $(...)                      -> Démarre tous ces IDs

[ATTENTION] ATTENTION : Démarre TOUT ce qui est arrêté !
Vérifiez avant :
docker ps -a --filter "status=exited"


QUAND l'utiliser ?
[OK] Redémarrer un service après maintenance
[OK] Redémarrer après un reboot du PC
[OK] Après avoir arrêté volontairement (docker stop)
[OK] Workflow quotidien : start le matin, stop le soir

AU LIEU DE : docker run (qui créerait un nouveau conteneur)


PIÈGES À ÉVITER :

[X] Utiliser start sur un conteneur qui n'existe pas
   docker start mon-app
   ERREUR : Error: No such container: mon-app
   
   SOLUTION : Vérifiez d'abord : docker ps -a

[X] Oublier -ai pour conteneurs interactifs
   docker start test-ubuntu  -> Démarre puis s'arrête
   [OK] docker start -ai test-ubuntu

[X] Confondre start et run
   Conteneur existe ? -> start
   Première fois ? -> run

[X] Start d'un conteneur déjà actif
   docker start mon-site-web
   RÉSULTAT : Rien ne se passe (déjà actif)
   Pas d'erreur, mais inutile


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker stop                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Arrête un conteneur en cours d'exécution proprement              │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Arrêter temporairement un service
• Libérer des ressources (CPU, RAM)
• Maintenance ou modifications
• Arrêt propre avant suppression
• Arrêt quotidien (économie ressources)

COMMENT l'utiliser ?

SYNTAXE :
docker stop [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS UTILES :
-t, --time SECONDS    -> Temps d'attente avant force kill (défaut: 10s)


COMMENT FONCTIONNE docker stop ?

PROCESSUS D'ARRÊT PROPRE :
1. Docker envoie SIGTERM au conteneur
   └─ Signal "Termine-toi proprement s'il te plaît"
2. Le conteneur a 10 secondes pour :
   - Sauvegarder les données
   - Fermer les connexions
   - Nettoyer les ressources
3. Si après 10 secondes il tourne encore :
   └─ Docker envoie SIGKILL (force l'arrêt)

ANALOGIE :
Fermer un programme avec "Quitter" vs "Forcer à quitter"


EXEMPLE 1 : Arrêt simple
────────────────────────

COMMANDE :
docker stop mon-site-web

RÉSULTAT :
mon-site-web

Le conteneur s'arrête proprement.

DURÉE : 0-10 secondes (selon le temps que met le conteneur à se terminer)

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   STATUS   NAMES
(vide, le conteneur n'apparaît plus)

docker ps -a

CONTAINER ID   IMAGE   STATUS                     NAMES
f6e5d4c3b2a1   nginx   Exited (0) 5 seconds ago   mon-site-web
                       └──────────────────────┘
                       Arrêté proprement (code 0)


EXEMPLE 2 : Arrêter par ID
───────────────────────────

COMMANDE :
docker stop f6e5d4c3b2a1

OU :
docker stop f6e5

Les deux fonctionnent !


EXEMPLE 3 : Arrêter plusieurs conteneurs
─────────────────────────────────────────

COMMANDE :
docker stop mon-site-web ma-db mon-cache

RÉSULTAT :
mon-site-web
ma-db
mon-cache

Les 3 s'arrêtent (séquentiellement).

DURÉE : Jusqu'à 30 secondes (10s × 3)


EXEMPLE 4 : Arrêt rapide avec timeout personnalisé
───────────────────────────────────────────────────

COMMANDE :
docker stop -t 2 mon-site-web

EXPLICATION :
-t 2  -> Attendre seulement 2 secondes avant force kill

QUAND l'utiliser ?
[OK] Tests rapides (pas besoin d'attendre 10s)
[OK] Conteneurs qui ne sauvegardent rien
[X] Bases de données (risque de corruption !)


EXEMPLE 5 : Arrêt forcé immédiat (DANGEREUX)
─────────────────────────────────────────────

COMMANDE :
docker stop -t 0 mon-site-web

OU équivalent :
docker kill mon-site-web

RÉSULTAT :
Arrêt IMMÉDIAT, sans attendre.

[ATTENTION] RISQUES :
- Données non sauvegardées perdues
- Transactions en cours annulées
- Fichiers corrompus
- Connexions coupées brutalement

QUAND l'utiliser ?
[OK] Conteneur bloqué qui ne répond plus
[OK] Tests où les données n'ont pas d'importance
[X] JAMAIS en production avec base de données


EXEMPLE 6 : Arrêter tous les conteneurs actifs
───────────────────────────────────────────────

COMMANDE :
docker stop $(docker ps -q)

EXPLICATION :
docker ps -q  -> Liste tous les IDs des conteneurs actifs
docker stop $(...)  -> Arrête tous ces IDs

RÉSULTAT :
Tous les conteneurs s'arrêtent !

UTILITÉ :
- Avant un reboot du PC
- Nettoyage complet
- Fin de journée de travail

[ATTENTION] ATTENTION : Vraiment TOUS les conteneurs !
Vérifiez avant :
docker ps


EXEMPLE 7 : Arrêt avec vérification du temps
─────────────────────────────────────────────

CONTEXTE : Une base de données met longtemps à s'arrêter proprement

COMMANDE :
time docker stop ma-db

RÉSULTAT :
ma-db

real    0m8.234s
user    0m0.012s
sys     0m0.008s

La base a mis 8.2 secondes pour s'arrêter proprement.

Si ça prend souvent ~10s -> La base attend le timeout
-> Peut-être un problème de configuration !


EXEMPLE 8 : Arrêt d'un conteneur qui ne répond plus
────────────────────────────────────────────────────

SITUATION : Un conteneur est bloqué

COMMANDE :
docker stop mon-app
(Vous attendez... ça prend 10 secondes exactes)

EXPLICATION :
Le conteneur ne répond pas à SIGTERM.
Docker attend 10s puis envoie SIGKILL.

SOLUTION IMMÉDIATE :
docker kill mon-app
-> Arrêt immédiat (mais brutal)

SOLUTION À LONG TERME :
Déboguer pourquoi le conteneur ne répond plus :
docker logs mon-app


QUAND l'utiliser ?
[OK] Tous les jours (start/stop selon besoins)
[OK] Avant modifications d'un conteneur
[OK] Libérer des ressources temporairement
[OK] Maintenance (backup, mise à jour)
[OK] Fin de journée (économie ressources)

AU LIEU DE : docker kill (sauf urgence)


PIÈGES À ÉVITER :

[X] Stop d'un conteneur déjà arrêté
   docker stop mon-app
   ERREUR : Error response from daemon: container already stopped
   
   VÉRIFIEZ : docker ps (pas docker ps -a)

[X] Utiliser stop au lieu de kill pour urgence
   Conteneur bloqué depuis 5 minutes
   [X] docker stop (attend encore 10s)
   [OK] docker kill (immédiat)

[X] Stop sans sauvegarder les données importantes
   Base de données avec transactions en cours
   -> Attendez la fin des transactions avant stop
   -> Ou utilisez un shutdown propre de la DB

[X] Timeout trop court pour bases de données
   docker stop -t 2 postgres
   -> PostgreSQL peut avoir besoin de 5-10s
   [OK] Utilisez le timeout par défaut (10s)


DIFFÉRENCE : docker stop vs docker kill

┌──────────────────────────────────────────────────────────────────────────┐
│ docker stop                                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Envoie SIGTERM (arrêt propre)                                          │
│ • Attend 10 secondes                                                     │
│ • Puis SIGKILL si nécessaire                                             │
│ • Les données sont sauvegardées                                          │
│ • EXIT CODE : 0 (succès)                                                 │
│                                                                          │
│ ANALOGIE : Fermer proprement une application                            │
│ QUAND : Toujours (sauf urgence)                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker kill                                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Envoie SIGKILL (force)                                                 │
│ • Arrêt IMMÉDIAT                                                         │
│ • Aucune chance de sauvegarder                                           │
│ • Risque de corruption                                                   │
│ • EXIT CODE : 137 (killed)                                               │
│                                                                          │
│ ANALOGIE : "Forcer à quitter" ou tuer le processus                      │
│ QUAND : Seulement si bloqué ou urgence                                   │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker restart                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Redémarre un conteneur (stop + start en une commande)            │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Appliquer des changements de configuration
• Résoudre des problèmes temporaires
• Après modification de fichiers montés
• Nettoyer la mémoire/ressources
• Plus rapide que stop puis start séparément

COMMENT l'utiliser ?

SYNTAXE :
docker restart [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS UTILES :
-t, --time SECONDS    -> Temps d'attente avant force kill (défaut: 10s)


ÉQUIVALENT :
docker restart mon-app   ≈   docker stop mon-app && docker start mon-app


EXEMPLE 1 : Redémarrage simple
───────────────────────────────

COMMANDE :
docker restart mon-site-web

RÉSULTAT :
mon-site-web

PROCESSUS :
1. Arrête le conteneur (SIGTERM)
2. Attend jusqu'à 10 secondes
3. Force l'arrêt si nécessaire (SIGKILL)
4. Démarre le conteneur
5. Le conteneur repart à neuf

DURÉE : 1-12 secondes typiquement


EXEMPLE 2 : Redémarrage rapide
───────────────────────────────

COMMANDE :
docker restart -t 2 mon-site-web

Attend seulement 2 secondes avant force kill.

UTILITÉ : Tests rapides où les données n'importent pas


EXEMPLE 3 : Redémarrer plusieurs conteneurs
────────────────────────────────────────────

COMMANDE :
docker restart mon-site-web ma-db mon-cache

RÉSULTAT :
Les 3 redémarrent séquentiellement.


EXEMPLE 4 : Redémarrer tous les conteneurs actifs
──────────────────────────────────────────────────

COMMANDE :
docker restart $(docker ps -q)

[ATTENTION] ATTENTION : Redémarre vraiment TOUS les conteneurs !

UTILITÉ :
- Après changement de configuration réseau Docker
- Après mise à jour Docker
- Nettoyage général


EXEMPLE 5 : Redémarrage automatique lors d'un crash
────────────────────────────────────────────────────

CONTEXTE : Configurer un conteneur pour redémarrer automatiquement

COMMANDE (lors du run) :
docker run -d --restart=always --name mon-app nginx

OPTIONS --restart :
--restart=no            -> Jamais (défaut)
--restart=on-failure    -> Seulement si erreur (exit code ≠ 0)
--restart=on-failure:5  -> Max 5 tentatives
--restart=always        -> Toujours (même après reboot PC)
--restart=unless-stopped -> Toujours, sauf si stop manuel

EXEMPLE CONCRET :
docker run -d --restart=always --name ma-db postgres

Maintenant :
- Si le conteneur crash -> Redémarre auto
- Si vous rebootez le PC -> Redémarre auto
- Si vous faites docker stop -> Ne redémarre PAS (respect du stop manuel)


EXEMPLE 6 : Modifier la politique de restart
─────────────────────────────────────────────

COMMANDE :
docker update --restart=always mon-app

Maintenant mon-app redémarrera automatiquement.


QUAND l'utiliser ?
[OK] Après modification de fichiers de config
[OK] Application qui fuite de la mémoire (redémarrage nettoie)
[OK] Problème temporaire (réseau, etc.)
[OK] Après changement d'environnement
[OK] Plus rapide que stop + start

AU LIEU DE : docker stop && docker start


PIÈGES À ÉVITER :

[X] Redémarrer au lieu de déboguer
   App qui crash en boucle
   [X] docker restart (cache le problème)
   [OK] docker logs (comprendre pourquoi)

[X] Restart d'une base de données sans backup
   [ATTENTION] Risque de perte de données si problème
   [OK] Backup avant restart

[X] Oublier que restart est asynchrone
   docker restart mon-app
   curl http://localhost:8080  # ERREUR si trop rapide
   
   [OK] Attendre quelques secondes
   sleep 5 && curl http://localhost:8080


DIFFÉRENCE : restart vs stop+start vs kill+start

┌──────────────────────────────────────────────────────────────────────────┐
│ docker restart                                                           │
├──────────────────────────────────────────────────────────────────────────┤
│ • Stop propre (SIGTERM) puis start                                       │
│ • Une seule commande                                                     │
│ • Durée : 1-12s typiquement                                              │
│ QUAND : Usage normal                                                     │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker stop + docker start                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • Deux commandes séparées                                                │
│ • Plus de contrôle (pause entre stop et start)                           │
│ • Utile si vous voulez faire quelque chose entre les deux                │
│ QUAND : Besoin de modifications entre stop et start                      │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker kill + docker start                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • Arrêt brutal (SIGKILL) puis start                                      │
│ • Risque de corruption                                                   │
│ • Plus rapide (immédiat)                                                 │
│ QUAND : Conteneur bloqué, urgence                                        │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker pause / docker unpause                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Met en pause / reprend un conteneur (sans l'arrêter)              │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Geler temporairement un conteneur
• Libérer CPU sans perdre l'état
• Tests/débogage (figer l'état)
• Économiser des ressources temporairement
• Moins radical que stop (état préservé en mémoire)

COMMENT ça marche ?

DIFFÉRENCE AVEC STOP :

┌──────────────────────────────────────────────────────────────────────────┐
│ docker stop                                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Arrête le conteneur complètement                                       │
│ • Processus terminés                                                     │
│ • Mémoire libérée                                                        │
│ • Connexions réseau fermées                                              │
│ • Redémarrage = repartir de zéro                                         │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker pause                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ • "Gèle" le conteneur sur place                                          │
│ • Processus suspendus (pas terminés)                                     │
│ • Mémoire conservée                                                      │
│ • Connexions réseau maintenues                                           │
│ • Unpause = reprend exactement où on était                               │
│                                                                          │
│ ANALOGIE : Mettre sur pause une vidéo                                   │
└──────────────────────────────────────────────────────────────────────────┘


SYNTAXE :
docker pause CONTAINER [CONTAINER...]
docker unpause CONTAINER [CONTAINER...]


EXEMPLE 1 : Pause simple
────────────────────────

COMMANDE :
docker pause mon-site-web

RÉSULTAT :
mon-site-web

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   STATUS                  NAMES
f6e5d4c3b2a1   nginx   Up 10 minutes (Paused)  mon-site-web
                       └──────────────────────┘
                       Le conteneur est gelé !

ACCÈS :
http://localhost:8080
-> La page ne charge pas (conteneur gelé)


EXEMPLE 2 : Reprendre (unpause)
────────────────────────────────

COMMANDE :
docker unpause mon-site-web

RÉSULTAT :
mon-site-web

VÉRIFICATION :
docker ps

CONTAINER ID   IMAGE   STATUS         NAMES
f6e5d4c3b2a1   nginx   Up 11 minutes  mon-site-web
                       └────────────┘
                       Plus de "(Paused)" !

ACCÈS :
http://localhost:8080
-> La page charge à nouveau ! [OK]


EXEMPLE 3 : Pause de plusieurs conteneurs
──────────────────────────────────────────

COMMANDE :
docker pause app1 app2 app3

Les 3 sont gelés simultanément.


EXEMPLE 4 : Pause de tous les conteneurs actifs
────────────────────────────────────────────────

COMMANDE :
docker pause $(docker ps -q)

Tous les conteneurs sont gelés !

UTILITÉ :
- Avant une sauvegarde système
- Tests de charge (geler certains services)
- Économiser CPU temporairement


EXEMPLE 5 : Mesurer l'impact de pause
──────────────────────────────────────

AVANT PAUSE :
docker stats --no-stream mon-app

CONTAINER   CPU %   MEM USAGE / LIMIT
mon-app     25.5%   512 MB / 2 GB

APRÈS PAUSE :
docker pause mon-app
docker stats --no-stream mon-app

CONTAINER   CPU %   MEM USAGE / LIMIT
mon-app     0.00%   512 MB / 2 GB
            └───┘
        CPU libéré !

La mémoire reste utilisée, mais CPU = 0% !


QUAND l'utiliser ?
[OK] Tests/débogage (figer l'état momentanément)
[OK] Libérer CPU temporairement sans perdre l'état
[OK] Suspendre des traitements longs
[X] RAREMENT en production (stop est préférable)

AU LIEU DE : docker stop (si vous voulez garder l'état exact)


PIÈGES À ÉVITER :

[X] Pause prolongée (heures/jours)
   Mémoire reste utilisée
   Connexions réseau peuvent expirer
   [OK] Utilisez docker stop pour arrêts longs

[X] Pause d'une base de données avec transactions actives
   Les transactions restent suspendues
   Les clients attendent indéfiniment
   [OK] Stop proprement les transactions avant pause

[X] Oublier d'unpause
   docker pause mon-app
   [... vous oubliez pendant 2 jours ...]
   "Pourquoi mon app ne marche plus ??"
   
   VÉRIFICATION : docker ps (cherchez "(Paused)")

[X] Pause via docker pause au lieu de signal applicatif
   Certaines apps ont leur propre mécanisme de pause
   [OK] Utilisez-le en priorité


TABLEAU RÉCAPITULATIF : États des conteneurs

COMMANDE              ÉTAT RÉSULTANT        CPU    MÉMOIRE   CONNEXIONS
──────────────────────────────────────────────────────────────────────────
docker run            Running               [OK]      [OK]         [OK]
docker stop           Exited (0)            [X]      [X]         [X]
docker kill           Exited (137)          [X]      [X]         [X]
docker pause          Paused                [X]      [OK]         [OK] (gelées)
docker start          Running (redémarré)   [OK]      [OK]         [OK]
docker restart        Running (réinitialisé) [OK]     [OK]         [OK]
docker unpause        Running (reprise)     [OK]      [OK]         [OK]


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker rm                                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Supprime définitivement un ou plusieurs conteneurs               │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Nettoyer les conteneurs arrêtés
• Libérer de l'espace disque
• Supprimer des tests/conteneurs temporaires
• Maintenance régulière
• Avant de recréer un conteneur avec le même nom

[ATTENTION] ATTENTION : Suppression DÉFINITIVE !
Les données dans le conteneur sont PERDUES (sauf volumes)

COMMENT l'utiliser ?

SYNTAXE :
docker rm [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS UTILES :
-f, --force     -> Force la suppression (même si actif)
-v, --volumes   -> Supprime aussi les volumes anonymes associés


RÈGLE IMPORTANTE :
Vous ne pouvez PAS supprimer un conteneur actif (sauf avec -f).


EXEMPLE 1 : Suppression simple
───────────────────────────────

CONTEXTE : Vous avez un conteneur arrêté

COMMANDE :
docker ps -a

CONTAINER ID   IMAGE    STATUS                     NAMES
a1b2c3d4e5f6   ubuntu   Exited (0) 5 minutes ago   test-ubuntu

COMMANDE :
docker rm test-ubuntu

RÉSULTAT :
test-ubuntu

Le conteneur est supprimé !

VÉRIFICATION :
docker ps -a
(Le conteneur n'apparaît plus)


EXEMPLE 2 : Erreur - Conteneur actif
─────────────────────────────────────

COMMANDE :
docker rm mon-site-web

ERREUR :
Error response from daemon: You cannot remove a running container a1b2c3d4e5f6. 
Stop the container before attempting removal or force remove

SOLUTION 1 : Arrêter puis supprimer
docker stop mon-site-web
docker rm mon-site-web

SOLUTION 2 : Forcer ([ATTENTION] brutal)
docker rm -f mon-site-web


EXEMPLE 3 : Force suppression (-f)
───────────────────────────────────

COMMANDE :
docker rm -f mon-site-web

RÉSULTAT :
mon-site-web

QUE SE PASSE-T-IL ?
1. Docker envoie SIGKILL (arrêt brutal)
2. Le conteneur s'arrête immédiatement
3. Il est supprimé

[ATTENTION] RISQUES :
- Données non sauvegardées perdues
- Connexions coupées brutalement
- Pas de nettoyage propre

QUAND l'utiliser ?
[OK] Tests/développement
[OK] Conteneurs de test
[X] Production avec données importantes


EXEMPLE 4 : Supprimer plusieurs conteneurs
───────────────────────────────────────────

COMMANDE :
docker rm test1 test2 test3

RÉSULTAT :
test1
test2
test3

Les 3 sont supprimés !


EXEMPLE 5 : Supprimer tous les conteneurs arrêtés
──────────────────────────────────────────────────

COMMANDE :
docker rm $(docker ps -aq --filter "status=exited")

EXPLICATION :
docker ps -aq --filter "status=exited"  -> Liste IDs des conteneurs arrêtés
docker rm $(...)                         -> Supprime tous ces IDs

RÉSULTAT :
Tous les conteneurs avec statut "Exited" sont supprimés.

VÉRIFICATION :
docker ps -a --filter "status=exited"
(Vide maintenant)


EXEMPLE 6 : Supprimer TOUS les conteneurs ([ATTENTION] DANGEREUX)
─────────────────────────────────────────────────────────

COMMANDE :
docker rm -f $(docker ps -aq)

[ATTENTION][ATTENTION][ATTENTION] ATTENTION [ATTENTION][ATTENTION][ATTENTION]
Supprime TOUS les conteneurs (actifs ET arrêtés) !

QUAND l'utiliser ?
[OK] Nettoyage complet pour repartir de zéro
[OK] Environnement de test
[X] JAMAIS en production sans backup


EXEMPLE 7 : Supprimer avec les volumes anonymes
────────────────────────────────────────────────

CONTEXTE : Certains conteneurs créent des volumes anonymes

COMMANDE :
docker rm -v mon-app

RÉSULTAT :
Le conteneur ET ses volumes anonymes sont supprimés.

DIFFÉRENCE :
docker rm mon-app      -> Conteneur supprimé, volumes restent
docker rm -v mon-app   -> Conteneur + volumes anonymes supprimés

VOLUMES NOMMÉS :
Les volumes nommés ne sont JAMAIS supprimés automatiquement.
docker rm -v mon-db  -> Volume "postgres-data" reste intact [OK]


EXEMPLE 8 : Supprimer en cascade (conteneur + image)
─────────────────────────────────────────────────────

WORKFLOW COMPLET :
# 1. Arrêter le conteneur
docker stop mon-app

# 2. Supprimer le conteneur
docker rm mon-app

# 3. Supprimer l'image (optionnel)
docker rmi mon-app:latest

OU en une ligne :
docker stop mon-app && docker rm mon-app && docker rmi mon-app:latest


EXEMPLE 9 : Supprimer avec confirmation
────────────────────────────────────────

SCRIPT SÉCURISÉ :
#!/bin/bash
echo "Conteneurs à supprimer :"
docker ps -a --filter "status=exited"
read -p "Confirmer ? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    docker rm $(docker ps -aq --filter "status=exited")
    echo "[OK] Suppression effectuée"
else
    echo "[X] Annulé"
fi


QUAND l'utiliser ?
[OK] Quotidiennement (nettoyer conteneurs de test)
[OK] Après avoir terminé avec un conteneur
[OK] Avant de recréer avec le même nom
[OK] Libérer de l'espace disque

AU LIEU DE : Laisser des dizaines de conteneurs arrêtés


PIÈGES À ÉVITER :

[X] Supprimer sans sauvegarder les données
   [ATTENTION] Les données dans le conteneur sont PERDUES
   [OK] Utilisez des volumes pour données importantes

[X] rm au lieu de stop
   docker rm mon-app  -> ERREUR si actif
   [OK] docker stop mon-app PUIS docker rm mon-app

[X] Confondre docker rm (conteneur) et docker rmi (image)
   docker rm nginx     -> Supprime le CONTENEUR nginx
   docker rmi nginx    -> Supprime l'IMAGE nginx

[X] Oublier -v et laisser des volumes orphelins
   Après des mois : des dizaines de Go de volumes inutiles
   [OK] Utilisez : docker volume prune


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker container prune                                       ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Supprime automatiquement tous les conteneurs arrêtés             │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Nettoyage rapide et sûr
• Plus simple que docker rm manuel
• Libère de l'espace en une commande
• Maintenance mensuelle recommandée

COMMENT l'utiliser ?

SYNTAXE :
docker container prune [OPTIONS]

OPTIONS UTILES :
-f, --force     -> Sans confirmation
--filter        -> Filtrer par critère


EXEMPLE 1 : Nettoyage basique
──────────────────────────────

COMMANDE :
docker container prune

RÉSULTAT :
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y

Deleted Containers:
a1b2c3d4e5f6f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5
f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5
[...]

Total reclaimed space: 125.4MB

EXPLICATION :
- Liste tous les conteneurs supprimés
- Affiche l'espace libéré


EXEMPLE 2 : Sans confirmation (scripts)
────────────────────────────────────────

COMMANDE :
docker container prune -f

Supprime directement, sans demander confirmation.

UTILITÉ : Scripts automatisés, cron jobs


EXEMPLE 3 : Filtrer par ancienneté
───────────────────────────────────

COMMANDE :
docker container prune --filter "until=24h"

RÉSULTAT :
Supprime les conteneurs arrêtés depuis plus de 24 heures.

AUTRES EXEMPLES :
--filter "until=72h"        -> Plus de 3 jours
--filter "until=168h"       -> Plus d'une semaine
--filter "until=2024-01-01" -> Avant le 1er janvier 2024


EXEMPLE 4 : Voir ce qui sera supprimé SANS supprimer
─────────────────────────────────────────────────────

COMMANDE :
docker ps -a --filter "status=exited"

RÉSULTAT :
Liste des conteneurs qui seraient supprimés par prune.

SI OK -> docker container prune


EXEMPLE 5 : Nettoyage mensuel automatisé
─────────────────────────────────────────

CRON JOB (Linux) :
# Exécuter le 1er de chaque mois à 3h du matin
0 3 1 * * docker container prune -f --filter "until=168h"

SCRIPT MAINTENANCE :
#!/bin/bash
echo "=== Nettoyage Docker ==="
echo "Conteneurs arrêtés..."
docker container prune -f

echo "Images non utilisées..."
docker image prune -a -f

echo "Volumes orphelins..."
docker volume prune -f

echo "Réseaux non utilisés..."
docker network prune -f

echo "[OK] Nettoyage terminé"
docker system df


QUAND l'utiliser ?
[OK] Toutes les semaines (maintenance)
[OK] Après une session de tests intensive
[OK] Quand le disque est plein
[OK] Avant de partir en vacances (nettoyer)

AU LIEU DE : docker rm manuel de chaque conteneur


DIFFÉRENCE : docker rm vs docker container prune

┌──────────────────────────────────────────────────────────────────────────┐
│ docker rm                                                                │
├──────────────────────────────────────────────────────────────────────────┤
│ • Supprime des conteneurs spécifiques                                    │
│ • Vous choisissez lesquels                                               │
│ • Contrôle précis                                                        │
│ QUAND : Supprimer 1-5 conteneurs précis                                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker container prune                                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ • Supprime TOUS les conteneurs arrêtés                                   │
│ • Automatique                                                            │
│ • Nettoyage en masse                                                     │
│ QUAND : Nettoyage général, maintenance                                   │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker logs                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche les logs (STDOUT/STDERR) d'un conteneur                  │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Déboguer des erreurs
• Voir ce qui se passe dans le conteneur
• Surveiller l'activité en temps réel
• Comprendre pourquoi un conteneur crash
• LA commande de débogage #1 !

COMMENT l'utiliser ?

SYNTAXE :
docker logs [OPTIONS] CONTAINER

OPTIONS ESSENTIELLES :
-f, --follow          -> Suivre en temps réel (comme tail -f)
--tail N              -> Afficher les N dernières lignes
--since TIMESTAMP     -> Depuis un moment (5m, 1h, 2024-01-01)
--until TIMESTAMP     -> Jusqu'à un moment
-t, --timestamps      -> Afficher les timestamps


EXEMPLE 1 : Logs simples
─────────────────────────

COMMANDE :
docker logs mon-site-web

RÉSULTAT :
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/15 10:30:25 [notice] 1#1: using the "epoll" event method
2024/01/15 10:30:25 [notice] 1#1: nginx/1.25.2
2024/01/15 10:30:25 [notice] 1#1: start worker processes
172.17.0.1 - - [15/Jan/2024:10:32:15 +0000] "GET / HTTP/1.1" 200 615

EXPLICATION :
- Logs de démarrage de nginx
- Logs d'accès (quelqu'un a visité /)


EXEMPLE 2 : Suivre en temps réel (-f)
──────────────────────────────────────

COMMANDE :
docker logs -f mon-site-web

RÉSULTAT :
[logs existants]
[attend de nouveaux logs...]
172.17.0.1 - - [15/Jan/2024:10:35:20 +0000] "GET /api HTTP/1.1" 404
172.17.0.1 - - [15/Jan/2024:10:35:25 +0000] "GET /about HTTP/1.1" 200

Les nouveaux logs apparaissent en direct ! [OK]

POUR SORTIR : Ctrl+C (le conteneur continue de tourner)

UTILITÉ :
- Déboguer en direct
- Voir les requêtes arriver
- Surveiller l'activité


EXEMPLE 3 : Seulement les dernières lignes (--tail)
────────────────────────────────────────────────────

COMMANDE :
docker logs --tail 50 mon-site-web

RÉSULTAT :
[Affiche seulement les 50 dernières lignes]

UTILITÉ :
Éviter de voir 10 000 lignes de logs !

COMBINAISON PUISSANTE :
docker logs --tail 100 -f mon-site-web
-> 100 dernières lignes + suivi en temps réel


EXEMPLE 4 : Logs avec timestamps (-t)
──────────────────────────────────────

COMMANDE :
docker logs -t mon-site-web

RÉSULTAT :
2024-01-15T10:30:25.123456789Z /docker-entrypoint.sh: Configuration complete
2024-01-15T10:30:25.987654321Z [notice] 1#1: nginx/1.25.2
2024-01-15T10:32:15.456789123Z 172.17.0.1 - - "GET / HTTP/1.1" 200

UTILITÉ :
Savoir EXACTEMENT quand chaque log est apparu.


EXEMPLE 5 : Logs depuis un moment (--since)
────────────────────────────────────────────

COMMANDE : Logs des 10 dernières minutes
docker logs --since 10m mon-site-web

COMMANDE : Logs depuis 1 heure
docker logs --since 1h mon-site-web

COMMANDE : Logs depuis une date précise
docker logs --since 2024-01-15T10:00:00 mon-site-web

UTILITÉ :
Ignorer les vieux logs, se concentrer sur le récent.


EXEMPLE 6 : Logs jusqu'à un moment (--until)
─────────────────────────────────────────────

COMMANDE : Logs avant un crash
docker logs --until 2024-01-15T11:30:00 mon-site-web

UTILITÉ :
Voir ce qui s'est passé AVANT un problème.


EXEMPLE 7 : Plage de temps (--since + --until)
───────────────────────────────────────────────

COMMANDE :
docker logs --since 2024-01-15T10:00:00 --until 2024-01-15T11:00:00 mon-site-web

RÉSULTAT :
Seulement les logs entre 10h et 11h !

UTILITÉ :
Analyser une période spécifique (pendant un incident).


EXEMPLE 8 : Rechercher dans les logs (grep)
────────────────────────────────────────────

COMMANDE : Chercher les erreurs
docker logs mon-site-web | grep -i error

COMMANDE : Compter les erreurs
docker logs mon-site-web | grep -i error | wc -l

COMMANDE : Chercher un mot clé
docker logs mon-site-web | grep "404"

COMMANDE : Exclure certaines lignes
docker logs mon-site-web | grep -v "GET /health"


EXEMPLE 9 : Sauvegarder les logs
─────────────────────────────────

COMMANDE :
docker logs mon-site-web > logs_$(date +%Y%m%d).txt

RÉSULTAT :
Fichier "logs_20240115.txt" avec tous les logs.

UTILITÉ :
- Archivage
- Analyse hors ligne
- Envoyer à un collègue


EXEMPLE 10 : Logs d'un conteneur arrêté
────────────────────────────────────────

COMMANDE :
docker logs mon-app-qui-a-crash

RÉSULTAT :
[logs normaux]
[...]
Traceback (most recent call last):
  File "app.py", line 42, in <module>
    result = 10 / 0
ZeroDivisionError: division by zero

Vous voyez l'erreur qui a causé le crash ! [OBJECTIF]

UTILITÉ :
Déboguer POURQUOI un conteneur s'est arrêté.


EXEMPLE 11 : Logs colorisés (pour lisibilité)
──────────────────────────────────────────────

COMMANDE (avec ccze) :
docker logs -f mon-site-web | ccze -A

RÉSULTAT :
Logs colorisés (erreurs en rouge, info en vert, etc.)

INSTALLATION ccze :
sudo apt-get install ccze    # Ubuntu/Debian
brew install ccze            # Mac


EXEMPLE 12 : Surveiller plusieurs conteneurs
─────────────────────────────────────────────

TERMINAL 1 :
docker logs -f web

TERMINAL 2 :
docker logs -f api

TERMINAL 3 :
docker logs -f db

Vous surveillez 3 conteneurs en parallèle !

ALTERNATIVE (tmux) :
tmux new-session \; \
  split-window -h \; \
  split-window -v \; \
  send-keys 'docker logs -f web' C-m \; \
  select-pane -t 1 \; \
  send-keys 'docker logs -f api' C-m \; \
  select-pane -t 2 \; \
  send-keys 'docker logs -f db' C-m


QUAND l'utiliser ?
[OK] TOUJOURS quand un conteneur ne marche pas
[OK] Déboguer des erreurs
[OK] Surveiller l'activité (avec -f)
[OK] Comprendre un crash
[OK] Vérifier qu'une app démarre bien

AU LIEU DE : Deviner ce qui ne va pas


PIÈGES À ÉVITER :

[X] Oublier -f pour le suivi temps réel
   docker logs mon-app  -> Affiche et s'arrête
   [OK] docker logs -f mon-app

[X] Regarder 100 000 lignes de logs
   [OK] Utilisez --tail 100

[X] Chercher des logs qui n'existent pas
   Si l'app écrit dans des fichiers (pas STDOUT)
   -> Les logs ne sont pas dans docker logs
   -> Utilisez : docker exec mon-app cat /var/log/app.log

[X] Logs sans timestamps
   Difficile de savoir quand les erreurs se sont produites
   [OK] Utilisez -t


LOGS vs FICHIERS DE LOGS :

┌──────────────────────────────────────────────────────────────────────────┐
│ docker logs (STDOUT/STDERR)                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Ce que l'app écrit sur la console                                      │
│ • Capturé automatiquement par Docker                                     │
│ • Accessible avec docker logs                                            │
│ EXEMPLE : print("Hello") en Python                                       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ Fichiers de logs (/var/log/app.log)                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ • Ce que l'app écrit dans des fichiers                                   │
│ • PAS capturé par docker logs                                            │
│ • Accès : docker exec ou volumes                                         │
│ EXEMPLE : logging.FileHandler("app.log") en Python                      │
└──────────────────────────────────────────────────────────────────────────┘

BONNE PRATIQUE :
En Docker, toujours logger sur STDOUT/STDERR (pas fichiers).


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker stats                                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche les statistiques d'utilisation des ressources en direct  │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Surveiller CPU, mémoire, réseau, disque
• Identifier les conteneurs gourmands
• Détecter les fuites mémoire
• Optimiser les ressources
• Monitoring en temps réel

COMMENT l'utiliser ?

SYNTAXE :
docker stats [OPTIONS] [CONTAINER...]

OPTIONS UTILES :
-a, --all           -> Tous les conteneurs (même arrêtés)
--no-stream         -> Snapshot unique (pas de rafraîchissement)
--no-trunc          -> Noms complets
--format            -> Format personnalisé


EXEMPLE 1 : Stats en temps réel
────────────────────────────────

COMMANDE :
docker stats

RÉSULTAT :
CONTAINER ID   NAME          CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O
f6e5d4c3b2a1   mon-site-web  0.25%     45.2MiB / 7.765GiB    0.57%     1.2kB / 648B      0B / 8.19kB
a1b2c3d4e5f6   ma-db         12.5%     512MiB / 7.765GiB     6.44%     15kB / 8.5kB      125MB / 45MB
b2c3d4e5f6a7   mon-cache     1.8%      25.6MiB / 7.765GiB    0.32%     850B / 320B       0B / 0B

L'affichage se rafraîchit toutes les secondes ! [SYNC]

POUR SORTIR : Ctrl+C

EXPLICATION DES COLONNES :

┌──────────────────────────────────────────────────────────────────────────┐
│ CPU %                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ Pourcentage d'utilisation CPU                                           │
│ 0.25%  -> Quasi rien (idle)                                              │
│ 50%    -> Utilise la moitié d'un cœur                                    │
│ 100%   -> Utilise 1 cœur complet                                         │
│ 200%   -> Utilise 2 cœurs complets                                       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ MEM USAGE / LIMIT                                                        │
├──────────────────────────────────────────────────────────────────────────┤
│ Mémoire utilisée / Limite allouée                                       │
│ 45.2MiB / 7.765GiB  -> Utilise 45 MB sur 7.7 GB disponibles             │
│                                                                          │
│ Si pas de limite : affiche la RAM totale du système                     │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ MEM %                                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ Pourcentage de mémoire utilisée                                         │
│ 0.57%  -> Presque rien                                                   │
│ 50%    -> La moitié de la limite                                         │
│ 90%+   -> [ATTENTION] Risque de problème !                                        │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ NET I/O                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ Données réseau : Reçues / Envoyées                                      │
│ 1.2kB / 648B  -> 1.2 ko reçus, 648 octets envoyés                       │
│                                                                          │
│ DEPUIS le démarrage du conteneur (cumul)                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ BLOCK I/O                                                                │
├──────────────────────────────────────────────────────────────────────────┤
│ Lecture disque / Écriture disque                                        │
│ 125MB / 45MB  -> 125 MB lus, 45 MB écrits                               │
│                                                                          │
│ DEPUIS le démarrage du conteneur (cumul)                                │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Snapshot unique (--no-stream)
──────────────────────────────────────────

COMMANDE :
docker stats --no-stream

RÉSULTAT :
[Même affichage mais s'arrête après 1 affichage]

UTILITÉ :
- Scripts
- Captures ponctuelles
- Pas besoin de surveillance continue


EXEMPLE 3 : Stats d'un conteneur spécifique
────────────────────────────────────────────

COMMANDE :
docker stats mon-site-web

RÉSULTAT :
Seulement les stats de "mon-site-web" (rafraîchissement continu)

COMMANDE : Plusieurs conteneurs spécifiques
docker stats mon-site-web ma-db

Seulement ces 2 conteneurs.


EXEMPLE 4 : Format personnalisé
────────────────────────────────

COMMANDE :
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

RÉSULTAT :
NAME            CPU %     MEM USAGE
mon-site-web    0.25%     45.2MiB / 7.765GiB
ma-db           12.5%     512MiB / 7.765GiB
mon-cache       1.8%      25.6MiB / 7.765GiB

Plus compact et lisible !


EXEMPLE 5 : Détecter une fuite mémoire
───────────────────────────────────────

OBSERVATION :
Lancez : docker stats mon-app

T+0min  : MEM USAGE = 100 MB
T+5min  : MEM USAGE = 200 MB
T+10min : MEM USAGE = 350 MB
T+15min : MEM USAGE = 550 MB
T+20min : MEM USAGE = 800 MB  <- [ATTENTION] FUITE MÉMOIRE !

La mémoire augmente constamment sans jamais redescendre.

ACTION :
1. docker logs mon-app  -> Chercher des erreurs
2. docker restart mon-app  -> Solution temporaire
3. Corriger le code -> Solution définitive


EXEMPLE 6 : Identifier le conteneur gourmand
─────────────────────────────────────────────

COMMANDE :
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}" | sort -k2 -rh

RÉSULTAT :
NAME            CPU %
service-heavy   85.3%    <- Le coupable !
ma-db           12.5%
mon-site-web    0.25%
mon-cache       0.8%

"service-heavy" utilise presque tout le CPU !


EXEMPLE 7 : Surveiller tous les conteneurs (même arrêtés)
──────────────────────────────────────────────────────────

COMMANDE :
docker stats -a

RÉSULTAT :
Inclut aussi les conteneurs arrêtés (affichent 0% CPU, 0 B MEM)


EXEMPLE 8 : Exporter les stats (monitoring)
────────────────────────────────────────────

SCRIPT :
#!/bin/bash
while true; do
    docker stats --no-stream --format "{{.Name}},{{.CPUPerc}},{{.MemUsage}},$(date +%s)" >> stats.csv
    sleep 60
done

RÉSULTAT :
Fichier stats.csv avec historique des stats toutes les minutes.

Ouvrez dans Excel pour faire des graphiques !


EXEMPLE 9 : Alertes simples
────────────────────────────

SCRIPT :
#!/bin/bash
while true; do
    MEM=$(docker stats --no-stream --format "{{.MemPerc}}" mon-app | sed 's/%//')
    if (( $(echo "$MEM > 80" | bc -l) )); then
        echo "ALERTE : mon-app utilise ${MEM}% de mémoire !"
        # Envoyer notification, email, etc.
    fi
    sleep 60
done


QUAND l'utiliser ?
[OK] Surveiller les performances en temps réel
[OK] Identifier les conteneurs gourmands
[OK] Détecter les fuites mémoire
[OK] Optimiser l'allocation de ressources
[OK] Diagnostiquer la lenteur

AU LIEU DE : Utiliser htop/top (qui ne sépare pas par conteneur)


PIÈGES À ÉVITER :

[X] Interpréter CPU % de façon absolue
   100% = 1 cœur complet (pas 100% de tous les cœurs)
   Sur une machine 8 cœurs : 800% = tous les cœurs

[X] Paniquer pour 90% mémoire
   C'est normal si la limite est bien configurée
   Docker gère automatiquement

[X] Oublier que NET I/O et BLOCK I/O sont cumulatifs
   Ce ne sont PAS des débits instantanés
   Ce sont des totaux depuis le démarrage

[X] Surveiller sans agir
   Les stats ne servent à rien si vous ne les analysez pas
   [OK] Identifiez les problèmes et corrigez-les


COMPARAISON : docker stats vs outils système

┌──────────────────────────────────────────────────────────────────────────┐
│ docker stats                                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ • Vue par conteneur                                                      │
│ • Facile à lire                                                          │
│ • Intégré à Docker                                                       │
│ • Pas d'installation supplémentaire                                      │
│ QUAND : Surveillance quotidienne Docker                                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ htop / top                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • Vue par processus                                                      │
│ • Plus de détails techniques                                             │
│ • Tous les processus (pas que Docker)                                    │
│ QUAND : Débogage système avancé                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ cAdvisor / Prometheus + Grafana                                          │
├──────────────────────────────────────────────────────────────────────────┤
│ • Historique des métriques                                               │
│ • Graphiques et dashboards                                               │
│ • Alertes automatiques                                                   │
│ • Complexe à mettre en place                                             │
│ QUAND : Production, monitoring professionnel                             │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker inspect (CONTENEURS)                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche TOUTES les informations d'un conteneur en JSON            │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir la configuration complète d'un conteneur
• Trouver l'adresse IP d'un conteneur
• Voir les variables d'environnement
• Comprendre les volumes montés
• Déboguer des problèmes réseau
• Voir l'état détaillé

COMMENT l'utiliser ?

SYNTAXE :
docker inspect [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS UTILES :
--format '{{.NetworkSettings.IPAddress}}'  -> Extraire info spécifique
-f '{{json .Config}}'                      -> Section en JSON


RAPPEL : Différence inspect IMAGE vs CONTENEUR

docker inspect ubuntu           -> Inspecte l'IMAGE ubuntu
docker inspect mon-conteneur    -> Inspecte le CONTENEUR mon-conteneur

Même commande, contenu différent !


EXEMPLE 1 : Inspection complète
────────────────────────────────

COMMANDE :
docker inspect mon-site-web

RÉSULTAT (extrait simplifié) :
[
    {
        "Id": "f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5",
        "Created": "2024-01-15T10:30:25.123456789Z",
        "Path": "nginx",
        "Args": ["-g", "daemon off;"],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 12345,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2024-01-15T10:30:26.456789123Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:5e6f7a8b9c0d...",
        "Name": "/mon-site-web",
        "RestartCount": 0,
        "HostConfig": {
            "Binds": null,
            "NetworkMode": "bridge",
            "PortBindings": {
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "8080"
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "Memory": 0,
            "MemorySwap": 0,
            "CpuShares": 0
        },
        "Config": {
            "Hostname": "f6e5d4c3b2a1",
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.25.2"
            ],
            "Cmd": ["nginx", "-g", "daemon off;"],
            "Image": "nginx",
            "WorkingDir": "",
            "Entrypoint": ["/docker-entrypoint.sh"],
            "ExposedPorts": {
                "80/tcp": {}
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "Gateway": "172.17.0.1",
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAddress": "172.17.0.2",
                    "Gateway": "172.17.0.1"
                }
            },
            "Ports": {
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "8080"
                    }
                ]
            }
        },
        "Mounts": []
    }
]

SECTIONS IMPORTANTES :

┌──────────────────────────────────────────────────────────────────────────┐
│ State - État du conteneur                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ "Status": "running"      -> État actuel                                  │
│ "Running": true          -> En cours d'exécution                         │
│ "Paused": false          -> Pas en pause                                 │
│ "Pid": 12345             -> PID du processus principal                   │
│ "ExitCode": 0            -> Code de sortie (si arrêté)                   │
│ "StartedAt": "..."       -> Quand il a démarré                           │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ HostConfig - Configuration de l'hôte                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ "PortBindings": {...}    -> Mapping des ports                            │
│ "RestartPolicy": {...}   -> Politique de redémarrage                     │
│ "Memory": 0              -> Limite mémoire (0 = illimité)                │
│ "NetworkMode": "bridge"  -> Mode réseau                                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ Config - Configuration du conteneur                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ "Env": [...]             -> Variables d'environnement                    │
│ "Cmd": [...]             -> Commande exécutée                            │
│ "WorkingDir": ""         -> Répertoire de travail                        │
│ "ExposedPorts": {...}    -> Ports exposés                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ NetworkSettings - Configuration réseau                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ "IPAddress": "172.17.0.2" -> Adresse IP du conteneur                     │
│ "Gateway": "172.17.0.1"   -> Passerelle                                  │
│ "Ports": {...}            -> Mapping des ports effectif                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ Mounts - Volumes montés                                                 │
├──────────────────────────────────────────────────────────────────────────┤
│ Liste de tous les volumes et bind mounts                                │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 2 : Extraire l'adresse IP
──────────────────────────────────

COMMANDE :
docker inspect --format='{{.NetworkSettings.IPAddress}}' mon-site-web

RÉSULTAT :
172.17.0.2

C'est l'IP du conteneur sur le réseau Docker !

UTILITÉ :
Se connecter directement au conteneur :
curl http://172.17.0.2


EXEMPLE 3 : Voir les variables d'environnement
───────────────────────────────────────────────

COMMANDE :
docker inspect --format='{{.Config.Env}}' mon-app

RÉSULTAT :
[PATH=/usr/local/bin:/usr/bin POSTGRES_PASSWORD=secret POSTGRES_USER=admin]

PLUS LISIBLE :
docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' mon-app

RÉSULTAT :
PATH=/usr/local/bin:/usr/bin
POSTGRES_PASSWORD=secret
POSTGRES_USER=admin


EXEMPLE 4 : Voir les volumes montés
────────────────────────────────────

COMMANDE :
docker inspect --format='{{json .Mounts}}' mon-app | jq

RÉSULTAT :
[
  {
    "Type": "volume",
    "Name": "postgres-data",
    "Source": "/var/lib/docker/volumes/postgres-data/_data",
    "Destination": "/var/lib/postgresql/data",
    "Driver": "local",
    "Mode": "z",
    "RW": true,
    "Propagation": ""
  }
]

INFORMATIONS :
- Type: volume (volume nommé)
- Source: Emplacement sur l'hôte
- Destination: Emplacement dans le conteneur
- RW: true = Read-Write (lecture-écriture)


EXEMPLE 5 : Voir les ports mappés
──────────────────────────────────

COMMANDE :
docker inspect --format='{{json .NetworkSettings.Ports}}' mon-site-web | jq

RÉSULTAT :
{
  "80/tcp": [
    {
      "HostIp": "0.0.0.0",
      "HostPort": "8080"
    }
  ]
}

SIGNIFICATION :
Port 80 du conteneur -> Port 8080 de toutes les interfaces (0.0.0.0)


EXEMPLE 6 : Vérifier pourquoi un conteneur s'est arrêté
────────────────────────────────────────────────────────

COMMANDE :
docker inspect --format='{{.State.Status}} {{.State.ExitCode}} {{.State.Error}}' mon-app

RÉSULTAT :
exited 137 

EXPLICATION :
- Status: exited (arrêté)
- ExitCode: 137 (killed par signal SIGKILL)
- Error: (vide)

EXIT CODES COURANTS :
0   -> Arrêt normal
1   -> Erreur générique
2   -> Mauvaise utilisation
126 -> Commande non exécutable
127 -> Commande introuvable
137 -> Tué par SIGKILL (docker kill ou OOM)
143 -> Tué par SIGTERM (docker stop)


EXEMPLE 7 : Voir quand le conteneur a démarré
──────────────────────────────────────────────

COMMANDE :
docker inspect --format='{{.State.StartedAt}}' mon-site-web

RÉSULTAT :
2024-01-15T10:30:26.456789123Z

CALCUL UPTIME :
docker inspect --format='{{.State.StartedAt}}' mon-site-web
-> Comparez avec l'heure actuelle


EXEMPLE 8 : Vérifier si OOM (Out Of Memory)
────────────────────────────────────────────

COMMANDE :
docker inspect --format='{{.State.OOMKilled}}' mon-app

RÉSULTAT :
true

SIGNIFICATION :
Le conteneur a été tué par le système car il a dépassé sa limite mémoire !

ACTION :
1. Augmenter la limite : docker run -m 2g ...
2. Optimiser l'application (fuite mémoire ?)


EXEMPLE 9 : Voir la politique de restart
─────────────────────────────────────────

COMMANDE :
docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' mon-app

RÉSULTAT :
always

Signifie que le conteneur redémarre automatiquement.


EXEMPLE 10 : Comparer deux conteneurs
──────────────────────────────────────

COMMANDE :
docker inspect mon-app1 mon-app2 > comparaison.json

Analysez le fichier pour voir les différences de config.


QUAND l'utiliser ?
[OK] Trouver l'IP d'un conteneur
[OK] Déboguer des problèmes réseau
[OK] Vérifier les variables d'environnement
[OK] Comprendre pourquoi un conteneur crash
[OK] Voir les volumes montés
[OK] Documentation (configuration actuelle)

AU LIEU DE : Deviner la configuration


PIÈGES À ÉVITER :

[X] JSON trop long et illisible
   [OK] Utilisez --format pour extraire ce qui vous intéresse
   [OK] Ou pipe vers jq : docker inspect mon-app | jq '.[] | .State'

[X] Confondre inspect d'image et de conteneur
   docker inspect nginx  -> Image SI pas de conteneur nommé nginx
   docker inspect mon-nginx  -> Conteneur

[X] Oublier que certaines infos changent
   L'IP peut changer si le conteneur redémarre


FORMATS UTILES À RETENIR :

# Adresse IP
--format='{{.NetworkSettings.IPAddress}}'

# Variables d'environnement
--format='{{range .Config.Env}}{{println .}}{{end}}'

# État et code de sortie
--format='{{.State.Status}} {{.State.ExitCode}}'

# Ports mappés
--format='{{json .NetworkSettings.Ports}}'

# Volumes montés
--format='{{json .Mounts}}'

# Heure de démarrage
--format='{{.State.StartedAt}}'

# Politique restart
--format='{{.HostConfig.RestartPolicy.Name}}'


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker top                                                   ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Affiche les processus qui tournent dans un conteneur              │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir ce qui tourne exactement dans le conteneur
• Identifier les processus gourmands
• Déboguer des problèmes de performance
• Comprendre l'architecture interne

COMMENT l'utiliser ?

SYNTAXE :
docker top CONTAINER [ps OPTIONS]


EXEMPLE 1 : Processus basiques
───────────────────────────────

COMMANDE :
docker top mon-site-web

RÉSULTAT :
UID        PID     PPID    C    STIME   TTY   TIME       CMD
root       12345   12320   0    10:30   ?     00:00:00   nginx: master process
nginx      12346   12345   0    10:30   ?     00:00:01   nginx: worker process
nginx      12347   12345   0    10:30   ?     00:00:01   nginx: worker process

EXPLICATION :

┌──────────────────────────────────────────────────────────────────────────┐
│ UID     -> Utilisateur qui exécute le processus                          │
│ PID     -> Process ID (identifiant du processus)                         │
│ PPID    -> Parent Process ID (processus parent)                          │
│ C       -> Utilisation CPU (%)                                           │
│ STIME   -> Heure de démarrage                                            │
│ TTY     -> Terminal associé (? = aucun)                                  │
│ TIME    -> Temps CPU cumulé                                              │
│ CMD     -> Commande exécutée                                             │
└──────────────────────────────────────────────────────────────────────────┘

ANALYSE :
- 1 processus master (nginx)
- 2 processus workers (nginx)
- Tous lancés par root ou nginx


EXEMPLE 2 : Processus PostgreSQL
─────────────────────────────────

COMMANDE :
docker top ma-db

RÉSULTAT :
UID       PID     PPID    CMD
postgres  23456   23430   postgres
postgres  23457   23456   postgres: checkpointer
postgres  23458   23456   postgres: background writer
postgres  23459   23456   postgres: walwriter
postgres  23460   23456   postgres: autovacuum launcher
postgres  23461   23456   postgres: stats collector
postgres  23462   23456   postgres: logical replication launcher

ANALYSE :
PostgreSQL lance plusieurs processus :
- 1 processus principal
- 6 processus auxiliaires (checkpointer, writer, etc.)


EXEMPLE 3 : Options personnalisées (format ps)
───────────────────────────────────────────────

COMMANDE :
docker top mon-site-web aux

RÉSULTAT :
USER   PID    %CPU  %MEM   VSZ     RSS    STAT  START  TIME    COMMAND
root   12345  0.0   0.1    10240   2048   Ss    10:30  0:00    nginx: master
nginx  12346  0.2   0.3    12288   3072   S     10:30  0:01    nginx: worker
nginx  12347  0.1   0.3    12288   3072   S     10:30  0:01    nginx: worker

Plus de détails sur CPU et mémoire !


EXEMPLE 4 : Identifier un processus zombie
───────────────────────────────────────────

COMMANDE :
docker top mon-app

RÉSULTAT :
UID    PID     PPID    STAT   CMD
root   12345   12320   Ss     python app.py
root   12346   12345   Z      [python] <defunct>
                       ^
                  Zombie !

STAT = Z -> Processus zombie (terminé mais pas nettoyé)

PROBLÈME :
L'application ne nettoie pas correctement ses processus enfants.

SOLUTION :
Corriger le code pour gérer proprement les signaux (SIGCHLD).


EXEMPLE 5 : Compter les processus
──────────────────────────────────

COMMANDE :
docker top mon-app | wc -l

RÉSULTAT :
15

Le conteneur a 14 processus (15 - 1 ligne d'en-tête)


EXEMPLE 6 : Voir les threads
─────────────────────────────

COMMANDE :
docker top mon-app -L

Affiche aussi les threads (pas seulement les processus).


QUAND l'utiliser ?
[OK] Déboguer des problèmes de performance
[OK] Comprendre ce qui tourne dans le conteneur
[OK] Identifier des processus zombies
[OK] Vérifier qu'un service démarre bien

AU LIEU DE : docker exec mon-app ps aux (fonctionne aussi mais plus lourd)


PIÈGES À ÉVITER :

[X] Confondre PID hôte et PID conteneur
   Les PIDs affichés sont ceux de l'HÔTE (votre machine)
   Pas ceux VUS depuis le conteneur

[X] S'inquiéter de multiples processus
   C'est normal ! Nginx, PostgreSQL, etc. en lancent plusieurs
   Ce n'est PAS un problème

[X] Utiliser top au lieu de docker top
   docker exec mon-app top  -> Charge le conteneur
   [OK] docker top mon-app  -> N'impacte pas le conteneur


DIFFÉRENCE : docker top vs docker exec top

┌──────────────────────────────────────────────────────────────────────────┐
│ docker top mon-app                                                       │
├──────────────────────────────────────────────────────────────────────────┤
│ • Vue depuis l'HÔTE                                                      │
│ • N'entre pas dans le conteneur                                          │
│ • Snapshot unique                                                        │
│ • Léger                                                                  │
│ QUAND : Inspection rapide                                                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker exec -it mon-app top                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Vue depuis le CONTENEUR                                                │
│ • Lance top dans le conteneur                                            │
│ • Interactif (rafraîchissement)                                          │
│ • Plus lourd (processus supplémentaire)                                  │
│ QUAND : Surveillance continue, plus de détails                           │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker events                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Flux en temps réel de tous les événements Docker                  │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Surveiller l'activité Docker en temps réel
• Déboguer des problèmes intermittents
• Audit et logging
• Comprendre ce qui se passe en coulisses
• Monitoring avancé

COMMENT l'utiliser ?

SYNTAXE :
docker events [OPTIONS]

OPTIONS UTILES :
--since TIMESTAMP    -> Depuis un moment
--until TIMESTAMP    -> Jusqu'à un moment
--filter             -> Filtrer par type, conteneur, etc.


EXEMPLE 1 : Flux en temps réel
───────────────────────────────

COMMANDE :
docker events

RÉSULTAT (attend les événements) :
2024-01-15T10:30:25.123456789Z container create f6e5d4c3b2a1 (image=nginx, name=mon-site-web)
2024-01-15T10:30:25.456789123Z container start f6e5d4c3b2a1 (image=nginx, name=mon-site-web)
2024-01-15T10:30:26.789123456Z network connect bridge (container=f6e5d4c3b2a1)
[attend de nouveaux événements...]

Maintenant, dans un autre terminal :
docker stop mon-site-web

Immédiatement dans le premier terminal :
2024-01-15T10:32:10.123456789Z container kill f6e5d4c3b2a1 (image=nginx, name=mon-site-web, signal=15)
2024-01-15T10:32:10.456789123Z container die f6e5d4c3b2a1 (exitCode=0, image=nginx, name=mon-site-web)
2024-01-15T10:32:10.789123456Z network disconnect bridge (container=f6e5d4c3b2a1)
2024-01-15T10:32:11.123456789Z container stop f6e5d4c3b2a1 (image=nginx, name=mon-site-web)

Vous voyez TOUT ce qui se passe ! [OBJECTIF]


EXEMPLE 2 : Filtrer par type d'événement
─────────────────────────────────────────

COMMANDE : Seulement les événements conteneurs
docker events --filter 'type=container'

COMMANDE : Seulement les événements réseau
docker events --filter 'type=network'

COMMANDE : Seulement les événements images
docker events --filter 'type=image'

COMMANDE : Seulement les événements volumes
docker events --filter 'type=volume'


EXEMPLE 3 : Filtrer par conteneur spécifique
─────────────────────────────────────────────

COMMANDE :
docker events --filter 'container=mon-site-web'

RÉSULTAT :
Seulement les événements liés à "mon-site-web"

UTILITÉ :
Surveiller un conteneur spécifique qui pose problème.


EXEMPLE 4 : Filtrer par action
───────────────────────────────

COMMANDE : Seulement les démarrages
docker events --filter 'event=start'

COMMANDE : Seulement les arrêts
docker events --filter 'event=stop'

COMMANDE : Seulement les créations
docker events --filter 'event=create'

COMMANDE : Seulement les morts
docker events --filter 'event=die'


EXEMPLE 5 : Historique des événements
──────────────────────────────────────

COMMANDE : Événements des dernières 24h
docker events --since 24h

COMMANDE : Événements entre deux dates
docker events --since 2024-01-15T09:00:00 --until 2024-01-15T12:00:00

RÉSULTAT :
Tous les événements dans cette plage horaire.

UTILITÉ :
Analyser ce qui s'est passé pendant un incident.


EXEMPLE 6 : Format JSON (pour parsing)
───────────────────────────────────────

COMMANDE :
docker events --format '{{json .}}'

RÉSULTAT :
{"status":"create","id":"f6e5d4c3b2a1","from":"nginx","Type":"container","Action":"create","Actor":{"ID":"f6e5d4c3b2a1","Attributes":{"image":"nginx","name":"mon-site-web"}},"time":1705315825,"timeNano":1705315825123456789}

Format JSON pour intégration avec outils de monitoring.


EXEMPLE 7 : Surveiller les redémarrages
────────────────────────────────────────

COMMANDE :
docker events --filter 'event=restart'

UTILITÉ :
Détecter si des conteneurs redémarrent trop souvent (problème).


EXEMPLE 8 : Logger les événements
──────────────────────────────────

SCRIPT :
#!/bin/bash
docker events --format '{{.Time}} {{.Type}} {{.Action}} {{.Actor.Attributes.name}}' >> docker-events.log

Lance en background :
./log-events.sh &

RÉSULTAT :
Fichier docker-events.log avec historique complet.


EXEMPLE 9 : Alertes sur événements critiques
─────────────────────────────────────────────

SCRIPT :
#!/bin/bash
docker events --filter 'event=die' --filter 'event=oom' --format '{{.Actor.Attributes.name}}' | \
while read container; do
    echo "ALERTE : Le conteneur $container est mort !"
    # Envoyer email, notification Slack, etc.
done


EXEMPLE 10 : Types d'événements disponibles
────────────────────────────────────────────

ÉVÉNEMENTS CONTENEURS :
- create, start, restart, stop, kill, die, destroy
- pause, unpause
- attach, detach
- exec_create, exec_start, exec_die
- oom (Out Of Memory)
- health_status (healthcheck)

ÉVÉNEMENTS IMAGES :
- pull, push, delete, tag, untag, save, load

ÉVÉNEMENTS VOLUMES :
- create, mount, unmount, destroy

ÉVÉNEMENTS RÉSEAUX :
- create, connect, disconnect, destroy

ÉVÉNEMENTS DAEMON :
- reload (configuration rechargée)


QUAND l'utiliser ?
[OK] Déboguer des problèmes intermittents
[OK] Audit et conformité (qui a fait quoi ?)
[OK] Monitoring avancé
[OK] Comprendre l'ordre des événements
[OK] Détecter des comportements anormaux

AU LIEU DE : Vérifier manuellement docker ps toutes les 5 secondes


PIÈGES À ÉVITER :

[X] Laisser tourner sans limite
   events génère beaucoup de données
   [OK] Utilisez --since et --until
   [OK] Ou redirigez vers un fichier avec rotation

[X] Ignorer les événements OOM
   event=oom -> Conteneur tué par manque de mémoire
   [OK] Augmentez la limite mémoire

[X] Ne pas filtrer assez
   Trop d'événements = illisible
   [OK] Utilisez --filter pour cibler


DIFFÉRENCE : docker events vs docker logs

┌──────────────────────────────────────────────────────────────────────────┐
│ docker events                                                            │
├──────────────────────────────────────────────────────────────────────────┤
│ • Événements DOCKER (lifecycle)                                          │
│ • start, stop, create, destroy, etc.                                     │
│ • Niveau infrastructure                                                  │
│ EXEMPLE : "Le conteneur X a démarré"                                     │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker logs                                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Logs APPLICATION (STDOUT/STDERR)                                       │
│ • Ce que l'app écrit                                                     │
│ • Niveau application                                                     │
│ EXEMPLE : "Erreur 404 sur /api/users"                                    │
└──────────────────────────────────────────────────────────────────────────┘

Les deux sont complémentaires !


================================================================================
                    RÉCAPITULATIF COMPLET DES COMMANDES
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║                      TABLEAU DE RÉFÉRENCE RAPIDE                         ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ INFORMATIONS SYSTÈME                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ docker --version          Version courte                                 │
│ docker version            Version complète (client + serveur)            │
│ docker info               Infos système complètes                        │
│ docker --help             Aide générale                                  │
│ docker COMMAND --help     Aide commande spécifique                       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ GESTION DES IMAGES                                                       │
├──────────────────────────────────────────────────────────────────────────┤
│ docker search ubuntu      Rechercher une image                           │
│ docker pull ubuntu        Télécharger une image                          │
│ docker images             Lister les images                              │
│ docker rmi ubuntu         Supprimer une image                            │
│ docker image prune        Nettoyer images non utilisées                  │
│ docker inspect ubuntu     Inspecter une image                            │
│ docker history ubuntu     Voir l'historique des layers                   │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CYCLE DE VIE DES CONTENEURS                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ docker run ubuntu         Créer et démarrer                              │
│ docker start mon-app      Démarrer (conteneur existant)                  │
│ docker stop mon-app       Arrêter proprement                             │
│ docker restart mon-app    Redémarrer                                     │
│ docker pause mon-app      Mettre en pause                                │
│ docker unpause mon-app    Reprendre                                      │
│ docker kill mon-app       Arrêter brutalement                            │
│ docker rm mon-app         Supprimer                                      │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ SURVEILLANCE ET INSPECTION                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ docker ps                 Lister conteneurs actifs                       │
│ docker ps -a              Lister tous les conteneurs                     │
│ docker logs mon-app       Voir les logs                                  │
│ docker logs -f mon-app    Suivre les logs (temps réel)                   │
│ docker stats              Statistiques ressources                        │
│ docker inspect mon-app    Inspecter configuration                        │
│ docker top mon-app        Processus dans le conteneur                    │
│ docker events             Événements Docker temps réel                   │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ NETTOYAGE ET MAINTENANCE                                                 │
├──────────────────────────────────────────────────────────────────────────┤
│ docker container prune    Supprimer conteneurs arrêtés                   │
│ docker image prune -a     Supprimer images non utilisées                 │
│ docker volume prune       Supprimer volumes non utilisés                 │
│ docker network prune      Supprimer réseaux non utilisés                 │
│ docker system prune -a    Nettoyage complet ([ATTENTION])                         │
│ docker system df          Voir utilisation disque                        │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║                    OPTIONS DOCKER RUN ESSENTIELLES                       ║
╚══════════════════════════════════════════════════════════════════════════╝

SYNTAXE COMPLÈTE :
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

┌──────────────────────────────────────────────────────────────────────────┐
│ OPTION               UTILITÉ                           EXEMPLE            │
├──────────────────────────────────────────────────────────────────────────┤
│ -d                   Background (détaché)              -d                 │
│ -it                  Interactif + terminal             -it                │
│ --name NOM           Nommer le conteneur               --name web         │
│ --rm                 Auto-supprimer après arrêt        --rm               │
│ -p HOST:CONT         Mapper port                       -p 8080:80         │
│ -v HOST:CONT         Monter volume                     -v /data:/app      │
│ -e KEY=VAL           Variable environnement            -e DEBUG=1         │
│ --env-file FILE      Fichier .env                      --env-file .env    │
│ -w /path             Répertoire travail                -w /app            │
│ -u user              Utilisateur                       -u 1000            │
│ --restart POLICY     Politique redémarrage             --restart always   │
│ -m 512m              Limite mémoire                    -m 1g              │
│ --cpus 2             Limite CPU                        --cpus 1.5         │
│ --network NET        Réseau                            --network mynet    │
│ --link NAME          Lier à conteneur                  --link db:db       │
│ --read-only          Système fichiers RO               --read-only        │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLES DE COMBINAISONS COURANTES :

# Serveur web permanent
docker run -d --name web --restart always -p 80:80 nginx

# Base de données avec volume
docker run -d --name db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres

# Développement interactif
docker run -it --rm -v $(pwd):/app -w /app python:3.11 bash

# Service avec limites ressources
docker run -d --name app -m 512m --cpus 1 -p 8080:8080 myapp

# Test rapide one-shot
docker run --rm alpine echo "Hello World"


╔══════════════════════════════════════════════════════════════════════════╗
║                         WORKFLOWS QUOTIDIENS                             ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW 1 : Démarrer un nouveau projet                                  │
└──────────────────────────────────────────────────────────────────────────┘

# 1. Chercher l'image appropriée
docker search postgres --filter is-official=true

# 2. Télécharger l'image
docker pull postgres:15

# 3. Créer un réseau (optionnel mais recommandé)
docker network create monprojet

# 4. Lancer la base de données
docker run -d \
  --name db \
  --network monprojet \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:15

# 5. Vérifier que ça tourne
docker ps
docker logs db

# 6. Tester la connexion
docker run --rm --network monprojet postgres:15 \
  psql -h db -U postgres -c "SELECT version();"


┌──────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW 2 : Déboguer un conteneur qui ne marche pas                     │
└──────────────────────────────────────────────────────────────────────────┘

# 1. Est-il actif ?
docker ps -a | grep mon-app

# 2. Voir les logs
docker logs mon-app
docker logs --tail 100 mon-app

# 3. Si crash, voir pourquoi
docker inspect --format='{{.State.ExitCode}}' mon-app
docker inspect --format='{{.State.Error}}' mon-app

# 4. Vérifier la configuration
docker inspect mon-app | jq '.[] | .Config'

# 5. Tester interactivement
docker run -it --rm --entrypoint /bin/bash mon-app

# 6. Vérifier les ressources
docker stats mon-app --no-stream


┌──────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW 3 : Maintenance hebdomadaire                                    │
└──────────────────────────────────────────────────────────────────────────┘

#!/bin/bash
echo "=== Maintenance Docker hebdomadaire ==="

# 1. Voir l'utilisation actuelle
echo "Utilisation disque AVANT :"
docker system df

# 2. Supprimer conteneurs arrêtés
echo "Nettoyage conteneurs..."
docker container prune -f

# 3. Supprimer images non utilisées
echo "Nettoyage images..."
docker image prune -a -f --filter "until=168h"  # Plus d'une semaine

# 4. Supprimer volumes orphelins
echo "Nettoyage volumes..."
docker volume prune -f

# 5. Supprimer réseaux non utilisés
echo "Nettoyage réseaux..."
docker network prune -f

# 6. Résultat
echo "Utilisation disque APRÈS :"
docker system df

echo "[OK] Maintenance terminée"


┌──────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW 4 : Arrêt propre du système                                     │
└──────────────────────────────────────────────────────────────────────────┘

#!/bin/bash
echo "Arrêt propre de tous les conteneurs..."

# 1. Lister ce qui va être arrêté
docker ps --format "{{.Names}}"

# 2. Demander confirmation
read -p "Arrêter tous ces conteneurs ? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "Annulé"
    exit 1
fi

# 3. Arrêter proprement (SIGTERM)
docker stop $(docker ps -q)

# 4. Attendre que tout soit arrêté
sleep 5

# 5. Vérifier
RUNNING=$(docker ps -q | wc -l)
if [ $RUNNING -eq 0 ]; then
    echo "[OK] Tous les conteneurs sont arrêtés"
else
    echo "[ATTENTION] $RUNNING conteneurs encore actifs"
    docker ps
fi


┌──────────────────────────────────────────────────────────────────────────┐
│ WORKFLOW 5 : Déployer une mise à jour                                    │
└──────────────────────────────────────────────────────────────────────────┘

# 1. Télécharger la nouvelle version
docker pull monapp:v2

# 2. Arrêter l'ancienne version
docker stop monapp

# 3. Sauvegarder l'ancien conteneur (au cas où)
docker rename monapp monapp-old

# 4. Lancer la nouvelle version
docker run -d \
  --name monapp \
  -p 8080:8080 \
  -v appdata:/data \
  --restart always \
  monapp:v2

# 5. Tester
sleep 5
curl http://localhost:8080/health

# 6. Si OK, supprimer l'ancien
docker rm monapp-old

# 7. Si KO, rollback
# docker stop monapp
# docker rm monapp
# docker rename monapp-old monapp
# docker start monapp


╔══════════════════════════════════════════════════════════════════════════╗
║                          BONNES PRATIQUES                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. TOUJOURS NOMMER VOS CONTENEURS                                        │
└──────────────────────────────────────────────────────────────────────────┘

[X] MAUVAIS :
docker run -d nginx
-> Nom aléatoire : "quirky_tesla"
-> Difficile à retrouver

[OK] BON :
docker run -d --name web nginx
-> Facile : docker stop web, docker logs web


┌──────────────────────────────────────────────────────────────────────────┐
│ 2. UTILISER DES VERSIONS FIXES EN PRODUCTION                             │
└──────────────────────────────────────────────────────────────────────────┘

[X] MAUVAIS :
docker run nginx:latest
-> "latest" peut changer
-> Votre code peut casser

[OK] BON :
docker run nginx:1.25.2
-> Version fixe
-> Reproductible


┌──────────────────────────────────────────────────────────────────────────┐
│ 3. TOUJOURS UTILISER DES VOLUMES POUR LES DONNÉES                        │
└──────────────────────────────────────────────────────────────────────────┘

[X] MAUVAIS :
docker run -d postgres
-> Données dans le conteneur
-> Perdues si suppression

[OK] BON :
docker run -d -v pgdata:/var/lib/postgresql/data postgres
-> Données dans un volume
-> Survivent à la suppression


┌──────────────────────────────────────────────────────────────────────────┐
│ 4. UTILISER --rm POUR LES TESTS                                          │
└──────────────────────────────────────────────────────────────────────────┘

[X] MAUVAIS :
docker run ubuntu echo "test"
-> Conteneur reste après exécution
-> Accumulation de déchets

[OK] BON :
docker run --rm ubuntu echo "test"
-> Auto-suppression
-> Pas de pollution


┌──────────────────────────────────────────────────────────────────────────┐
│ 5. CONFIGURER LES POLITIQUES DE RESTART                                  │
└──────────────────────────────────────────────────────────────────────────┘

POUR PRODUCTION :
docker run -d --restart unless-stopped --name web nginx

POLITIQUES :
no              -> Jamais (défaut)
on-failure      -> Seulement si erreur
on-failure:3    -> Max 3 tentatives
always          -> Toujours (même après reboot)
unless-stopped  -> Toujours sauf si stop manuel (RECOMMANDÉ)


┌──────────────────────────────────────────────────────────────────────────┐
│ 6. SURVEILLER LES LOGS RÉGULIÈREMENT                                     │
└──────────────────────────────────────────────────────────────────────────┘

QUOTIDIEN :
docker logs --tail 100 -f mon-app

RECHERCHE D'ERREURS :
docker logs mon-app | grep -i error

ENTRE DEUX DATES :
docker logs --since 2024-01-15T09:00:00 --until 2024-01-15T12:00:00 mon-app


┌──────────────────────────────────────────────────────────────────────────┐
│ 7. NETTOYAGE RÉGULIER                                                    │
└──────────────────────────────────────────────────────────────────────────┘

HEBDOMADAIRE :
docker container prune -f
docker image prune -a -f

MENSUEL :
docker system prune -a --volumes

AVANT CHAQUE ACTION :
docker system df  # Voir l'espace utilisé


┌──────────────────────────────────────────────────────────────────────────┐
│ 8. SÉCURITÉ DE BASE                                                      │
└──────────────────────────────────────────────────────────────────────────┘

[OK] Ne pas exécuter en root dans les conteneurs
[OK] Utiliser des images officielles
[OK] Scanner les vulnérabilités (docker scan)
[OK] Ne pas mettre de secrets dans les images
[OK] Limiter les ressources (-m, --cpus)
[OK] Utiliser --read-only quand possible


┌──────────────────────────────────────────────────────────────────────────┐
│ 9. DOCUMENTATION ET ORGANISATION                                         │
└──────────────────────────────────────────────────────────────────────────┘

[OK] Commenter vos Dockerfiles
[OK] Utiliser docker-compose.yml pour projets multi-conteneurs
[OK] Nommer de façon cohérente (projet-service-environnement)
[OK] Documenter les ports, volumes, variables env
[OK] Versionner vos Dockerfiles (Git)


┌──────────────────────────────────────────────────────────────────────────┐
│ 10. BACKUP ET DISASTER RECOVERY                                          │
└──────────────────────────────────────────────────────────────────────────┘

SAUVEGARDER VOLUMES :
docker run --rm -v myvolume:/data -v $(pwd):/backup alpine \
  tar czf /backup/backup.tar.gz /data

SAUVEGARDER IMAGES :
docker save -o monapp.tar monapp:latest

EXPORTER CONFIGURATION :
docker inspect mon-app > mon-app-config.json


╔══════════════════════════════════════════════════════════════════════════╗
║                         COMMANDES À ÉVITER                               ║
╚══════════════════════════════════════════════════════════════════════════╝

[X] docker rm -f $(docker ps -aq)
   -> Supprime TOUS les conteneurs brutalement
   -> Risque de perte de données

[X] docker system prune -a --volumes -f
   -> Supprime TOUT sans confirmation
   -> Utilisez seulement si vous êtes SÛR

[X] docker run --privileged
   -> Donne tous les droits au conteneur
   -> Risque de sécurité majeur

[X] docker run sans --name
   -> Noms aléatoires difficiles à gérer

[X] docker pull :latest en production
   -> Version changeante
   -> Non reproductible


╔══════════════════════════════════════════════════════════════════════════╗
║                            AIDE-MÉMOIRE FINAL                            ║
╚══════════════════════════════════════════════════════════════════════════╝

COMMANDES QUOTIDIENNES :
docker ps                    # Que tourne-t-il ?
docker logs -f mon-app       # Que se passe-t-il ?
docker stats                 # Ressources ?

COMMANDES DE DÉBOGAGE :
docker logs mon-app          # Erreurs ?
docker inspect mon-app       # Configuration ?
docker exec -it mon-app bash # Aller voir dedans

COMMANDES DE MAINTENANCE :
docker container prune       # Nettoyer conteneurs
docker image prune -a        # Nettoyer images
docker system df             # Espace utilisé

ORDRE D'ARRÊT PROPRE :
1. docker stop (propre, 10s timeout)
2. docker kill (brutal, immédiat)

ORDRE DE SUPPRESSION :
1. docker stop mon-app
2. docker rm mon-app
3. docker rmi mon-image (optionnel)


[OK] COMMANDES INTERMÉDIAIRES


# === EXÉCUTION DE COMMANDES ===


================================================================================
          DOCKER - COMMANDES INTERMÉDIAIRES POUR DÉBUTANTS (PARTIE 8)
           Exécution de Commandes, Attachement et Copie de Fichiers
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION À CETTE SECTION                          ║
╚══════════════════════════════════════════════════════════════════════════╝

OBJECTIF :
Apprendre à interagir avec des conteneurs en cours d'exécution.

CE QUE VOUS ALLEZ APPRENDRE :
1. Exécuter des commandes dans un conteneur actif (docker exec)
2. S'attacher à un conteneur (docker attach)
3. Copier des fichiers entre hôte et conteneur (docker cp)

PRÉREQUIS :
[OK] Comprendre les commandes de base (run, ps, start, stop)
[OK] Savoir ce qu'est un conteneur actif
[OK] Connaître les bases de la ligne de commande (ls, cd, etc.)

CAS D'USAGE :
• Déboguer un conteneur en production
• Modifier des fichiers de configuration
• Exécuter des scripts de maintenance
• Récupérer des logs ou fichiers
• Inspecter l'état interne


================================================================================
[OK] SECTION 1 : DOCKER EXEC - EXÉCUTER DES COMMANDES
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker exec                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Exécute une commande dans un conteneur EN COURS D'EXÉCUTION      │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Déboguer un conteneur qui tourne
• Exécuter des tâches de maintenance
• Inspecter l'état interne
• Modifier des fichiers de configuration
• Lancer des scripts
• Accéder à un shell interactif

COMMENT l'utiliser ?

SYNTAXE :
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

OPTIONS ESSENTIELLES :
-i, --interactive    -> Garde STDIN ouvert (mode interactif)
-t, --tty           -> Alloue un pseudo-terminal
-u, --user USER     -> Exécute en tant qu'utilisateur spécifique
-w, --workdir DIR   -> Définit le répertoire de travail
-e, --env KEY=VAL   -> Définit une variable d'environnement
-d, --detach        -> Mode détaché (background)


DIFFÉRENCE FONDAMENTALE : docker run vs docker exec

┌──────────────────────────────────────────────────────────────────────────┐
│ docker run                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • CRÉE un NOUVEAU conteneur                                              │
│ • Démarre le conteneur                                                   │
│ • Exécute la commande                                                    │
│ • Le conteneur s'arrête quand la commande se termine                     │
│                                                                          │
│ EXEMPLE : docker run ubuntu ls /                                        │
│ -> Crée conteneur, liste /, s'arrête, conteneur reste                    │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker exec                                                              │
├──────────────────────────────────────────────────────────────────────────┤
│ • Utilise un conteneur EXISTANT et ACTIF                                 │
│ • Exécute une commande SUPPLÉMENTAIRE dedans                             │
│ • La commande se termine, le conteneur continue                          │
│                                                                          │
│ EXEMPLE : docker exec mon-conteneur ls /                                │
│ -> Liste /, mon-conteneur continue de tourner                             │
└──────────────────────────────────────────────────────────────────────────┘

ANALOGIE :
docker run  = Ouvrir un nouveau document Word
docker exec = Travailler dans un document Word déjà ouvert


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Commande simple (ls)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Vous avez un conteneur nginx qui tourne

COMMANDE :
docker exec mon-site-web ls /

RÉSULTAT :
bin
boot
dev
docker-entrypoint.d
docker-entrypoint.sh
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

EXPLICATION :
- Liste le contenu de la racine (/) du conteneur
- Le conteneur continue de tourner après
- Aucune interaction nécessaire

UTILITÉ :
Explorer la structure de fichiers d'un conteneur.


COMMANDE : Avec options détaillées
docker exec mon-site-web ls -lah /etc/nginx

RÉSULTAT :
total 40K
drwxr-xr-x 1 root root 4.0K Jan 15 10:30 .
drwxr-xr-x 1 root root 4.0K Jan 15 10:30 ..
drwxr-xr-x 2 root root 4.0K Jan 10 00:00 conf.d
-rw-r--r-- 1 root root 1.1K Jan 10 00:00 fastcgi.conf
-rw-r--r-- 1 root root 5.3K Jan 10 00:00 mime.types
-rw-r--r-- 1 root root  648 Jan 10 00:00 nginx.conf

Vous voyez les fichiers de configuration nginx !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Shell interactif avec bash
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker exec -it mon-site-web bash

QUE SE PASSE-T-IL ?
1. Docker lance bash dans le conteneur
2. Vous obtenez un terminal interactif
3. Vous êtes "dans" le conteneur

RÉSULTAT :
root@f6e5d4c3b2a1:/#
                  ^
        Prompt du conteneur !

Vous pouvez maintenant taper des commandes :

root@f6e5d4c3b2a1:/# whoami
root

root@f6e5d4c3b2a1:/# pwd
/

root@f6e5d4c3b2a1:/# cat /etc/nginx/nginx.conf
[contenu du fichier de configuration]

root@f6e5d4c3b2a1:/# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  10240  2048 ?        Ss   10:30   0:00 nginx: master
nginx       29  0.0  0.3  12288  3072 ?        S    10:30   0:01 nginx: worker
root        42  0.0  0.1   4116  1024 pts/0    Ss   10:35   0:00 bash
root        58  0.0  0.1   5724  1536 pts/0    R+   10:35   0:00 ps aux

POUR SORTIR :
root@f6e5d4c3b2a1:/# exit
OU : Ctrl+D

Le conteneur continue de tourner ! [OK]


POURQUOI -it ?

-i (--interactive)
└─ Garde STDIN ouvert
   Permet de taper des commandes

-t (--tty)
└─ Alloue un pseudo-terminal
   Affiche le prompt, les couleurs, etc.

TESTS :

docker exec mon-conteneur bash
-> La commande se lance puis se termine immédiatement
-> Pas interactif

docker exec -i mon-conteneur bash
-> Vous pouvez taper mais pas de prompt joli
-> Utilisable mais pas confortable

docker exec -t mon-conteneur bash
-> Prompt joli mais ne peut pas recevoir d'entrée
-> Inutile

docker exec -it mon-conteneur bash
-> Parfait ! Interactif + terminal propre [OK]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Shell pour Alpine Linux (sh)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Conteneurs Alpine n'ont pas bash (trop léger)

COMMANDE :
docker exec -it mon-app bash

ERREUR :
OCI runtime exec failed: exec: "bash": executable file not found

POURQUOI ?
Alpine Linux utilise "sh" (shell minimal) au lieu de bash.

SOLUTION :
docker exec -it mon-app sh

RÉSULTAT :
/ # 
    ^
Prompt Alpine (différent de bash)

Maintenant ça marche ! [OK]

/ # whoami
root

/ # ls /bin
[liste des binaires disponibles]

/ # apk add bash  # Si vous voulez vraiment bash
[installation de bash]


COMMENT SAVOIR QUEL SHELL UTILISER ?

MÉTHODE 1 : Regarder l'image de base
docker inspect mon-app --format='{{.Config.Image}}'
-> Si contient "alpine" : utilisez sh
-> Si contient "ubuntu", "debian" : utilisez bash

MÉTHODE 2 : Essayer bash, puis sh si ça échoue
docker exec -it mon-app bash || docker exec -it mon-app sh

MÉTHODE 3 : Vérifier ce qui est disponible
docker exec mon-app ls /bin | grep -E 'bash|sh'


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Exécuter en tant qu'utilisateur spécifique (-u)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Par défaut, exec s'exécute avec l'utilisateur du conteneur

COMMANDE : Voir l'utilisateur actuel
docker exec mon-site-web whoami

RÉSULTAT :
nginx

Le conteneur tourne avec l'utilisateur "nginx".


COMMANDE : Forcer l'exécution en root
docker exec -u root mon-site-web whoami

RÉSULTAT :
root

Maintenant vous êtes root ! [SECURISE]


UTILITÉ : Opérations nécessitant des privilèges

EXEMPLE 1 : Installer un paquet
docker exec -u root mon-app apt-get update
docker exec -u root mon-app apt-get install -y vim

EXEMPLE 2 : Modifier des fichiers système
docker exec -u root mon-app chmod 755 /app/script.sh

EXEMPLE 3 : Déboguer avec tous les droits
docker exec -u root -it mon-app bash


COMMANDE : Exécuter en tant qu'utilisateur spécifique par UID
docker exec -u 1000 mon-app whoami

RÉSULTAT :
appuser

UID 1000 correspond à l'utilisateur "appuser".


COMMANDE : Exécuter en tant qu'utilisateur:groupe
docker exec -u nginx:nginx mon-app id

RÉSULTAT :
uid=101(nginx) gid=101(nginx) groups=101(nginx)


[ATTENTION] ATTENTION SÉCURITÉ :

[X] MAUVAIS :
docker exec -u root mon-app bash
# Faire des modifications hasardeuses
# Installer n'importe quoi
# Changer des permissions critiques

[OK] BON :
docker exec -u root mon-app apt-get install -y curl
# Action précise et nécessaire
# Documentée
# Réversible


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Changer le répertoire de travail (-w)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Par défaut, la commande s'exécute dans le WORKDIR du conteneur

COMMANDE : Voir le répertoire par défaut
docker exec mon-app pwd

RÉSULTAT :
/

C'est la racine (par défaut si WORKDIR pas défini).


COMMANDE : Exécuter dans un répertoire spécifique
docker exec -w /app mon-app pwd

RÉSULTAT :
/app

Maintenant on est dans /app !


UTILITÉ : Éviter de faire "cd" dans chaque commande

[X] SANS -w :
docker exec mon-app sh -c "cd /app && ls"
docker exec mon-app sh -c "cd /app && python script.py"
docker exec mon-app sh -c "cd /app && cat config.json"

[OK] AVEC -w :
docker exec -w /app mon-app ls
docker exec -w /app mon-app python script.py
docker exec -w /app mon-app cat config.json

Plus propre et lisible !


EXEMPLE PRATIQUE : Lancer des tests
docker exec -w /app mon-app pytest tests/

EXEMPLE PRATIQUE : Compilation
docker exec -w /src mon-app make


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Définir des variables d'environnement (-e)
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Variable simple
docker exec -e DEBUG=1 mon-app python script.py

Dans script.py :
import os
if os.getenv('DEBUG') == '1':
    print("Mode debug activé")

RÉSULTAT :
Mode debug activé


COMMANDE : Plusieurs variables
docker exec -e DEBUG=1 -e ENV=test -e TIMEOUT=30 mon-app node app.js


UTILITÉ : Tests avec configuration différente

EXEMPLE 1 : Tester avec différentes DB
docker exec -e DB_HOST=test-db mon-app python manage.py migrate

EXEMPLE 2 : Activer des features flags
docker exec -e FEATURE_X=enabled mon-app ./run-tests.sh


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Mode détaché (-d)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker exec -d mon-app python long-running-task.py

RÉSULTAT :
(Retourne immédiatement, le script tourne en background)

UTILITÉ : Tâches longues sans bloquer le terminal

EXEMPLE 1 : Backup
docker exec -d mon-db pg_dump -U postgres mydb > /backups/backup.sql

EXEMPLE 2 : Traitement batch
docker exec -d mon-app python process-queue.py


VÉRIFICATION : La tâche tourne-t-elle ?
docker exec mon-app ps aux | grep python


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Combinaisons avancées
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Tout combiné !
docker exec -it -u root -w /app -e DEBUG=1 mon-app bash

EXPLICATION :
-it          -> Interactif + terminal
-u root      -> En tant que root
-w /app      -> Dans le dossier /app
-e DEBUG=1   -> Avec variable DEBUG
bash         -> Lance bash

Vous êtes maintenant root dans /app avec DEBUG activé !


EXEMPLE PRATIQUE : Déboguer en production
docker exec -it -u root -w /var/log mon-app bash
# Vous êtes root dans /var/log
# Parfait pour inspecter les logs


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Cas d'usage courants
═══════════════════════════════════════════════════════════════════════════

CAS 1 : Vérifier les processus
──────────────────────────────
docker exec mon-app ps aux
docker exec mon-app top -bn1


CAS 2 : Inspecter les logs internes
────────────────────────────────────
docker exec mon-app cat /var/log/nginx/error.log
docker exec mon-app tail -f /app/logs/app.log


CAS 3 : Vérifier la connectivité réseau
────────────────────────────────────────
docker exec mon-app ping -c 3 google.com
docker exec mon-app curl http://api.example.com/health
docker exec mon-app nslookup db


CAS 4 : Tester la base de données
──────────────────────────────────
docker exec -it ma-db psql -U postgres
docker exec ma-db psql -U postgres -c "SELECT version();"
docker exec ma-db mysql -u root -p'secret' -e "SHOW DATABASES;"


CAS 5 : Créer des fichiers
───────────────────────────
docker exec mon-app touch /tmp/test.txt
docker exec mon-app sh -c "echo 'Hello' > /tmp/test.txt"


CAS 6 : Modifier des permissions
─────────────────────────────────
docker exec -u root mon-app chmod 755 /app/script.sh
docker exec -u root mon-app chown nginx:nginx /app/data


CAS 7 : Redémarrer un service
──────────────────────────────
docker exec mon-app nginx -s reload
docker exec ma-db pg_ctl reload


CAS 8 : Exécuter des migrations
────────────────────────────────
docker exec -w /app mon-app python manage.py migrate
docker exec -w /app mon-app npm run db:migrate


CAS 9 : Nettoyer des fichiers temporaires
──────────────────────────────────────────
docker exec mon-app rm -rf /tmp/*
docker exec mon-app find /var/log -name "*.log" -mtime +7 -delete


CAS 10 : Installer des outils de debug
───────────────────────────────────────
docker exec -u root mon-app apt-get update
docker exec -u root mon-app apt-get install -y curl vim net-tools


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Scripts et automatisation
═══════════════════════════════════════════════════════════════════════════

SCRIPT : Backup automatique
──────────────────────────
#!/bin/bash
CONTAINER="ma-db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${DATE}.sql"

echo "Création du backup..."
docker exec $CONTAINER pg_dump -U postgres mydb > $BACKUP_FILE

if [ $? -eq 0 ]; then
    echo "[OK] Backup créé : $BACKUP_FILE"
else
    echo "[X] Erreur lors du backup"
    exit 1
fi


SCRIPT : Vérification santé
───────────────────────────
#!/bin/bash
CONTAINERS="web api db cache"

for container in $CONTAINERS; do
    echo "Vérification de $container..."
    
    # Est-il actif ?
    if ! docker ps --format '{{.Names}}' | grep -q "^${container}$"; then
        echo "[X] $container est arrêté !"
        continue
    fi
    
    # Répond-il ?
    if docker exec $container echo "OK" > /dev/null 2>&1; then
        echo "[OK] $container fonctionne"
    else
        echo "[X] $container ne répond pas"
    fi
done


SCRIPT : Exécuter des tests
───────────────────────────
#!/bin/bash
docker exec -w /app mon-app pytest tests/ --verbose

if [ $? -eq 0 ]; then
    echo "[OK] Tous les tests passent"
else
    echo "[X] Des tests ont échoué"
    docker exec -w /app mon-app pytest tests/ --lf  # Re-run les failed
fi


QUAND l'utiliser ?
[OK] Déboguer un conteneur en production
[OK] Exécuter des tâches de maintenance
[OK] Inspecter l'état interne
[OK] Modifier des configurations temporairement
[OK] Tester des commandes avant de les mettre dans le Dockerfile

AU LIEU DE : Recréer le conteneur à chaque fois


PIÈGES À ÉVITER :

[X] Exécuter sur un conteneur arrêté
   docker exec mon-app-arrete bash
   ERREUR : Error: container is not running
   
   VÉRIFIEZ : docker ps | grep mon-app-arrete
   SOLUTION : docker start mon-app-arrete

[X] Oublier -it pour les commandes interactives
   docker exec mon-app bash  -> Rien ne se passe
   [OK] docker exec -it mon-app bash

[X] Utiliser bash sur Alpine
   docker exec -it alpine-app bash  -> Erreur
   [OK] docker exec -it alpine-app sh

[X] Modifier des fichiers sans backup
   [ATTENTION] Les changements sont perdus si le conteneur est supprimé
   [OK] Utilisez des volumes pour les données importantes

[X] Installer des paquets sans les documenter
   docker exec -u root mon-app apt-get install curl
   -> Perdu au prochain redémarrage
   [OK] Ajoutez au Dockerfile pour que ce soit permanent


DIFFÉRENCE : exec vs run (tableau récapitulatif)

CRITÈRE              docker run              docker exec
──────────────────────────────────────────────────────────────────
Conteneur            Crée un nouveau         Utilise existant
État requis          Aucun (crée)            Doit être actif
Après exécution      Conteneur reste         Conteneur continue
Persistance          Nouveau conteneur       Changements temporaires
Usage typique        Lancer app              Déboguer/maintenir
Exemple              Créer serveur web       Inspecter serveur web


================================================================================
[OK] SECTION 2 : DOCKER ATTACH - S'ATTACHER À UN CONTENEUR
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker attach                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : S'attache au processus principal (PID 1) d'un conteneur          │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Voir la sortie en temps réel du processus principal
• Interagir avec une application interactive
• Déboguer des problèmes de démarrage
• Voir les logs "live" (alternative à docker logs -f)

COMMENT l'utiliser ?

SYNTAXE :
docker attach [OPTIONS] CONTAINER

OPTIONS UTILES :
--no-stdin          -> Ne pas attacher STDIN (lecture seule)
--sig-proxy=false   -> Ne pas transmettre les signaux


DIFFÉRENCE FONDAMENTALE : docker attach vs docker exec

┌──────────────────────────────────────────────────────────────────────────┐
│ docker exec -it mon-app bash                                             │
├──────────────────────────────────────────────────────────────────────────┤
│ • Lance un NOUVEAU processus (bash)                                      │
│ • Processus indépendant                                                  │
│ • exit -> Seulement bash se termine                                       │
│ • Le conteneur continue                                                  │
│                                                                          │
│ ANALOGIE : Ouvrir une nouvelle fenêtre dans un programme                │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ docker attach mon-app                                                    │
├──────────────────────────────────────────────────────────────────────────┤
│ • S'attache au processus PRINCIPAL (PID 1)                               │
│ • Même processus que le conteneur                                        │
│ • Ctrl+C -> TUE le processus = ARRÊTE le conteneur                        │
│ • Le conteneur s'arrête !                                                │
│                                                                          │
│ ANALOGIE : Prendre le contrôle du programme principal                    │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE 1 : Attach basique
───────────────────────────

CONTEXTE : Conteneur lancé en détaché
docker run -d --name mon-app python:3.11 python -u -c "import time; [print(f'Message {i}') or time.sleep(2) for i in range(100)]"

Le conteneur affiche des messages toutes les 2 secondes.

COMMANDE :
docker attach mon-app

RÉSULTAT :
Message 5
Message 6
Message 7
[...]

Vous voyez maintenant la sortie en temps réel ! [OK]


POUR DÉTACHER SANS ARRÊTER :
Ctrl+P puis Ctrl+Q (rapidement)

[ATTENTION] NE PAS faire Ctrl+C sinon le conteneur s'arrête !


EXEMPLE 2 : Application interactive
────────────────────────────────────

CONTEXTE : Conteneur avec Python REPL
docker run -dit --name python-repl python:3.11

COMMANDE :
docker attach python-repl

RÉSULTAT :
Python 3.11.0 (main, Oct 24 2022, 18:26:48)
>>> 

Vous pouvez maintenant taper du code Python :
>>> print("Hello!")
Hello!
>>> 2 + 2
4

POUR SORTIR SANS ARRÊTER :
Ctrl+P puis Ctrl+Q


EXEMPLE 3 : Voir les logs live
───────────────────────────────

docker attach mon-site-web

Vous voyez les requêtes HTTP en temps réel :
172.17.0.1 - - [15/Jan/2024:10:35:20 +0000] "GET / HTTP/1.1" 200
172.17.0.1 - - [15/Jan/2024:10:35:25 +0000] "GET /api HTTP/1.1" 404

C'est comme : docker logs -f mon-site-web


EXEMPLE 4 : Mode lecture seule (--no-stdin)
────────────────────────────────────────────

COMMANDE :
docker attach --no-stdin mon-app

RÉSULTAT :
Vous voyez la sortie mais ne pouvez pas envoyer d'entrée.

UTILITÉ :
Surveillance sans risque d'envoyer des commandes accidentelles.


EXEMPLE 5 : Ne pas transmettre les signaux
───────────────────────────────────────────

COMMANDE :
docker attach --sig-proxy=false mon-app

RÉSULTAT :
Ctrl+C ne tue PAS le conteneur, vous détache juste.

UTILITÉ :
Plus sûr si vous avez peur d'arrêter le conteneur par erreur.


QUAND l'utiliser ?
[OK] Voir la sortie d'une app interactive
[OK] Déboguer le démarrage d'un conteneur
[OK] Applications qui attendent une entrée utilisateur

QUAND NE PAS l'utiliser ?
[X] Pour exécuter des commandes (utilisez exec)
[X] Sur des conteneurs sans sortie interactive
[X] En production (risque d'arrêt accidentel)

AU LIEU DE : docker logs -f (qui est plus sûr)


PIÈGES À ÉVITER :

[X] Faire Ctrl+C par réflexe
   -> Tue le conteneur !
   [OK] Utilisez Ctrl+P puis Ctrl+Q

[X] Attach sur un conteneur qui n'a rien à afficher
   -> Écran vide, vous ne voyez rien
   [OK] Vérifiez d'abord : docker logs mon-app

[X] Confondre attach et exec
   attach = Processus principal
   exec = Nouveau processus

[X] Utiliser attach pour exécuter des commandes
   [OK] Utilisez docker exec -it mon-app bash


TABLEAU RÉCAPITULATIF :

BESOIN                          COMMANDE
──────────────────────────────────────────────────────────────
Exécuter une commande           docker exec mon-app ls
Shell interactif                docker exec -it mon-app bash
Voir sortie temps réel          docker attach mon-app
Voir logs sans risque           docker logs -f mon-app


COMMENT DÉTACHER ?

┌────────────────────────────────────────────────────────────┐
│ Séquence de détachement :                                  │
├────────────────────────────────────────────────────────────┤
│ 1. Appuyez sur Ctrl+P                                      │
│ 2. PUIS (rapidement) sur Ctrl+Q                            │
│ 3. Vous revenez à votre terminal                           │
│ 4. Le conteneur continue de tourner                        │
└────────────────────────────────────────────────────────────┘

[ATTENTION] IMPORTANT : Les deux touches doivent être pressées rapidement l'une après l'autre !

SI ÇA NE MARCHE PAS :
Essayez : docker attach --sig-proxy=false mon-app
Puis Ctrl+C fonctionnera pour détacher sans arrêter.


================================================================================
[OK] SECTION 3 : DOCKER CP - COPIER DES FICHIERS
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker cp                                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Copie des fichiers/dossiers entre hôte et conteneur               │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI l'utiliser ?
• Récupérer des logs ou fichiers générés
• Injecter des fichiers de configuration
• Extraire des backups
• Déboguer (copier des fichiers)

# Copier fichiers entre hôte et conteneur
docker cp file.txt container_name:/path/destination/
docker cp container_name:/path/source/file.txt ./destination/
docker cp folder/ container_name:/destination/
docker cp container_name:/source/folder/ ./local/

# Copier vers racine du conteneur
docker cp config.json container_name:/

# Copier depuis conteneur vers répertoire courant
docker cp container_name:/var/log/app.log .

# Copier dossier entier (avec contenu)
docker cp container_name:/app/data ./backup/

# Copier plusieurs fichiers (utiliser archive tar)
docker cp container_name:/app/file1.txt - > files.tar
docker cp container_name:/app/file2.txt - >> files.tar

# Cas d'usage courants
docker cp backup.sql db_container:/tmp/              # Import DB
docker cp db_container:/backups/dump.sql ./          # Export DB
docker cp nginx_config.conf web:/etc/nginx/          # Config
docker cp web:/var/log/nginx/error.log ./logs/      # Logs

# Avec conteneur arrêté (fonctionne aussi!)
docker cp stopped_container:/data/important.txt .

# Permissions préservées
docker cp --archive container:/source /dest          # Garde permissions

# NOTES:
# - Fonctionne sur conteneurs actifs ET arrêtés
# - Crée fichiers/dossiers si n'existent pas
# - Écrase si existe déjà
# - Préserve permissions avec --archive (-a)
# - Peut copier vers/depuis conteneur stoppé
# - Pas de copie récursive automatique des dossiers (utilisez folder/ pour inclure contenu)


═══════════════════════════════════════════════════════════════════════════
  DOCKER CP - EXEMPLES DÉTAILLÉS
═══════════════════════════════════════════════════════════════════════════

╔══════════════════════════════════════════════════════════════════════════╗
║  SYNTAXE GÉNÉRALE                                                        ║
╚══════════════════════════════════════════════════════════════════════════╝

SYNTAXE :
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

OPTIONS :
-a, --archive    -> Préserve attributs, permissions, timestamps
-L, --follow-link -> Suit les liens symboliques dans SRC_PATH

RÈGLES IMPORTANTES :
1. Le chemin peut être relatif ou absolu
2. Si DEST_PATH n'existe pas, il est créé
3. Si DEST_PATH existe et est un fichier, il est écrasé
4. Si DEST_PATH est un dossier, le fichier est copié dedans
5. Fonctionne sur conteneurs en cours d'exécution OU arrêtés


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Copier un fichier vers un conteneur
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Vous avez un fichier de config local à copier

COMMANDE :
docker cp config.json mon-site-web:/etc/myapp/config.json

RÉSULTAT :
config.json est copié dans le conteneur à /etc/myapp/config.json

VÉRIFICATION :
docker exec mon-site-web cat /etc/myapp/config.json


COMMANDE : Copier dans un dossier (nom préservé)
docker cp config.json mon-site-web:/etc/myapp/

RÉSULTAT :
Fichier copié à /etc/myapp/config.json
(Le nom est préservé automatiquement)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Copier un fichier depuis un conteneur
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker cp mon-site-web:/var/log/nginx/access.log ./logs/

RÉSULTAT :
access.log est copié dans ./logs/access.log sur l'hôte


COMMANDE : Copier vers répertoire courant avec nouveau nom
docker cp mon-site-web:/app/data/output.json ./backup_output.json

RÉSULTAT :
Fichier copié et renommé localement


COMMANDE : Extraire tous les logs
docker cp mon-site-web:/var/log/nginx/ ./nginx-logs/

RÉSULTAT :
Dossier nginx/ et tout son contenu copié dans ./nginx-logs/


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Copier un dossier entier
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Copier dossier avec contenu

[ATTENTION] DIFFÉRENCE IMPORTANTE :

docker cp mon-app:/app/data ./backup
-> Crée ./backup/data/ (copie le dossier lui-même)

docker cp mon-app:/app/data/ ./backup
-> Crée ./backup/ avec contenu de data (sans créer data/)
   (Note le "/" final après data)


EXEMPLE 1 : Backup d'une application
docker cp mon-app:/app/uploads ./backup-uploads

RÉSULTAT :
./backup-uploads/
    ├── photo1.jpg
    ├── photo2.jpg
    └── doc.pdf


EXEMPLE 2 : Copier configuration complète
docker cp mon-app:/etc/nginx ./backup-nginx-config/


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Copier vers conteneur arrêté
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Besoin de modifier fichiers avant redémarrage

COMMANDE : Vérifier que conteneur est arrêté
docker ps -a | grep mon-app

RÉSULTAT :
mon-app    Exited (0) 2 hours ago

COMMANDE : Copier malgré tout
docker cp new-config.json mon-app:/etc/app/config.json

RÉSULTAT :
Successfully copied 2.05kB to mon-app:/etc/app/config.json

[OK] Fonctionne ! docker cp ne nécessite pas que le conteneur soit actif


UTILITÉ :
- Correction de config sans redémarrer
- Préparation de données avant démarrage
- Débogage de conteneur crashé


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Préserver permissions avec --archive
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Copie normale
docker cp mon-app:/app/script.sh ./

Résultat :
-rw-r--r-- 1 user user 1234 Jan 15 10:00 script.sh
(Permissions changées selon umask de l'hôte)


COMMANDE : Copie avec --archive
docker cp --archive mon-app:/app/script.sh ./

Résultat :
-rwxr-xr-x 1 root root 1234 Jan 15 10:00 script.sh
(Permissions originales préservées, y compris +x)


UTILITÉ :
- Backup exact
- Scripts exécutables
- Fichiers système


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Cas d'usage - Backup base de données
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Créer dump dans le conteneur
docker exec ma-db pg_dump -U postgres mydb > /tmp/backup.sql

ÉTAPE 2 : Copier le dump vers l'hôte
docker cp ma-db:/tmp/backup.sql ./backups/backup_$(date +%Y%m%d).sql

RÉSULTAT :
./backups/backup_20240115.sql créé


ALTERNATIVE : En une seule commande
docker exec ma-db pg_dump -U postgres mydb > backup_$(date +%Y%m%d).sql

SCRIPT COMPLET :
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${DATE}.sql"

echo "Création du backup..."
docker exec ma-db pg_dump -U postgres mydb > /tmp/${BACKUP_FILE}

echo "Copie vers l'hôte..."
docker cp ma-db:/tmp/${BACKUP_FILE} ./backups/

echo "Nettoyage..."
docker exec ma-db rm /tmp/${BACKUP_FILE}

echo "[OK] Backup créé : ./backups/${BACKUP_FILE}"


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Cas d'usage - Restauration base de données
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Copier dump vers le conteneur
docker cp ./backups/backup.sql ma-db:/tmp/

ÉTAPE 2 : Restaurer dans le conteneur
docker exec ma-db psql -U postgres mydb < /tmp/backup.sql

SCRIPT COMPLET :
#!/bin/bash
BACKUP_FILE=$1

if [ -z "$BACKUP_FILE" ]; then
    echo "Usage: $0 <backup_file>"
    exit 1
fi

if [ ! -f "$BACKUP_FILE" ]; then
    echo "Erreur : Fichier $BACKUP_FILE introuvable"
    exit 1
fi

echo "Copie du backup vers le conteneur..."
docker cp "$BACKUP_FILE" ma-db:/tmp/restore.sql

echo "Restauration..."
docker exec ma-db psql -U postgres mydb < /tmp/restore.sql

echo "Nettoyage..."
docker exec ma-db rm /tmp/restore.sql

echo "[OK] Restauration terminée"


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Cas d'usage - Déploiement de code
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Mise à jour hot-fix en production

COMMANDE : Copier fichiers modifiés
docker cp ./src/fixed_module.py mon-app:/app/src/

COMMANDE : Recharger l'application (sans redémarrage)
docker exec mon-app kill -HUP 1

OU : Redémarrer le service dans le conteneur
docker exec mon-app supervisorctl restart app


MEILLEURE PRATIQUE :
Utilisez des volumes montés plutôt que docker cp pour le développement !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Cas d'usage - Extraction de logs
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Logs application
docker cp mon-app:/var/log/app/ ./logs-$(date +%Y%m%d)/

COMMANDE : Logs nginx
docker cp mon-site-web:/var/log/nginx/ ./nginx-logs/

COMMANDE : Un seul fichier de log
docker cp mon-app:/app/logs/error.log ./debug/


SCRIPT : Extraction automatique périodique
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
LOG_DIR="logs_${DATE}"

mkdir -p "$LOG_DIR"

echo "Extraction des logs..."
docker cp mon-app:/var/log/ "$LOG_DIR/app-logs/"
docker cp mon-site-web:/var/log/nginx/ "$LOG_DIR/nginx-logs/"
docker cp ma-db:/var/log/postgresql/ "$LOG_DIR/db-logs/"

echo "Compression..."
tar -czf "${LOG_DIR}.tar.gz" "$LOG_DIR"
rm -rf "$LOG_DIR"

echo "[OK] Logs archivés : ${LOG_DIR}.tar.gz"


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Cas d'usage - Modification configuration nginx
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Extraire config actuelle
docker cp mon-site-web:/etc/nginx/nginx.conf ./nginx.conf.backup

ÉTAPE 2 : Modifier localement
vim nginx.conf.backup
# Faire vos modifications

ÉTAPE 3 : Copier nouvelle config
docker cp nginx.conf.backup mon-site-web:/etc/nginx/nginx.conf

ÉTAPE 4 : Tester la config
docker exec mon-site-web nginx -t

RÉSULTAT :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

ÉTAPE 5 : Recharger nginx
docker exec mon-site-web nginx -s reload

RÉSULTAT :
Configuration rechargée sans interruption ! [OK]


SI ERREUR :
docker cp nginx.conf.backup mon-site-web:/etc/nginx/nginx.conf
Restaurer l'ancienne config immédiatement !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 11 : Copier plusieurs fichiers
═══════════════════════════════════════════════════════════════════════════

MÉTHODE 1 : Copier un par un
docker cp file1.txt mon-app:/app/
docker cp file2.txt mon-app:/app/
docker cp file3.txt mon-app:/app/

MÉTHODE 2 : Créer archive tar locale puis copier
tar -czf files.tar.gz file1.txt file2.txt file3.txt
docker cp files.tar.gz mon-app:/tmp/
docker exec mon-app tar -xzf /tmp/files.tar.gz -C /app/
docker exec mon-app rm /tmp/files.tar.gz

MÉTHODE 3 : Copier dossier entier
mkdir temp-files
cp file1.txt file2.txt file3.txt temp-files/
docker cp temp-files/ mon-app:/app/


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 12 : Streaming avec STDIN/STDOUT (avancé)
═══════════════════════════════════════════════════════════════════════════

SYNTAXE AVEC "-" :
docker cp mon-app:/path/file.txt -

Envoie le contenu vers STDOUT au lieu d'un fichier


EXEMPLE 1 : Lire fichier sans le copier
docker cp mon-app:/app/config.json - | tar -xO

RÉSULTAT :
Affiche contenu du fichier JSON directement


EXEMPLE 2 : Copier vers autre conteneur directement
docker cp mon-app1:/app/data.txt - | docker cp - mon-app2:/app/data.txt

RÉSULTAT :
Fichier copié entre conteneurs sans passer par l'hôte


EXEMPLE 3 : Backup vers archive tar
docker cp mon-app:/app/data/ - > backup.tar

RÉSULTAT :
Dossier entier archivé en tar


EXEMPLE 4 : Restaurer depuis archive tar
docker cp - mon-app:/app/ < backup.tar

RÉSULTAT :
Archive décompressée dans /app/


═══════════════════════════════════════════════════════════════════════════
  BONNES PRATIQUES
═══════════════════════════════════════════════════════════════════════════

[OK] À FAIRE :

1. Backup avant modification critique
   docker cp mon-app:/etc/config.json ./config.json.backup
   docker cp new-config.json mon-app:/etc/config.json

2. Vérifier que chemin existe
   docker exec mon-app ls /app/data
   docker cp file.txt mon-app:/app/data/

3. Utiliser volumes pour données permanentes
   docker run -v $(pwd)/data:/app/data mon-app
   Meilleur que docker cp pour développement !

4. Tester après modification
   docker exec mon-app nginx -t
   docker exec mon-app python -c "import json; json.load(open('config.json'))"

5. Documenter les copies importantes
   # Script ou README avec historique


[X] À ÉVITER :

1. Remplacer volumes par docker cp
   [X] docker cp pour chaque modification
   [OK] Utiliser volumes montés

2. Copier fichiers énormes
   [X] docker cp large-file.iso mon-app:/data/
   [OK] Utiliser volumes ou télécharger dans conteneur

3. Modifier fichiers système critiques sans backup
   [X] docker cp new-config mon-app:/etc/important.conf
   [OK] Backup d'abord !

4. Oublier permissions
   [X] docker cp script.sh mon-app:/app/
   [OK] docker cp -a script.sh mon-app:/app/
   OU : docker exec mon-app chmod +x /app/script.sh

5. Copier dans conteneur éphémère
   Les modifications sont perdues à la suppression du conteneur !
   [OK] Utilisez volumes ou rebuild image


═══════════════════════════════════════════════════════════════════════════
  PIÈGES COURANTS
═══════════════════════════════════════════════════════════════════════════

[X] PIÈGE 1 : Slash final oublié

docker cp mon-app:/app/data ./backup
-> Crée ./backup/data/

docker cp mon-app:/app/data/ ./backup
-> Crée ./backup/ avec contenu de data

Soyez précis avec les chemins !


[X] PIÈGE 2 : Permissions changées

docker cp script.sh mon-app:/app/
docker exec mon-app ./app/script.sh
-> Permission denied

[OK] SOLUTION :
docker cp -a script.sh mon-app:/app/
OU
docker exec mon-app chmod +x /app/script.sh


[X] PIÈGE 3 : Copier vers conteneur inexistant

docker cp file.txt wrong-name:/app/
-> Error: No such container: wrong-name

[OK] VÉRIFIER D'ABORD :
docker ps -a | grep wrong-name


[X] PIÈGE 4 : Chemin inexistant dans conteneur

docker cp file.txt mon-app:/nonexistent/path/
-> No such container:path

[OK] CRÉER LE DOSSIER D'ABORD :
docker exec mon-app mkdir -p /nonexistent/path
docker cp file.txt mon-app:/nonexistent/path/


[X] PIÈGE 5 : Modifications perdues

docker cp new-config.json mon-app:/etc/
docker stop mon-app
docker rm mon-app
docker run --name mon-app myimage
-> new-config.json a disparu !

[OK] SOLUTION :
Utilisez volumes ou modifiez l'image


═══════════════════════════════════════════════════════════════════════════
  RÉCAPITULATIF RAPIDE
═══════════════════════════════════════════════════════════════════════════

COMMANDES ESSENTIELLES :

# Hôte -> Conteneur
docker cp file.txt container:/path/

# Conteneur -> Hôte
docker cp container:/path/file.txt ./

# Dossier complet
docker cp container:/folder/ ./local-folder/

# Avec permissions
docker cp -a container:/file ./

# Streaming
docker cp container:/file - | tar -xO


QUAND UTILISER docker cp ?
[OK] Backup/restauration ponctuelle
[OK] Hot-fix urgent en production
[OK] Extraction de logs
[OK] Débogage de conteneur crashé
[OK] Migration de données

QUAND NE PAS UTILISER docker cp ?
[X] Développement régulier -> Volumes !
[X] Données permanentes -> Volumes !
[X] Configuration d'image -> Dockerfile !
[X] Fichiers énormes -> Téléchargement dans conteneur


═══════════════════════════════════════════════════════════════════════════
  FIN DE LA PARTIE 8
═══════════════════════════════════════════════════════════════════════════

RÉSUMÉ DE CETTE SECTION :

1. docker exec : Exécuter commandes dans conteneur actif
   - docker exec -it container bash (shell interactif)
   - docker exec container ls (commande simple)
   - Options : -u (user), -w (workdir), -e (env)

2. docker attach : S'attacher au processus principal
   - Voir sortie en temps réel
   - [ATTENTION] Ctrl+C tue le conteneur !
   - Détacher : Ctrl+P puis Ctrl+Q

3. docker cp : Copier fichiers hôte <-> conteneur
   - docker cp file container:/path
   - docker cp container:/path file
   - Fonctionne sur conteneurs arrêtés aussi !

PROCHAINE SECTION :
Partie 9 - Gestion des images Docker (build, tag, push, pull)


================================================================================
          DOCKER - PORTS ET RÉSEAUX POUR DÉBUTANTS (PARTIE 9)
                Mapping de Ports et Gestion des Réseaux
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION À CETTE SECTION                          ║
╚══════════════════════════════════════════════════════════════════════════╝

OBJECTIF :
Maîtriser la communication entre conteneurs et avec le monde extérieur.

CE QUE VOUS ALLEZ APPRENDRE :
1. Mapper des ports (exposer services)
2. Gérer les réseaux Docker
3. Faire communiquer des conteneurs entre eux
4. Isoler et sécuriser les communications

PRÉREQUIS :
[OK] Comprendre docker run
[OK] Connaître les bases des réseaux (IP, ports)
[OK] Savoir ce qu'est un serveur web

CAS D'USAGE :
• Exposer une application web au public
• Connecter une app à une base de données
• Créer des microservices qui communiquent
• Isoler des environnements (dev, test, prod)
• Sécuriser les communications internes


================================================================================
[OK] SECTION 1 : MAPPING DE PORTS - EXPOSER DES SERVICES
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Ports Docker                                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Relier un port du conteneur à un port de l'hôte                  │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI mapper des ports ?
• Rendre un service accessible depuis l'extérieur
• Accéder à une application web dans le navigateur
• Permettre les connexions à une base de données
• Exposer une API REST
• Tester localement avant déploiement

ANALOGIE :
Un conteneur = Une maison avec des portes (ports)
Par défaut : Toutes les portes sont fermées de l'extérieur
Mapper un port = Créer une porte d'entrée accessible


CONCEPT FONDAMENTAL :

┌──────────────────────────────────────────────────────────────────────────┐
│                         HÔTE (votre machine)                             │
│                                                                          │
│  Port 8080 <-─────┐                                                       │
│                  │  Mapping                                              │
│            ┌─────┴─────────────────────┐                                │
│            │     CONTENEUR              │                                │
│            │                            │                                │
│            │  nginx écoute sur port 80  │                                │
│            │                            │                                │
│            └────────────────────────────┘                                │
│                                                                          │
│  Accès : http://localhost:8080  ->  redirige vers port 80 du conteneur   │
└──────────────────────────────────────────────────────────────────────────┘

SANS mapping : Impossible d'accéder au service
AVEC mapping : http://localhost:8080 fonctionne !


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker run -p (port mapping)                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

SYNTAXE :
docker run -p [HOST_IP:]HOST_PORT:CONTAINER_PORT[/PROTOCOL] IMAGE

COMPOSANTS :
HOST_IP         -> Adresse IP de l'hôte (optionnel, défaut: 0.0.0.0)
HOST_PORT       -> Port sur l'hôte
CONTAINER_PORT  -> Port dans le conteneur
PROTOCOL        -> tcp ou udp (optionnel, défaut: tcp)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Mapping simple (basique)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -p 8080:80 --name mon-site-web nginx

EXPLICATION :
-d              -> Mode détaché (background)
-p 8080:80      -> Mappe port 8080 de l'hôte -> port 80 du conteneur
--name          -> Nom du conteneur
nginx           -> Image à utiliser

SCHÉMA :
┌─────────────┐
│   HÔTE      │
│ Port 8080   │  <-─── Vous accédez ici
└──────┬──────┘
       │ Mapping
       v
┌─────────────┐
│ CONTENEUR   │
│   Port 80   │  <-─── nginx écoute ici
└─────────────┘


VÉRIFICATION :
# 1. Vérifier que le conteneur tourne
docker ps

RÉSULTAT :
CONTAINER ID   IMAGE   PORTS                  NAMES
abc123...      nginx   0.0.0.0:8080->80/tcp   mon-site-web
                       ^
              Mapping visible ici !

# 2. Tester dans le navigateur
http://localhost:8080

RÉSULTAT :
Page d'accueil nginx s'affiche ! [OK]


# 3. Tester avec curl
curl http://localhost:8080

RÉSULTAT :
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...


NOTES IMPORTANTES :
• Port 8080 -> choisi par vous (peut être n'importe quel port libre)
• Port 80 -> défini par nginx (standard pour HTTP)
• localhost = votre machine
• Sans -p : nginx tourne mais est inaccessible !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Même port sur hôte et conteneur
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -p 3000:3000 --name mon-app-node myapp

EXPLICATION :
Port 3000 partout ! Cohérent et simple.

SCHÉMA :
HÔTE Port 3000  <-->  CONTENEUR Port 3000

ACCÈS :
http://localhost:3000


QUAND utiliser le même port ?
[OK] Application Node.js (souvent 3000)
[OK] API REST (3000, 5000, 8000)
[OK] Plus intuitif en développement

MAIS : Si port déjà occupé sur l'hôte, utilisez un port différent !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Mapper plusieurs ports
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application avec frontend et API

COMMANDE :
docker run -d \
  -p 3000:3000 \
  -p 8080:80 \
  --name mon-app-complete \
  myapp

EXPLICATION :
-p 3000:3000    -> API accessible sur localhost:3000
-p 8080:80      -> Frontend accessible sur localhost:8080

SCHÉMA :
┌─────────────────┐
│      HÔTE       │
│  Port 3000      │  <-─── API
│  Port 8080      │  <-─── Frontend
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
┌───[BLACK_DOWN-POINTING_TRIANGLE]──┐  ┌──[BLACK_DOWN-POINTING_TRIANGLE]───┐
│ 3000 │  │  80  │  CONTENEUR
└──────┘  └──────┘


VÉRIFICATION :
docker port mon-app-complete

RÉSULTAT :
80/tcp -> 0.0.0.0:8080
3000/tcp -> 0.0.0.0:3000


ACCÈS :
Frontend : http://localhost:8080
API      : http://localhost:3000/api


EXEMPLE RÉEL : Application full-stack
docker run -d \
  -p 3000:3000 \
  -p 5432:5432 \
  -p 6379:6379 \
  --name stack-complete \
  mystack

Ports :
3000 -> API
5432 -> PostgreSQL
6379 -> Redis


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Mapper sur IP spécifique (127.0.0.1)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -p 127.0.0.1:8080:80 --name mon-site-local nginx

EXPLICATION :
127.0.0.1:8080:80
^
Écoute SEULEMENT sur localhost (pas sur réseau externe)


DIFFÉRENCE :

┌──────────────────────────────────────────────────────────────────────────┐
│ -p 8080:80                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ Équivalent : -p 0.0.0.0:8080:80                                          │
│ Écoute sur TOUTES les interfaces réseau                                 │
│                                                                          │
│ Accessible depuis :                                                      │
│ [OK] localhost:8080                                                        │
│ [OK] 192.168.1.100:8080 (IP locale)                                        │
│ [OK] Autres machines du réseau                                             │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ -p 127.0.0.1:8080:80                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ Écoute SEULEMENT sur localhost                                           │
│                                                                          │
│ Accessible depuis :                                                      │
│ [OK] localhost:8080                                                        │
│ [X] 192.168.1.100:8080 (pas accessible)                                   │
│ [X] Autres machines du réseau (pas accessible)                            │
└──────────────────────────────────────────────────────────────────────────┘


QUAND utiliser 127.0.0.1 ?
[OK] Développement local seulement
[OK] Sécurité : empêche accès externe
[OK] Services internes (DB, cache)

EXEMPLE : Base de données
docker run -d -p 127.0.0.1:5432:5432 postgres

PostgreSQL accessible seulement depuis votre machine ! [VERROUILLE]


VÉRIFICATION :
# Fonctionne
curl http://localhost:8080

# Ne fonctionne pas (depuis une autre machine)
curl http://192.168.1.100:8080
-> Connection refused


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Ports aléatoires (-P majuscule)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -P --name mon-site nginx

EXPLICATION :
-P (majuscule) -> Docker choisit automatiquement des ports libres

[ATTENTION] DIFFÉRENCE :
-p (minuscule) -> Vous choisissez les ports
-P (majuscule) -> Docker choisit automatiquement


COMMENT ça marche ?
Docker regarde les ports EXPOSÉS dans l'image (via EXPOSE dans Dockerfile)
et les mappe sur des ports aléatoires de l'hôte (généralement 32768-60999).


VÉRIFICATION :
docker ps

RÉSULTAT :
CONTAINER ID   IMAGE   PORTS                    NAMES
abc123...      nginx   0.0.0.0:32768->80/tcp    mon-site
                       ^
              Port aléatoire choisi par Docker


TROUVER LE PORT ATTRIBUÉ :
docker port mon-site

RÉSULTAT :
80/tcp -> 0.0.0.0:32768

Le port 80 du conteneur est mappé sur le port 32768 de l'hôte.


ACCÈS :
http://localhost:32768


QUAND utiliser -P ?
[OK] Tests rapides
[OK] Éviter les conflits de ports
[OK] CI/CD avec plusieurs conteneurs simultanés

QUAND NE PAS utiliser -P ?
[X] Production (ports doivent être prévisibles)
[X] Documentation (ports changent à chaque démarrage)
[X] Configuration frontend (URL avec port aléatoire ?)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Protocole UDP
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Services utilisant UDP (DNS, VPN, jeux)

COMMANDE :
docker run -d -p 53:53/udp --name mon-dns dnsmasq

EXPLICATION :
53:53/udp
      ^
   Spécifie UDP (défaut: tcp)


COMMANDE : UDP et TCP en même temps
docker run -d \
  -p 53:53/tcp \
  -p 53:53/udp \
  --name mon-dns-complet \
  dnsmasq

DNS nécessite souvent les deux protocoles.


VÉRIFICATION :
docker port mon-dns-complet

RÉSULTAT :
53/tcp -> 0.0.0.0:53
53/udp -> 0.0.0.0:53


EXEMPLES COURANTS :

# DNS
-p 53:53/udp

# VPN
-p 1194:1194/udp

# Jeu vidéo
-p 27015:27015/udp

# Streaming vidéo
-p 5000:5000/udp


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Plage de ports
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -p 3000-3005:3000-3005 --name mon-app myapp

EXPLICATION :
Mappe 6 ports d'un coup :
3000 -> 3000
3001 -> 3001
3002 -> 3002
3003 -> 3003
3004 -> 3004
3005 -> 3005


VÉRIFICATION :
docker port mon-app

RÉSULTAT :
3000/tcp -> 0.0.0.0:3000
3001/tcp -> 0.0.0.0:3001
3002/tcp -> 0.0.0.0:3002
3003/tcp -> 0.0.0.0:3003
3004/tcp -> 0.0.0.0:3004
3005/tcp -> 0.0.0.0:3005


QUAND utiliser ?
[OK] Clusters (plusieurs instances)
[OK] Services avec ports dynamiques
[OK] FTP (mode passif)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Voir les ports mappés (docker port)
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Voir tous les ports d'un conteneur
docker port mon-site-web

RÉSULTAT :
80/tcp -> 0.0.0.0:8080
443/tcp -> 0.0.0.0:8443


COMMANDE : Voir mapping d'un port spécifique
docker port mon-site-web 80

RÉSULTAT :
0.0.0.0:8080


COMMANDE : Lister ports de tous les conteneurs
docker ps --format "table {{.Names}}\t{{.Ports}}"

RÉSULTAT :
NAMES              PORTS
mon-site-web       0.0.0.0:8080->80/tcp
ma-db              0.0.0.0:5432->5432/tcp
mon-cache          0.0.0.0:6379->6379/tcp


SCRIPT : Afficher tous les ports mappés
#!/bin/bash
for container in $(docker ps --format '{{.Names}}'); do
    echo "=== $container ==="
    docker port $container
    echo
done


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Cas d'usage - Stack web complète
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application avec frontend, API et base de données

# 1. Base de données (interne seulement)
docker run -d \
  --name ma-db \
  -p 127.0.0.1:5432:5432 \
  -e POSTGRES_PASSWORD=secret \
  postgres

# 2. Backend API (accessible localement)
docker run -d \
  --name mon-api \
  -p 127.0.0.1:3000:3000 \
  -e DB_HOST=ma-db \
  myapi

# 3. Frontend (accessible publiquement)
docker run -d \
  --name mon-frontend \
  -p 80:80 \
  -p 443:443 \
  myfrontend


RÉSULTAT :
┌─────────────────────────────────────────────────┐
│             INTERNET / RÉSEAU LOCAL             │
└───────────────────┬─────────────────────────────┘
                    │
          ┌─────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
          │   Port 80, 443     │  Frontend (public)
          └────────────────────┘
                    │
          ┌─────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
          │   Port 3000        │  API (local only)
          │   127.0.0.1        │
          └────────────────────┘
                    │
          ┌─────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
          │   Port 5432        │  DB (local only)
          │   127.0.0.1        │
          └────────────────────┘


ACCÈS :
Frontend : http://localhost (tout le monde)
API      : http://localhost:3000 (seulement votre machine)
DB       : localhost:5432 (seulement votre machine)


═══════════════════════════════════════════════════════════════════════════
  BONNES PRATIQUES - Ports
═══════════════════════════════════════════════════════════════════════════

[OK] À FAIRE :

1. Documenter les ports utilisés
   # README.md
   - Frontend : http://localhost:3000
   - API : http://localhost:8080
   - DB : localhost:5432

2. Utiliser ports standards pour services connus
   - HTTP : 80
   - HTTPS : 443
   - PostgreSQL : 5432
   - MySQL : 3306
   - Redis : 6379
   - MongoDB : 27017

3. Éviter conflits de ports
   docker ps --format '{{.Ports}}'  # Voir ports utilisés
   lsof -i :8080                    # Vérifier si port libre

4. Sécuriser avec 127.0.0.1 quand approprié
   docker run -p 127.0.0.1:5432:5432 postgres

5. Utiliser variables d'environnement
   PORT=8080
   docker run -p $PORT:80 nginx


[X] À ÉVITER :

1. Mapper tous les ports publiquement
   [X] docker run -p 5432:5432 postgres
   [OK] docker run -p 127.0.0.1:5432:5432 postgres

2. Ports aléatoires (-P) en production
   [X] docker run -P myapp
   [OK] docker run -p 3000:3000 myapp

3. Oublier de mapper les ports
   docker run -d nginx
   -> Inaccessible ! [X]

4. Conflits de ports
   docker run -p 8080:80 nginx
   docker run -p 8080:80 apache  <- Erreur !

5. Mapper ports privilégiés (<1024) sans permission
   docker run -p 80:80 nginx
   -> Peut nécessiter sudo selon OS


═══════════════════════════════════════════════════════════════════════════
  PIÈGES COURANTS - Ports
═══════════════════════════════════════════════════════════════════════════

[X] PIÈGE 1 : Port déjà utilisé

docker run -p 8080:80 nginx

ERREUR :
Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use

[OK] SOLUTION 1 : Trouver quel processus utilise le port
# Linux/Mac
lsof -i :8080
sudo netstat -tulpn | grep 8080

# Windows
netstat -ano | findstr :8080

[OK] SOLUTION 2 : Utiliser un port différent
docker run -p 8081:80 nginx


[X] PIÈGE 2 : Inverser les ports

docker run -p 80:8080 nginx

Nginx écoute sur 80, pas 8080 ! [X]

[OK] CORRECT :
docker run -p 8080:80 nginx
           ^    ^
        Hôte Conteneur


[X] PIÈGE 3 : Oublier le mapping

docker run -d --name mon-site nginx
curl http://localhost:80
-> Connection refused

[OK] SOLUTION :
docker run -d -p 8080:80 --name mon-site nginx


[X] PIÈGE 4 : Firewall bloque les connexions

docker run -p 8080:80 nginx
# Depuis autre machine : connection refused

[OK] VÉRIFIER :
# Firewall autorise le port ?
sudo ufw allow 8080  # Ubuntu
sudo firewall-cmd --add-port=8080/tcp  # CentOS


[X] PIÈGE 5 : Conteneur écoute sur mauvais port

docker run -p 8080:80 myapp
# Mais myapp écoute sur port 3000 !

[OK] SOLUTION :
docker run -p 8080:3000 myapp
OU modifier l'app pour écouter sur 80


================================================================================
[OK] SECTION 2 : RÉSEAUX DOCKER - CONNECTER DES CONTENEURS
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Réseaux Docker                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Réseaux virtuels pour faire communiquer les conteneurs           │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI utiliser des réseaux ?
• Faire communiquer conteneurs entre eux
• Isoler des groupes de conteneurs
• Utiliser des noms de domaine au lieu d'IP
• Sécuriser les communications
• Architectures microservices

ANALOGIE :
Réseau Docker = Un réseau local (LAN)
Conteneurs = Ordinateurs sur ce réseau
Par défaut : Peuvent se parler
Réseaux différents = Isolés les uns des autres


TYPES DE RÉSEAUX DOCKER :

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. BRIDGE (par défaut)                                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ • Réseau privé isolé                                                     │
│ • Conteneurs peuvent communiquer par nom                                 │
│ • Utilisé pour la plupart des cas                                        │
│                                                                          │
│ USAGE : Applications multi-conteneurs sur un seul hôte                  │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 2. HOST                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ • Partage le réseau de l'hôte                                            │
│ • Pas d'isolation réseau                                                 │
│ • Performance maximale                                                   │
│                                                                          │
│ USAGE : Monitoring, outils réseau                                       │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 3. NONE                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│ • Aucun réseau                                                           │
│ • Isolation complète                                                     │
│ • Seulement interface loopback (localhost)                               │
│                                                                          │
│ USAGE : Sécurité maximale, traitement de données sensibles              │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 4. OVERLAY                                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • Réseau distribué sur plusieurs hôtes                                   │
│ • Docker Swarm / Kubernetes                                              │
│                                                                          │
│ USAGE : Clusters multi-hôtes                                            │
└──────────────────────────────────────────────────────────────────────────┘


RÉSEAU PAR DÉFAUT :

Quand vous lancez un conteneur sans spécifier de réseau :
docker run nginx

Il est automatiquement connecté au réseau "bridge" par défaut.


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker network ls                                            ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Lister tous les réseaux Docker                                   │
└──────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Lister les réseaux
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker network ls

RÉSULTAT :
NETWORK ID     NAME      DRIVER    SCOPE
abc123...      bridge    bridge    local
def456...      host      host      local
ghi789...      none      null      local

EXPLICATION :
bridge -> Réseau par défaut pour conteneurs
host   -> Réseau de l'hôte (pas d'isolation)
none   -> Aucun réseau


COLONNES :
NETWORK ID -> Identifiant unique
NAME       -> Nom du réseau
DRIVER     -> Type de réseau (bridge, host, overlay, etc.)
SCOPE      -> Portée (local, swarm, global)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Inspecter un réseau
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker network inspect bridge

RÉSULTAT :
[
    {
        "Name": "bridge",
        "Id": "abc123...",
        "Driver": "bridge",
        "Scope": "local",
        "IPAM": {
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Containers": {
            "def456...": {
                "Name": "mon-site-web",
                "IPv4Address": "172.17.0.2/16",
                ...
            }
        }
    }
]


INFORMATIONS UTILES :
• Subnet : Plage d'adresses IP (172.17.0.0/16)
• Gateway : Passerelle (172.17.0.1)
• Containers : Conteneurs connectés à ce réseau


COMMANDE : Voir seulement les conteneurs
docker network inspect bridge --format='{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}'

RÉSULTAT :
mon-site-web 172.17.0.2/16
ma-db 172.17.0.3/16


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Créer un réseau personnalisé
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Création simple
docker network create mon-reseau

RÉSULTAT :
abc123def456...  (ID du réseau créé)


VÉRIFICATION :
docker network ls

RÉSULTAT :
NETWORK ID     NAME         DRIVER    SCOPE
abc123...      bridge       bridge    local
def456...      host         host      local
ghi789...      none         null      local
jkl012...      mon-reseau   bridge    local
               ^
           Nouveau réseau !


COMMANDE : Avec options explicites
docker network create --driver bridge mon-reseau-app

EXPLICATION :
--driver bridge -> Type de réseau (défaut si non spécifié)


COMMANDE : Avec subnet personnalisé
docker network create \
  --subnet=172.20.0.0/16 \
  --gateway=172.20.0.1 \
  mon-reseau-custom

EXPLICATION :
--subnet    -> Plage d'adresses IP
--gateway   -> Adresse de la passerelle


VÉRIFICATION :
docker network inspect mon-reseau-custom --format='{{.IPAM.Config}}'

RÉSULTAT :
[{172.20.0.0/16  172.20.0.1 map[]}]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Lancer conteneur sur réseau personnalisé
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d \
  --name mon-site \
  --network mon-reseau \
  nginx

EXPLICATION :
--network mon-reseau -> Connecte au réseau personnalisé

VÉRIFICATION :
docker network inspect mon-reseau

RÉSULTAT :
"Containers": {
    "abc123...": {
        "Name": "mon-site",
        "IPv4Address": "172.20.0.2/16",
        ...
    }
}

Le conteneur est sur le réseau personnalisé ! [OK]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Communication entre conteneurs (RÉSOLUTION DNS)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application web + base de données

ÉTAPE 1 : Créer un réseau
docker network create app-network

ÉTAPE 2 : Lancer base de données
docker run -d \
  --name ma-db \
  --network app-network \
  -e POSTGRES_PASSWORD=secret \
  postgres

ÉTAPE 3 : Lancer application
docker run -d \
  --name mon-app \
  --network app-network \
  -e DB_HOST=ma-db \
  -e DB_PORT=5432 \
  -e DB_PASSWORD=secret \
  myapp


MAGIE : Résolution DNS automatique !

L'application peut se connecter à la DB avec :
DB_HOST=ma-db
        ^
    Nom du conteneur !

Docker résout automatiquement "ma-db" vers l'IP du conteneur.


VÉRIFICATION : Test de connexion
docker exec mon-app ping ma-db

RÉSULTAT :
PING ma-db (172.20.0.2) 56(84) bytes of data.
64 bytes from ma-db.app-network (172.20.0.2): icmp_seq=1 ttl=64 time=0.123 ms

La résolution DNS fonctionne ! [OK]


SCHÉMA :
┌──────────────────────────────────────┐
│      Réseau: app-network             │
│                                      │
│  ┌─────────────┐   ┌──────────────┐ │
│  │   mon-app   │──[BLACK_RIGHT-POINTING_TRIANGLE]│    ma-db     │ │
│  │ 172.20.0.3  │   │  172.20.0.2  │ │
│  └─────────────┘   └──────────────┘ │
│                                      │
│  mon-app se connecte via "ma-db"    │
│  (pas besoin de connaître l'IP!)    │
└──────────────────────────────────────┘


AVANTAGE :
Pas besoin de connaître l'IP !
Utilisez le nom du conteneur comme hostname.


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Connecter conteneur existant à un réseau
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Conteneur déjà lancé, besoin de le connecter à un réseau

COMMANDE :
docker network connect app-network mon-cache

EXPLICATION :
Connecte le conteneur "mon-cache" au réseau "app-network"


VÉRIFICATION :
docker network inspect app-network

RÉSULTAT :
"Containers": {
    "abc123...": {
        "Name": "mon-app",
        "IPv4Address": "172.20.0.3/16"
    },
    "def456...": {
        "Name": "ma-db",
        "IPv4Address": "172.20.0.2/16"
    },
    "ghi789...": {
        "Name": "mon-cache",
        "IPv4Address": "172.20.0.4/16"
    }
}

Trois conteneurs sont maintenant sur le même réseau !


COMMANDE : Vérifier les réseaux d'un conteneur
docker inspect mon-cache --format='{{range $net,$v := .NetworkSettings.Networks}}{{$net}} {{end}}'

RÉSULTAT :
bridge app-network

Le conteneur est sur 2 réseaux : bridge (défaut) et app-network !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Déconnecter d'un réseau
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker network disconnect app-network mon-cache

EXPLICATION :
Retire le conteneur du réseau "app-network"


VÉRIFICATION :
docker network inspect app-network

RÉSULTAT :
"Containers": {
    "abc123...": {
        "Name": "mon-app",
        ...
    },
    "def456...": {
        "Name": "ma-db",
        ...
    }
}

"mon-cache" n'est plus dans la liste ! [OK]


USAGE :
• Isoler un conteneur
• Reconfigurer les connexions
• Sécurité (retirer accès)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Supprimer un réseau
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker network rm mon-reseau

RÉSULTAT :
mon-reseau


ERREUR SI CONTENEURS CONNECTÉS :
docker network rm app-network

ERREUR :
Error response from daemon: network app-network has active endpoints

[OK] SOLUTION : Déconnecter ou arrêter les conteneurs d'abord
docker stop mon-app ma-db
docker network rm app-network


VÉRIFICATION :
docker network ls | grep app-network

RÉSULTAT :
(vide - le réseau a été supprimé)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Nettoyer réseaux inutilisés (prune)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker network prune

RÉSULTAT :
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y

Deleted Networks:
old-network
test-network
unused-network


COMMANDE : Sans confirmation
docker network prune -f

UTILITÉ :
Nettoyer les réseaux créés pour des tests


[ATTENTION] ATTENTION :
Supprime TOUS les réseaux sans conteneurs connectés !
Les réseaux bridge, host, none (par défaut) ne sont jamais supprimés.


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Réseau host (partage réseau de l'hôte)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d --network host --name mon-app-host nginx

EXPLICATION :
--network host -> Le conteneur utilise directement le réseau de l'hôte


DIFFÉRENCE AVEC BRIDGE :

┌──────────────────────────────────────────────────────────────────────────┐
│ BRIDGE (défaut)                                                          │
├──────────────────────────────────────────────────────────────────────────┤
│ docker run -d -p 8080:80 nginx                                           │
│                                                                          │
│ • Conteneur a sa propre IP                                               │
│ • Port mapping nécessaire                                                │
│ • Isolation réseau                                                       │
│ • Accès : http://localhost:8080                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ HOST                                                                     │
├──────────────────────────────────────────────────────────────────────────┤
│ docker run -d --network host nginx                                       │
│                                                                          │
│ • Conteneur partage IP de l'hôte                                         │
│ • PAS de port mapping (automatique)                                      │
│ • Pas d'isolation réseau                                                 │
│ • Accès : http://localhost:80 (directement)                              │
└──────────────────────────────────────────────────────────────────────────┘


VÉRIFICATION :
# Nginx écoute sur port 80 de l'hôte directement
curl http://localhost:80

RÉSULTAT :
Welcome to nginx!


[ATTENTION] ATTENTION :
Avec --network host, l'option -p est IGNORÉE !
docker run -d --network host -p 8080:80 nginx
                                ^
                        Cette option ne fait rien !


QUAND utiliser host ?
[OK] Performance maximale (pas de NAT)
[OK] Outils de monitoring réseau
[OK] Services nécessitant accès direct au réseau

QUAND NE PAS utiliser host ?
[X] Isolation nécessaire
[X] Plusieurs conteneurs sur même port
[X] Sécurité (conteneur a accès complet au réseau)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 11 : Réseau none (aucun réseau)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d --network none --name mon-app-isole alpine sleep 3600

EXPLICATION :
--network none -> Aucune connectivité réseau


VÉRIFICATION :
docker exec mon-app-isole ip addr

RÉSULTAT :
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    inet 127.0.0.1/8 scope host lo

Seulement l'interface loopback (localhost) !


TEST :
docker exec mon-app-isole ping google.com

RÉSULTAT :
ping: bad address 'google.com'

Aucune connectivité ! [OK]


QUAND utiliser none ?
[OK] Traitement de données sensibles (isolation complète)
[OK] Sécurité maximale
[OK] Tâches batch sans besoin réseau


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 12 : Architecture multi-conteneurs complète
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application web avec frontend, backend, DB et cache

# ÉTAPE 1 : Créer deux réseaux (frontend et backend)
docker network create frontend-network
docker network create backend-network

# ÉTAPE 2 : Lancer base de données (backend seulement)
docker run -d \
  --name postgres \
  --network backend-network \
  -e POSTGRES_PASSWORD=secret \
  postgres

# ÉTAPE 3 : Lancer cache (backend seulement)
docker run -d \
  --name redis \
  --network backend-network \
  redis

# ÉTAPE 4 : Lancer API (backend ET frontend)
docker run -d \
  --name api \
  --network backend-network \
  -e DB_HOST=postgres \
  -e REDIS_HOST=redis \
  myapi

# Connecter API au frontend aussi
docker network connect frontend-network api

# ÉTAPE 5 : Lancer frontend (frontend seulement)
docker run -d \
  --name web \
  --network frontend-network \
  -p 80:80 \
  -e API_URL=http://api:3000 \
  myfrontend


SCHÉMA DE L'ARCHITECTURE :

┌───────────────────────────────────────────────────────────────┐
│                        INTERNET                               │
└──────────────────────────┬────────────────────────────────────┘
                           │
                    Port 80 (public)
                           │
┌──────────────────────────[BLACK_DOWN-POINTING_TRIANGLE]────────────────────────────────────┐
│              RÉSEAU: frontend-network                         │
│                                                               │
│    ┌─────────────┐              ┌─────────────┐              │
│    │     web     │─────────────[BLACK_RIGHT-POINTING_TRIANGLE]│     api     │              │
│    │   (nginx)   │              │  (Node.js)  │              │
│    └─────────────┘              └──────┬──────┘              │
│         Public                         │                      │
└────────────────────────────────────────┼──────────────────────┘
                                         │
┌────────────────────────────────────────[BLACK_DOWN-POINTING_TRIANGLE]──────────────────────┐
│              RÉSEAU: backend-network                          │
│                                                               │
│         ┌──────────────┐          ┌──────────────┐           │
│         │   postgres   │          │    redis     │           │
│         │     (DB)     │          │   (cache)    │           │
│         └──────────────┘          └──────────────┘           │
│              Privé                      Privé                 │
└───────────────────────────────────────────────────────────────┘


RÉSULTAT :
• Frontend accessible publiquement (port 80)
• Frontend peut parler à API
• API peut parler à DB et Cache
• DB et Cache ISOLÉS du frontend (sécurité)
• Tout communique par noms (postgres, redis, api)


VÉRIFICATIONS :

# Frontend peut atteindre API
docker exec web ping api
-> [OK] Fonctionne

# Frontend NE PEUT PAS atteindre DB
docker exec web ping postgres
-> [X] ping: bad address (isolé !)

# API peut atteindre DB
docker exec api ping postgres
-> [OK] Fonctionne

# API peut atteindre Cache
docker exec api ping redis
-> [OK] Fonctionne


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 13 : Alias réseau
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Ajouter un alias lors de la connexion
docker network connect --alias db --alias database backend-network postgres

EXPLICATION :
Le conteneur "postgres" est maintenant accessible via :
- postgres (nom du conteneur)
- db (alias)
- database (alias)


VÉRIFICATION :
docker exec api ping db

RÉSULTAT :
PING db (172.20.0.2) ...

docker exec api ping database

RÉSULTAT :
PING database (172.20.0.2) ...

Tous les noms pointent vers le même conteneur ! [OK]


UTILITÉ :
• Noms plus courts
• Compatibilité avec legacy code
• Multiples noms pour même service


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 14 : IP statique
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Créer réseau avec plage
docker network create --subnet=172.30.0.0/16 mon-reseau-fixe

COMMANDE : Lancer conteneur avec IP fixe
docker run -d \
  --name mon-app \
  --network mon-reseau-fixe \
  --ip 172.30.0.100 \
  nginx


VÉRIFICATION :
docker inspect mon-app --format='{{.NetworkSettings.Networks.mon_reseau_fixe.IPAddress}}'

RÉSULTAT :
172.30.0.100


QUAND utiliser ?
[OK] Configuration legacy nécessitant IP fixe
[OK] Firewall basé sur IP
[OK] Tests réseau spécifiques

MAIS : Préférez les noms de conteneurs (DNS) ! Plus flexible.


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 15 : Lister conteneurs d'un réseau
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Format simple
docker network inspect app-network --format='{{range .Containers}}{{.Name}} {{end}}'

RÉSULTAT :
mon-app ma-db mon-cache


COMMANDE : Avec adresses IP
docker network inspect app-network \
  --format='{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{println}}{{end}}'

RÉSULTAT :
mon-app: 172.20.0.3/16
ma-db: 172.20.0.2/16
mon-cache: 172.20.0.4/16


SCRIPT : Afficher tous les réseaux et leurs conteneurs
#!/bin/bash
for network in $(docker network ls --format '{{.Name}}'); do
    echo "=== Réseau: $network ==="
    docker network inspect $network \
      --format='{{range .Containers}}  - {{.Name}} ({{.IPv4Address}}){{println}}{{end}}'
    echo
done


═══════════════════════════════════════════════════════════════════════════
  BONNES PRATIQUES - Réseaux
═══════════════════════════════════════════════════════════════════════════

[OK] À FAIRE :

1. Utiliser réseaux personnalisés (pas le bridge par défaut)
   [OK] docker network create app-network
   [OK] docker run --network app-network nginx
   [X] docker run nginx  (utilise bridge par défaut)

   POURQUOI ?
   • Résolution DNS automatique par nom
   • Meilleure isolation
   • Plus de contrôle

2. Nommer les réseaux de façon descriptive
   [OK] docker network create frontend-network
   [OK] docker network create backend-network
   [OK] docker network create mon-app-prod-network
   [X] docker network create net1

3. Isoler les services sensibles
   • DB et cache -> Réseau backend privé
   • Frontend -> Réseau public
   • API -> Les deux réseaux

4. Utiliser noms de conteneurs pour communication
   [OK] DB_HOST=postgres  (nom du conteneur)
   [X] DB_HOST=172.20.0.2  (IP peut changer)

5. Documenter l'architecture réseau
   # README.md ou docker-compose.yml
   # Réseau frontend : web, api
   # Réseau backend : api, db, cache

6. Nettoyer réseaux inutilisés régulièrement
   docker network prune


[X] À ÉVITER :

1. Tout mettre sur le réseau bridge par défaut
   [X] Pas de résolution DNS entre conteneurs
   [X] Moins de contrôle

2. Utiliser --network host en production
   [X] Pas d'isolation
   [X] Conflits de ports possibles
   [X] Risque sécurité

3. Hardcoder des adresses IP
   [X] DB_HOST=172.20.0.5
   [OK] DB_HOST=postgres

4. Oublier de créer des réseaux séparés
   [X] Tout accessible par tout le monde
   [OK] Isolation par couches (frontend/backend)

5. Laisser des réseaux inutilisés
   Consomme des ressources
   -> docker network prune


═══════════════════════════════════════════════════════════════════════════
  PIÈGES COURANTS - Réseaux
═══════════════════════════════════════════════════════════════════════════

[X] PIÈGE 1 : Conteneurs sur réseau bridge par défaut

docker run --name app nginx
docker run --name db postgres

docker exec app ping db

ERREUR :
ping: bad address 'db'

POURQUOI ?
Le réseau bridge par défaut ne supporte PAS la résolution DNS !

[OK] SOLUTION :
docker network create mon-reseau
docker run --name app --network mon-reseau nginx
docker run --name db --network mon-reseau postgres
docker exec app ping db  -> [OK] Fonctionne !


[X] PIÈGE 2 : Conteneurs sur réseaux différents

docker network create net1
docker network create net2

docker run -d --name app1 --network net1 nginx
docker run -d --name app2 --network net2 nginx

docker exec app1 ping app2

ERREUR :
ping: bad address 'app2'

POURQUOI ?
Réseaux différents = Isolation complète !

[OK] SOLUTION :
# Connecter app1 au réseau de app2
docker network connect net2 app1
docker exec app1 ping app2  -> [OK] Fonctionne !


[X] PIÈGE 3 : Supprimer réseau avec conteneurs actifs

docker network rm app-network

ERREUR :
Error: network app-network has active endpoints

[OK] SOLUTION :
# Option 1 : Arrêter conteneurs
docker stop $(docker ps -q --filter network=app-network)
docker network rm app-network

# Option 2 : Déconnecter puis supprimer
docker network disconnect app-network conteneur1
docker network disconnect app-network conteneur2
docker network rm app-network


[X] PIÈGE 4 : Conflit de subnet

docker network create --subnet=172.17.0.0/16 mon-reseau

ERREUR :
Error response from daemon: Pool overlaps with other one on this address space

POURQUOI ?
Le subnet 172.17.0.0/16 est déjà utilisé par le réseau bridge !

[OK] SOLUTION :
Utilisez un subnet différent :
docker network create --subnet=172.30.0.0/16 mon-reseau


[X] PIÈGE 5 : Oublier que --network host ignore -p

docker run -d --network host -p 8080:80 nginx

L'option -p 8080:80 est IGNORÉE !
nginx écoute directement sur le port 80 de l'hôte.

[OK] COMPRENDRE :
Avec --network host, pas de mapping nécessaire (ni possible).


═══════════════════════════════════════════════════════════════════════════
  CAS D'USAGE COMPLETS
═══════════════════════════════════════════════════════════════════════════

CAS 1 : Application WordPress complète
──────────────────────────────────────

# 1. Créer réseau
docker network create wordpress-network

# 2. Lancer MySQL
docker run -d \
  --name wp-db \
  --network wordpress-network \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wpuser \
  -e MYSQL_PASSWORD=wppass \
  mysql:8.0

# 3. Lancer WordPress
docker run -d \
  --name wp-site \
  --network wordpress-network \
  -p 8080:80 \
  -e WORDPRESS_DB_HOST=wp-db \
  -e WORDPRESS_DB_USER=wpuser \
  -e WORDPRESS_DB_PASSWORD=wppass \
  -e WORDPRESS_DB_NAME=wordpress \
  wordpress:latest

# Accès : http://localhost:8080


CAS 2 : Stack Node.js + MongoDB + Redis
────────────────────────────────────────

# 1. Créer réseau
docker network create app-stack

# 2. MongoDB
docker run -d \
  --name mongo \
  --network app-stack \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  mongo:7

# 3. Redis
docker run -d \
  --name redis \
  --network app-stack \
  redis:alpine

# 4. Application Node.js
docker run -d \
  --name app \
  --network app-stack \
  -p 3000:3000 \
  -e MONGO_URL=mongodb://admin:secret@mongo:27017 \
  -e REDIS_URL=redis://redis:6379 \
  mon-app-node

# L'app se connecte via "mongo" et "redis" (DNS automatique)


CAS 3 : Microservices avec isolation
─────────────────────────────────────

# Réseau pour services publics
docker network create public-network

# Réseau pour services internes
docker network create private-network

# API Gateway (public + private)
docker run -d \
  --name gateway \
  --network public-network \
  -p 80:80 \
  api-gateway

docker network connect private-network gateway

# Service utilisateurs (private seulement)
docker run -d \
  --name user-service \
  --network private-network \
  user-service

# Service produits (private seulement)
docker run -d \
  --name product-service \
  --network private-network \
  product-service

# Base de données (private seulement)
docker run -d \
  --name database \
  --network private-network \
  postgres

RÉSULTAT :
• Gateway accessible depuis Internet (port 80)
• Gateway peut communiquer avec tous les services
• Services et DB isolés du réseau public
• Services communiquent entre eux via réseau privé


═══════════════════════════════════════════════════════════════════════════
  DÉBOGAGE RÉSEAU
═══════════════════════════════════════════════════════════════════════════

PROBLÈME : Conteneur ne peut pas joindre un autre

ÉTAPE 1 : Vérifier qu'ils sont sur le même réseau
docker inspect conteneur1 --format='{{json .NetworkSettings.Networks}}' | jq
docker inspect conteneur2 --format='{{json .NetworkSettings.Networks}}' | jq

ÉTAPE 2 : Tester la connectivité
docker exec conteneur1 ping conteneur2

ÉTAPE 3 : Vérifier résolution DNS
docker exec conteneur1 nslookup conteneur2
docker exec conteneur1 dig conteneur2

ÉTAPE 4 : Vérifier IP
docker inspect conteneur2 --format='{{.NetworkSettings.Networks.mon_reseau.IPAddress}}'

ÉTAPE 5 : Installer outils réseau si nécessaire
docker exec -u root conteneur1 apt-get update
docker exec -u root conteneur1 apt-get install -y iputils-ping dnsutils curl

ÉTAPE 6 : Tester avec IP directement
docker exec conteneur1 ping 172.20.0.5


COMMANDE : Voir toutes les connexions réseau
docker inspect conteneur1 | jq '.[0].NetworkSettings.Networks'


═══════════════════════════════════════════════════════════════════════════
  RÉCAPITULATIF RAPIDE
═══════════════════════════════════════════════════════════════════════════

PORTS :
# Mapper port
docker run -p 8080:80 nginx              # Host:Container
docker run -p 127.0.0.1:8080:80 nginx    # IP spécifique
docker run -P nginx                      # Ports aléatoires
docker run -p 3000:3000 -p 80:80 app     # Multiple

# Voir ports
docker port container_name
docker ps --format '{{.Names}}: {{.Ports}}'


RÉSEAUX :
# Lister réseaux
docker network ls

# Créer réseau
docker network create mon-reseau
docker network create --subnet=172.20.0.0/16 mon-reseau

# Utiliser réseau
docker run --network mon-reseau nginx

# Connecter/déconnecter
docker network connect mon-reseau container
docker network disconnect mon-reseau container

# Inspecter
docker network inspect mon-reseau

# Supprimer
docker network rm mon-reseau
docker network prune  # Nettoyer inutilisés


COMMUNICATION :
# Entre conteneurs (même réseau)
Utilisez le NOM du conteneur comme hostname
DB_HOST=postgres (pas l'IP!)

# Types de réseaux
bridge -> Réseau par défaut, isolé
host   -> Partage réseau de l'hôte (performance)
none   -> Aucun réseau (isolation totale)


ARCHITECTURE TYPIQUE :
┌──────────────────────┐
│   Public Network     │  <- Frontend (port 80)
│   - web              │
│   - api-gateway      │
└──────────┬───────────┘
           │
┌──────────[BLACK_DOWN-POINTING_TRIANGLE]───────────┐
│  Private Network     │  <- Backend
│  - api               │
│  - database          │
│  - cache             │
└──────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  DIFFÉRENCES CLÉS : RÉSEAU BRIDGE PAR DÉFAUT VS PERSONNALISÉ
═══════════════════════════════════════════════════════════════════════════

┌──────────────────────────────────────────────────────────────────────────┐
│ RÉSEAU BRIDGE PAR DÉFAUT (docker run sans --network)                    │
├──────────────────────────────────────────────────────────────────────────┤
│ [X] PAS de résolution DNS automatique                                     │
│    docker exec app ping db -> ERREUR                                      │
│                                                                          │
│ [OK] Connexion via IP possible (mais IP change!)                           │
│    docker exec app ping 172.17.0.5 -> Fonctionne                         │
│                                                                          │
│ [X] Utilisation découragée pour applications multi-conteneurs             │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ RÉSEAU PERSONNALISÉ (docker network create mon-reseau)                  │
├──────────────────────────────────────────────────────────────────────────┤
│ [OK] Résolution DNS automatique                                            │
│    docker exec app ping db -> Fonctionne                                  │
│                                                                          │
│ [OK] Noms de conteneurs comme hostnames                                    │
│    DB_HOST=postgres (simple et clair)                                    │
│                                                                          │
│ [OK] Meilleure isolation                                                   │
│                                                                          │
│ [OK] Recommandé pour toutes les applications                               │
└──────────────────────────────────────────────────────────────────────────┘


EXEMPLE COMPARATIF :

# [X] MAUVAISE APPROCHE (bridge par défaut)
docker run -d --name db postgres
docker run -d --name app \
  -e DB_HOST=172.17.0.2 \  <- IP hardcodée!
  myapp

Problèmes :
• IP peut changer au redémarrage
• Pas de DNS
• Difficile à maintenir


# [OK] BONNE APPROCHE (réseau personnalisé)
docker network create app-network
docker run -d --name db --network app-network postgres
docker run -d --name app --network app-network \
  -e DB_HOST=db \  <- Nom simple!
  myapp

Avantages :
• DNS automatique
• IP transparente
• Facile à comprendre


═══════════════════════════════════════════════════════════════════════════
  TABLEAU RÉCAPITULATIF COMPLET
═══════════════════════════════════════════════════════════════════════════

PORTS :
╔════════════════════════════════╦═══════════════════════════════════════╗
║ COMMANDE                       ║ USAGE                                 ║
╠════════════════════════════════╬═══════════════════════════════════════╣
║ -p 8080:80                     ║ Mapper port (basique)                 ║
║ -p 127.0.0.1:8080:80           ║ Localhost seulement                   ║
║ -p 3000:3000 -p 80:80          ║ Multiple ports                        ║
║ -P                             ║ Ports aléatoires                      ║
║ -p 53:53/udp                   ║ Protocole UDP                         ║
║ docker port container          ║ Voir ports mappés                     ║
╚════════════════════════════════╩═══════════════════════════════════════╝


RÉSEAUX :
╔════════════════════════════════╦═══════════════════════════════════════╗
║ COMMANDE                       ║ USAGE                                 ║
╠════════════════════════════════╬═══════════════════════════════════════╣
║ docker network ls              ║ Lister réseaux                        ║
║ docker network create net      ║ Créer réseau                          ║
║ docker network inspect net     ║ Détails réseau                        ║
║ docker network rm net          ║ Supprimer réseau                      ║
║ docker network prune           ║ Nettoyer inutilisés                   ║
║ docker network connect net c   ║ Connecter conteneur                   ║
║ docker network disconnect net c║ Déconnecter conteneur                 ║
╚════════════════════════════════╩═══════════════════════════════════════╝


TYPES DE RÉSEAUX :
╔════════════╦════════════════════════════════════════════════════════════╗
║ TYPE       ║ CARACTÉRISTIQUES                                           ║
╠════════════╬════════════════════════════════════════════════════════════╣
║ bridge     ║ • Réseau isolé (défaut)                                    ║
║ (défaut)   ║ • Pas de DNS automatique                                   ║
║            ║ • Usage : tests rapides seulement                          ║
╠════════════╬════════════════════════════════════════════════════════════╣
║ bridge     ║ • Réseau isolé personnalisé                                ║
║ (custom)   ║ • DNS automatique [OK]                                       ║
║            ║ • Usage : RECOMMANDÉ pour tout                             ║
╠════════════╬════════════════════════════════════════════════════════════╣
║ host       ║ • Partage réseau hôte                                      ║
║            ║ • Pas d'isolation                                          ║
║            ║ • Usage : monitoring, performance                          ║
╠════════════╬════════════════════════════════════════════════════════════╣
║ none       ║ • Aucun réseau                                             ║
║            ║ • Isolation totale                                         ║
║            ║ • Usage : sécurité maximale                                ║
╠════════════╬════════════════════════════════════════════════════════════╣
║ overlay    ║ • Multi-hôtes                                              ║
║            ║ • Usage : Docker Swarm, Kubernetes                         ║
╚════════════╩════════════════════════════════════════════════════════════╝


═══════════════════════════════════════════════════════════════════════════
  COMMANDES AVANCÉES
═══════════════════════════════════════════════════════════════════════════

# Créer réseau avec driver et options
docker network create \
  --driver bridge \
  --subnet 172.25.0.0/16 \
  --ip-range 172.25.5.0/24 \
  --gateway 172.25.0.1 \
  --opt "com.docker.network.bridge.name=my-bridge" \
  mon-reseau-avance


# Créer réseau avec labels
docker network create \
  --label env=production \
  --label app=myapp \
  prod-network


# Filtrer réseaux par label
docker network ls --filter "label=env=production"


# Voir utilisation réseau
docker network inspect mon-reseau --format='{{len .Containers}} conteneurs'


# Supprimer tous réseaux sauf ceux en cours d'utilisation
docker network prune -f


# Créer réseau isolé (pas de passerelle)
docker network create --internal backend-secure


═══════════════════════════════════════════════════════════════════════════
  SCRIPT D'INITIALISATION COMPLET
═══════════════════════════════════════════════════════════════════════════

#!/bin/bash
# setup-app.sh - Déploiement stack complète

set -e  # Arrêt en cas d'erreur

echo "[RAPIDE] Déploiement de l'application..."

# Nettoyer ancienne installation
echo "[NETTOYAGE] Nettoyage..."
docker stop app-web app-api app-db app-cache 2>/dev/null || true
docker rm app-web app-api app-db app-cache 2>/dev/null || true
docker network rm frontend-net backend-net 2>/dev/null || true

# Créer réseaux
echo "[WEB] Création des réseaux..."
docker network create frontend-net
docker network create backend-net

# Lancer services backend
echo "[ARCHIVE] Démarrage PostgreSQL..."
docker run -d \
  --name app-db \
  --network backend-net \
  -e POSTGRES_DB=myapp \
  -e POSTGRES_USER=appuser \
  -e POSTGRES_PASSWORD=secret123 \
  -v app-db-data:/var/lib/postgresql/data \
  postgres:15-alpine

echo "[RAPIDE] Démarrage Redis..."
docker run -d \
  --name app-cache \
  --network backend-net \
  redis:alpine

# Attendre que la DB soit prête
echo "[HOURGLASS_WITH_FLOWING_SAND] Attente DB..."
sleep 5

# Lancer API
echo "[PLUGIN] Démarrage API..."
docker run -d \
  --name app-api \
  --network backend-net \
  -e DB_HOST=app-db \
  -e DB_NAME=myapp \
  -e DB_USER=appuser \
  -e DB_PASSWORD=secret123 \
  -e REDIS_HOST=app-cache \
  -e REDIS_PORT=6379 \
  myapp-api:latest

# Connecter API au frontend
docker network connect frontend-net app-api

# Lancer frontend
echo "[DESIGN] Démarrage Frontend..."
docker run -d \
  --name app-web \
  --network frontend-net \
  -p 80:80 \
  -p 443:443 \
  -e API_URL=http://app-api:3000 \
  myapp-web:latest

# Vérifications
echo ""
echo "[OK] Déploiement terminé!"
echo ""
echo "[GRAPHIQUE] État des services:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "[WEB] Réseaux:"
docker network ls --filter "name=frontend-net|backend-net"
echo ""
echo "[LIEN] Accès:"
echo "  Frontend: http://localhost"
echo "  API (interne): http://app-api:3000"
echo ""

# Tests de connectivité
echo "[TEST] Tests de connectivité..."
echo -n "  API -> DB: "
docker exec app-api ping -c 1 app-db >/dev/null 2>&1 && echo "[OK]" || echo "[X]"

echo -n "  API -> Cache: "
docker exec app-api ping -c 1 app-cache >/dev/null 2>&1 && echo "[OK]" || echo "[X]"

echo -n "  Web -> API: "
docker exec app-web ping -c 1 app-api >/dev/null 2>&1 && echo "[OK]" || echo "[X]"

echo -n "  Web -> DB (doit échouer): "
docker exec app-web ping -c 1 app-db >/dev/null 2>&1 && echo "[X] PROBLÈME!" || echo "[OK] Isolé"

echo ""
echo "[BRAVO] Tout fonctionne!"


═══════════════════════════════════════════════════════════════════════════
  TROUBLESHOOTING GUIDE
═══════════════════════════════════════════════════════════════════════════

PROBLÈME 1 : Port déjà utilisé
──────────────────────────────
SYMPTÔME : Error: bind: address already in use

DIAGNOSTIC :
lsof -i :8080                    # Linux/Mac
netstat -ano | findstr :8080     # Windows

SOLUTIONS :
1. Arrêter le processus qui utilise le port
2. Utiliser un port différent : -p 8081:80
3. Arrêter le conteneur qui utilise le port


PROBLÈME 2 : Conteneurs ne communiquent pas
────────────────────────────────────────────
SYMPTÔME : ping: bad address 'other-container'

DIAGNOSTIC :
# Vérifier qu'ils sont sur le même réseau
docker inspect container1 | grep -A 10 Networks
docker inspect container2 | grep -A 10 Networks

SOLUTIONS :
1. S'assurer même réseau personnalisé
2. Connecter au même réseau :
   docker network connect mon-reseau container1


PROBLÈME 3 : DNS ne fonctionne pas
───────────────────────────────────
SYMPTÔME : Ping par IP fonctionne, par nom échoue

DIAGNOSTIC :
docker exec container1 nslookup container2

SOLUTIONS :
1. Utiliser réseau personnalisé (pas bridge par défaut)
2. Vérifier noms des conteneurs : docker ps
3. Redémarrer conteneurs sur réseau personnalisé


PROBLÈME 4 : Service inaccessible depuis navigateur
────────────────────────────────────────────────────
SYMPTÔME : Connection refused dans le navigateur

DIAGNOSTIC :
docker ps    # Vérifier mapping ports
docker logs container-name    # Vérifier que service démarre

SOLUTIONS :
1. Vérifier port mapping : -p HOST:CONTAINER
2. Vérifier que service écoute sur 0.0.0.0, pas 127.0.0.1
3. Vérifier firewall hôte


PROBLÈME 5 : Réseau ne se supprime pas
───────────────────────────────────────
SYMPTÔME : Error: network has active endpoints

SOLUTIONS :
# Voir quels conteneurs sont connectés
docker network inspect mon-reseau | grep Name

# Déconnecter ou arrêter
docker stop $(docker ps -q --filter network=mon-reseau)
docker network rm mon-reseau


═══════════════════════════════════════════════════════════════════════════
  CHECKLIST DÉPLOIEMENT
═══════════════════════════════════════════════════════════════════════════

AVANT LE DÉPLOIEMENT :
[ ] Planifier architecture réseau (frontend/backend/db)
[ ] Choisir noms de réseaux descriptifs
[ ] Identifier services publics vs privés
[ ] Documenter ports nécessaires
[ ] Vérifier conflits de ports

CRÉATION :
[ ] Créer réseaux personnalisés
[ ] Lancer services backend (DB, cache)
[ ] Attendre que services soient prêts
[ ] Lancer services applicatifs
[ ] Lancer services frontend
[ ] Mapper ports publics appropriés

APRÈS DÉPLOIEMENT :
[ ] Vérifier tous conteneurs actifs : docker ps
[ ] Tester connectivité entre conteneurs
[ ] Vérifier isolation (services privés inaccessibles)
[ ] Tester accès depuis navigateur
[ ] Vérifier logs : docker logs container-name
[ ] Documenter architecture réseau

SÉCURITÉ :
[ ] Services sensibles sur 127.0.0.1 seulement
[ ] DB/Cache sur réseau privé
[ ] Pas d'exposition de ports inutiles
[ ] Utiliser secrets pour mots de passe (pas -e)
[ ] Firewall configuré si nécessaire


═══════════════════════════════════════════════════════════════════════════
  FIN DE LA PARTIE 9
═══════════════════════════════════════════════════════════════════════════

RÉSUMÉ DE CETTE SECTION :

1. PORTS : Exposer services au monde extérieur
   - docker run -p 8080:80 nginx
   - Format : HOST_PORT:CONTAINER_PORT
   - Utiliser 127.0.0.1:PORT pour local seulement
   - docker port pour voir mappings

2. RÉSEAUX : Faire communiquer conteneurs
   - docker network create mon-reseau
   - docker run --network mon-reseau app
   - DNS automatique par nom de conteneur
   - Isolation par réseaux séparés

3. TYPES DE RÉSEAUX :
   - bridge (custom) : RECOMMANDÉ
   - host : Performance, monitoring
   - none : Isolation maximale

4. ARCHITECTURE :
   - Réseau frontend : Services publics
   - Réseau backend : Services privés
   - API sur les deux réseaux (pont)

5. BONNES PRATIQUES :
   - Toujours utiliser réseaux personnalisés
   - Noms de conteneurs comme hostnames
   - Isoler services sensibles
   - Documenter architecture

PROCHAINE SECTION :
Partie 10 - Volumes et persistance des données


================================================================================
          DOCKER - VOLUMES ET STOCKAGE POUR DÉBUTANTS (PARTIE 10)
                  Persistance et Gestion des Données
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION À CETTE SECTION                          ║
╚══════════════════════════════════════════════════════════════════════════╝

OBJECTIF :
Maîtriser la persistance et le stockage des données dans Docker.

CE QUE VOUS ALLEZ APPRENDRE :
1. Comprendre le système de fichiers des conteneurs
2. Utiliser les volumes Docker
3. Utiliser les bind mounts
4. Gérer les données persistantes
5. Partager des données entre conteneurs

PRÉREQUIS :
[OK] Comprendre docker run
[OK] Connaître les bases des systèmes de fichiers
[OK] Savoir ce qu'est un chemin (path)

CAS D'USAGE :
• Persister données d'une base de données
• Partager code source en développement
• Sauvegarder logs et configurations
• Partager données entre conteneurs
• Développement avec hot-reload


================================================================================
[OK] SECTION 1 : COMPRENDRE LE PROBLÈME - POURQUOI LES VOLUMES ?
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Conteneurs éphémères                                          ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ PROBLÈME : Les données dans un conteneur sont TEMPORAIRES               │
└──────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  DÉMONSTRATION : Perte de données
═══════════════════════════════════════════════════════════════════════════

# ÉTAPE 1 : Créer un conteneur et y écrire des données
docker run -it --name test-data ubuntu bash

# Dans le conteneur
echo "Données importantes" > /data.txt
cat /data.txt
-> Données importantes

exit


# ÉTAPE 2 : Arrêter et redémarrer le conteneur
docker stop test-data
docker start test-data
docker exec test-data cat /data.txt
-> Données importantes [OK]

Les données sont toujours là ! (conteneur existe encore)


# ÉTAPE 3 : Supprimer et recréer le conteneur
docker rm test-data

docker run -it --name test-data ubuntu bash
cat /data.txt
-> cat: /data.txt: No such file or directory [X]

Les données ont DISPARU ! [!]


CONCLUSION :
┌──────────────────────────────────────────────────────────────────────────┐
│ Quand vous supprimez un conteneur, TOUTES ses données sont perdues !    │
│                                                                          │
│ [OK] Données présentes : Tant que conteneur existe (même arrêté)          │
│ [X] Données perdues : Dès que conteneur est supprimé (docker rm)         │
└──────────────────────────────────────────────────────────────────────────┘


POURQUOI C'EST UN PROBLÈME ?

EXEMPLE 1 : Base de données
docker run --name ma-db postgres
# Ajout de données dans PostgreSQL
docker stop ma-db
docker rm ma-db
docker run --name ma-db postgres
-> Base de données VIDE ! Toutes les données perdues ! [SKULL]

EXEMPLE 2 : Application avec uploads
docker run --name mon-app myapp
# Utilisateurs uploadent des fichiers
docker rm mon-app
docker run --name mon-app myapp
-> Tous les fichiers uploadés ont DISPARU !


ANALOGIE :
Conteneur = Ordinateur portable jetable
• Vous pouvez l'éteindre et le rallumer (stop/start)
• Mais si vous le JETEZ (rm), tout est perdu
• Sauf si vous avez sauvegardé sur un disque dur externe (VOLUME)


SOLUTION : VOLUMES !
Les volumes permettent de sauvegarder les données HORS du conteneur.


╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Trois types de stockage Docker                                ║
╚══════════════════════════════════════════════════════════════════════════╝

Docker offre 3 façons de persister les données :

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. VOLUMES (Recommandé *)                                               │
├──────────────────────────────────────────────────────────────────────────┤
│ • Gérés par Docker                                                       │
│ • Stockés dans /var/lib/docker/volumes/                                 │
│ • Isolés du système hôte                                                 │
│ • Peuvent être partagés entre conteneurs                                 │
│ • Sauvegardés et restaurés facilement                                    │
│                                                                          │
│ USAGE : Base de données, données d'application, production              │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 2. BIND MOUNTS                                                           │
├──────────────────────────────────────────────────────────────────────────┤
│ • Montent un dossier de l'hôte dans le conteneur                         │
│ • Chemin absolu nécessaire                                               │
│ • Accès direct aux fichiers de l'hôte                                    │
│ • Modifications visibles immédiatement                                   │
│                                                                          │
│ USAGE : Développement, partage de code, configuration                   │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ 3. TMPFS (Mémoire temporaire)                                           │
├──────────────────────────────────────────────────────────────────────────┤
│ • Stocké en RAM                                                          │
│ • Très rapide                                                            │
│ • Perdu au redémarrage                                                   │
│ • Jamais écrit sur disque                                                │
│                                                                          │
│ USAGE : Données sensibles temporaires, cache, secrets                   │
└──────────────────────────────────────────────────────────────────────────┘


SCHÉMA COMPARATIF :

┌─────────────────────────────────────────────────────────────────────────┐
│                         SYSTÈME HÔTE                                    │
│                                                                         │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ /var/lib/docker/volumes/                                         │  │
│  │   └── myvolume/                                                  │  │
│  │       └── _data/          [BLACK_LEFT-POINTING_POINTER]──── VOLUME (géré par Docker)         │  │
│  │           └── fichier.txt                                        │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                          │                                              │
│                          │ Monté dans                                   │
│                          [BLACK_DOWN-POINTING_TRIANGLE]                                              │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │              CONTENEUR                                           │  │
│  │  /data/                                                          │  │
│  │    └── fichier.txt  [BLACK_LEFT-POINTING_POINTER]──── Accessible ici                         │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                                                                         │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ /home/user/projet/                                               │  │
│  │   └── app.py          [BLACK_LEFT-POINTING_POINTER]──── BIND MOUNT (dossier hôte)            │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                          │                                              │
│                          │ Monté dans                                   │
│                          [BLACK_DOWN-POINTING_TRIANGLE]                                              │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │              CONTENEUR                                           │  │
│  │  /app/                                                           │  │
│  │    └── app.py  [BLACK_LEFT-POINTING_POINTER]──── Même fichier qu'en haut !                   │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                                                                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ RAM (Mémoire)                                                    │  │
│  │   └── tmpfs [BLACK_LEFT-POINTING_POINTER]──── TMPFS (temporaire en mémoire)                  │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                          │                                              │
│                          │ Monté dans                                   │
│                          [BLACK_DOWN-POINTING_TRIANGLE]                                              │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │              CONTENEUR                                           │  │
│  │  /tmp/  [BLACK_LEFT-POINTING_POINTER]──── En RAM, très rapide, perdu au redémarrage          │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘


QUAND UTILISER QUOI ?

╔════════════════════════════╦══════════════════════════════════════════╗
║ SITUATION                  ║ SOLUTION                                 ║
╠════════════════════════════╬══════════════════════════════════════════╣
║ Base de données            ║ [OK] VOLUME                                ║
║ Logs d'application         ║ [OK] VOLUME                                ║
║ Uploads utilisateurs       ║ [OK] VOLUME                                ║
║ Données de production      ║ [OK] VOLUME                                ║
╠════════════════════════════╬══════════════════════════════════════════╣
║ Développement (code)       ║ [OK] BIND MOUNT                            ║
║ Configuration locale       ║ [OK] BIND MOUNT                            ║
║ Hot-reload (auto-refresh)  ║ [OK] BIND MOUNT                            ║
║ Partage fichiers hôte      ║ [OK] BIND MOUNT                            ║
╠════════════════════════════╬══════════════════════════════════════════╣
║ Secrets temporaires        ║ [OK] TMPFS                                 ║
║ Cache rapide               ║ [OK] TMPFS                                 ║
║ Données sensibles          ║ [OK] TMPFS                                 ║
╚════════════════════════════╩══════════════════════════════════════════╝


================================================================================
[OK] SECTION 2 : VOLUMES DOCKER - LA SOLUTION RECOMMANDÉE
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker volume create                                         ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Créer un volume Docker pour persister les données                │
└──────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Créer et utiliser un volume basique
═══════════════════════════════════════════════════════════════════════════

ÉTAPE 1 : Créer un volume
docker volume create myvolume

RÉSULTAT :
myvolume


VÉRIFICATION :
docker volume ls

RÉSULTAT :
DRIVER    VOLUME NAME
local     myvolume


ÉTAPE 2 : Utiliser le volume dans un conteneur
docker run -it --name test-volume -v myvolume:/data ubuntu bash

EXPLICATION :
-v myvolume:/data
   ^         ^
   |         └─── Chemin dans le conteneur
   └─────────── Nom du volume


ÉTAPE 3 : Écrire des données dans le volume
# Dans le conteneur
echo "Données persistantes" > /data/fichier.txt
ls /data/
-> fichier.txt

cat /data/fichier.txt
-> Données persistantes

exit


ÉTAPE 4 : Supprimer le conteneur
docker rm test-volume


ÉTAPE 5 : Créer un NOUVEAU conteneur avec le MÊME volume
docker run -it --name nouveau-test -v myvolume:/data ubuntu bash

# Vérifier que les données sont toujours là
cat /data/fichier.txt
-> Données persistantes [OK]

LES DONNÉES ONT SURVÉCU ! [BRAVO]


SCHÉMA :

┌─────────────────────────────────────────────────────────────────────────┐
│                           CYCLE DE VIE                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│ 1. docker volume create myvolume                                        │
│    └─[BLACK_RIGHT-POINTING_POINTER] Volume créé (vide)                                               │
│                                                                         │
│ 2. docker run -v myvolume:/data conteneur1                              │
│    └─[BLACK_RIGHT-POINTING_POINTER] Écriture de données dans /data/                                  │
│         Données sauvegardées dans myvolume                              │
│                                                                         │
│ 3. docker rm conteneur1                                                 │
│    └─[BLACK_RIGHT-POINTING_POINTER] Conteneur supprimé                                               │
│         [OK] Données toujours dans myvolume                               │
│                                                                         │
│ 4. docker run -v myvolume:/data conteneur2                              │
│    └─[BLACK_RIGHT-POINTING_POINTER] Nouveau conteneur accède aux MÊMES données !                     │
│                                                                         │
│ 5. docker volume rm myvolume                                            │
│    └─[BLACK_RIGHT-POINTING_POINTER] Volume supprimé                                                  │
│         [X] Maintenant les données sont perdues                          │
└─────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Volume avec base de données PostgreSQL
═══════════════════════════════════════════════════════════════════════════

CAS D'USAGE : Persister les données d'une base de données

ÉTAPE 1 : Créer un volume pour la DB
docker volume create postgres-data


ÉTAPE 2 : Lancer PostgreSQL avec le volume
docker run -d \
  --name ma-db \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15-alpine

EXPLICATION :
-v postgres-data:/var/lib/postgresql/data
                 ^
   Répertoire où PostgreSQL stocke ses données


ÉTAPE 3 : Ajouter des données
docker exec -it ma-db psql -U postgres

-- Dans PostgreSQL
CREATE DATABASE testdb;
\c testdb
CREATE TABLE users (id SERIAL, name TEXT);
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
SELECT * FROM users;

 id | name
----+------
  1 | Alice
  2 | Bob

\q


ÉTAPE 4 : Supprimer le conteneur (DANGER ?)
docker stop ma-db
docker rm ma-db


ÉTAPE 5 : Recréer le conteneur avec le MÊME volume
docker run -d \
  --name ma-db-nouvelle \
  -e POSTGRES_PASSWORD=secret \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15-alpine


ÉTAPE 6 : Vérifier que les données sont toujours là
docker exec -it ma-db-nouvelle psql -U postgres

\c testdb
SELECT * FROM users;

 id | name
----+------
  1 | Alice
  2 | Bob

[OK] Les données ont survécu ! La base de données est intacte !


AVANTAGE :
Vous pouvez :
• Mettre à jour l'image PostgreSQL
• Changer le nom du conteneur
• Redémarrer le système
• Supprimer et recréer le conteneur

-> Les données restent TOUJOURS dans le volume !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Volume anonyme (créé automatiquement)
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Sans nom de volume
docker run -d --name test -v /data nginx

EXPLICATION :
-v /data
   ^
Pas de nom de volume -> Docker en crée un automatique


VÉRIFICATION :
docker volume ls

RÉSULTAT :
DRIVER    VOLUME NAME
local     abc123def456789...  <- Volume avec ID aléatoire


VOIR LE NOM EXACT :
docker inspect test --format='{{.Mounts}}'

RÉSULTAT :
[{volume abc123def456789... /var/lib/docker/volumes/abc123.../_ data /data local true }]


PROBLÈME AVEC LES VOLUMES ANONYMES :
[X] Nom difficile à retenir (abc123def456...)
[X] Difficile à réutiliser
[X] Reste après suppression du conteneur (pollution)


[OK] BONNE PRATIQUE : Toujours nommer vos volumes !
docker volume create mon-volume
docker run -v mon-volume:/data nginx


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Créer volume avec options
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Volume avec driver et options
docker volume create \
  --driver local \
  --opt type=none \
  --opt device=/path/on/host \
  --opt o=bind \
  custom-volume

EXPLICATION :
--driver local     -> Driver de stockage (défaut)
--opt type=none    -> Pas de type spécifique
--opt device=...   -> Emplacement sur l'hôte
--opt o=bind       -> Options de montage


COMMANDE : Volume avec labels
docker volume create \
  --label env=production \
  --label app=myapp \
  --label backup=daily \
  prod-data


VÉRIFICATION :
docker volume inspect prod-data --format='{{.Labels}}'

RÉSULTAT :
map[app:myapp backup:daily env:production]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Lister les volumes
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Liste simple
docker volume ls

RÉSULTAT :
DRIVER    VOLUME NAME
local     myvolume
local     postgres-data
local     redis-data
local     abc123def456...


COMMANDE : Filtrer par nom
docker volume ls --filter name=postgres

RÉSULTAT :
DRIVER    VOLUME NAME
local     postgres-data


COMMANDE : Filtrer par label
docker volume ls --filter label=env=production

RÉSULTAT :
DRIVER    VOLUME NAME
local     prod-data


COMMANDE : Volumes "pendants" (dangling = non utilisés)
docker volume ls --filter dangling=true

RÉSULTAT :
DRIVER    VOLUME NAME
local     abc123def456...  <- Volume anonyme non utilisé


COMMANDE : Format personnalisé
docker volume ls --format "table {{.Name}}\t{{.Driver}}\t{{.Mountpoint}}"

RÉSULTAT :
NAME            DRIVER    MOUNTPOINT
myvolume        local     /var/lib/docker/volumes/myvolume/_data
postgres-data   local     /var/lib/docker/volumes/postgres-data/_data


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Inspecter un volume
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker volume inspect myvolume

RÉSULTAT :
[
    {
        "CreatedAt": "2024-11-18T10:30:00Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
        "Name": "myvolume",
        "Options": {},
        "Scope": "local"
    }
]


INFORMATIONS IMPORTANTES :
• Name : Nom du volume
• Driver : Type de stockage (local, nfs, etc.)
• Mountpoint : Où les données sont stockées sur l'hôte
• CreatedAt : Date de création
• Labels : Labels personnalisés


COMMANDE : Extraire juste le Mountpoint
docker volume inspect myvolume --format='{{.Mountpoint}}'

RÉSULTAT :
/var/lib/docker/volumes/myvolume/_data


COMMANDE : Voir les labels
docker volume inspect prod-data --format='{{json .Labels}}' | jq

RÉSULTAT :
{
  "app": "myapp",
  "backup": "daily",
  "env": "production"
}


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Partager un volume entre plusieurs conteneurs
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application avec serveur web et worker qui partagent des fichiers

ÉTAPE 1 : Créer un volume partagé
docker volume create shared-files


ÉTAPE 2 : Lancer le premier conteneur (écrit des fichiers)
docker run -d \
  --name writer \
  -v shared-files:/data \
  alpine sh -c "while true; do date >> /data/log.txt; sleep 5; done"

Ce conteneur écrit la date toutes les 5 secondes dans /data/log.txt


ÉTAPE 3 : Lancer le deuxième conteneur (lit les fichiers)
docker run -d \
  --name reader \
  -v shared-files:/data \
  alpine sh -c "tail -f /data/log.txt"


ÉTAPE 4 : Vérifier que ça fonctionne
docker logs reader

RÉSULTAT :
Tue Nov 18 10:30:00 UTC 2024
Tue Nov 18 10:30:05 UTC 2024
Tue Nov 18 10:30:10 UTC 2024

Les deux conteneurs partagent le même volume ! [OK]


SCHÉMA :

┌─────────────────────────────────────────────────────────────────────────┐
│                        VOLUME: shared-files                             │
│                     /var/lib/docker/volumes/...                         │
│                          └── log.txt                                    │
└────────────────────┬────────────────────────┬───────────────────────────┘
                     │                        │
          ┌──────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐   ┌──────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
          │   CONTENEUR 1      │   │   CONTENEUR 2      │
          │    (writer)        │   │    (reader)        │
          │                    │   │                    │
          │ Écrit dans /data/  │   │ Lit depuis /data/  │
          └────────────────────┘   └────────────────────┘


CAS D'USAGE RÉELS :
• Application web + worker de traitement
• Nginx + génération de contenu
• Logger + analyseur de logs
• Producteur + consommateur de fichiers


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Volume en lecture seule
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Conteneur doit lire mais pas modifier

COMMANDE :
docker run -it --name reader-only \
  -v myvolume:/data:ro \
  ubuntu bash
                    ^
                 Lecture seule !


VÉRIFICATION :
# Dans le conteneur
cat /data/fichier.txt
-> Fonctionne [OK]

echo "test" > /data/nouveau.txt
-> bash: /data/nouveau.txt: Read-only file system [X]


CAS D'USAGE :
[OK] Configuration partagée (lecture seule)
[OK] Assets statiques (images, CSS, JS)
[OK] Données de référence
[OK] Sécurité : empêcher modifications accidentelles


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Supprimer un volume
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker volume rm myvolume

RÉSULTAT :
myvolume


ERREUR SI VOLUME EN COURS D'UTILISATION :
docker volume rm postgres-data

ERREUR :
Error response from daemon: remove postgres-data: volume is in use - [abc123...]

[OK] SOLUTION : Arrêter/supprimer les conteneurs d'abord
docker stop ma-db
docker rm ma-db
docker volume rm postgres-data


[ATTENTION] ATTENTION :
Supprimer un volume = PERTE DÉFINITIVE DES DONNÉES !
Assurez-vous d'avoir des sauvegardes avant !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Nettoyer les volumes inutilisés (prune)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker volume prune

RÉSULTAT :
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y

Deleted Volumes:
abc123def456...
old-volume
test-volume

Total reclaimed space: 1.2GB


COMMANDE : Sans confirmation
docker volume prune -f


COMMANDE : Supprimer volumes avec un label spécifique
docker volume prune --filter label=env=test


[ATTENTION] DANGER :
Cette commande supprime TOUS les volumes non utilisés !
Utilisez avec précaution en production !


BONNE PRATIQUE :
# Voir ce qui sera supprimé avant
docker volume ls --filter dangling=true

# Puis nettoyer
docker volume prune


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 11 : Voir l'utilisation disque des volumes
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Vue d'ensemble
docker system df

RÉSULTAT :
TYPE            TOTAL   ACTIVE   SIZE      RECLAIMABLE
Images          10      5        2.5GB     1.2GB (48%)
Containers      15      3        100MB     50MB (50%)
Local Volumes   8       4        5.5GB     2.0GB (36%)
Build Cache     20      0        1.0GB     1.0GB (100%)


COMMANDE : Vue détaillée
docker system df -v

RÉSULTAT :
Volumes space usage:

VOLUME NAME       LINKS   SIZE
postgres-data     1       1.2GB
redis-data        1       50MB
myvolume          0       0B       <- Non utilisé
shared-files      2       100MB
...


LIENS :
0 = Volume non utilisé par aucun conteneur
1 = Utilisé par 1 conteneur
2+ = Partagé entre plusieurs conteneurs


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 12 : Copier données depuis/vers un volume
═══════════════════════════════════════════════════════════════════════════

MÉTHODE 1 : Via docker cp

# Créer conteneur temporaire avec le volume
docker run -d --name temp -v myvolume:/data alpine sleep 3600

# Copier fichier vers le volume
docker cp local-file.txt temp:/data/

# Copier fichier depuis le volume
docker cp temp:/data/file.txt ./local-copy.txt

# Nettoyer
docker rm -f temp


MÉTHODE 2 : Backup d'un volume complet

# Créer une archive du volume
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/myvolume-backup.tar.gz -C /data .

RÉSULTAT :
myvolume-backup.tar.gz créé dans le dossier actuel !


MÉTHODE 3 : Restaurer depuis un backup

# Créer nouveau volume
docker volume create myvolume-restored

# Restaurer l'archive
docker run --rm \
  -v myvolume-restored:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/myvolume-backup.tar.gz -C /data


SCRIPT COMPLET DE BACKUP :

#!/bin/bash
# backup-volume.sh

VOLUME_NAME=$1
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${VOLUME_NAME}-${DATE}.tar.gz"

mkdir -p $BACKUP_DIR

docker run --rm \
  -v $VOLUME_NAME:/data \
  -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/$(basename $BACKUP_FILE) -C /data .

echo "[OK] Backup créé : $BACKUP_FILE"

USAGE :
./backup-volume.sh postgres-data


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 13 : Volume avec syntaxe --mount (moderne)
═══════════════════════════════════════════════════════════════════════════

SYNTAXE CLASSIQUE (-v) :
docker run -v myvolume:/data nginx


SYNTAXE MODERNE (--mount) :
docker run --mount source=myvolume,target=/data nginx


COMPARAISON :

┌──────────────────────────────────────────────────────────────────────────┐
│ SYNTAXE -v (classique)                                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ docker run -v myvolume:/data nginx                                       │
│                                                                          │
│ [OK] Plus courte                                                           │
│ [OK] Facile à retenir                                                      │
│ [X] Moins explicite                                                       │
│ [X] Crée volume automatiquement si inexistant                             │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ SYNTAXE --mount (moderne)                                                │
├──────────────────────────────────────────────────────────────────────────┤
│ docker run --mount source=myvolume,target=/data nginx                    │
│                                                                          │
│ [OK] Plus explicite                                                        │
│ [OK] Erreur si volume n'existe pas                                         │
│ [OK] Options plus claires                                                  │
│ [X] Plus verbeux                                                          │
└──────────────────────────────────────────────────────────────────────────┘


COMMANDE : --mount avec options complètes
docker run --mount \
  type=volume,\
  source=myvolume,\
  target=/data,\
  readonly \
  nginx

EXPLICATION :
type=volume       -> Type de montage (volume, bind, tmpfs)
source=myvolume   -> Nom du volume
target=/data      -> Chemin dans le conteneur
readonly          -> Lecture seule


AVANTAGE : --mount échoue si le volume n'existe pas
docker run --mount source=inexistant,target=/data nginx

ERREUR :
Error: No such volume: inexistant

Avec -v, le volume serait créé automatiquement !


RECOMMANDATION :
• Développement / Tests : -v (plus rapide)
• Production / Scripts : --mount (plus sûr)


================================================================================
[OK] SECTION 3 : BIND MOUNTS - PARTAGER DES DOSSIERS DE L'HÔTE
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Bind Mount                                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Monter un dossier de l'hôte directement dans le conteneur        │
└──────────────────────────────────────────────────────────────────────────┘

DIFFÉRENCE AVEC VOLUME :

VOLUME :
• Géré par Docker
• Stocké dans /var/lib/docker/volumes/
• Isolé du système hôte

BIND MOUNT :
• Chemin absolu de l'hôte
• Accès direct aux fichiers
• Modifications immédiates visibles


SCHÉMA :

┌─────────────────────────────────────────────────────────────────────────┐
│                         SYSTÈME HÔTE                                    │
│                                                                         │
│  /home/user/mon-projet/                                                 │
│    ├── app.py                                                           │
│    ├── config.json                                                      │
│    └── data/                                                            │
│                                                                         │
│         │ Bind Mount                                                    │
│         │ (lien direct)                                                 │
│         [BLACK_DOWN-POINTING_TRIANGLE]                                                               │
│                                                                         │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │              CONTENEUR                                           │  │
│  │                                                                  │  │
│  │  /app/                                                           │  │
│  │    ├── app.py         [BLACK_LEFT-POINTING_POINTER]─── MÊME fichier qu'en haut !             │  │
│  │    ├── config.json    [BLACK_LEFT-POINTING_POINTER]─── Modifications synchronisées           │  │
│  │    └── data/          [BLACK_LEFT-POINTING_POINTER]─── En temps réel !                       │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Bind mount basique
═══════════════════════════════════════════════════════════════════════════

PRÉPARATION : Créer un dossier avec un fichier
mkdir ~/mon-projet
echo "print('Hello depuis l'hôte !')" > ~/mon-projet/app.py


COMMANDE : Monter le dossier dans le conteneur
docker run -it \
  -v ~/mon-projet:/app \
  python:3.11 bash
     ^            ^
  Chemin hôte   Chemin conteneur


VÉRIFICATION : Dans le conteneur
ls /app/
-> app.py

cat /app/app.py
-> print('Hello depuis l'hôte !')

python /app/app.py
-> Hello depuis l'hôte !


MAGIE : Modifier le fichier depuis l'hôte

# Sur l'hôte (autre terminal)
echo "print('Fichier modifié !')" > ~/mon-projet/app.py

# Dans le conteneur (immédiatement)
python /app/app.py
-> Fichier modifié !

Changement INSTANTANÉ ! *


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Développement avec hot-reload
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Application Node.js avec auto-refresh

STRUCTURE DU PROJET :
mon-app/
├── app.js
├── package.json
└── node_modules/


COMMANDE : Lancer avec bind mount
docker run -d \
  --name dev-app \
  -v $(pwd):/app \
  -w /app \
  -p 3000:3000 \
  node:18 \
  npm run dev

EXPLICATION :
-v $(pwd):/app    -> Monte dossier actuel dans /app
-w /app           -> Définit /app comme working directory
npm run dev       -> Lance en mode développement


FICHIER package.json :
{
  "scripts": {
    "dev": "nodemon app.js"
  }
}

nodemon détecte les changements et redémarre automatiquement !


WORKFLOW DE DÉVELOPPEMENT :

1. Modifier app.js sur votre machine
2. Sauvegarder
3. nodemon détecte le changement dans le conteneur
4. Application redémarre automatiquement
5. Rafraîchir le navigateur -> changements visibles !

AUCUN rebuild d'image nécessaire ! [RAPIDE]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Chemins absolus vs relatifs
═══════════════════════════════════════════════════════════════════════════

[X] ERREUR COURANTE : Chemin relatif
docker run -v ./mon-dossier:/app nginx

ERREUR :
Invalid mode: ./mon-dossier


[OK] SOLUTION 1 : Chemin absolu
docker run -v /home/user/mon-dossier:/app nginx


[OK] SOLUTION 2 : Utiliser $(pwd) (Linux/Mac)
docker run -v $(pwd)/mon-dossier:/app nginx


[OK] SOLUTION 3 : Utiliser %cd% (Windows CMD)
docker run -v %cd%\mon-dossier:/app nginx


[OK] SOLUTION 4 : Utiliser ${PWD} (Windows PowerShell)
docker run -v ${PWD}/mon-dossier:/app nginx


ASTUCE : Créer un alias
# Linux/Mac
alias drun='docker run -v $(pwd):/app -w /app'
drun python:3.11 python app.py

# Windows (PowerShell)
function drun { docker run -v ${PWD}:/app -w /app @args }


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Windows - Syntaxe spéciale
═══════════════════════════════════════════════════════════════════════════

WINDOWS CMD :
docker run -v C:\Users\user\projet:/app nginx


WINDOWS PowerShell :
docker run -v C:/Users/user/projet:/app nginx

OU

docker run -v "C:\Users\user\projet":/app nginx


WINDOWS avec espaces dans le chemin :
docker run -v "C:\Users\Mon Utilisateur\Mon Projet":/app nginx


CHEMIN RÉSEAU Windows :
docker run -v //server/share:/data nginx


WSL2 (Windows Subsystem for Linux) :
docker run -v /mnt/c/Users/user/projet:/app nginx


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Bind mount en lecture seule
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -it \
  -v $(pwd)/config:/etc/config:ro \
  ubuntu bash
                              ^
                          Lecture seule


VÉRIFICATION :
# Dans le conteneur
cat /etc/config/app.conf
-> Fonctionne [OK]

echo "test" > /etc/config/new.conf
-> Read-only file system [X]


CAS D'USAGE :
[OK] Fichiers de configuration (empêcher modification accidentelle)
[OK] Assets statiques (images, CSS, JS)
[OK] Code source en production
[OK] Certificats SSL


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Monter un seul fichier (pas un dossier)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -it \
  -v $(pwd)/config.json:/app/config.json \
  ubuntu bash


VÉRIFICATION :
# Dans le conteneur
cat /app/config.json
-> Contenu du fichier


CAS D'USAGE :
• Fichier de configuration spécifique
• Certificat SSL
• Script d'initialisation
• Variables d'environnement (.env)


EXEMPLE : Nginx avec configuration personnalisée
docker run -d \
  -p 80:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx


[ATTENTION] ATTENTION :
Le fichier DOIT exister sur l'hôte avant le docker run !
Sinon Docker crée un DOSSIER vide au lieu du fichier.


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Bind mount avec --mount (syntaxe moderne)
═══════════════════════════════════════════════════════════════════════════

SYNTAXE :
docker run --mount \
  type=bind,\
  source=/chemin/hote,\
  target=/chemin/conteneur \
  nginx


EXEMPLE COMPLET :
docker run -d \
  --name web-dev \
  --mount type=bind,source=$(pwd),target=/app \
  -p 3000:3000 \
  node:18


AVEC OPTIONS :
docker run --mount \
  type=bind,\
  source=$(pwd),\
  target=/app,\
  readonly \
  nginx


AVANTAGE : Erreur explicite si source n'existe pas
docker run --mount type=bind,source=/inexistant,target=/app nginx

ERREUR :
Error: invalid mount config: bind source path does not exist


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Partager socket Docker (Docker-in-Docker)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Conteneur qui exécute des commandes Docker

COMMANDE :
docker run -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  docker:cli


VÉRIFICATION :
# Dans le conteneur
docker ps
-> Liste des conteneurs de l'hôte !


CAS D'USAGE :
• CI/CD (Jenkins, GitLab Runner)
• Orchestration (Portainer)
• Monitoring (cAdvisor)
• Build d'images dans conteneur


[ATTENTION] SÉCURITÉ :
Donner accès au socket Docker = Accès ROOT à l'hôte !
Utilisez avec précaution !


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Problème des permissions (Linux)
═══════════════════════════════════════════════════════════════════════════

PROBLÈME COURANT :

# Créer fichier dans conteneur
docker run -v $(pwd)/data:/data ubuntu touch /data/test.txt

# Sur l'hôte
ls -l data/
-> -rw-r--r-- 1 root root 0 Nov 18 10:00 test.txt
                ^    ^
            Propriétaire : root !


# Essayer de modifier
nano data/test.txt
-> Permission denied [X]


POURQUOI ?
Par défaut, processus dans conteneur = root
Fichiers créés = propriétaire root


SOLUTION 1 : Spécifier user
docker run -u $(id -u):$(id -g) -v $(pwd)/data:/data ubuntu touch /data/test.txt

Fichier créé avec votre UID/GID !


SOLUTION 2 : Chown après création
sudo chown -R $USER:$USER data/


SOLUTION 3 : Dockerfile avec user non-root
FROM ubuntu
RUN useradd -m appuser
USER appuser


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Cas d'usage complet - Stack de développement
═══════════════════════════════════════════════════════════════════════════

PROJET : Application Python + PostgreSQL

STRUCTURE :
projet/
├── app/
│   ├── main.py
│   ├── models.py
│   └── requirements.txt
├── docker-compose.yml
└── init.sql


COMMANDE : Backend avec code source monté
docker run -d \
  --name backend \
  -v $(pwd)/app:/app \
  -w /app \
  -p 5000:5000 \
  -e DB_HOST=db \
  python:3.11 \
  sh -c "pip install -r requirements.txt && python main.py"


COMMANDE : PostgreSQL avec script d'init
docker run -d \
  --name db \
  -v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
  -v db-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:15


RÉSULTAT :
• Code Python modifiable en temps réel
• Script init.sql exécuté au démarrage de la DB
• Données DB persistantes dans volume
• Développement rapide sans rebuild !


================================================================================
[OK] SECTION 4 : TMPFS - STOCKAGE TEMPORAIRE EN MÉMOIRE
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : tmpfs Mount                                                   ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Monter un système de fichiers en RAM (mémoire)                   │
└──────────────────────────────────────────────────────────────────────────┘


CARACTÉRISTIQUES :
[OK] Très rapide (RAM)
[OK] Jamais écrit sur disque
[OK] Sécurisé (données volatiles)
[X] Perdu au redémarrage
[X] Consomme de la RAM


SCHÉMA :

┌─────────────────────────────────────────────────────────────────────────┐
│                         SYSTÈME HÔTE                                    │
│                                                                         │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │                    RAM (Mémoire)                               │    │
│  │                                                                │    │
│  │  tmpfs [BLACK_LEFT-POINTING_POINTER]─── Stockage temporaire, très rapide                  │    │
│  │                                                                │    │
│  └──────────────────────────┬─────────────────────────────────────┘    │
│                             │                                          │
│                             │ Monté dans                               │
│                             [BLACK_DOWN-POINTING_TRIANGLE]                                          │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │              CONTENEUR                                           │  │
│  │                                                                  │  │
│  │  /tmp/  [BLACK_LEFT-POINTING_POINTER]─── Données en RAM                                      │  │
│  │              - Très rapide                                       │  │
│  │              - Perdu au redémarrage                              │  │
│  │              - Jamais écrit sur disque                           │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : tmpfs basique
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -it \
  --tmpfs /tmp \
  ubuntu bash


VÉRIFICATION :
# Dans le conteneur
df -h | grep tmpfs
-> tmpfs  64M  0  64M  0%  /tmp

echo "test" > /tmp/fichier.txt
cat /tmp/fichier.txt
-> test

exit


REDÉMARRER LE CONTENEUR :
docker start -ai <container_id>

cat /tmp/fichier.txt
-> No such file or directory

Données perdues ! (comme prévu)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : tmpfs avec options
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Limiter la taille
docker run --tmpfs /tmp:rw,size=100m,mode=1777 ubuntu


OPTIONS :
rw          -> Lecture-écriture (défaut)
size=100m   -> Limite à 100 MB
mode=1777   -> Permissions (sticky bit)


COMMANDE : Avec --mount (syntaxe moderne)
docker run --mount \
  type=tmpfs,\
  destination=/tmp,\
  tmpfs-size=100m,\
  tmpfs-mode=1777 \
  ubuntu


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Cas d'usage - Secrets temporaires
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Traiter des données sensibles

COMMANDE :
docker run -d \
  --name secure-app \
  --tmpfs /secrets:ro,size=10m \
  myapp


AVANTAGE :
• Clés API stockées en RAM
• Jamais écrites sur disque
• Disparaissent au redémarrage
• Pas de traces sur le disque


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Cache rapide
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d \
  --name redis-cache \
  --tmpfs /data:rw,size=512m \
  redis redis-server --save ""


EXPLICATION :
--save ""   -> Désactive la sauvegarde sur disque
/data       -> En RAM pour performance maximale


CAS D'USAGE :
[OK] Cache Redis/Memcached
[OK] Sessions temporaires
[OK] Traitement de données volatiles
[OK] Build artifacts temporaires


================================================================================
[OK] SECTION 5 : BONNES PRATIQUES ET COMPARAISONS
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  TABLEAU COMPARATIF COMPLET                                              ║
╚══════════════════════════════════════════════════════════════════════════╝

╔═══════════════════╦════════════════╦═══════════════╦════════════════╗
║ CARACTÉRISTIQUE   ║ VOLUME         ║ BIND MOUNT    ║ TMPFS          ║
╠═══════════════════╬════════════════╬═══════════════╬════════════════╣
║ Géré par          ║ Docker         ║ Utilisateur   ║ Docker         ║
║ Emplacement       ║ /var/lib/...   ║ Anywhere      ║ RAM            ║
║ Performance       ║ Bonne          ║ Bonne         ║ Excellente     ║
║ Persistance       ║ [OK] Oui         ║ [OK] Oui        ║ [X] Non        ║
║ Partage facile    ║ [OK] Oui         ║ [ATTENTION]  Moyen     ║ [X] Non        ║
║ Backup            ║ [OK] Facile      ║ [ATTENTION]  Manuel    ║ [X] Impossible ║
║ Hot-reload        ║ [X] Non         ║ [OK] Oui        ║ [X] Non        ║
║ Permissions       ║ [OK] Simples     ║ [ATTENTION]  Complexes ║ [OK] Simples    ║
║ Sécurité disque   ║ [ATTENTION]  Écrit      ║ [ATTENTION]  Écrit     ║ [OK] Jamais     ║
║ OS portabilité    ║ [OK] Excellente  ║ [ATTENTION]  Limitée   ║ [OK] Bonne      ║
╠═══════════════════╬════════════════╬═══════════════╬════════════════╣
║ USAGE PRINCIPAL   ║ Production     ║ Développement ║ Cache/Secrets  ║
╚═══════════════════╩════════════════╩═══════════════╩════════════════╝


QUAND UTILISER QUOI ?

┌──────────────────────────────────────────────────────────────────────────┐
│ VOLUMES - Utilisez pour :                                                │
├──────────────────────────────────────────────────────────────────────────┤
│ [OK] Base de données (PostgreSQL, MySQL, MongoDB)                          │
│ [OK] Uploads utilisateurs (images, documents)                              │
│ [OK] Logs d'application                                                    │
│ [OK] Données de production                                                 │
│ [OK] Données partagées entre conteneurs                                    │
│ [OK] Backup et restore nécessaires                                         │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ BIND MOUNTS - Utilisez pour :                                            │
├──────────────────────────────────────────────────────────────────────────┤
│ [OK] Développement (code source)                                           │
│ [OK] Hot-reload / auto-refresh                                             │
│ [OK] Fichiers de configuration                                             │
│ [OK] Accès direct aux fichiers de l'hôte                                   │
│ [OK] Partage de données existantes                                         │
│ [OK] Tests avec données réelles                                            │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ TMPFS - Utilisez pour :                                                 │
├──────────────────────────────────────────────────────────────────────────┤
│ [OK] Secrets temporaires (tokens, clés API)                                │
│ [OK] Cache haute performance                                               │
│ [OK] Données sensibles (jamais sur disque)                                 │
│ [OK] Build artifacts temporaires                                           │
│ [OK] Sessions utilisateurs                                                 │
│ [OK] Fichiers temporaires de calcul                                        │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  BONNES PRATIQUES GÉNÉRALES                                              ║
╚══════════════════════════════════════════════════════════════════════════╝

[OK] À FAIRE :

1. Nommer vos volumes explicitement
   [OK] docker volume create postgres-data
   [X] docker run -v /data postgres (volume anonyme)

2. Séparer données persistantes et temporaires
   [OK] Volume pour DB, tmpfs pour cache
   [X] Tout dans un seul volume

3. Documenter les volumes nécessaires
   # README.md
   Volumes requis:
   - postgres-data: Base de données
   - uploads: Fichiers utilisateurs

4. Sauvegarder régulièrement
   # Script de backup quotidien
   docker run --rm -v myvolume:/data -v $(pwd):/backup \
     alpine tar czf /backup/backup-$(date +%F).tar.gz -C /data .

5. Utiliser volumes pour production
   [OK] docker run -v db-data:/var/lib/postgresql/data postgres
   [X] docker run -v $(pwd)/data:/var/lib/postgresql/data postgres

6. Utiliser bind mounts pour développement
   [OK] docker run -v $(pwd):/app -w /app node:18 npm run dev
   [X] Rebuild image à chaque changement

7. Nettoyer volumes inutilisés
   docker volume prune -f


[X] À ÉVITER :

1. Stocker données importantes sans volume
   [X] docker run postgres (données perdues à la suppression)
   [OK] docker run -v db-data:/var/lib/postgresql/data postgres

2. Volumes anonymes en production
   [X] docker run -v /data app (ID aléatoire)
   [OK] docker volume create app-data && docker run -v app-data:/data app

3. Bind mounts en production
   [X] docker run -v $(pwd)/data:/data app (dépendance à l'hôte)
   [OK] docker run -v app-data:/data app

4. Chemins relatifs dans bind mounts
   [X] docker run -v ./data:/data app (ne fonctionne pas)
   [OK] docker run -v $(pwd)/data:/data app

5. Oublier de sauvegarder les volumes
   Volumes = données critiques !
   Backup régulier obligatoire !

6. Mixer données et logs dans même volume
   [OK] Volumes séparés pour chaque type de données
   -v app-data:/app/data
   -v app-logs:/app/logs

7. Permissions root dans bind mounts
   [ATTENTION] Peut causer problèmes de permissions
   Solution: docker run -u $(id -u):$(id -g) ...


╔══════════════════════════════════════════════════════════════════════════╗
║  PIÈGES COURANTS                                                         ║
╚══════════════════════════════════════════════════════════════════════════╝

[X] PIÈGE 1 : Volume anonyme créé accidentellement

docker run -v /data nginx

RÉSULTAT :
Volume avec ID aléatoire créé (abc123def456...)

[OK] SOLUTION :
docker volume create myvolume
docker run -v myvolume:/data nginx


[X] PIÈGE 2 : Bind mount crée un dossier vide au lieu de fichier

# Fichier n'existe pas encore
docker run -v $(pwd)/config.json:/app/config.json nginx

RÉSULTAT :
Docker crée un DOSSIER /app/config.json au lieu d'un fichier !

[OK] SOLUTION :
touch config.json  # Créer le fichier d'abord
docker run -v $(pwd)/config.json:/app/config.json nginx


[X] PIÈGE 3 : Données écrasées par image

# Image contient déjà des données dans /app
docker run -v $(pwd):/app myapp

RÉSULTAT :
Contenu de l'image dans /app est masqué par le bind mount !

[OK] SOLUTION :
# Copier les données de l'image d'abord
docker run --rm myapp tar c /app | tar x


[X] PIÈGE 4 : Permissions en bind mount (Linux)

docker run -v $(pwd):/data ubuntu bash -c "echo test > /data/file.txt"

RÉSULTAT :
file.txt appartient à root, pas accessible depuis l'hôte !

[OK] SOLUTION :
docker run -u $(id -u):$(id -g) -v $(pwd):/data ubuntu bash -c "echo test > /data/file.txt"


[X] PIÈGE 5 : Chemin Windows non reconnu

docker run -v C:\Users\data:/data nginx

ERREUR :
Invalid volume specification

[OK] SOLUTION (PowerShell) :
docker run -v C:/Users/data:/data nginx
OU
docker run -v "C:\Users\data":/data nginx


[X] PIÈGE 6 : Volume reste après docker rm

docker run -v myvolume:/data --name test nginx
docker rm test

Volume myvolume EXISTE TOUJOURS !

[OK] COMPRENDRE :
C'est normal ! Volumes sont indépendants des conteneurs.
Supprimez explicitement : docker volume rm myvolume


[X] PIÈGE 7 : tmpfs trop petit

docker run --tmpfs /tmp:size=10m ubuntu
# Processus essaie d'écrire 50 MB dans /tmp

RÉSULTAT :
No space left on device

[OK] SOLUTION :
docker run --tmpfs /tmp:size=100m ubuntu


================================================================================
[OK] SECTION 6 : CAS D'USAGE PRATIQUES
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 1 : Application Web avec Uploads                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

CONTEXTE : Site web avec uploads d'images utilisateurs

# Volume pour uploads
docker volume create app-uploads

# Volume pour base de données
docker volume create app-db

# Lancer DB
docker run -d \
  --name db \
  --network app-net \
  -v app-db:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:15

# Lancer application
docker run -d \
  --name web \
  --network app-net \
  -v app-uploads:/app/uploads \
  -p 80:80 \
  -e DB_HOST=db \
  myapp

RÉSULTAT :
• Uploads persistants même après redémarrage
• Base de données persistante
• Peut mettre à jour l'app sans perdre les données


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 2 : Développement Full-Stack                                        ║
╚══════════════════════════════════════════════════════════════════════════╝

STRUCTURE :
projet/
├── frontend/
│   ├── src/
│   └── package.json
├── backend/
│   ├── app.py
│   └── requirements.txt
└── docker-compose.yml

# Backend avec hot-reload
docker run -d \
  --name backend \
  -v $(pwd)/backend:/app \
  -w /app \
  -p 5000:5000 \
  python:3.11 \
  sh -c "pip install -r requirements.txt && python app.py"

# Frontend avec hot-reload
docker run -d \
  --name frontend \
  -v $(pwd)/frontend:/app \
  -v /app/node_modules \
  -w /app \
  -p 3000:3000 \
  node:18 \
  npm run dev

# Base de données avec volume
docker run -d \
  --name db \
  -v dev-db:/var/lib/postgresql/data \
  postgres:15

RÉSULTAT :
• Modifications code instantanées
• node_modules dans volume anonyme (performance)
• DB persistante entre sessions
• Workflow de dev optimal !


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 3 : Backup et Restore                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

SCRIPT DE BACKUP :
#!/bin/bash
# backup.sh - Sauvegarde tous les volumes

BACKUP_DIR="./backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

for volume in $(docker volume ls -q); do
    echo "[PACKAGE] Backup de $volume..."
    docker run --rm \
      -v $volume:/data:ro \
      -v $BACKUP_DIR:/backup \
      alpine tar czf /backup/${volume}.tar.gz -C /data .
    echo "[OK] $volume sauvegardé"
done

echo "[BRAVO] Tous les volumes sont sauvegardés dans $BACKUP_DIR"


SCRIPT DE RESTORE :
#!/bin/bash
# restore.sh - Restaure un volume depuis backup

VOLUME_NAME=$1
BACKUP_FILE=$2

if [ -z "$VOLUME_NAME" ] || [ -z "$BACKUP_FILE" ]; then
    echo "Usage: ./restore.sh <volume_name> <backup_file>"
    exit 1
fi

# Créer le volume s'il n'existe pas
docker volume create $VOLUME_NAME

# Restaurer
docker run --rm \
  -v $VOLUME_NAME:/data \
  -v $(pwd):/backup \
  alpine sh -c "rm -rf /data/* && tar xzf /backup/$BACKUP_FILE -C /data"

echo "[OK] Volume $VOLUME_NAME restauré depuis $BACKUP_FILE"

USAGE :
./backup.sh
./restore.sh postgres-data backups/20241118/postgres-data.tar.gz


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 4 : Migration de données entre volumes                              ║
╚══════════════════════════════════════════════════════════════════════════╝

CONTEXTE : Copier données d'un ancien volume vers un nouveau

# Créer nouveau volume
docker volume create new-volume

# Copier données
docker run --rm \
  -v old-volume:/source:ro \
  -v new-volume:/dest \
  alpine sh -c "cp -av /source/. /dest/"

VÉRIFICATION :
docker run --rm -v new-volume:/data alpine ls -la /data


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 5 : Partage de configuration entre environnements                   ║
╚══════════════════════════════════════════════════════════════════════════╝

STRUCTURE :
config/
├── dev.conf
├── prod.conf
└── secrets/

# Développement
docker run -d \
  --name app-dev \
  -v $(pwd)/config/dev.conf:/app/config.conf:ro \
  myapp

# Production
docker run -d \
  --name app-prod \
  -v $(pwd)/config/prod.conf:/app/config.conf:ro \
  myapp

AVANTAGE :
• Même image, configuration différente
• Fichiers versionnés avec Git
• Facile à modifier


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 6 : Logs centralisés                                                ║
╚══════════════════════════════════════════════════════════════════════════╝

# Volume partagé pour logs
docker volume create app-logs

# Application 1
docker run -d \
  --name service1 \
  -v app-logs:/var/log/app \
  service1

# Application 2
docker run -d \
  --name service2 \
  -v app-logs:/var/log/app \
  service2

# Analyseur de logs
docker run -d \
  --name loganalyzer \
  -v app-logs:/logs:ro \
  loganalyzer

RÉSULTAT :
Tous les services écrivent dans le même volume de logs !


================================================================================
[OK] SECTION 7 : DÉBOGAGE ET TROUBLESHOOTING
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  PROBLÈME 1 : Impossible de supprimer un volume                          ║
╚══════════════════════════════════════════════════════════════════════════╝

SYMPTÔME :
docker volume rm myvolume

ERREUR :
Error: remove myvolume: volume is in use

DIAGNOSTIC :
# Trouver quels conteneurs utilisent le volume
docker ps -a --filter volume=myvolume

SOLUTION :
# Arrêter et supprimer les conteneurs
docker rm -f $(docker ps -aq --filter volume=myvolume)

# Puis supprimer le volume
docker volume rm myvolume


╔══════════════════════════════════════════════════════════════════════════╗
║  PROBLÈME 2 : Données disparues après redémarrage                        ║
╚══════════════════════════════════════════════════════════════════════════╝

SYMPTÔME :
Données présentes, puis disparues après docker restart

DIAGNOSTIC :
# Vérifier si volume est bien monté
docker inspect container_name --format='{{.Mounts}}'

CAUSES POSSIBLES :
1. [X] Pas de volume utilisé
2. [X] Volume anonyme différent à chaque création
3. [X] Mauvais chemin de montage

SOLUTION :
[OK] Utiliser un volume nommé
[OK] Vérifier le chemin de montage correspond aux données de l'app


╔══════════════════════════════════════════════════════════════════════════╗
║  PROBLÈME 3 : Bind mount ne se met pas à jour                            ║
╚══════════════════════════════════════════════════════════════════════════╝

SYMPTÔME :
Modification de fichiers sur l'hôte, pas visible dans conteneur

DIAGNOSTIC :
# Vérifier le montage
docker inspect container_name --format='{{.Mounts}}'

CAUSES POSSIBLES :
1. [X] Cache du système de fichiers
2. [X] Montage en lecture seule (:ro)
3. [X] Application ne détecte pas les changements

SOLUTIONS :
# Recharger application
docker exec container_name kill -HUP 1

# Ou redémarrer conteneur
docker restart container_name

# Pour Node.js : utiliser nodemon
npm install nodemon
nodemon app.js


╔══════════════════════════════════════════════════════════════════════════╗
║  PROBLÈME 4 : "No space left on device"                                  ║
╚══════════════════════════════════════════════════════════════════════════╝

DIAGNOSTIC :
# Voir utilisation disque Docker
docker system df

# Voir détails volumes
docker system df -v

SOLUTIONS :
# 1. Nettoyer volumes inutilisés
docker volume prune -f

# 2. Nettoyer tout le système
docker system prune -a --volumes

# 3. Augmenter espace disque Docker
# (Paramètres Docker Desktop)


╔══════════════════════════════════════════════════════════════════════════╗
║  PROBLÈME 5 : Permissions denied (Linux)                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

SYMPTÔME :
Permission denied lors d'accès aux fichiers en bind mount

DIAGNOSTIC :
# Vérifier permissions
ls -la /chemin/vers/dossier

# Vérifier UID/GID dans conteneur
docker exec container_name id

SOLUTIONS :
# 1. Lancer avec votre UID/GID
docker run -u $(id -u):$(id -g) -v $(pwd):/app myapp

# 2. Changer permissions sur l'hôte
sudo chown -R $USER:$USER /chemin/vers/dossier

# 3. Dockerfile avec user non-root
FROM ubuntu
RUN groupadd -g 1000 appuser && \
    useradd -u 1000 -g appuser appuser
USER appuser


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDES DE DÉBOGAGE UTILES                                            ║
╚══════════════════════════════════════════════════════════════════════════╝

# Voir tous les montages d'un conteneur
docker inspect container_name --format='{{json .Mounts}}' | jq

# Lister fichiers dans un volume
docker run --rm -v myvolume:/data alpine ls -laR /data

# Voir taille d'un volume
docker run --rm -v myvolume:/data alpine du -sh /data

# Copier fichiers depuis un volume
docker run --rm -v myvolume:/data -v $(pwd):/backup alpine cp -r /data/. /backup/

# Accéder à un volume avec shell
docker run --rm -it -v myvolume:/data alpine sh

# Comparer deux volumes
docker run --rm -v vol1:/vol1:ro -v vol2:/vol2:ro alpine diff -r /vol1 /vol2


================================================================================
[OK] SECTION 8 : CHEAT SHEET COMPLET
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  VOLUMES                                                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

# Créer
docker volume create myvolume

# Lister
docker volume ls
docker volume ls --filter dangling=true

# Inspecter
docker volume inspect myvolume

# Utiliser
docker run -v myvolume:/data nginx
docker run --mount source=myvolume,target=/data nginx

# Lecture seule
docker run -v myvolume:/data:ro nginx

# Supprimer
docker volume rm myvolume
docker volume prune              # Inutilisés
docker volume prune -f           # Sans confirmation

# Backup
docker run --rm -v myvolume:/data -v $(pwd):/backup \
  alpine tar czf /backup/backup.tar.gz -C /data .

# Restore
docker run --rm -v myvolume:/data -v $(pwd):/backup \
  alpine tar xzf /backup/backup.tar.gz -C /data

# Copier entre volumes
docker run --rm -v old:/source:ro -v new:/dest alpine cp -av /source/. /dest/


╔══════════════════════════════════════════════════════════════════════════╗
║  BIND MOUNTS                                                             ║
╚══════════════════════════════════════════════════════════════════════════╝

# Basique
docker run -v /chemin/hote:/chemin/conteneur nginx
docker run -v $(pwd):/app python:3.11

# Lecture seule
docker run -v $(pwd):/app:ro nginx

# Avec --mount
docker run --mount type=bind,source=$(pwd),target=/app nginx

# Windows (PowerShell)
docker run -v ${PWD}:/app nginx
docker run -v C:/Users/user/projet:/app nginx

# Avec permissions utilisateur
docker run -u $(id -u):$(id -g) -v $(pwd):/app alpine

# Fichier unique
docker run -v $(pwd)/config.json:/app/config.json nginx


╔══════════════════════════════════════════════════════════════════════════╗
║  TMPFS                                                                   ║
╚══════════════════════════════════════════════════════════════════════════╝

# Basique
docker run --tmpfs /tmp ubuntu

# Avec options
docker run --tmpfs /tmp:rw,size=100m,mode=1777 ubuntu

# Avec --mount
docker run --mount type=tmpfs,destination=/tmp,tmpfs-size=100m ubuntu


╔══════════════════════════════════════════════════════════════════════════╗
║  DIAGNOSTIC                                                              ║
╚══════════════════════════════════════════════════════════════════════════╝

# Voir montages conteneur
docker inspect container_name --format='{{.Mounts}}'
docker inspect container_name --format='{{json .Mounts}}' | jq

# Utilisation disque
docker system df
docker system df -v

# Conteneurs utilisant un volume
docker ps -a --filter volume=myvolume

# Explorer volume
docker run --rm -it -v myvolume:/data alpine sh


╔══════════════════════════════════════════════════════════════════════════╗
║  NETTOYAGE                                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

# Volumes uniquement
docker volume prune -f

# Tout (conteneurs, images, volumes, réseaux)
docker system prune -a --volumes

# Supprimer volumes spécifiques
docker volume rm volume1 volume2 volume3


================================================================================
[OK] SECTION 9 : EXEMPLES AVANCÉS
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  Volume Driver personnalisé (NFS, Cloud, etc.)                           ║
╚══════════════════════════════════════════════════════════════════════════╝

# Volume NFS
docker volume create --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/path/to/share \
  nfs-volume

# Volume avec driver SSHFS
docker plugin install --grant-all-permissions vieux/sshfs
docker volume create --driver vieux/sshfs \
  -o sshcmd=user@server:/path \
  -o password=secret \
  ssh-volume


╔══════════════════════════════════════════════════════════════════════════╗
║  Performance : Volume vs Bind Mount                                      ║
╚══════════════════════════════════════════════════════════════════════════╝

BENCHMARK :

# Volume (rapide)
docker run --rm -v testvol:/data alpine \
  sh -c "dd if=/dev/zero of=/data/test bs=1M count=1000"
-> 1000+0 records in/out, 3.5 GB/s

# Bind mount (plus lent selon OS)
docker run --rm -v $(pwd)/test:/data alpine \
  sh -c "dd if=/dev/zero of=/data/test bs=1M count=1000"
-> 1000+0 records in/out, 2.1 GB/s

CONCLUSION :
Volumes sont plus rapides pour I/O intensif !


╔══════════════════════════════════════════════════════════════════════════╗
║  Stack complète de production                                            ║
╚══════════════════════════════════════════════════════════════════════════╝

#!/bin/bash
# deploy-prod.sh

# Création des volumes
docker volume create prod-db
docker volume create prod-uploads
docker volume create prod-logs

# Réseau
docker network create prod-network

# Base de données
docker run -d \
  --name prod-db \
  --network prod-network \
  --restart unless-stopped \
  -v prod-db:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=$(cat /secrets/db_password) \
  postgres:15-alpine

# Application
docker run -d \
  --name prod-app \
  --network prod-network \
  --restart unless-stopped \
  -v prod-uploads:/app/uploads \
  -v prod-logs:/app/logs \
  -p 443:443 \
  -e DB_HOST=prod-db \
  myapp:latest

# Backup quotidien (cron)
echo "0 2 * * * /root/backup-volumes.sh" | crontab -


════════════════════════════════════════════════════════════════════════════
  RÉSUMÉ FINAL
════════════════════════════════════════════════════════════════════════════

[PACKAGE] VOLUMES :
• Gérés par Docker
• Persistants et partageable
• [OK] RECOMMANDÉ pour production
• Commandes :
  - docker volume create
  - docker run -v myvolume:/data

[DOSSIER] BIND MOUNTS :
• Dossiers de l'hôte
• Hot-reload instantané
• [OK] PARFAIT pour développement
• Commandes :
  - docker run -v $(pwd):/app
  - docker run -v /path:/container/path

[RAPIDE] TMPFS :
• En mémoire (RAM)
• Très rapide, non persistant
• [OK] IDÉAL pour secrets/cache
• Commandes :
  - docker run --tmpfs /tmp

RÈGLE D'OR :
┌──────────────────────────────────────────────────────────────────────────┐
│ Production    -> VOLUMES                                                  │
│ Développement -> BIND MOUNTS                                              │
│ Cache/Secrets -> TMPFS                                                    │
└──────────────────────────────────────────────────────────────────────────┘


PROCHAINE SECTION :
Partie 11 - Docker Compose (Orchestration multi-conteneurs)


# FIN DU FICHIER docker_volumes_stockage.txt - PARTIE 10


================================================================================
    DOCKER - VARIABLES D'ENVIRONNEMENT ET GESTION DES RESSOURCES (PARTIE 11)
           Configuration et Optimisation des Conteneurs
================================================================================


╔══════════════════════════════════════════════════════════════════════════╗
║                    INTRODUCTION À CETTE SECTION                          ║
╚══════════════════════════════════════════════════════════════════════════╝

OBJECTIF :
Maîtriser la configuration et l'optimisation des conteneurs Docker.

CE QUE VOUS ALLEZ APPRENDRE :
1. Utiliser les variables d'environnement
2. Gérer les fichiers .env
3. Limiter l'utilisation des ressources (CPU, RAM)
4. Optimiser les performances
5. Monitorer la consommation

PRÉREQUIS :
[OK] Comprendre docker run
[OK] Connaître les bases Linux (variables)
[OK] Avoir lancé des conteneurs

CAS D'USAGE :
• Configurer des applications sans rebuild
• Gérer plusieurs environnements (dev, test, prod)
• Limiter ressources pour éviter surcharge
• Optimiser performances
• Sécuriser les secrets


================================================================================
[OK] SECTION 1 : VARIABLES D'ENVIRONNEMENT - CONFIGURATION DYNAMIQUE
================================================================================

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Variables d'environnement                                     ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ QUOI : Paramètres de configuration passés au conteneur au démarrage     │
└──────────────────────────────────────────────────────────────────────────┘

POURQUOI utiliser des variables ?
• Configuration sans modifier l'image
• Différentes configs (dev/test/prod) avec la même image
• Secrets (mots de passe, clés API)
• Comportement adaptatif
• Déploiement flexible

ANALOGIE :
Image Docker = Application Android/iOS
Variables d'env = Paramètres de l'application
-> Même app, configuration différente selon l'utilisateur


EXEMPLE CONCRET :

┌──────────────────────────────────────────────────────────────────────────┐
│                         SANS VARIABLES                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ Image : myapp-dev                                                        │
│ Image : myapp-test                                                       │
│ Image : myapp-prod                                                       │
│                                                                          │
│ [X] 3 images différentes à maintenir                                      │
│ [X] Rebuild pour chaque changement                                        │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│                         AVEC VARIABLES                                   │
├──────────────────────────────────────────────────────────────────────────┤
│ Image : myapp (une seule !)                                              │
│                                                                          │
│ Dev  : -e ENV=dev -e DB_HOST=localhost                                  │
│ Test : -e ENV=test -e DB_HOST=test-db                                   │
│ Prod : -e ENV=prod -e DB_HOST=prod-db                                   │
│                                                                          │
│ [OK] Une seule image                                                       │
│ [OK] Configuration flexible                                                │
└──────────────────────────────────────────────────────────────────────────┘


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : docker run -e (passer des variables)                         ║
╚══════════════════════════════════════════════════════════════════════════╝

SYNTAXE :
docker run -e "VARIABLE=valeur" IMAGE
docker run --env VARIABLE=valeur IMAGE


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Variable simple
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -it -e "MESSAGE=Bonjour Docker" ubuntu bash

EXPLICATION :
-e "MESSAGE=Bonjour Docker"
   ^        ^
   Nom      Valeur


VÉRIFICATION : Dans le conteneur
echo $MESSAGE
-> Bonjour Docker

env | grep MESSAGE
-> MESSAGE=Bonjour Docker


EXEMPLE : Utilisation dans un script
# Dans le conteneur
cat << 'EOF' > script.sh
#!/bin/bash
echo "Le message est : $MESSAGE"
EOF

chmod +x script.sh
./script.sh
-> Le message est : Bonjour Docker


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Plusieurs variables
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d \
  -e "DB_HOST=localhost" \
  -e "DB_PORT=5432" \
  -e "DB_NAME=mydb" \
  -e "DB_USER=admin" \
  --name myapp \
  myapp-image


VÉRIFICATION :
docker exec myapp env | grep DB_

RÉSULTAT :
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=admin


EXEMPLE RÉEL : Application Node.js
docker run -d \
  -p 3000:3000 \
  -e "NODE_ENV=production" \
  -e "PORT=3000" \
  -e "DATABASE_URL=postgresql://localhost/mydb" \
  -e "REDIS_URL=redis://cache:6379" \
  -e "API_KEY=secret123" \
  node-app


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Variables avec espaces et caractères spéciaux
═══════════════════════════════════════════════════════════════════════════

AVEC GUILLEMETS :
docker run -e "MESSAGE=Bonjour le monde !" ubuntu

docker run -e "JSON={\"key\": \"value\"}" ubuntu

docker run -e "PATH_WITH_SPACE=/mon dossier/app" ubuntu


SANS GUILLEMETS (erreur potentielle) :
docker run -e MESSAGE=Bonjour le monde ubuntu
                      ^
              Seulement "Bonjour" sera pris !


RÈGLE :
[OK] Toujours utiliser des guillemets si la valeur contient :
  - Espaces
  - Caractères spéciaux ($, !, ", etc.)
  - JSON


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Variable depuis l'environnement de l'hôte
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Utiliser une variable déjà définie sur l'hôte

# Sur l'hôte
export MY_SECRET="secret_value_123"

# Passer au conteneur
docker run -e MY_SECRET ubuntu

OU (explicite) :
docker run -e MY_SECRET=$MY_SECRET ubuntu


VÉRIFICATION :
docker run -it -e MY_SECRET ubuntu bash
echo $MY_SECRET
-> secret_value_123


UTILITÉ :
• Variables de CI/CD
• Secrets du système
• Configuration de l'hôte


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : Fichier .env (recommandé)
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Beaucoup de variables -> Fichier .env

CRÉER UN FICHIER .env :
cat << EOF > .env
# Configuration de la base de données
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASSWORD=secret123

# Configuration de l'application
NODE_ENV=production
PORT=3000
LOG_LEVEL=info

# Services externes
REDIS_URL=redis://cache:6379
API_KEY=my_secret_api_key
EOF


COMMANDE : Utiliser le fichier
docker run -d --env-file .env myapp


VÉRIFICATION :
docker exec container_name env

RÉSULTAT :
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
... (toutes les variables du fichier)


AVANTAGES :
[OK] Lisible et organisé
[OK] Versionnable (avec .gitignore pour secrets)
[OK] Réutilisable
[OK] Facile à maintenir


STRUCTURE RECOMMANDÉE :
projet/
├── .env.example          # Exemple sans secrets (commit Git)
├── .env                  # Valeurs réelles (dans .gitignore)
├── .env.development
├── .env.production
└── docker-compose.yml


FICHIER .env.example :
# Base de données
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASSWORD=CHANGE_ME

# Application
NODE_ENV=production
API_KEY=YOUR_API_KEY_HERE


FICHIER .gitignore :
.env
.env.production


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Format du fichier .env
═══════════════════════════════════════════════════════════════════════════

SYNTAXE CORRECTE :

# Commentaires
VAR1=valeur1
VAR2=valeur2

# Avec guillemets (optionnel)
VAR3="valeur avec espaces"
VAR4='autre valeur'

# Sans espaces autour du =
CORRECT=value
[X] INCORRECT = value    # Ne fonctionne pas !

# Variables multi-lignes (pas supporté)
[X] VAR="valeur
       sur plusieurs
       lignes"        # Ne fonctionne pas !

# JSON en une ligne
JSON='{"key": "value", "number": 123}'


EXEMPLE COMPLET :
# Fichier .env valide
DB_HOST=postgres
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASSWORD=secret123

# Application
APP_NAME="My Application"
APP_VERSION=1.0.0
DEBUG=false

# URLs
API_URL=https://api.example.com
CALLBACK_URL=http://localhost:3000/callback

# JSON config
CONFIG='{"timeout": 30, "retries": 3}'


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Variables avec valeurs par défaut (Dockerfile)
═══════════════════════════════════════════════════════════════════════════

DANS LE DOCKERFILE :
FROM node:18

# Définir valeurs par défaut
ENV NODE_ENV=development
ENV PORT=3000
ENV LOG_LEVEL=info

COPY . /app
WORKDIR /app

CMD ["npm", "start"]


LANCER AVEC VALEURS PAR DÉFAUT :
docker build -t myapp .
docker run myapp
# Utilise NODE_ENV=development, PORT=3000


LANCER AVEC OVERRIDE :
docker run -e NODE_ENV=production -e PORT=8080 myapp
# Override : NODE_ENV=production, PORT=8080


PRIORITÉ :
1. docker run -e (plus haute priorité)
2. ENV dans Dockerfile
3. Variable système de l'image de base


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Voir les variables d'un conteneur
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Toutes les variables
docker exec container_name env

RÉSULTAT :
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/root
DB_HOST=localhost
NODE_ENV=production
...


COMMANDE : Filtrer les variables
docker exec container_name env | grep DB_

RÉSULTAT :
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp


COMMANDE : Avec docker inspect
docker inspect container_name --format='{{range .Config.Env}}{{println .}}{{end}}'

RÉSULTAT :
PATH=/usr/local/bin:/usr/bin:/bin
DB_HOST=localhost
NODE_ENV=production


COMMANDE : Format JSON
docker inspect container_name --format='{{json .Config.Env}}' | jq

RÉSULTAT :
[
  "PATH=/usr/local/bin:/usr/bin:/bin",
  "DB_HOST=localhost",
  "NODE_ENV=production"
]


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Variables dynamiques avec substitution
═══════════════════════════════════════════════════════════════════════════

CONTEXTE : Construire des valeurs à partir d'autres variables

FICHIER .env :
DB_HOST=postgres
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASSWORD=secret

# [X] Ceci NE FONCTIONNE PAS dans .env Docker :
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}


SOLUTION 1 : Construire dans l'application
# Dans l'app (Node.js exemple)
const DATABASE_URL = `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`;


SOLUTION 2 : Script d'initialisation
#!/bin/bash
export DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}"
exec "$@"


SOLUTION 3 : Docker Compose (supporte substitution)
# docker-compose.yml
services:
  app:
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/myapp


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Cas d'usage - Environnements multiples
═══════════════════════════════════════════════════════════════════════════

STRUCTURE :
projet/
├── .env.development
├── .env.test
├── .env.production
└── Dockerfile


FICHIER .env.development :
NODE_ENV=development
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev
LOG_LEVEL=debug
DEBUG=true


FICHIER .env.production :
NODE_ENV=production
DB_HOST=prod-db.example.com
DB_PORT=5432
DB_NAME=myapp_prod
LOG_LEVEL=error
DEBUG=false


COMMANDES :

# Développement
docker run --env-file .env.development myapp

# Test
docker run --env-file .env.test myapp

# Production
docker run --env-file .env.production myapp


SCRIPT DE DÉPLOIEMENT :
#!/bin/bash
ENV=${1:-development}  # Par défaut : development

echo "[RAPIDE] Déploiement en mode: $ENV"

docker run -d \
  --name myapp-$ENV \
  --env-file .env.$ENV \
  -p $PORT:3000 \
  myapp:latest

echo "[OK] Application déployée en mode $ENV"

USAGE :
./deploy.sh development
./deploy.sh production


═══════════════════════════════════════════════════════════════════════════
  BONNES PRATIQUES - Variables d'environnement
═══════════════════════════════════════════════════════════════════════════

[OK] À FAIRE :

1. Utiliser fichiers .env pour organisation
   [OK] --env-file .env
   [X] -e VAR1=... -e VAR2=... -e VAR3=... (trop long)

2. Ne JAMAIS commiter les secrets
   # .gitignore
   .env
   .env.production
   *.secret

3. Fournir .env.example
   # Avec valeurs fictives
   DB_PASSWORD=CHANGE_ME
   API_KEY=YOUR_KEY_HERE

4. Valider les variables requises
   # Dans l'app ou script
   if [ -z "$DB_HOST" ]; then
       echo "ERROR: DB_HOST is required"
       exit 1
   fi

5. Utiliser noms descriptifs
   [OK] DATABASE_HOST
   [OK] REDIS_CONNECTION_URL
   [X] DB (trop vague)
   [X] X (pas clair)

6. Documenter les variables
   # .env.example
   # Database connection
   DB_HOST=localhost      # Hostname of the database
   DB_PORT=5432          # Port number (default: 5432)

7. Préfixer les variables par service
   DB_HOST=...
   DB_PORT=...
   REDIS_HOST=...
   REDIS_PORT=...


[X] À ÉVITER :

1. Secrets en clair dans Dockerfile
   [X] ENV DB_PASSWORD=secret123
   [OK] Passer via -e au runtime

2. Mélanger dev et prod dans même .env
   [X] Un seul .env pour tout
   [OK] .env.development, .env.production

3. Oublier de valider
   [X] L'app crash car variable manquante
   [OK] Vérifier au démarrage

4. Variables sensibles dans logs
   # [X] Ne pas faire :
   echo "Connecting with password: $DB_PASSWORD"
   
   # [OK] À faire :
   echo "Connecting to database..."

5. Hardcoder les valeurs
   [X] const DB_HOST = "localhost";
   [OK] const DB_HOST = process.env.DB_HOST;


═══════════════════════════════════════════════════════════════════════════
  PIÈGES COURANTS - Variables
═══════════════════════════════════════════════════════════════════════════

[X] PIÈGE 1 : Espaces autour du =

# Fichier .env
DB_HOST = localhost    # [X] Ne fonctionne pas !
DB_HOST=localhost      # [OK] Correct


[X] PIÈGE 2 : Variable non définie

docker run -e DB_HOST myapp

# Dans le conteneur
echo $DB_HOST
-> (vide)

[OK] SOLUTION :
docker run -e DB_HOST=localhost myapp


[X] PIÈGE 3 : Guillemets dans .env

# .env
MESSAGE="Hello World"

# Dans conteneur
echo $MESSAGE
-> "Hello World"    # Guillemets inclus !

[OK] Si vous ne voulez pas les guillemets :
MESSAGE=Hello World


[X] PIÈGE 4 : Variables non exportées sur l'hôte

# Sur l'hôte
MY_VAR=test    # Pas exporté !
docker run -e MY_VAR ubuntu
# MY_VAR est vide dans le conteneur

[OK] SOLUTION :
export MY_VAR=test
docker run -e MY_VAR ubuntu


[X] PIÈGE 5 : Chemins avec espaces

docker run -e PATH="/mon dossier/app" ubuntu
                    ^
              Problème avec l'espace

[OK] SOLUTION :
docker run -e "PATH=/mon\ dossier/app" ubuntu
OU
docker run -e 'PATH=/mon dossier/app' ubuntu


════════════════════════════════════════════════════════════════════════════
[OK] SECTION 2 : GESTION DES RESSOURCES - LIMITES ET OPTIMISATION
════════════════════════════════════════════════════════════════════════════

╔══════════════════════════════════════════════════════════════════════════╗
║  CONCEPT : Pourquoi limiter les ressources ?                             ║
╚══════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────┐
│ PROBLÈME : Sans limites, un conteneur peut consommer TOUTES les         │
│            ressources de l'hôte                                          │
└──────────────────────────────────────────────────────────────────────────┘


SCÉNARIO CATASTROPHE :

Un conteneur avec une fuite mémoire :
docker run myapp-buggy

RÉSULTAT :
• Le conteneur consomme toute la RAM (16 GB)
• L'hôte commence à swapper
• Tous les autres conteneurs ralentissent
• Le système devient inutilisable
• Crash du serveur ! [IMPACT]


AVEC LIMITES :
docker run -m 512m myapp-buggy

RÉSULTAT :
• Le conteneur limité à 512 MB
• S'il dépasse : Docker le tue (OOM Kill)
• Les autres conteneurs continuent normalement
• Système stable [OK]


RAISONS DE LIMITER :

1. STABILITÉ :
   • Empêcher qu'un conteneur monopolise tout
   • Prévenir les crashs système
   • Isoler les problèmes

2. PRÉVISIBILITÉ :
   • Garantir des ressources aux services critiques
   • Performance constante
   • Planification de capacité

3. SÉCURITÉ :
   • Limiter l'impact d'un conteneur compromis
   • DoS prevention
   • Isolation des workloads

4. MULTI-TENANCY :
   • Partager équitablement les ressources
   • Facturation par ressources
   • SLA garantis


╔══════════════════════════════════════════════════════════════════════════╗
║  COMMANDE : Limiter la mémoire                                           ║
╚══════════════════════════════════════════════════════════════════════════╝

SYNTAXE :
docker run -m <valeur> IMAGE
docker run --memory=<valeur> IMAGE

UNITÉS :
b  -> bytes
k  -> kilobytes
m  -> megabytes
g  -> gigabytes


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 1 : Limite mémoire simple
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d -m 512m --name limited-app myapp

EXPLICATION :
-m 512m  -> Limite à 512 megabytes de RAM


VÉRIFICATION :
docker stats limited-app --no-stream

RÉSULTAT :
CONTAINER ID   NAME           MEM USAGE / LIMIT   MEM %
abc123...      limited-app    250MiB / 512MiB     48.83%
                              ^        ^
                           Usage    Limite


TEST : Dépasser la limite
docker run -it -m 100m ubuntu bash

# Dans le conteneur
apt-get update && apt-get install -y stress
stress --vm 1 --vm-bytes 200M

RÉSULTAT :
Killed  <- Le conteneur est tué (OOM Kill)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 2 : Mémoire + Swap
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d \
  -m 512m \
  --memory-swap=1g \
  myapp

EXPLICATION :
-m 512m              -> 512 MB de RAM
--memory-swap=1g     -> 1 GB total (RAM + Swap)
                       Donc : 512 MB RAM + 512 MB Swap


SCHÉMA :
┌────────────────────────────────────────────────┐
│ Mémoire totale disponible : 1 GB               │
├────────────────────────────────────────────────┤
│ RAM  : 512 MB  (rapide)                        │
│ Swap : 512 MB  (plus lent, sur disque)         │
└────────────────────────────────────────────────┘


COMMANDE : Désactiver le swap
docker run -m 512m --memory-swap=512m myapp
                   ^
        Swap = Memory -> Pas de swap !


COMMANDE : Swap illimité (déconseillé)
docker run -m 512m --memory-swap=-1 myapp


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 3 : Réservation de mémoire (soft limit)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d \
  --memory-reservation=256m \
  -m 512m \
  myapp

EXPLICATION :
--memory-reservation=256m  -> Mémoire "garantie"
-m 512m                    -> Limite maximale

Le conteneur a :
• Au moins 256 MB (si disponible sur l'hôte)
• Peut utiliser jusqu'à 512 MB
• Si mémoire hôte faible : limité à 256 MB


USAGE :
Services critiques qui ont besoin d'une mémoire minimale garantie.


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 4 : Limiter le CPU
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Nombre de CPUs
docker run -d --cpus="1.5" myapp

EXPLICATION :
--cpus="1.5"  -> Peut utiliser 1.5 CPU
              -> Sur un système 4 cores : max 37.5% d'utilisation


EXEMPLES :
--cpus="0.5"  -> Moitié d'un CPU
--cpus="1"    -> Un CPU complet
--cpus="2"    -> Deux CPUs
--cpus="0.25" -> Quart d'un CPU


VÉRIFICATION :
docker stats myapp --no-stream

RÉSULTAT :
CONTAINER ID   NAME    CPU %
abc123...      myapp   37.50%  <- Limite à 37.5% (1.5/4 CPUs)


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 5 : CPU Shares (priorité relative)
═══════════════════════════════════════════════════════════════════════════

COMMANDE :
docker run -d --cpu-shares=512 --name app1 myapp1
docker run -d --cpu-shares=1024 --name app2 myapp2

EXPLICATION :
--cpu-shares=512   -> Priorité basse (par défaut : 1024)
--cpu-shares=1024  -> Priorité normale
--cpu-shares=2048  -> Priorité haute


FONCTIONNEMENT :
• Si CPU n'est PAS saturé : pas d'effet
• Si CPU EST saturé : partage selon les shares

Exemple avec CPU saturé :
app1 : 512 shares  -> 33% du CPU (512/(512+1024))
app2 : 1024 shares -> 67% du CPU (1024/(512+1024))


SCHÉMA :
┌────────────────────────────────────────────────┐
│             CPU saturé à 100%                  │
├────────────────────────────────────────────────┤
│ app1 (512 shares)   : ████████░░ 33%          │
│ app2 (1024 shares)  : ████████████████████ 67%│
└────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 6 : Épingler sur des CPUs spécifiques
═══════════════════════════════════════════════════════════════════════════

COMMANDE : CPUs spécifiques
docker run -d --cpuset-cpus="0,1" myapp

EXPLICATION :
--cpuset-cpus="0,1"  -> Utilise SEULEMENT les CPUs 0 et 1


COMMANDE : Plage de CPUs
docker run -d --cpuset-cpus="0-3" myapp
# Utilise CPUs 0, 1, 2, 3


COMMANDE : Un seul CPU
docker run -d --cpuset-cpus="0" myapp
# Utilise SEULEMENT le CPU 0


USAGE :
• Isolation de workloads
• Performance prévisible
• NUMA optimization
• Tests de performance


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 7 : Limiter I/O disque
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Limite lecture
docker run -d \
  --device-read-bps=/dev/sda:1mb \
  myapp

EXPLICATION :
--device-read-bps=/dev/sda:1mb
                  ^         ^
              Disque    Limite : 1 MB/s


COMMANDE : Limite écriture
docker run -d \
  --device-write-bps=/dev/sda:10mb \
  myapp


COMMANDE : Limite IOPS (opérations/seconde)
docker run -d \
  --device-read-iops=/dev/sda:100 \
  --device-write-iops=/dev/sda:50 \
  myapp


TEST :
docker run -it --rm \
  --device-write-bps=/dev/sda:1mb \
  ubuntu bash

# Dans conteneur
dd if=/dev/zero of=/tmp/test bs=1M count=100
-> Limité à ~1 MB/s


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 8 : Mettre à jour limites conteneur en cours
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Mettre à jour mémoire
docker update --memory=1g container_name

COMMANDE : Mettre à jour CPU
docker update --cpus=2 container_name

COMMANDE : Plusieurs à la fois
docker update --memory=2g --cpus=3 container_name


VÉRIFICATION :
docker inspect container_name --format='{{.HostConfig.Memory}}'
-> 2147483648  (2 GB en bytes)


USAGE :
• Ajuster sans redémarrer
• Répondre à la charge
• Tests de performance


EXEMPLE : Script d'auto-scaling
#!/bin/bash
CONTAINER="myapp"
CPU_THRESHOLD=80

while true; do
    CPU_USAGE=$(docker stats $CONTAINER --no-stream --format "{{.CPUPerc}}" | sed 's/%//')
    
    if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) )); then
        echo "[ATTENTION]  CPU élevé : ${CPU_USAGE}% - Augmentation des ressources"
        docker update --cpus=4 --memory=2g $CONTAINER
    else
        echo "[OK] CPU normal : ${CPU_USAGE}%"
        docker update --cpus=2 --memory=1g $CONTAINER
    fi
    
    sleep 60
done


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 9 : Voir les limites d'un conteneur
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Avec docker inspect
docker inspect container_name --format='Memory: {{.HostConfig.Memory}} bytes'
docker inspect container_name --format='CPUs: {{.HostConfig.NanoCpus}}'


COMMANDE : Toutes les limites
docker inspect container_name --format='{{json .HostConfig}}' | jq '{
  Memory: .Memory,
  MemorySwap: .MemorySwap,
  MemoryReservation: .MemoryReservation,
  NanoCpus: .NanoCpus,
  CpuShares: .CpuShares,
  CpusetCpus: .CpusetCpus
}'


RÉSULTAT :
{
  "Memory": 536870912,
  "MemorySwap": 1073741824,
  "MemoryReservation": 268435456,
  "NanoCpus": 1500000000,
  "CpuShares": 1024,
  "CpusetCpus": "0,1"
}


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 10 : Monitoring en temps réel
═══════════════════════════════════════════════════════════════════════════

COMMANDE : Stats d'un conteneur
docker stats container_name

RÉSULTAT :
CONTAINER ID   NAME      CPU %   MEM USAGE / LIMIT   MEM %   NET I/O       BLOCK I/O
abc123...      myapp     25.5%   512MiB / 1GiB       50%     1.2MB / 850kB 5MB / 3MB


COMMANDE : Tous les conteneurs
docker stats

COMMANDE : Format personnalisé
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

RÉSULTAT :
NAME        CPU %     MEM USAGE / LIMIT     NET I/O
web         15.5%     256MiB / 512MiB       1.2MB / 850kB
api         35.2%     1.2GiB / 2GiB         5.5MB / 3.2MB
db          8.1%      800MiB / 2GiB         200kB / 150kB


COMMANDE : Une seule lecture (pas de stream)
docker stats --no-stream

COMMANDE : JSON pour traitement
docker stats --no-stream --format "{{json .}}"


═══════════════════════════════════════════════════════════════════════════
  EXEMPLE 11 : Profils complets (Stack applicative)
═══════════════════════════════════════════════════════════════════════════

CAS D'USAGE : Application web typique

# Frontend (léger)
docker run -d \
  --name frontend \
  -m 256m \
  --cpus="0.5" \
  -p 80:80 \
  nginx

# Backend API (moyen)
docker run -d \
  --name api \
  -m 1g \
  --memory-reservation=512m \
  --cpus="2" \
  -p 3000:3000 \
  node-api

# Base de données (gourmand)
docker run -d \
  --name database \
  -m 4g \
  --memory-reservation=2g \
  --cpus="4" \
  --cpuset-cpus="0-3" \
  -v db-data:/var/lib/postgresql/data \
  postgres:15

# Worker (traitement intensif)
docker run -d \
  --name worker \
  -m 2g \
  --cpus="6" \
  --cpu-shares=2048 \
  worker-app

# Cache (rapide, limité)
docker run -d \
  --name cache \
  -m 512m \
  --cpus="1" \
  redis:alpine


RÉSUMÉ DES RESSOURCES :
╔════════════════╦═════════╦═════════╦══════════════════════╗
║ SERVICE        ║ RAM     ║ CPU     ║ PRIORITÉ             ║
╠════════════════╬═════════╬═════════╬══════════════════════╣
║ Frontend       ║ 256 MB  ║ 0.5     ║ Basse (léger)        ║
║ API            ║ 1 GB    ║ 2.0     ║ Normale              ║
║ Database       ║ 4 GB    ║ 4.0     ║ Haute (dédiée CPUs)  ║
║ Worker         ║ 2 GB    ║ 6.0     ║ Haute (shares: 2048) ║
║ Cache          ║ 512 MB  ║ 1.0     ║ Normale              ║
╠════════════════╬═════════╬═════════╬══════════════════════╣
║ TOTAL          ║ 7.75 GB ║ 13.5    ║                      ║
╚════════════════╩═════════╩═════════╩══════════════════════╝


═══════════════════════════════════════════════════════════════════════════
  BONNES PRATIQUES - Gestion des ressources
═══════════════════════════════════════════════════════════════════════════

[OK] À FAIRE :

1. TOUJOURS limiter en production
   [OK] docker run -m 1g --cpus=2 myapp
   [X] docker run myapp (illimité)

2. Réserver pour services critiques
   docker run -m 2g --memory-reservation=1g database

3. Monitorer régulièrement
   docker stats --no-stream > stats.log

4. Tester les limites
   # Commencer bas, augmenter si nécessaire
   -m 256m  -> Tester
   -m 512m  -> Tester
   -m 1g    -> OK !

5. Documenter les requirements
   # README.md
   ## Ressources minimales
   - Frontend: 256 MB RAM, 0.5 CPU
   - API: 1 GB RAM, 2 CPUs
   - DB: 4 GB RAM, 4 CPUs

6. Utiliser docker update pour ajuster
   # Pas besoin de redémarrer
   docker update --memory=2g myapp

7. Prévoir une marge
   # Si app utilise 800 MB en moyenne
   -m 1g  [OK] (marge de 25%)
   -m 900m [X] (trop juste)


[X] À ÉVITER :

1. Pas de limites en production
   [X] Un conteneur peut tout consommer
   [OK] Toujours limiter

2. Limites trop strictes
   [X] -m 100m pour une app Java (crash OOM)
   [OK] Tester et ajuster

3. Ignorer le swap
   [X] --memory-swap=-1 (illimité)
   [OK] Contrôler le swap

4. Sur-allouer les ressources
   # Hôte avec 8 GB RAM
   [X] 10 conteneurs × 1 GB = 10 GB (impossible)
   [OK] Total ≤ 80% des ressources hôte

5. Limites CPU sans considérer le type de workload
   [X] --cpus=0.5 pour calcul intensif
   [OK] Adapter aux besoins

6. Ne pas monitorer
   [X] Lancer et oublier
   [OK] docker stats régulièrement


═══════════════════════════════════════════════════════════════════════════
  PIÈGES COURANTS - Ressources
═══════════════════════════════════════════════════════════════════════════

[X] PIÈGE 1 : OOM Kill sans comprendre

SYMPTÔME :
docker logs myapp
-> Killed

CAUSE :
Conteneur a dépassé la limite mémoire

DIAGNOSTIC :
docker inspect myapp --format='{{.State.OOMKilled}}'
-> true

[OK] SOLUTION :
# Augmenter limite
docker update --memory=2g myapp
# OU optimiser l'application


[X] PIÈGE 2 : CPU "gelé" avec --cpus

docker run --cpus="0.1" myapp
# Application très lente !

CAUSE :
0.1 CPU = 10% d'un seul core
Trop peu pour la plupart des apps

[OK] SOLUTION :
Minimum 0.5 CPU pour apps standard


[X] PIÈGE 3 : Swap ralentit tout

docker run -m 512m --memory-swap=10g myapp
# Performance horrible !

CAUSE :
Swap = disque (lent)
App utilise 5 GB de swap -> très lent

[OK] SOLUTION :
Limiter swap proche de la RAM
--memory-swap=1g (512m RAM + 512m swap max)


[X] PIÈGE 4 : Limites en bytes

docker run -m 536870912 myapp
           ^
      512 MB en bytes (difficile à lire)

[OK] SOLUTION :
docker run -m 512m myapp  (plus clair)


[X] PIÈGE 5 : Sur-commitment sans comprendre

# Hôte : 4 CPUs
docker run --cpus=2 app1
docker run --cpus=2 app2
docker run --cpus=2 app3
# Total : 6 CPUs demandés, seulement 4 disponibles !

RÉSULTAT :
Les 3 apps se partagent les 4 CPUs -> ralentissement

[OK] COMPRENDRE :
--cpus est une LIMITE, pas une GARANTIE
Les conteneurs partagent le CPU disponible


════════════════════════════════════════════════════════════════════════════
[OK] SECTION 3 : CAS D'USAGE AVANCÉS
════════════════════════════════════════════════════════════════════════════

╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 1 : Environnements Development / Staging / Production               ║
╚══════════════════════════════════════════════════════════════════════════╝

STRUCTURE :
projet/
├── .env.development
├── .env.staging
├── .env.production
└── deploy.sh


FICHIER .env.development :
# Development (ressources minimales)
NODE_ENV=development
DEBUG=true
LOG_LEVEL=debug

DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev

MEMORY_LIMIT=512m
CPU_LIMIT=1


FICHIER .env.production :
# Production (ressources optimales)
NODE_ENV=production
DEBUG=false
LOG_LEVEL=error

DB_HOST=prod-db.example.com
DB_PORT=5432
DB_NAME=myapp_prod

MEMORY_LIMIT=4g
CPU_LIMIT=4
MEMORY_RESERVATION=2g


SCRIPT deploy.sh :
#!/bin/bash
set -e

ENV=${1:-development}
ENV_FILE=".env.$ENV"

if [ ! -f "$ENV_FILE" ]; then
    echo "[X] Fichier $ENV_FILE introuvable"
    exit 1
fi

# Charger variables
source $ENV_FILE

echo "[RAPIDE] Déploiement en environnement: $ENV"

# Arrêter ancien conteneur
docker stop myapp-$ENV 2>/dev/null || true
docker rm myapp-$ENV 2>/dev/null || true

# Lancer nouveau conteneur
docker run -d \
  --name myapp-$ENV \
  --env-file $ENV_FILE \
  -m $MEMORY_LIMIT \
  --memory-reservation=${MEMORY_RESERVATION:-256m} \
  --cpus=$CPU_LIMIT \
  -p ${PORT:-3000}:3000 \
  myapp:latest

echo "[OK] Application déployée : myapp-$ENV"
docker stats myapp-$ENV --no-stream

USAGE :
./deploy.sh development
./deploy.sh staging
./deploy.sh production


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 2 : Load Balancing avec limites                                     ║
╚══════════════════════════════════════════════════════════════════════════╝

CONTEXTE : Plusieurs instances d'API avec répartition équitable

# API Instance 1
docker run -d \
  --name api-1 \
  -m 1g \
  --cpus=2 \
  --cpu-shares=1024 \
  -e INSTANCE_ID=1 \
  -p 3001:3000 \
  api-service

# API Instance 2
docker run -d \
  --name api-2 \
  -m 1g \
  --cpus=2 \
  --cpu-shares=1024 \
  -e INSTANCE_ID=2 \
  -p 3002:3000 \
  api-service

# API Instance 3
docker run -d \
  --name api-3 \
  -m 1g \
  --cpus=2 \
  --cpu-shares=1024 \
  -e INSTANCE_ID=3 \
  -p 3003:3000 \
  api-service

# Load Balancer (Nginx)
docker run -d \
  --name loadbalancer \
  -m 256m \
  --cpus=1 \
  -p 80:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx


FICHIER nginx.conf :
upstream api_backend {
    server host.docker.internal:3001;
    server host.docker.internal:3002;
    server host.docker.internal:3003;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://api_backend;
    }
}


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 3 : Monitoring et Alertes                                           ║
╚══════════════════════════════════════════════════════════════════════════╝

SCRIPT monitor.sh :
#!/bin/bash
# Monitoring et alertes sur ressources

ALERT_MEMORY_THRESHOLD=80
ALERT_CPU_THRESHOLD=80
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

send_alert() {
    local message=$1
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"$message\"}" \
        $SLACK_WEBHOOK
}

while true; do
    # Pour chaque conteneur
    for container in $(docker ps --format '{{.Names}}'); do
        # Récupérer stats
        STATS=$(docker stats $container --no-stream --format "{{.MemPerc}},{{.CPUPerc}}")
        MEM_PERC=$(echo $STATS | cut -d',' -f1 | sed 's/%//')
        CPU_PERC=$(echo $STATS | cut -d',' -f2 | sed 's/%//')
        
        # Vérifier mémoire
        if (( $(echo "$MEM_PERC > $ALERT_MEMORY_THRESHOLD" | bc -l) )); then
            MSG="[ATTENTION] ALERTE: $container utilise ${MEM_PERC}% de mémoire"
            echo $MSG
            send_alert "$MSG"
        fi
        
        # Vérifier CPU
        if (( $(echo "$CPU_PERC > $ALERT_CPU_THRESHOLD" | bc -l) )); then
            MSG="[ATTENTION] ALERTE: $container utilise ${CPU_PERC}% de CPU"
            echo $MSG
            send_alert "$MSG"
        fi
    done
    
    sleep 300  # Vérifier toutes les 5 minutes
done


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 4 : Tests de charge avec limites                                    ║
╚══════════════════════════════════════════════════════════════════════════╝

SCRIPT load_test.sh :
#!/bin/bash
# Tester app sous différentes contraintes de ressources

APP_IMAGE="myapp:latest"
TEST_DURATION=60

echo "[TEST] Tests de charge avec différentes limites"

# Test 1 : Ressources minimales
echo "[GRAPHIQUE] Test 1: 256MB RAM, 0.5 CPU"
docker run -d --name test1 -m 256m --cpus=0.5 -p 8001:3000 $APP_IMAGE
sleep 5
ab -n 10000 -c 100 http://localhost:8001/ > test1_results.txt
docker stop test1 && docker rm test1

# Test 2 : Ressources normales
echo "[GRAPHIQUE] Test 2: 512MB RAM, 1 CPU"
docker run -d --name test2 -m 512m --cpus=1 -p 8002:3000 $APP_IMAGE
sleep 5
ab -n 10000 -c 100 http://localhost:8002/ > test2_results.txt
docker stop test2 && docker rm test2

# Test 3 : Ressources élevées
echo "[GRAPHIQUE] Test 3: 1GB RAM, 2 CPUs"
docker run -d --name test3 -m 1g --cpus=2 -p 8003:3000 $APP_IMAGE
sleep 5
ab -n 10000 -c 100 http://localhost:8003/ > test3_results.txt
docker stop test3 && docker rm test3

# Analyser résultats
echo "[HAUSSE] Résultats:"
grep "Requests per second" test*_results.txt


╔══════════════════════════════════════════════════════════════════════════╗
║  CAS 5 : Auto-scaling basé sur métriques                                 ║
╚══════════════════════════════════════════════════════════════════════════╝

SCRIPT autoscale.sh :
#!/bin/bash
# Auto-scaling simple basé sur utilisation CPU

CONTAINER="myapp"
MIN_CPU=1
MAX_CPU=8
MIN_MEM="512m"
MAX_MEM="4g"

SCALE_UP_THRESHOLD=75
SCALE_DOWN_THRESHOLD=30

get_cpu_usage() {
    docker stats $CONTAINER --no-stream --format "{{.CPUPerc}}" | sed 's/%//'
}

get_current_cpu_limit() {
    docker inspect $CONTAINER --format='{{.HostConfig.NanoCpus}}' | \
        awk '{print $1/1000000000}'
}

scale_up() {
    local current=$(get_current_cpu_limit)
    local new=$(echo "$current * 1.5" | bc)
    
    if (( $(echo "$new > $MAX_CPU" | bc -l) )); then
        new=$MAX_CPU
    fi
    
    echo "[HAUSSE] Scaling UP: ${current} -> ${new} CPUs"
    docker update --cpus=$new $CONTAINER
}

scale_down() {
    local current=$(get_current_cpu_limit)
    local new=$(echo "$current * 0.75" | bc)
    
    if (( $(echo "$new < $MIN_CPU" | bc -l) )); then
        new=$MIN_CPU
    fi
    
    echo "[BAISSE] Scaling DOWN: ${current} -> ${new} CPUs"
    docker update --cpus=$new $CONTAINER
}

while true; do
    CPU_USAGE=$(get_cpu_usage)
    
    if (( $(echo "$CPU_USAGE > $SCALE_UP_THRESHOLD" | bc -l) )); then
        scale_up
    elif (( $(echo "$CPU_USAGE < $SCALE_DOWN_THRESHOLD" | bc -l) )); then
        scale_down
    else
        echo "[OK] CPU OK: ${CPU_USAGE}%"
    fi
    
    sleep 60
done


════════════════════════════════════════════════════════════════════════════
[OK] SECTION 4 : CHEAT SHEET COMPLET
════════════════════════════════════════════════════════════════════════════

╔══════════════════════════════════════════════════════════════════════════╗
║  VARIABLES D'ENVIRONNEMENT                                               ║
╚══════════════════════════════════════════════════════════════════════════╝

# Passer une variable
docker run -e VAR=value ubuntu
docker run --env VAR=value ubuntu

# Plusieurs variables
docker run -e VAR1=val1 -e VAR2=val2 ubuntu

# Depuis environnement hôte
export MY_VAR=test
docker run -e MY_VAR ubuntu

# Fichier .env
docker run --env-file .env ubuntu
docker run --env-file .env.production ubuntu

# Voir variables
docker exec container_name env
docker exec container_name printenv VAR_NAME
docker inspect container_name --format='{{range .Config.Env}}{{println .}}{{end}}'


╔══════════════════════════════════════════════════════════════════════════╗
║  LIMITES MÉMOIRE                                                         ║
╚══════════════════════════════════════════════════════════════════════════╝

# Limite simple
docker run -m 512m ubuntu
docker run --memory=1g ubuntu

# Mémoire + Swap
docker run -m 512m --memory-swap=1g ubuntu

# Réservation (soft limit)
docker run --memory-reservation=256m -m 512m ubuntu

# Désactiver swap
docker run -m 512m --memory-swap=512m ubuntu

# Voir limite
docker inspect container --format='{{.HostConfig.Memory}}'


╔══════════════════════════════════════════════════════════════════════════╗
║  LIMITES CPU                                                             ║
╚══════════════════════════════════════════════════════════════════════════╝

# Nombre de CPUs
docker run --cpus="1.5" ubuntu
docker run --cpus="0.5" ubuntu

# CPU shares (priorité)
docker run --cpu-shares=512 ubuntu
docker run --cpu-shares=2048 ubuntu

# CPUs spécifiques
docker run --cpuset-cpus="0,1" ubuntu
docker run --cpuset-cpus="0-3" ubuntu

# Voir limite
docker inspect container --format='{{.HostConfig.NanoCpus}}'


╔══════════════════════════════════════════════════════════════════════════╗
║  LIMITES I/O DISQUE                                                      ║
╚══════════════════════════════════════════════════════════════════════════╝

# Limite lecture/écriture (bps)
docker run --device-read-bps=/dev/sda:1mb ubuntu
docker run --device-write-bps=/dev/sda:10mb ubuntu

# Limite IOPS
docker run --device-read-iops=/dev/sda:100 ubuntu
docker run --device-write-iops=/dev/sda:50 ubuntu


╔══════════════════════════════════════════════════════════════════════════╗
║  MONITORING                                                              ║
╚══════════════════════════════════════════════════════════════════════════╝

# Stats en temps réel
docker stats
docker stats container_name

# Une seule lecture
docker stats --no-stream

# Format personnalisé
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# JSON
docker stats --no-stream --format "{{json .}}"


╔══════════════════════════════════════════════════════════════════════════╗
║  MISE À JOUR                                                             ║
╚══════════════════════════════════════════════════════════════════════════╝

# Mettre à jour mémoire
docker update --memory=1g container_name

# Mettre à jour CPU
docker update --cpus=2 container_name

# Plusieurs à la fois
docker update --memory=2g --cpus=4 container_name

# Tous les conteneurs
docker update --restart=always $(docker ps -q)


════════════════════════════════════════════════════════════════════════════
  TABLEAU RÉCAPITULATIF - LIMITES RECOMMANDÉES
════════════════════════════════════════════════════════════════════════════

╔═══════════════════╦═══════════╦═══════════╦════════════════════════╗
║ TYPE D'APP        ║ RAM       ║ CPU       ║ NOTES                  ║
╠═══════════════════╬═══════════╬═══════════╬════════════════════════╣
║ Static web        ║ 128-256m  ║ 0.25-0.5  ║ Nginx, Apache          ║
║ Node.js API       ║ 512m-1g   ║ 1-2       ║ Express, Fastify       ║
║ Python API        ║ 512m-2g   ║ 1-2       ║ Flask, Django          ║
║ Java/Spring       ║ 1-2g      ║ 2-4       ║ JVM needs more RAM     ║
║ PostgreSQL        ║ 2-8g      ║ 2-8       ║ Dépend du trafic       ║
║ MySQL             ║ 1-4g      ║ 2-4       ║ Dépend du trafic       ║
║ MongoDB           ║ 2-8g      ║ 2-4       ║ Dépend données         ║
║ Redis             ║ 256m-2g   ║ 0.5-2     ║ Cache en mémoire       ║
║ Elasticsearch     ║ 4-16g     ║ 4-8       ║ Très gourmand          ║
║ Worker/Queue      ║ 512m-2g   ║ 2-4       ║ Dépend du job          ║
║ ML/AI Training    ║ 8-32g     ║ 8-16      ║ GPU recommandé         ║
╚═══════════════════╩═══════════╩═══════════╩════════════════════════╝


════════════════════════════════════════════════════════════════════════════
  RÉSUMÉ FINAL
════════════════════════════════════════════════════════════════════════════

[OUTIL] VARIABLES D'ENVIRONNEMENT :
• Configuration flexible sans rebuild
• Utiliser fichiers .env pour organisation
• JAMAIS commiter les secrets
• Documenter avec .env.example

[GRAPHIQUE] LIMITES RESSOURCES :
• TOUJOURS limiter en production
• Mémoire : -m ou --memory
• CPU : --cpus
• I/O : --device-*-bps/iops

[HAUSSE] MONITORING :
• docker stats pour surveiller
• Alertes si dépassement
• Auto-scaling si nécessaire

RÈGLES D'OR :
┌──────────────────────────────────────────────────────────────────────────┐
│ 1. Variables pour configuration -> Pas de rebuild nécessaire             │
│ 2. Limites toujours -> Stabilité système                                 │
│ 3. Monitorer régulièrement -> Détecter problèmes tôt                     │
│ 4. Tester les limites -> Trouver l'optimal                               │
│ 5. Documenter les requirements -> Déploiement reproductible              │
└──────────────────────────────────────────────────────────────────────────┘


PROCHAINE SECTION :
Partie 12 - Docker Compose (Orchestration multi-conteneurs)


[OK] DOCKERFILE - CRÉATION D'IMAGES


# DOCKERFILE - Guide Complet pour Grand Débutant

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[PACKAGE] QU'EST-CE QU'UN DOCKERFILE ?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Un Dockerfile est un fichier texte contenant des instructions pour créer
une IMAGE Docker. Une image est comme un "modèle" ou un "template" qui
contient tout ce dont votre application a besoin pour fonctionner :
- Le système d'exploitation de base
- Les bibliothèques et dépendances
- Votre code
- Les configurations

Pensez au Dockerfile comme une RECETTE DE CUISINE :
- Les ingrédients = les dépendances et fichiers
- Les étapes = les instructions (FROM, RUN, COPY, etc.)
- Le plat final = l'image Docker prête à être utilisée


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OK] STRUCTURE DE BASE D'UN DOCKERFILE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Dockerfile minimal (exemple le plus simple)
FROM ubuntu:22.04                    # 1. Image de base
RUN apt-get update                   # 2. Installer des choses
CMD ["echo", "Hello World"]          # 3. Commande à exécuter

# Dockerfile Python typique
FROM python:3.11-slim                # Image Python officielle
WORKDIR /app                         # Créer et aller dans /app
COPY requirements.txt .              # Copier fichier requirements
RUN pip install -r requirements.txt  # Installer dépendances Python
COPY . .                             # Copier tout le reste
CMD ["python", "app.py"]             # Lancer l'application

# Dockerfile Node.js typique
FROM node:18-alpine                  # Image Node.js légère
WORKDIR /usr/src/app                 # Dossier de travail
COPY package*.json ./                # Copier fichiers package
RUN npm install                      # Installer dépendances
COPY . .                             # Copier code source
EXPOSE 3000                          # Documenter le port
CMD ["node", "server.js"]            # Lancer le serveur


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[NOTE] TOUTES LES INSTRUCTIONS DOCKERFILE EXPLIQUÉES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ FROM - Choisir l'image de base (OBLIGATOIRE - toujours en 1er)    ║
╚════════════════════════════════════════════════════════════════════╝

# C'est l'image de départ sur laquelle vous allez construire
# Pensez-y comme au "système d'exploitation de base"

FROM ubuntu:22.04                    # Ubuntu version 22.04
FROM python:3.11                     # Python 3.11 (complet)
FROM python:3.11-slim                # Python 3.11 (léger - RECOMMANDÉ)
FROM python:3.11-alpine              # Python 3.11 (très léger)
FROM node:18                         # Node.js version 18
FROM node:18-alpine                  # Node.js 18 (léger)
FROM nginx:latest                    # Serveur web Nginx
FROM postgres:15                     # Base de données PostgreSQL
FROM scratch                         # Image totalement vide (avancé)

# Nommer un stage (pour multi-stage builds - avancé)
FROM python:3.11 AS builder          # Stage nommé "builder"

# [IDEE] CONSEIL : Préférez les versions "-slim" ou "-alpine" pour des images
#              plus petites et plus rapides à télécharger


╔════════════════════════════════════════════════════════════════════╗
║ WORKDIR - Définir le répertoire de travail                        ║
╚════════════════════════════════════════════════════════════════════╝

# Comme faire "cd /app" dans un terminal
# Crée le dossier s'il n'existe pas
# Toutes les commandes suivantes se font dans ce dossier

WORKDIR /app                         # Va dans /app (le crée si besoin)
WORKDIR /usr/src/app                 # Autre convention commune
WORKDIR /home/user/project           # Chemin complet

# Les commandes suivantes s'exécuteront dans /app
COPY requirements.txt .              # Copie dans /app/
RUN pip install -r requirements.txt  # S'exécute dans /app/

# [IDEE] CONSEIL : Utilisez toujours WORKDIR plutôt que RUN cd /app


╔════════════════════════════════════════════════════════════════════╗
║ COPY - Copier des fichiers de votre ordinateur vers l'image       ║
╚════════════════════════════════════════════════════════════════════╝

# Syntaxe : COPY <source sur votre PC> <destination dans l'image>

COPY app.py /app/                    # Copie app.py vers /app/
COPY app.py .                        # Copie vers le WORKDIR actuel
COPY requirements.txt .              # Copie requirements.txt
COPY src/ /app/src/                  # Copie dossier src/
COPY . .                             # Copie TOUT le dossier courant

# Copier avec permissions spécifiques
COPY --chown=user:group file.txt /app/

# Copier plusieurs fichiers
COPY file1.txt file2.txt /app/
COPY *.py /app/                      # Tous les fichiers .py

# [IDEE] CONSEIL : Copiez d'abord requirements.txt, puis installez, puis
#              copiez le reste. Cela optimise le cache de build !

# [OK] BON : Cache efficace
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# [X] MAUVAIS : Rebuild complet à chaque changement
COPY . .
RUN pip install -r requirements.txt


╔════════════════════════════════════════════════════════════════════╗
║ ADD - Comme COPY mais avec des fonctionnalités supplémentaires    ║
╚════════════════════════════════════════════════════════════════════╝

# [ATTENTION]  ÉVITEZ ADD ! Utilisez COPY sauf cas spéciaux

# ADD peut :
# 1. Télécharger depuis une URL
ADD https://example.com/file.zip /app/

# 2. Extraire automatiquement des archives
ADD archive.tar.gz /app/             # Extrait automatiquement

# [IDEE] CONSEIL : Utilisez COPY pour les fichiers normaux, c'est plus clair


╔════════════════════════════════════════════════════════════════════╗
║ RUN - Exécuter des commandes durant le BUILD                      ║
╚════════════════════════════════════════════════════════════════════╝

# RUN exécute des commandes PENDANT la création de l'image
# Comme si vous tapiez des commandes dans un terminal

# Installation de packages système
RUN apt-get update                   # Mettre à jour la liste des packages
RUN apt-get install -y curl          # Installer curl
RUN apt-get install -y python3-pip   # Installer pip

# Installation dépendances Python
RUN pip install flask                # Installer Flask
RUN pip install -r requirements.txt  # Installer depuis fichier

# Installation dépendances Node.js
RUN npm install                      # Installer depuis package.json
RUN npm ci --only=production         # Install prod (recommandé)

# Commandes shell normales
RUN mkdir -p /app/data               # Créer dossier
RUN chmod +x script.sh               # Rendre exécutable
RUN echo "Hello" > file.txt          # Écrire dans fichier

# [OK] BONNE PRATIQUE : Combiner les commandes avec &&
# Cela crée UNE SEULE couche (= image plus petite)

RUN apt-get update && \
    apt-get install -y \
        curl \
        vim \
        git \
    && rm -rf /var/lib/apt/lists/*   # Nettoyer le cache

# [X] MAUVAIS : Plusieurs RUN = plusieurs couches
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get install -y git

# [OK] BON : Un seul RUN = une couche
RUN apt-get update && apt-get install -y \
    curl \
    vim \
    git

# [IDEE] Le && signifie "ET" : exécute la commande suivante seulement
#    si la précédente a réussi
# [IDEE] Le \ permet de continuer sur la ligne suivante


╔════════════════════════════════════════════════════════════════════╗
║ ENV - Définir des variables d'environnement                       ║
╚════════════════════════════════════════════════════════════════════╝

# Les variables ENV sont disponibles :
# - Durant le build (après leur définition)
# - Durant l'exécution du container

ENV NODE_ENV=production              # Mode production
ENV PORT=8000                        # Port de l'application
ENV PYTHONUNBUFFERED=1               # Python sans buffer (logs)
ENV DATABASE_URL=postgresql://...    # URL base de données
ENV PATH="/app/bin:${PATH}"          # Ajouter au PATH

# Définir plusieurs variables
ENV NODE_ENV=production \
    PORT=8000 \
    LOG_LEVEL=info

# Utiliser une variable ENV
ENV APP_HOME=/app
WORKDIR $APP_HOME                    # Utilise la variable

# [IDEE] Les variables ENV persistent dans le container final


╔════════════════════════════════════════════════════════════════════╗
║ ARG - Arguments passés lors du BUILD                              ║
╚════════════════════════════════════════════════════════════════════╝

# ARG = valeur disponible SEULEMENT pendant le build
# On peut changer la valeur lors du build avec --build-arg

ARG VERSION=1.0.0                    # Valeur par défaut
ARG PYTHON_VERSION=3.11              # Version Python
ARG BUILD_DATE                       # Sans valeur par défaut

# Utiliser ARG
FROM python:${PYTHON_VERSION}-slim
RUN echo "Building version ${VERSION}"

# Pour passer une valeur lors du build :
# docker build --build-arg VERSION=2.0.0 -t myapp .
# docker build --build-arg PYTHON_VERSION=3.12 -t myapp .

# Différence ENV vs ARG :
# ARG  : seulement durant le BUILD
# ENV  : durant le BUILD et dans le CONTAINER

# Si vous voulez qu'un ARG devienne un ENV :
ARG VERSION=1.0.0
ENV APP_VERSION=${VERSION}


╔════════════════════════════════════════════════════════════════════╗
║ EXPOSE - Documenter les ports utilisés                            ║
╚════════════════════════════════════════════════════════════════════╝

# EXPOSE ne publie PAS réellement les ports !
# C'est juste de la DOCUMENTATION pour les utilisateurs

EXPOSE 80                            # Port HTTP
EXPOSE 8080                          # Port application
EXPOSE 5432                          # PostgreSQL
EXPOSE 3000/tcp                      # Explicitement TCP
EXPOSE 53/udp                        # Port UDP

# Plusieurs ports
EXPOSE 80 443

# [ATTENTION]  Pour VRAIMENT publier un port, utilisez -p lors du run :
# docker run -p 8080:80 myapp
#            ^    ^
#            │    └─ Port DANS le container
#            └────── Port sur VOTRE machine

# [IDEE] EXPOSE est utile pour la documentation et docker-compose


╔════════════════════════════════════════════════════════════════════╗
║ CMD - Commande par défaut à l'exécution                           ║
╚════════════════════════════════════════════════════════════════════╝

# CMD définit la commande qui s'exécute quand on lance le container
# Il ne peut y avoir qu'UN SEUL CMD (le dernier compte)

# Format JSON (RECOMMANDÉ) - Format "exec"
CMD ["python", "app.py"]             # Lance Python
CMD ["node", "server.js"]            # Lance Node.js
CMD ["npm", "start"]                 # Lance npm start
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]

# Format shell (éviter - moins performant)
CMD python app.py
CMD npm start

# CMD peut être remplacé à l'exécution :
# docker run myapp python autre_script.py
#                  └─ Remplace le CMD

# Différence CMD vs RUN :
# RUN : exécuté DURANT le build (pour installer/configurer)
# CMD : exécuté QUAND on lance le container


╔════════════════════════════════════════════════════════════════════╗
║ ENTRYPOINT - Point d'entrée de l'application                      ║
╚════════════════════════════════════════════════════════════════════╝

# ENTRYPOINT est similaire à CMD mais ne peut PAS être remplacé facilement
# Utilisé pour faire du container un "exécutable"

# Format JSON (RECOMMANDÉ)
ENTRYPOINT ["python"]
CMD ["app.py"]
# Résultat : python app.py
# On peut changer juste le script : docker run myapp autre.py

ENTRYPOINT ["python", "app.py"]
# Résultat : python app.py (fixe)

# Exemple avec script de démarrage
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

# [ATTENTION]  ENTRYPOINT + CMD ensemble :
# ENTRYPOINT = partie fixe
# CMD = partie qu'on peut changer

# [IDEE] CONSEIL : Pour la plupart des cas, CMD suffit


╔════════════════════════════════════════════════════════════════════╗
║ USER - Changer l'utilisateur                                      ║
╚════════════════════════════════════════════════════════════════════╝

# Par défaut, tout s'exécute en tant que root (= admin)
# C'est un risque de sécurité ! Créez un utilisateur normal

# Utiliser un utilisateur existant
USER nobody                          # Utilisateur "nobody"
USER 1001                            # Par ID

# Créer et utiliser un utilisateur
RUN useradd -m -u 1001 appuser       # Créer utilisateur
USER appuser                         # Basculer vers cet utilisateur

# Ou avec groupadd
RUN groupadd -r appgroup && \
    useradd -r -g appgroup appuser
USER appuser

# Exemple complet
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# Créer utilisateur non-root
RUN useradd -m -u 1001 appuser && \
    chown -R appuser:appuser /app
USER appuser

CMD ["python", "app.py"]

# [IDEE] CONSEIL : Basculez vers un utilisateur non-root avant le CMD


╔════════════════════════════════════════════════════════════════════╗
║ VOLUME - Définir des points de montage                            ║
╚════════════════════════════════════════════════════════════════════╝

# VOLUME marque un dossier comme "volume" (données persistantes)
# Les données dans un volume PERSISTENT même si le container est supprimé

VOLUME ["/data"]                     # Un volume
VOLUME ["/var/log", "/var/db"]       # Plusieurs volumes
VOLUME /app/uploads                  # Syntaxe simple

# Exemple avec base de données
FROM postgres:15
VOLUME /var/lib/postgresql/data      # Données PostgreSQL

# [IDEE] Les volumes sont utilisés pour :
# - Bases de données
# - Fichiers uploadés par les utilisateurs
# - Logs
# - Tout ce qui doit persister


╔════════════════════════════════════════════════════════════════════╗
║ HEALTHCHECK - Vérifier la santé du container                      ║
╚════════════════════════════════════════════════════════════════════╝

# Docker va vérifier régulièrement si le container est "en bonne santé"

# Vérifier avec curl
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
  CMD curl -f http://localhost:8000/health || exit 1

# Options :
# --interval=30s    : Vérifier toutes les 30 secondes
# --timeout=3s      : Timeout de 3 secondes
# --start-period=5s : Attendre 5s avant de commencer
# --retries=3       : 3 échecs = unhealthy

# Vérifier avec un script Python
HEALTHCHECK --interval=5m --timeout=3s \
  CMD python healthcheck.py || exit 1

# Désactiver le healthcheck (hérité d'une image parent)
HEALTHCHECK NONE

# Exemple complet
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask requests
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import requests; requests.get('http://localhost:5000/health')"
CMD ["python", "app.py"]


╔════════════════════════════════════════════════════════════════════╗
║ LABEL - Ajouter des métadonnées                                   ║
╚════════════════════════════════════════════════════════════════════╝

# Les labels sont des métadonnées (informations) sur l'image

LABEL version="1.0.0"
LABEL maintainer="email@example.com"
LABEL description="Application web Django"
LABEL org.opencontainers.image.authors="John Doe"

# Plusieurs labels
LABEL version="1.0.0" \
      maintainer="email@example.com" \
      description="My application"

# Voir les labels d'une image :
# docker inspect myapp


╔════════════════════════════════════════════════════════════════════╗
║ SHELL - Changer le shell par défaut                               ║
╚════════════════════════════════════════════════════════════════════╝

# Par défaut : /bin/sh -c
# Changer pour bash :

SHELL ["/bin/bash", "-c"]

# Maintenant les RUN utilisent bash
RUN source ~/.bashrc && echo "Hello"


╔════════════════════════════════════════════════════════════════════╗
║ STOPSIGNAL - Signal d'arrêt du container                          ║
╚════════════════════════════════════════════════════════════════════╝

# Signal envoyé au container lors de docker stop

STOPSIGNAL SIGTERM                   # Par défaut
STOPSIGNAL SIGKILL                   # Arrêt forcé
STOPSIGNAL SIGINT                    # Comme Ctrl+C


╔════════════════════════════════════════════════════════════════════╗
║ ONBUILD - Instructions pour les images dérivées (AVANCÉ)          ║
╚════════════════════════════════════════════════════════════════════╝

# ONBUILD s'exécute quand quelqu'un construit UNE AUTRE IMAGE basée
# sur la vôtre (cas rare, pour créer des images "templates")

ONBUILD COPY . /app
ONBUILD RUN npm install

# Peu utilisé - ignorez si vous débutez


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OUTIL] CONSTRUIRE UNE IMAGE (COMMANDES DOCKER BUILD)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Construire une image depuis le Dockerfile dans le dossier courant
docker build .

# Construire avec un nom (tag)
docker build -t myapp .              # Tag "latest" par défaut
docker build -t myapp:1.0 .          # Tag spécifique
docker build -t myapp:latest .       # Explicitement "latest"
docker build -t username/myapp:1.0 . # Avec username (pour Docker Hub)

# Spécifier un Dockerfile différent
docker build -f Dockerfile.prod .
docker build -f docker/Dockerfile.dev .

# Construire avec des arguments (ARG)
docker build --build-arg VERSION=2.0.0 -t myapp .
docker build --build-arg PYTHON_VERSION=3.12 -t myapp .
docker build --build-arg NODE_ENV=production -t myapp .

# Construire sans utiliser le cache (rebuild complet)
docker build --no-cache -t myapp .

# Construire seulement jusqu'à un stage (multi-stage)
docker build --target builder -t myapp:builder .
docker build --target production -t myapp:prod .

# Voir toutes les étapes en détail
docker build --progress=plain -t myapp .

# Construire et voir les logs complets
docker build --no-cache --progress=plain -t myapp . 2>&1 | tee build.log

# Ajouter plusieurs tags
docker build -t myapp:1.0 -t myapp:latest .

# Exemples concrets
docker build -t mon-app-python .
docker build -t mon-site-web:v1 .
docker build -f Dockerfile.prod -t mon-app:prod .
docker build --build-arg ENV=prod -t mon-app .


# APRÈS LE BUILD : Ajouter des tags
docker tag myapp:latest myapp:1.0.0
docker tag myapp:latest username/myapp:latest
docker tag myapp:latest myregistry.com/myapp:latest

# Lister les images
docker images
docker images myapp                  # Images "myapp" seulement

# Voir l'historique des couches
docker history myapp:latest

# Inspecter une image
docker inspect myapp:latest

# Voir la taille d'une image
docker images myapp --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OBJECTIF] EXEMPLES COMPLETS DE DOCKERFILE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Exemple 1 : Application Flask (Python)                            ║
╚════════════════════════════════════════════════════════════════════╝

# Dockerfile pour Flask
FROM python:3.11-slim

# Définir répertoire de travail
WORKDIR /app

# Copier requirements et installer dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le code de l'application
COPY . .

# Créer utilisateur non-root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

# Exposer le port
EXPOSE 5000

# Variable d'environnement
ENV FLASK_APP=app.py

# Vérification santé
HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1

# Commande de démarrage
CMD ["flask", "run", "--host=0.0.0.0"]

# Pour construire :
# docker build -t flask-app .

# Pour lancer :
# docker run -p 5000:5000 flask-app


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 2 : Application Node.js / Express                         ║
╚════════════════════════════════════════════════════════════════════╝

# Dockerfile pour Node.js
FROM node:18-alpine

# Répertoire de travail
WORKDIR /usr/src/app

# Copier package.json et package-lock.json
COPY package*.json ./

# Installer dépendances (production seulement)
RUN npm ci --only=production

# Copier le code source
COPY . .

# Créer utilisateur
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 && \
    chown -R nodejs:nodejs /usr/src/app
USER nodejs

# Exposer le port
EXPOSE 3000

# Variables d'environnement
ENV NODE_ENV=production
ENV PORT=3000

# Démarrage
CMD ["node", "server.js"]

# Pour construire :
# docker build -t node-app .

# Pour lancer :
# docker run -p 3000:3000 node-app


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 3 : Application Django                                    ║
╚════════════════════════════════════════════════════════════════════╝

# Dockerfile pour Django
FROM python:3.11-slim

# Variables d'environnement Python
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# Répertoire de travail
WORKDIR /app

# Installer dépendances système (PostgreSQL)
RUN apt-get update && apt-get install -y \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# Copier requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier projet Django
COPY . .

# Collecter fichiers statiques
RUN python manage.py collectstatic --noinput

# Créer utilisateur
RUN useradd -m django && chown -R django:django /app
USER django

# Exposer port
EXPOSE 8000

# Script de démarrage
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]

# Pour construire :
# docker build -t django-app .

# Pour lancer :
# docker run -p 8000:8000 django-app


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 4 : Application React (Frontend)                          ║
╚════════════════════════════════════════════════════════════════════╝

# Dockerfile pour React avec Nginx
FROM node:18-alpine AS build

# Build l'application React
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage production avec Nginx
FROM nginx:alpine

# Copier les fichiers buildés
COPY --from=build /app/build /usr/share/nginx/html

# Copier config Nginx personnalisée (optionnel)
# COPY nginx.conf /etc/nginx/conf.d/default.conf

# Exposer port
EXPOSE 80

# Nginx démarre automatiquement


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 5 : Script Python simple                                  ║
╚════════════════════════════════════════════════════════════════════╝

# Dockerfile pour un script Python
FROM python:3.11-slim

WORKDIR /app

# Installer dépendances si nécessaire
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le script
COPY script.py .

# Lancer le script
CMD ["python", "script.py"]

# Pour lancer :
# docker run mon-script


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SCENARIO] MULTI-STAGE BUILD (Optimisation Avancée)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Le multi-stage build permet de créer des images TRÈS LÉGÈRES
# en séparant l'étape de BUILD et l'étape de PRODUCTION

╔════════════════════════════════════════════════════════════════════╗
║ Exemple : Application Go avec multi-stage                         ║
╚════════════════════════════════════════════════════════════════════╝

# Stage 1 : BUILD (image lourde avec outils de compilation)
FROM golang:1.21 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

# Stage 2 : PRODUCTION (image légère, seulement l'exécutable)
FROM alpine:latest
WORKDIR /app
COPY --from=builder /src/app .
CMD ["./app"]

# Résultat : Image finale de quelques MB au lieu de plusieurs centaines !


╔════════════════════════════════════════════════════════════════════╗
║ Exemple : Python avec multi-stage                                 ║
╚════════════════════════════════════════════════════════════════════╝

# Stage 1 : BUILD (installer toutes les dépendances)
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Stage 2 : PRODUCTION (copier seulement ce qui est nécessaire)
FROM python:3.11-slim
WORKDIR /app

# Copier les packages installés depuis le builder
COPY --from=builder /root/.local /root/.local

# Copier le code
COPY . .

# Mettre à jour le PATH
ENV PATH=/root/.local/bin:$PATH

CMD ["python", "app.py"]


╔════════════════════════════════════════════════════════════════════╗
║ Exemple : Node.js avec multi-stage                                ║
╚════════════════════════════════════════════════════════════════════╝

# Stage 1 : BUILD
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2 : PRODUCTION
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
CMD ["node", "dist/index.js"]


# Pour builder un stage spécifique :
docker build --target builder -t myapp:builder .
docker build --target production -t myapp:prod .


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[DOSSIER] .dockerignore - Fichiers à Ignorer
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Le fichier .dockerignore fonctionne comme .gitignore
# Il dit à Docker quels fichiers NE PAS copier dans l'image

# Créer un fichier .dockerignore à la racine de votre projet :

# === .dockerignore pour Python ===

# Environnements virtuels
venv/
env/
.venv/
ENV/
.virtualenv/

# Cache Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python

# Distribution / packaging
build/
dist/
*.egg-info/
.eggs/
*.egg

# Tests et coverage
.pytest_cache/
.coverage
htmlcov/
.tox/

# Jupyter
.ipynb_checkpoints/

# IDE
.vscode/
.idea/
*.swp
*.swo
.DS_Store

# Environnement
.env
.env.local
.env.*.local

# Git
.git/
.gitignore
.gitattributes

# Docker
Dockerfile*
docker-compose*.yml
.dockerignore

# Documentation
*.md
docs/
README*
LICENSE

# Logs
*.log
logs/

# Autres
.mypy_cache/
.dmypy.json
dmypy.json


# === .dockerignore pour Node.js ===

node_modules/
npm-debug.log
yarn-error.log
.npm
.yarn
.pnp.*

# Tests
coverage/
.nyc_output/

# Build
dist/
build/

# Cache
.cache/
.parcel-cache/

# IDE
.vscode/
.idea/

# Git
.git/
.gitignore

# Environnement
.env
.env.local
.env.*.local

# Docker
Dockerfile*
docker-compose*.yml
.dockerignore

# Documentation
*.md
README*
docs/


# === .dockerignore pour Django ===

*.pyc
__pycache__/
db.sqlite3
.env
venv/
.venv/
staticfiles/
media/
.git/
.idea/
.vscode/
*.log
.coverage
htmlcov/


# [IDEE] POURQUOI .dockerignore EST IMPORTANT :
# 1. Réduit la taille de l'image
# 2. Accélère le build (moins de fichiers à copier)
# 3. Évite de copier des données sensibles (.env)
# 4. Évite de copier des fichiers inutiles (node_modules, venv)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* BONNES PRATIQUES DOCKERFILE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1⃣  UTILISER DES IMAGES OFFICIELLES ET LÉGÈRES
   [OK] FROM python:3.11-slim        # Léger
   [OK] FROM node:18-alpine          # Très léger
   [X] FROM python:3.11              # Trop lourd

2⃣  MINIMISER LE NOMBRE DE COUCHES (LAYERS)
   [OK] RUN apt-get update && apt-get install -y curl vim
   [X] RUN apt-get update
   [X] RUN apt-get install -y curl
   [X] RUN apt-get install -y vim

3⃣  OPTIMISER L'ORDRE DES INSTRUCTIONS (CACHE)
   [OK] COPY requirements.txt .       # Rarement modifié
   [OK] RUN pip install -r requirements.txt
   [OK] COPY . .                      # Souvent modifié
   
   [X] COPY . .                      # Invalide le cache à chaque modif
   [X] RUN pip install -r requirements.txt

4⃣  NETTOYER APRÈS INSTALLATION
   [OK] RUN apt-get update && apt-get install -y curl \
       && rm -rf /var/lib/apt/lists/*
   
   [OK] RUN pip install --no-cache-dir -r requirements.txt

5⃣  NE PAS EXÉCUTER EN ROOT
   [OK] RUN useradd -m appuser
   [OK] USER appuser
   [X] (pas de USER = root par défaut)

6⃣  UTILISER .dockerignore
   [OK] Créer un fichier .dockerignore
   [X] Copier node_modules, venv, .git

7⃣  DÉFINIR WORKDIR EXPLICITEMENT
   [OK] WORKDIR /app
   [X] RUN cd /app

8⃣  UTILISER MULTI-STAGE POUR RÉDUIRE LA TAILLE
   [OK] FROM python:3.11 AS builder
   [OK] FROM python:3.11-slim
   [OK] COPY --from=builder ...

9⃣  DOCUMENTER AVEC LABELS
   [OK] LABEL version="1.0.0"
   [OK] LABEL maintainer="email@example.com"

[10]  ÉPINGLER LES VERSIONS
   [OK] FROM python:3.11-slim         # Version spécifique
   [X] FROM python:latest            # Version changeante
   [OK] RUN pip install django==4.2.0 # Version fixe
   [X] RUN pip install django        # Version variable


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[RECHERCHE] COMPRENDRE LES LAYERS (COUCHES)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Chaque instruction dans le Dockerfile crée une COUCHE (layer)
# Les couches sont empilées comme des crêpes pour former l'image finale

Dockerfile:                          Layers créées:
─────────────                        ──────────────
FROM python:3.11-slim        ->       Layer 1: Image de base Python
WORKDIR /app                 ->       Layer 2: Création dossier /app
COPY requirements.txt .      ->       Layer 3: Fichier requirements.txt
RUN pip install -r ...       ->       Layer 4: Packages Python installés
COPY . .                     ->       Layer 5: Code de l'application
CMD ["python", "app.py"]     ->       Layer 6: Commande de démarrage

# CACHE DES LAYERS :
# Docker met en cache chaque layer. Si rien n'a changé, il réutilise
# le cache au lieu de reconstruire -> BUILD ULTRA RAPIDE !

# Exemple :
# 1er build : 2 minutes (tout est construit)
# Vous modifiez app.py
# 2ème build : 5 secondes (réutilise le cache jusqu'à COPY . .)

# INVALIDER LE CACHE :
# Si vous modifiez requirements.txt :
# - Layers 1, 2, 3 : cache OK [OK]
# - Layer 4 : REBUILD (requirements changés)
# - Layers 5, 6 : REBUILD (dépendent de 4)

# C'EST POURQUOI l'ordre est important :
# [OK] Copier requirements AVANT le code
#    -> Modif du code ne rebuild pas les dépendances
# [X] Copier tout d'un coup
#    -> Modif du code rebuild TOUT


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[RAPIDE] WORKFLOW COMPLET : DE ZÉRO À L'IMAGE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Projet Flask complet avec Docker                                  ║
╚════════════════════════════════════════════════════════════════════╝

# Structure du projet :
myflaskapp/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore

# 1. Créer l'application (app.py)
───────────────────────────────────────
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello from Docker!'

@app.route('/health')
def health():
    return {'status': 'healthy'}, 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)


# 2. Créer requirements.txt
───────────────────────────────────────
flask==3.0.0
gunicorn==21.2.0


# 3. Créer Dockerfile
───────────────────────────────────────
FROM python:3.11-slim

WORKDIR /app

# Copier et installer dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le code
COPY . .

# Créer utilisateur non-root
RUN useradd -m flaskuser && chown -R flaskuser:flaskuser /app
USER flaskuser

# Exposer le port
EXPOSE 5000

# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1

# Démarrer avec gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]


# 4. Créer .dockerignore
───────────────────────────────────────
__pycache__/
*.pyc
.venv/
venv/
.env
.git/
*.md


# 5. Construire l'image
───────────────────────────────────────
docker build -t myflaskapp:1.0 .

# Sortie attendue :
[+] Building 45.2s (11/11) FINISHED
 => [1/6] FROM python:3.11-slim
 => [2/6] WORKDIR /app
 => [3/6] COPY requirements.txt .
 => [4/6] RUN pip install --no-cache-dir -r requirements.txt
 => [5/6] COPY . .
 => [6/6] RUN useradd -m flaskuser && chown -R flaskuser:flaskuser /app
 => exporting to image
 => => naming to docker.io/library/myflaskapp:1.0


# 6. Vérifier l'image
───────────────────────────────────────
docker images myflaskapp

# Sortie :
REPOSITORY    TAG    IMAGE ID       CREATED         SIZE
myflaskapp    1.0    abc123def456   2 minutes ago   150MB


# 7. Lancer le container
───────────────────────────────────────
docker run -d -p 5000:5000 --name flask-container myflaskapp:1.0

# Options :
# -d : détaché (en arrière-plan)
# -p 5000:5000 : port 5000 de votre PC -> port 5000 du container
# --name : nom du container


# 8. Tester l'application
───────────────────────────────────────
curl http://localhost:5000
# Retourne : Hello from Docker!

curl http://localhost:5000/health
# Retourne : {"status":"healthy"}


# 9. Voir les logs
───────────────────────────────────────
docker logs flask-container
docker logs -f flask-container  # Suivre en temps réel


# 10. Arrêter et supprimer
───────────────────────────────────────
docker stop flask-container
docker rm flask-container

# Ou en une commande :
docker rm -f flask-container


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OUTIL] DÉBOGAGE ET DÉPANNAGE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Problème : Erreur durant le build                                 ║
╚════════════════════════════════════════════════════════════════════╝

# Construire avec logs détaillés
docker build --progress=plain --no-cache -t myapp .

# Voir où ça bloque exactement
docker build -t myapp . 2>&1 | tee build.log


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Image trop grosse                                      ║
╚════════════════════════════════════════════════════════════════════╝

# Voir la taille de chaque layer
docker history myapp:latest

# Solutions :
1. Utiliser une image de base plus légère (-alpine, -slim)
2. Nettoyer après installation (rm cache)
3. Utiliser multi-stage build
4. Combiner les RUN en une seule commande


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Build très lent                                        ║
╚════════════════════════════════════════════════════════════════════╝

# Solutions :
1. Vérifier .dockerignore (ne pas copier node_modules, venv)
2. Optimiser l'ordre des COPY (dépendances avant code)
3. Utiliser --no-cache seulement si nécessaire


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Permission denied                                      ║
╚════════════════════════════════════════════════════════════════════╝

# Si vous avez des erreurs de permissions :
RUN chown -R appuser:appuser /app
USER appuser


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Debugger une image                                     ║
╚════════════════════════════════════════════════════════════════════╝

# Lancer un shell dans l'image
docker run -it myapp /bin/bash
docker run -it myapp /bin/sh    # Alpine

# Entrer dans un container qui tourne
docker exec -it container-name /bin/bash

# Voir les fichiers dans l'image
docker run --rm myapp ls -la /app


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Module not found                                       ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier que les dépendances sont installées
docker run --rm myapp pip list          # Python
docker run --rm myapp npm list          # Node.js

# Vérifier le WORKDIR
docker run --rm myapp pwd

# Vérifier les fichiers copiés
docker run --rm myapp ls -la


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Container s'arrête immédiatement                       ║
╚════════════════════════════════════════════════════════════════════╝

# Voir les logs
docker logs container-name

# Voir pourquoi il s'est arrêté
docker ps -a                            # Voir tous les containers

# Lancer en interactif pour debugger
docker run -it myapp

# Lancer avec un shell pour tester
docker run -it myapp /bin/bash


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[DOCS] COMMANDES DOCKER UTILES AVEC IMAGES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# LISTER LES IMAGES
docker images                           # Toutes les images
docker images myapp                     # Images "myapp" seulement
docker images --filter "dangling=true"  # Images orphelines

# SUPPRIMER DES IMAGES
docker rmi myapp:1.0                    # Supprimer une image
docker rmi -f myapp:1.0                 # Forcer la suppression
docker image prune                      # Supprimer images non utilisées
docker image prune -a                   # Supprimer TOUTES images non utilisées

# INSPECTER UNE IMAGE
docker inspect myapp:latest             # Détails complets
docker history myapp:latest             # Historique des layers

# SAUVEGARDER / CHARGER UNE IMAGE
docker save myapp:1.0 -o myapp.tar      # Sauvegarder dans un fichier
docker load -i myapp.tar                # Charger depuis un fichier

# EXPORTER / IMPORTER
docker export container-name > container.tar
docker import container.tar myapp:imported

# POUSSER SUR UN REGISTRY
docker login                            # Se connecter à Docker Hub
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0

# TIRER DEPUIS UN REGISTRY
docker pull username/myapp:1.0


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[COURS] COMPRENDRE LE PROCESSUS COMPLET
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌─────────────────────────────────────────────────────────────────┐
│                     ÉTAPE 1 : ÉCRIRE LE DOCKERFILE              │
└─────────────────────────────────────────────────────────────────┘
Vous écrivez les instructions dans un fichier nommé "Dockerfile"
Il décrit comment construire votre image étape par étape

┌─────────────────────────────────────────────────────────────────┐
│                     ÉTAPE 2 : BUILD (CONSTRUIRE)                │
└─────────────────────────────────────────────────────────────────┘
Commande : docker build -t myapp:1.0 .

Docker lit le Dockerfile ligne par ligne et :
1. Télécharge l'image de base (FROM)
2. Exécute chaque instruction (RUN, COPY, etc.)
3. Crée une couche (layer) pour chaque instruction
4. Empile toutes les couches pour créer l'IMAGE finale

Résultat : Une IMAGE Docker (comme un template/modèle)

┌─────────────────────────────────────────────────────────────────┐
│                     ÉTAPE 3 : RUN (EXÉCUTER)                    │
└─────────────────────────────────────────────────────────────────┘
Commande : docker run -p 5000:5000 myapp:1.0

Docker :
1. Prend l'image
2. Crée un CONTAINER (= instance de l'image qui tourne)
3. Exécute la commande CMD ou ENTRYPOINT
4. Votre application démarre !

Résultat : Un CONTAINER en cours d'exécution

┌─────────────────────────────────────────────────────────────────┐
│                     ANALOGIE SIMPLE                             │
└─────────────────────────────────────────────────────────────────┘
Dockerfile  = Recette de cuisine (instructions)
Image       = Gâteau démoulé (produit fini, réutilisable)
Container   = Part de gâteau (instance que vous mangez)

Vous pouvez créer plusieurs containers (parts) depuis une image (gâteau)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[IDEE] CONSEILS POUR DÉBUTANTS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1. COMMENCEZ SIMPLE
   Dockerfile minimal :
   FROM python:3.11-slim
   COPY . /app
   WORKDIR /app
   CMD ["python", "app.py"]

2. TESTEZ SOUVENT
   Construisez et testez après chaque modification :
   docker build -t myapp .
   docker run myapp

3. UTILISEZ LES IMAGES OFFICIELLES
   [OK] FROM python:3.11-slim
   [OK] FROM node:18-alpine
   [OK] FROM nginx:alpine
   [X] FROM random-image-from-internet

4. LISEZ LES ERREURS ATTENTIVEMENT
   Docker vous dit exactement quelle ligne pose problème

5. NE STOCKEZ PAS DE SECRETS DANS L'IMAGE
   [X] COPY .env .
   [X] ENV API_KEY=secret123
   [OK] Passez les secrets à l'exécution : docker run -e API_KEY=...

6. DOCUMENTEZ VOTRE DOCKERFILE
   # Commentaires pour expliquer les choix
   FROM python:3.11-slim  # Image légère pour production

7. UTILISEZ docker-compose POUR PLUSIEURS SERVICES
   Si votre app a besoin d'une base de données, Redis, etc.
   Utilisez docker-compose.yml au lieu de tout mettre dans le Dockerfile

8. VÉRIFIEZ LA TAILLE DE VOS IMAGES
   docker images
   Visez < 500 MB pour une app web simple

9. N'INSTALLEZ QUE CE DONT VOUS AVEZ BESOIN
   [X] RUN apt-get install -y build-essential python-dev ...
   [OK] RUN apt-get install -y curl (seulement si nécessaire)

10. PRATIQUEZ !
    Créez des Dockerfiles pour vos projets existants
    Expérimentez avec différentes instructions


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[GUIDE] RESSOURCES POUR ALLER PLUS LOIN
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Documentation officielle
https://docs.docker.com/engine/reference/builder/

# Best practices officielles
https://docs.docker.com/develop/dev-best-practices/

# Exemples de Dockerfiles
https://github.com/docker-library/official-images

# Multi-stage builds
https://docs.docker.com/build/building/multi-stage/

# Optimisation des images
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OK] CHECKLIST AVANT DE CONSTRUIRE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[WHITE_SQUARE] Dockerfile créé à la racine du projet
[WHITE_SQUARE] .dockerignore créé pour exclure fichiers inutiles
[WHITE_SQUARE] Image de base choisie (FROM)
[WHITE_SQUARE] WORKDIR défini
[WHITE_SQUARE] Dépendances copiées et installées AVANT le code
[WHITE_SQUARE] Code copié
[WHITE_SQUARE] Utilisateur non-root créé (USER)
[WHITE_SQUARE] Port exposé si nécessaire (EXPOSE)
[WHITE_SQUARE] CMD ou ENTRYPOINT défini
[WHITE_SQUARE] Image testée localement
[WHITE_SQUARE] Taille de l'image vérifiée (<500 MB idéalement)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[BRAVO] FÉLICITATIONS !
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Vous savez maintenant :
[OK] Ce qu'est un Dockerfile
[OK] Toutes les instructions importantes
[OK] Comment construire une image
[OK] Les bonnes pratiques
[OK] Comment déboguer les problèmes courants
[OK] Comment optimiser vos images

Prochaine étape : Pratiquez en créant un Dockerfile pour votre projet !


# Commande rapide pour commencer :
echo 'FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]' > Dockerfile

docker build -t mon-premier-docker .
docker run mon-premier-docker

# Bon courage ! [RAPIDE]


[OK] DOCKER COMPOSE - ORCHESTRATION


# DOCKER COMPOSE - Guide Complet pour Grand Débutant

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[PACKAGE] QU'EST-CE QUE DOCKER COMPOSE ?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Docker Compose est un outil pour définir et gérer des applications
Docker MULTI-CONTAINERS. Au lieu de lancer plusieurs commandes
docker run, vous écrivez TOUT dans un fichier YAML et lancez tout
avec UNE SEULE COMMANDE.

ANALOGIE SIMPLE :
Dockerfile     = Recette pour UN plat (une image)
Docker Compose = Menu complet avec PLUSIEURS plats (plusieurs services)

EXEMPLE CONCRET :
Votre application web a besoin de :
- Un serveur web (Django/Flask/Node.js)
- Une base de données (PostgreSQL)
- Un cache (Redis)
- Un worker pour tâches asynchrones (Celery)

Sans Docker Compose : 4 commandes docker run compliquées
Avec Docker Compose : 1 fichier + 1 commande -> docker compose up

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OUTIL] INSTALLATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Docker Desktop (Windows / Mac)
# -> Docker Compose est DÉJÀ INCLUS ! [OK]

# Linux - Installer le plugin
sudo apt-get update
sudo apt-get install docker-compose-plugin

# Vérifier l'installation
docker compose version
# Sortie attendue : Docker Compose version v2.x.x

# [ATTENTION]  ANCIENNE VERSION (v1) :
# docker-compose (avec tiret) est l'ancienne version
# docker compose (sans tiret) est la nouvelle version (v2)
# Utilisez toujours la v2 (sans tiret)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[NOTE] STRUCTURE DU FICHIER docker-compose.yml
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Le fichier docker-compose.yml utilise le format YAML (attention à
l'indentation ! Utilisez des ESPACES, pas des TABS)

# Fichier : docker-compose.yml

version: '3.8'              # Version du format (3.8 recommandé)

services:                   # Liste des services (containers)
  nom-service:              # Nom que vous donnez au service
    image: ...              # Image Docker à utiliser
    ports: ...              # Ports à exposer
    volumes: ...            # Volumes (données persistantes)
    environment: ...        # Variables d'environnement

volumes:                    # Volumes nommés (optionnel)
  nom-volume:

networks:                   # Réseaux personnalisés (optionnel)
  nom-network:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OK] EXEMPLE ULTRA-SIMPLE POUR COMMENCER
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# docker-compose.yml
version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

# C'est tout ! Ce fichier crée un serveur Nginx

# Pour lancer :
docker compose up

# Pour arrêter :
Ctrl+C ou docker compose down

# Tester :
# Ouvrir navigateur -> http://localhost:8080


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[RAPIDE] COMMANDES DOCKER COMPOSE ESSENTIELLES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ docker compose up - Démarrer les services                          ║
╚════════════════════════════════════════════════════════════════════╝

# Démarrer tous les services (mode interactif)
docker compose up
# -> Affiche les logs en direct
# -> Ctrl+C pour arrêter

# Démarrer en arrière-plan (mode détaché)
docker compose up -d
# -> Les services tournent en background
# -> Vous récupérez votre terminal

# Démarrer avec rebuild des images
docker compose up --build
# -> Rebuild les images si le code a changé
# -> Utile après modification du Dockerfile

# Démarrer un service spécifique
docker compose up web
docker compose up db
# -> Lance seulement ce service (+ ses dépendances)

# Forcer la recréation des containers
docker compose up --force-recreate

# Ne pas démarrer les services dépendants
docker compose up --no-deps web


╔════════════════════════════════════════════════════════════════════╗
║ docker compose down - Arrêter et supprimer                         ║
╚════════════════════════════════════════════════════════════════════╝

# Arrêter et supprimer les containers
docker compose down
# -> Arrête tous les services
# -> Supprime les containers
# -> Garde les volumes (données persistantes)

# Supprimer aussi les volumes ([ATTENTION]  PERTE DE DONNÉES)
docker compose down -v
# -> Supprime TOUT : containers + volumes + réseaux

# Supprimer aussi les images
docker compose down --rmi all
docker compose down --rmi local    # Seulement images sans tag

# Tout supprimer (containers, volumes, images, orphelins)
docker compose down -v --rmi all --remove-orphans


╔════════════════════════════════════════════════════════════════════╗
║ docker compose logs - Voir les logs                                ║
╚════════════════════════════════════════════════════════════════════╝

# Voir tous les logs
docker compose logs

# Suivre les logs en temps réel (comme tail -f)
docker compose logs -f
# -> Affiche les nouveaux logs au fur et à mesure
# -> Ctrl+C pour arrêter

# Logs d'un service spécifique
docker compose logs web
docker compose logs db
docker compose logs -f web         # Suivre en temps réel

# Dernières N lignes
docker compose logs --tail=50 web
docker compose logs --tail=100 db

# Logs avec timestamps
docker compose logs -t

# Logs depuis un moment donné
docker compose logs --since 10m    # 10 dernières minutes
docker compose logs --since 1h     # 1 dernière heure


╔════════════════════════════════════════════════════════════════════╗
║ docker compose ps - Lister les services                            ║
╚════════════════════════════════════════════════════════════════════╝

# Lister services en cours d'exécution
docker compose ps

# Sortie exemple :
NAME         IMAGE        COMMAND      SERVICE   CREATED    STATUS      PORTS
myapp_web    nginx:alpine "nginx ..."  web       2 min ago  Up 2 min    0.0.0.0:8080->80/tcp
myapp_db     postgres:15  "postgres ..." db      2 min ago  Up 2 min    5432/tcp

# Lister tous les services (même arrêtés)
docker compose ps -a

# Format personnalisé
docker compose ps --format json


╔════════════════════════════════════════════════════════════════════╗
║ docker compose exec - Exécuter commande dans container             ║
╚════════════════════════════════════════════════════════════════════╝

# Ouvrir un shell dans un service
docker compose exec web bash
docker compose exec web sh         # Alpine (pas de bash)
docker compose exec db bash

# Exécuter une commande spécifique
docker compose exec web ls -la
docker compose exec web python manage.py migrate
docker compose exec db psql -U postgres
docker compose exec web npm test

# Sans TTY (pour scripts)
docker compose exec -T web python script.py


╔════════════════════════════════════════════════════════════════════╗
║ docker compose run - Lancer commande ponctuelle                   ║
╚════════════════════════════════════════════════════════════════════╝

# Différence exec vs run :
# exec : dans un container QUI TOURNE déjà
# run  : crée un NOUVEAU container temporaire

# Lancer un script
docker compose run web python script.py
docker compose run web npm test

# Avec suppression automatique après
docker compose run --rm web pytest
docker compose run --rm web python manage.py createsuperuser

# Sans démarrer les services liés
docker compose run --no-deps web python script.py

# Avec variables d'environnement
docker compose run -e DEBUG=1 web python script.py


╔════════════════════════════════════════════════════════════════════╗
║ docker compose restart - Redémarrer services                       ║
╚════════════════════════════════════════════════════════════════════╝

# Redémarrer tous les services
docker compose restart

# Redémarrer un service spécifique
docker compose restart web
docker compose restart db

# Avec timeout
docker compose restart -t 30 web


╔════════════════════════════════════════════════════════════════════╗
║ docker compose stop / start - Arrêter / Démarrer                  ║
╚════════════════════════════════════════════════════════════════════╝

# Arrêter les services (sans supprimer)
docker compose stop
docker compose stop web            # Service spécifique

# Redémarrer les services arrêtés
docker compose start
docker compose start web


╔════════════════════════════════════════════════════════════════════╗
║ docker compose build - Construire les images                       ║
╚════════════════════════════════════════════════════════════════════╝

# Builder toutes les images
docker compose build

# Builder sans cache
docker compose build --no-cache

# Builder un service spécifique
docker compose build web

# Builder en parallèle
docker compose build --parallel


╔════════════════════════════════════════════════════════════════════╗
║ docker compose pull - Télécharger les images                       ║
╚════════════════════════════════════════════════════════════════════╝

# Télécharger toutes les images
docker compose pull

# Image spécifique
docker compose pull db


╔════════════════════════════════════════════════════════════════════╗
║ docker compose config - Valider et voir la config                  ║
╚════════════════════════════════════════════════════════════════════╝

# Voir la configuration finale (avec interpolations)
docker compose config

# Lister les services
docker compose config --services

# Vérifier la syntaxe (sans erreur = valide)
docker compose config --quiet


╔════════════════════════════════════════════════════════════════════╗
║ Autres commandes utiles                                            ║
╚════════════════════════════════════════════════════════════════════╝

# Mettre à l'échelle (plusieurs instances)
docker compose up -d --scale web=3
# -> Lance 3 instances du service web

# Pause / Unpause (geler sans arrêter)
docker compose pause
docker compose unpause
docker compose pause web

# Voir les processus
docker compose top
docker compose top web

# Voir les events
docker compose events


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[GUIDE] TOUTES LES OPTIONS DU docker-compose.yml EXPLIQUÉES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ version - Version du format Compose                                ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'              # Recommandé (supporte toutes les features)
version: '3.9'              # Plus récent
version: '3'                # Générique

# [IDEE] Pour la plupart des cas, '3.8' suffit


╔════════════════════════════════════════════════════════════════════╗
║ services - Définir les containers                                  ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:                      # Nom du service (vous choisissez)
    # ... configuration ...
  
  db:                       # Autre service
    # ... configuration ...


╔════════════════════════════════════════════════════════════════════╗
║ image - Spécifier l'image Docker                                   ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    image: nginx:alpine             # Image officielle
    image: postgres:15              # Avec version
    image: python:3.11-slim         # Version + variante
    image: myregistry.com/app:1.0   # Registry privé
    image: username/app:latest      # Docker Hub user


╔════════════════════════════════════════════════════════════════════╗
║ build - Construire depuis un Dockerfile                            ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    # Simple : Dockerfile dans le dossier courant
    build: .
    
    # Avec contexte et Dockerfile spécifique
    build:
      context: .                    # Dossier de build
      dockerfile: Dockerfile.prod   # Nom du Dockerfile
    
    # Avec arguments de build
    build:
      context: .
      args:
        VERSION: 1.0.0
        BUILD_DATE: 2024-01-01
    
    # Avec target (multi-stage)
    build:
      context: .
      target: production
    
    # Avec cache
    build:
      context: .
      cache_from:
        - myapp:latest


╔════════════════════════════════════════════════════════════════════╗
║ container_name - Nom du container                                  ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    container_name: myapp_web       # Nom fixe

# Sans container_name : Docker génère un nom automatique
# Format : dossier_service_numéro (ex: myproject_web_1)

# [ATTENTION]  Si vous utilisez container_name, vous ne pouvez pas scaler
#     (docker compose up --scale web=3 ne marchera pas)


╔════════════════════════════════════════════════════════════════════╗
║ ports - Mapper les ports                                           ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    ports:
      - "8080:80"           # Port HOST:PORT CONTAINER
      # -> http://localhost:8080 -> port 80 dans le container
      
      - "3000:3000"         # Ports identiques
      - "5432:5432"         # PostgreSQL
      
      # Seulement PORT CONTAINER (HOST aléatoire)
      - "80"
      
      # Bind sur localhost seulement
      - "127.0.0.1:8080:80"
      
      # Range de ports
      - "8000-8005:8000-8005"
      
      # UDP
      - "53:53/udp"

# [IDEE] Format : "PORT_SUR_VOTRE_PC:PORT_DANS_CONTAINER"


╔════════════════════════════════════════════════════════════════════╗
║ expose - Exposer ports (pour autres containers seulement)          ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    expose:
      - "8000"              # Accessible par autres services
                            # PAS accessible depuis votre PC

# Différence ports vs expose :
# ports  : Accessible depuis VOTRE PC
# expose : Accessible seulement entre CONTAINERS


╔════════════════════════════════════════════════════════════════════╗
║ volumes - Données persistantes et bind mounts                      ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    volumes:
      # Bind mount : synchronise dossier PC <-> container
      - ./code:/app                     # Dossier local -> container
      - ./config.py:/app/config.py      # Fichier spécifique
      
      # Volume nommé (données persistantes)
      - db-data:/var/lib/postgresql/data
      
      # Volume anonyme
      - /app/node_modules
      
      # Read-only
      - ./code:/app:ro
      
      # Avec options
      - ./code:/app:rw,cached           # cached = performance Mac

# Bind mount : Parfait pour le DÉVELOPPEMENT
#              (vos changements sont visibles immédiatement)
# Volume nommé : Parfait pour les DONNÉES
#                (bases de données, uploads, etc.)


╔════════════════════════════════════════════════════════════════════╗
║ environment - Variables d'environnement                            ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    environment:
      # Format clé: valeur
      - DEBUG=1
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
      
      # Ou format dictionnaire
      DEBUG: 1
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      NODE_ENV: production
      
      # Utiliser variable du système hôte
      - MY_VAR=${MY_VAR}
      - PORT=${PORT:-8000}              # Avec valeur par défaut


╔════════════════════════════════════════════════════════════════════╗
║ env_file - Charger variables depuis fichier                        ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    env_file:
      - .env                # Fichier principal
      - .env.local          # Fichier local (overrides)

# Contenu de .env :
DEBUG=1
DATABASE_URL=postgresql://localhost/db
SECRET_KEY=my-secret-key

# [IDEE] .env est AUTOMATIQUEMENT chargé par docker compose
#    (pas besoin de spécifier env_file si le fichier s'appelle .env)


╔════════════════════════════════════════════════════════════════════╗
║ command - Remplacer la commande par défaut                         ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    image: python:3.11
    command: python manage.py runserver 0.0.0.0:8000
    
    # Ou format liste
    command: ["python", "manage.py", "runserver", "0.0.0.0:8000"]
    
    # Remplace le CMD du Dockerfile


╔════════════════════════════════════════════════════════════════════╗
║ entrypoint - Remplacer le point d'entrée                           ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    entrypoint: /app/docker-entrypoint.sh
    
    # Ou format liste
    entrypoint: ["docker-entrypoint.sh"]
    
    # Remplace le ENTRYPOINT du Dockerfile


╔════════════════════════════════════════════════════════════════════╗
║ depends_on - Définir les dépendances                               ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    depends_on:
      - db                  # web démarre APRÈS db
      - redis
  
  db:
    image: postgres:15

# [ATTENTION]  depends_on lance les services dans l'ordre MAIS n'attend pas
#     que le service soit vraiment "prêt" (juste démarré)

# Pour attendre que le service soit PRÊT, utilisez healthcheck :
services:
  web:
    depends_on:
      db:
        condition: service_healthy
  
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5


╔════════════════════════════════════════════════════════════════════╗
║ restart - Politique de redémarrage                                 ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    restart: no                 # Jamais (défaut)
    restart: always             # Toujours redémarrer
    restart: on-failure         # Seulement si erreur
    restart: unless-stopped     # Sauf si arrêt manuel

# [IDEE] Recommandations :
# - Développement : no ou on-failure
# - Production : unless-stopped ou always


╔════════════════════════════════════════════════════════════════════╗
║ networks - Réseaux personnalisés                                   ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    networks:
      - frontend
      - backend
  
  db:
    networks:
      - backend

networks:
  frontend:
  backend:

# Par défaut, Compose crée UN réseau pour tous les services
# Services peuvent communiquer par leurs noms (DNS automatique)


╔════════════════════════════════════════════════════════════════════╗
║ healthcheck - Vérifier la santé du service                         ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s         # Vérifier toutes les 30s
      timeout: 10s          # Timeout de 10s
      retries: 3            # 3 échecs = unhealthy
      start_period: 40s     # Attendre 40s avant de commencer

# Autres formats de test :
test: ["CMD-SHELL", "pg_isready -U postgres"]
test: curl -f http://localhost/health || exit 1

# Désactiver healthcheck hérité
healthcheck:
  disable: true


╔════════════════════════════════════════════════════════════════════╗
║ labels - Métadonnées                                               ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    labels:
      - "com.example.description=Web application"
      - "com.example.version=1.0"
      
      # Ou format dictionnaire
      com.example.description: "Web application"
      com.example.version: "1.0"


╔════════════════════════════════════════════════════════════════════╗
║ logging - Configuration des logs                                   ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    logging:
      driver: json-file
      options:
        max-size: "10m"     # Max 10 MB par fichier
        max-file: "3"       # Max 3 fichiers


╔════════════════════════════════════════════════════════════════════╗
║ user - Utilisateur à utiliser                                      ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    user: "1000:1000"       # UID:GID
    user: "node"            # Nom d'utilisateur


╔════════════════════════════════════════════════════════════════════╗
║ working_dir - Répertoire de travail                                ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    working_dir: /app


╔════════════════════════════════════════════════════════════════════╗
║ stdin_open / tty - Mode interactif                                 ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    stdin_open: true        # -i
    tty: true               # -t
    
# Équivalent à docker run -it


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OBJECTIF] EXEMPLES COMPLETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Exemple 1 : Application Flask + PostgreSQL + Redis                 ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  # Application Flask
  web:
    build: .
    command: flask run --host=0.0.0.0
    volumes:
      - .:/app                          # Code synchronisé
    ports:
      - "5000:5000"
    environment:
      - FLASK_APP=app.py
      - FLASK_ENV=development
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis

  # Base de données PostgreSQL
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    ports:
      - "5432:5432"

  # Cache Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

# Pour lancer :
# docker compose up
# Tester : http://localhost:5000


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 2 : Application Django complète (Production)               ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  # Application Django
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/media
    expose:
      - 8000
    environment:
      - DEBUG=0
      - DATABASE_URL=postgresql://django:password@db:5432/djangodb
      - REDIS_URL=redis://redis:6379/0
    env_file:
      - .env.prod
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  # Nginx (reverse proxy)
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - static_volume:/static:ro
      - media_volume:/media:ro
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - web
    restart: always

  # PostgreSQL
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: djangodb
      POSTGRES_USER: django
      POSTGRES_PASSWORD: password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U django"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: always

  # Redis
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    restart: always

   # Celery Worker
  celery:
    build: .
    command: celery -A myproject worker -l info
    volumes:
      - media_volume:/app/media
    environment:
      - DATABASE_URL=postgresql://django:password@db:5432/djangodb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    restart: unless-stopped

  # Celery Beat (tâches planifiées)
  celery-beat:
    build: .
    command: celery -A myproject beat -l info
    environment:
      - DATABASE_URL=postgresql://django:password@db:5432/djangodb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:

# Pour lancer en production :
# docker compose up -d
# docker compose logs -f


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 3 : Stack MERN (MongoDB, Express, React, Node.js)          ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  # Frontend React
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - ./frontend/src:/app/src       # Hot reload
      - /app/node_modules             # Ne pas écraser node_modules
    environment:
      - REACT_APP_API_URL=http://localhost:5000
    depends_on:
      - backend

  # Backend Node.js/Express
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "5000:5000"
    volumes:
      - ./backend:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - MONGODB_URI=mongodb://mongo:27017/myapp
      - PORT=5000
    depends_on:
      - mongo

  # Base de données MongoDB
  mongo:
    image: mongo:7
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=password

  # Mongo Express (Interface web pour MongoDB)
  mongo-express:
    image: mongo-express:latest
    ports:
      - "8081:8081"
    environment:
      - ME_CONFIG_MONGODB_ADMINUSERNAME=admin
      - ME_CONFIG_MONGODB_ADMINPASSWORD=password
      - ME_CONFIG_MONGODB_URL=mongodb://admin:password@mongo:27017/
    depends_on:
      - mongo

volumes:
  mongo_data:


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 4 : WordPress avec MySQL                                   ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8000:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html
    depends_on:
      - db
    restart: always

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
      MYSQL_ROOT_PASSWORD: rootpassword
    volumes:
      - db_data:/var/lib/mysql
    restart: always

volumes:
  wordpress_data:
  db_data:

# Ouvrir : http://localhost:8000


╔════════════════════════════════════════════════════════════════════╗
║ Exemple 5 : Stack complète avec monitoring                         ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  # Application
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db
      - redis

  # Base de données
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password

  # Cache
  redis:
    image: redis:7-alpine

  # Prometheus (Métriques)
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'

  # Grafana (Dashboards)
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - prometheus

volumes:
  postgres_data:
  prometheus_data:
  grafana_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SCENARIO] CONFIGURATIONS MULTIPLES (dev, prod, test)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Docker Compose permet d'avoir plusieurs fichiers de configuration
pour différents environnements.

╔════════════════════════════════════════════════════════════════════╗
║ Méthode 1 : docker-compose.override.yml (Automatique)              ║
╚════════════════════════════════════════════════════════════════════╝

# Structure des fichiers :
# docker-compose.yml          -> Configuration de BASE
# docker-compose.override.yml -> Configuration DÉVELOPPEMENT (auto)

# docker-compose.yml (BASE - commun à tous)
version: '3.8'

services:
  web:
    build: .
    image: myapp:latest
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db
  
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:


# docker-compose.override.yml (DÉVELOPPEMENT - chargé automatiquement)
version: '3.8'

services:
  web:
    volumes:
      - .:/app                # Code synchronisé en live
    environment:
      - DEBUG=1
      - FLASK_ENV=development
    command: flask run --reload --host=0.0.0.0
  
  db:
    ports:
      - "5432:5432"          # Accès direct à la DB

# Comment ça marche :
docker compose up
# -> Charge automatiquement :
#   1. docker-compose.yml
#   2. docker-compose.override.yml (s'il existe)
# -> Les valeurs de override REMPLACENT celles de base


╔════════════════════════════════════════════════════════════════════╗
║ Méthode 2 : Fichiers séparés (Production, Test, etc.)              ║
╚════════════════════════════════════════════════════════════════════╝

# Structure des fichiers :
# docker-compose.yml          -> Configuration de BASE
# docker-compose.prod.yml     -> Configuration PRODUCTION
# docker-compose.test.yml     -> Configuration TEST

# docker-compose.yml (BASE)
version: '3.8'

services:
  web:
    build: .
    image: myapp:latest
    depends_on:
      - db
  
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:


# docker-compose.prod.yml (PRODUCTION)
version: '3.8'

services:
  web:
    environment:
      - DEBUG=0
      - FLASK_ENV=production
    restart: always
    ports:
      - "80:8000"
  
  db:
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    restart: always

  # Nginx en plus pour la prod
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    restart: always


# docker-compose.test.yml (TEST)
version: '3.8'

services:
  web:
    environment:
      - TESTING=1
    command: pytest

# Utilisation :
# Développement (override automatique)
docker compose up

# Production (fichier spécifique)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Test (fichier spécifique)
docker compose -f docker-compose.yml -f docker-compose.test.yml run web

# Build pour production
docker compose -f docker-compose.yml -f docker-compose.prod.yml build

# [IDEE] Les fichiers sont FUSIONNÉS dans l'ordre spécifié


╔════════════════════════════════════════════════════════════════════╗
║ Méthode 3 : Variables d'environnement                              ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "${PORT:-8000}:8000"        # Port par défaut 8000
    environment:
      - DEBUG=${DEBUG:-0}
      - DATABASE_URL=${DATABASE_URL}
    image: myapp:${TAG:-latest}

# .env.dev
DEBUG=1
PORT=8000
TAG=dev
DATABASE_URL=postgresql://localhost/dev_db

# .env.prod
DEBUG=0
PORT=80
TAG=prod
DATABASE_URL=postgresql://prod-server/prod_db

# Utilisation :
docker compose --env-file .env.dev up
docker compose --env-file .env.prod up -d


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CIRCUS_TENT] PROFILES - Lancer services optionnels
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Les profiles permettent de définir des services optionnels qui ne
se lancent que si on les demande explicitement.

# docker-compose.yml
version: '3.8'

services:
  # Services principaux (toujours lancés)
  web:
    image: nginx:alpine
    ports:
      - "80:80"
  
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: password

  # Services de DEBUG (optionnels)
  adminer:
    image: adminer:latest
    ports:
      - "8080:8080"
    profiles:
      - debug                   # Ne démarre que avec --profile debug
  
  mailhog:
    image: mailhog/mailhog
    ports:
      - "8025:8025"
    profiles:
      - debug

  # Services de TEST (optionnels)
  test-runner:
    build: .
    command: pytest
    profiles:
      - test

# Utilisation :
docker compose up
# -> Lance : web, db (pas adminer ni mailhog ni test-runner)

docker compose --profile debug up
# -> Lance : web, db, adminer, mailhog

docker compose --profile test up
# -> Lance : web, db, test-runner

docker compose --profile debug --profile test up
# -> Lance TOUT

# [IDEE] Cas d'usage :
# - Outils de debug (pgAdmin, Mailhog, etc.)
# - Services de test
# - Services de développement
# - Monitoring optionnel


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[LIEN] RÉSEAUX (NETWORKS) AVANCÉS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Réseau par défaut                                                  ║
╚════════════════════════════════════════════════════════════════════╝

# Sans configuration de networks, Compose crée UN réseau automatique
# Tous les services peuvent communiquer entre eux par leur NOM

version: '3.8'

services:
  web:
    image: nginx
  
  api:
    image: python:3.11
    command: python app.py

# web peut accéder à api via : http://api:5000
# api peut accéder à web via : http://web:80


╔════════════════════════════════════════════════════════════════════╗
║ Réseaux multiples (isoler les services)                            ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Frontend (accessible publiquement)
  nginx:
    image: nginx:alpine
    networks:
      - frontend
    ports:
      - "80:80"
  
  # Backend (entre frontend et backend)
  api:
    build: .
    networks:
      - frontend              # Accessible par nginx
      - backend               # Accessible par db
  
  # Base de données (isolée dans backend)
  db:
    image: postgres:15
    networks:
      - backend               # Accessible seulement par api
    # db N'EST PAS accessible par nginx !

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

# Avantages :
# - Sécurité : db n'est pas accessible depuis le frontend
# - Organisation : séparation claire des responsabilités


╔════════════════════════════════════════════════════════════════════╗
║ Réseau externe (déjà existant)                                     ║
╚════════════════════════════════════════════════════════════════════╝

# Créer un réseau externe
docker network create my-external-network

# docker-compose.yml
version: '3.8'

services:
  web:
    image: nginx
    networks:
      - external-network

networks:
  external-network:
    external: true
    name: my-external-network

# Utile pour connecter plusieurs projets docker-compose


╔════════════════════════════════════════════════════════════════════╗
║ Alias de réseau                                                    ║
╚════════════════════════════════════════════════════════════════════╝

services:
  db:
    image: postgres:15
    networks:
      backend:
        aliases:
          - database
          - postgres-server

# db est accessible via 3 noms :
# - db (nom du service)
# - database (alias)
# - postgres-server (alias)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SAUVEGARDE] VOLUMES AVANCÉS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Types de volumes                                                   ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  web:
    image: nginx
    volumes:
      # 1. Bind mount (dossier local)
      - ./html:/usr/share/nginx/html
      - ./config.conf:/etc/nginx/nginx.conf:ro
      
      # 2. Volume nommé
      - app-data:/var/lib/app
      
      # 3. Volume anonyme
      - /var/cache/nginx
      
      # 4. tmpfs (en mémoire, rapide mais non persistant)
      - type: tmpfs
        target: /tmp

volumes:
  app-data:

# Bind mount : Développement (synchronisation live)
# Volume nommé : Production (données persistantes)
# Volume anonyme : Cache temporaire
# tmpfs : Données ultra-rapides et non persistantes


╔════════════════════════════════════════════════════════════════════╗
║ Volumes avec options                                               ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  db:
    image: postgres:15
    volumes:
      # Volume nommé avec driver spécifique
      - type: volume
        source: db-data
        target: /var/lib/postgresql/data
        volume:
          nocopy: true
      
      # Bind mount avec options
      - type: bind
        source: ./backup
        target: /backup
        read_only: true

volumes:
  db-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/data/postgres


╔════════════════════════════════════════════════════════════════════╗
║ Volume externe (déjà créé)                                         ║
╚════════════════════════════════════════════════════════════════════╝

# Créer un volume externe
docker volume create my-external-volume

# docker-compose.yml
version: '3.8'

services:
  web:
    image: nginx
    volumes:
      - external-vol:/data

volumes:
  external-vol:
    external: true
    name: my-external-volume


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SCALES]  SCALING - Plusieurs instances du même service
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    # [ATTENTION]  PAS de container_name (empêche le scaling)
    # [ATTENTION]  PAS de ports fixes (conflits)
    expose:
      - 8000
  
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    depends_on:
      - web

# Lancer 3 instances de web
docker compose up -d --scale web=3

# Compose crée :
# - myproject_web_1
# - myproject_web_2
# - myproject_web_3

# Nginx peut faire du load balancing vers ces 3 instances


# Configuration Nginx pour load balancing :
upstream backend {
    server web:8000;        # Docker DNS résout vers toutes les instances
}

server {
    location / {
        proxy_pass http://backend;
    }
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SECURISE] SECRETS ET CONFIGURATIONS SENSIBLES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Utiliser .env pour les secrets (DÉVELOPPEMENT)                     ║
╚════════════════════════════════════════════════════════════════════╝

# .env (NE PAS COMMITER dans Git !)
DATABASE_PASSWORD=super_secret_password
API_KEY=abc123xyz789
SECRET_KEY=django-insecure-key

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    environment:
      - DATABASE_PASSWORD=${DATABASE_PASSWORD}
      - API_KEY=${API_KEY}
    # Ou simplement :
    env_file:
      - .env

# .gitignore
.env


╔════════════════════════════════════════════════════════════════════╗
║ Docker Secrets (PRODUCTION avec Swarm)                             ║
╚════════════════════════════════════════════════════════════════════╝

# [ATTENTION]  Nécessite Docker Swarm (pas disponible avec docker compose up)

version: '3.8'

services:
  db:
    image: postgres:15
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

# Le fichier ./secrets/db_password.txt contient juste le mot de passe


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[COURS] BONNES PRATIQUES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1⃣  STRUCTURE DE PROJET ORGANISÉE

myproject/
├── docker-compose.yml              # Configuration principale
├── docker-compose.override.yml     # Développement
├── docker-compose.prod.yml         # Production
├── .env.example                    # Template de .env
├── .env                            # Variables (ne pas commiter)
├── .dockerignore
├── Dockerfile
├── nginx/
│   └── nginx.conf
├── app/
│   └── code...
└── README.md


2⃣  NOMMER LES CONTENEURS ET VOLUMES

[OK] BON :
services:
  web:
    container_name: myproject_web
    volumes:
      - postgres_data:/var/lib/postgresql/data

[X] MAUVAIS :
services:
  web:
    # Noms générés automatiquement (difficile à debugger)
    volumes:
      - /var/lib/postgresql/data


3⃣  UTILISER depends_on AVEC HEALTHCHECK

[OK] BON :
services:
  web:
    depends_on:
      db:
        condition: service_healthy
  
  db:
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 5s

[X] MAUVAIS :
services:
  web:
    depends_on:
      - db  # Ne garantit pas que db est prête


4⃣  SÉPARER LES ENVIRONNEMENTS

[OK] BON : Fichiers séparés
- docker-compose.yml (base)
- docker-compose.override.yml (dev)
- docker-compose.prod.yml (prod)

[X] MAUVAIS : Tout dans un fichier avec des if/else


5⃣  UTILISER DES VOLUMES NOMMÉS POUR LES DONNÉES

[OK] BON :
volumes:
  - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

[X] MAUVAIS :
volumes:
  - /var/lib/postgresql/data  # Volume anonyme perdu


6⃣  NE PAS EXPOSER DE PORTS EN PRODUCTION (sauf nécessaire)

[OK] BON (Développement) :
db:
  ports:
    - "5432:5432"  # Accès direct pour debugging

[OK] BON (Production) :
db:
  expose:
    - 5432  # Accessible par autres containers seulement


7⃣  UTILISER restart: unless-stopped EN PRODUCTION

[OK] BON :
services:
  web:
    restart: unless-stopped

[X] MAUVAIS :
services:
  web:
    restart: always  # Redémarre même si vous l'arrêtez manuellement


8⃣  TOUJOURS UTILISER .dockerignore

# .dockerignore
node_modules/
.git/
.env
*.log
__pycache__/


9⃣  DOCUMENTER VOTRE CONFIGURATION

# docker-compose.yml
version: '3.8'

services:
  web:
    # Application Flask principale
    # Port 5000 exposé pour le développement
    build: .
    ports:
      - "5000:5000"


[10]  NE JAMAIS COMMITER .env DANS GIT

# .gitignore
.env
.env.local
.env.*.local

# À la place, commiter .env.example :
DATABASE_URL=postgresql://user:password@db:5432/mydb
REDIS_URL=redis://redis:6379/0
SECRET_KEY=change-me


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[BUG] DÉBOGAGE ET DÉPANNAGE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Problème : Erreur de syntaxe YAML                                  ║
╚════════════════════════════════════════════════════════════════════╝

# Valider la syntaxe
docker compose config

# Si erreur : vérifiez l'indentation (ESPACES, pas TABS)
# Utilisez un validateur YAML en ligne


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Service ne démarre pas                                  ║
╚════════════════════════════════════════════════════════════════════╝

# Voir les logs
docker compose logs web
docker compose logs -f web

# Voir le statut
docker compose ps

# Entrer dans le container
docker compose exec web bash


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Cannot connect to database                              ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier que db est démarré
docker compose ps

# Vérifier les logs de la DB
docker compose logs db

# Utiliser le NOM du service, pas localhost
DATABASE_URL=postgresql://user:pass@db:5432/mydb
#                                    ^^
#                             Nom du service !

# Ajouter depends_on avec healthcheck
services:
  web:
    depends_on:
      db:
        condition: service_healthy


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Port already in use                                     ║
╚════════════════════════════════════════════════════════════════════╝

# Voir quel processus utilise le port
# Linux/Mac :
sudo lsof -i :5432
# Windows :
netstat -ano | findstr :5432

# Changer le port dans docker-compose.yml
ports:
  - "5433:5432"  # Port 5433 sur votre PC


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Volumes ne persistent pas                               ║
╚════════════════════════════════════════════════════════════════════╝

# Utiliser des volumes NOMMÉS
volumes:
  - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

# Ne PAS utiliser -v lors du down
docker compose down  # [OK] OK
docker compose down -v  # [X] Supprime les volumes


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Changements de code non pris en compte                  ║
╚════════════════════════════════════════════════════════════════════╝

# Rebuild les images
docker compose up --build

# Ou
docker compose build
docker compose up

# Forcer la recréation
docker compose up --force-recreate


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Variables d'environnement non définies                  ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier les variables
docker compose config

# Vérifier le fichier .env
cat .env

# Voir les variables dans le container
docker compose exec web env
docker compose exec web printenv

# Forcer le rechargement du .env
docker compose down
docker compose up


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Network error / Cannot reach service                    ║
╚════════════════════════════════════════════════════════════════════╝

# Lister les réseaux
docker network ls

# Inspecter le réseau
docker network inspect myproject_default

# Vérifier que les services sont sur le même réseau
docker compose ps

# Tester la connectivité
docker compose exec web ping db
docker compose exec web curl http://api:5000


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Permission denied sur volumes                           ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier les permissions du dossier
ls -la ./data

# Changer le propriétaire
sudo chown -R $USER:$USER ./data

# Ou spécifier l'utilisateur dans compose
services:
  web:
    user: "1000:1000"


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Container keeps restarting                              ║
╚════════════════════════════════════════════════════════════════════╝

# Voir les logs pour comprendre pourquoi
docker compose logs web

# Désactiver le restart temporairement
services:
  web:
    restart: "no"

# Lancer en mode interactif pour debugger
docker compose run --rm web bash


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SYNC] WORKFLOWS COURANTS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Workflow 1 : Démarrer un nouveau projet                            ║
╚════════════════════════════════════════════════════════════════════╝

# 1. Créer la structure
mkdir myproject && cd myproject
mkdir app nginx

# 2. Créer docker-compose.yml
cat > docker-compose.yml <<EOF
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - ./app:/app
    environment:
      - DEBUG=1
    depends_on:
      - db
  
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password

volumes:
  postgres_data:
EOF

# 3. Créer Dockerfile
cat > Dockerfile <<EOF
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
EOF

# 4. Créer .env.example
cat > .env.example <<EOF
DATABASE_URL=postgresql://postgres:password@db:5432/mydb
SECRET_KEY=change-me-in-production
EOF

# 5. Copier .env.example vers .env
cp .env.example .env

# 6. Créer .gitignore
cat > .gitignore <<EOF
.env
__pycache__/
*.pyc
.venv/
EOF

# 7. Démarrer
docker compose up -d

# 8. Voir les logs
docker compose logs -f


╔════════════════════════════════════════════════════════════════════╗
║ Workflow 2 : Développement quotidien                               ║
╚════════════════════════════════════════════════════════════════════╝

# Démarrer le projet le matin
docker compose up -d

# Voir les logs
docker compose logs -f web

# Exécuter des commandes (migrations, etc.)
docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser

# Installer une nouvelle dépendance
docker compose exec web pip install requests
docker compose exec web pip freeze > requirements.txt

# Rebuild après modification de requirements.txt
docker compose up -d --build

# Exécuter les tests
docker compose exec web pytest

# Arrêter le soir (garde les volumes)
docker compose stop

# Ou arrêter et supprimer les containers
docker compose down


╔════════════════════════════════════════════════════════════════════╗
║ Workflow 3 : Reset complet (problèmes)                             ║
╚════════════════════════════════════════════════════════════════════╝

# Tout arrêter et supprimer
docker compose down -v --rmi all --remove-orphans

# Rebuild from scratch
docker compose build --no-cache

# Redémarrer
docker compose up -d

# Voir les logs
docker compose logs -f


╔════════════════════════════════════════════════════════════════════╗
║ Workflow 4 : Backup de la base de données                          ║
╚════════════════════════════════════════════════════════════════════╝

# Backup PostgreSQL
docker compose exec -T db pg_dump -U postgres mydb > backup.sql

# Restore PostgreSQL
docker compose exec -T db psql -U postgres mydb < backup.sql

# Backup MySQL
docker compose exec -T db mysqldump -u root -ppassword mydb > backup.sql

# Restore MySQL
docker compose exec -T db mysql -u root -ppassword mydb < backup.sql

# Backup MongoDB
docker compose exec -T mongo mongodump --out=/backup
docker compose cp mongo:/backup ./backup

# Backup des volumes
docker run --rm \
  -v myproject_postgres_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/postgres_backup.tar.gz /data


╔════════════════════════════════════════════════════════════════════╗
║ Workflow 5 : Déploiement en production                             ║
╚════════════════════════════════════════════════════════════════════╗

# Sur le serveur de production :

# 1. Cloner le projet
git clone repo_url
cd project

# 2. Copier les variables d'environnement
cp .env.example .env
nano .env  # Éditer avec les vraies valeurs

# 3. Build avec la config production
docker compose -f docker-compose.yml -f docker-compose.prod.yml build

# 4. Lancer en production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 5. Vérifier les logs
docker compose logs -f

# 6. Migrations si nécessaire
docker compose exec web python manage.py migrate

# Pour mettre à jour :
git pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[GRAPHIQUE] MONITORING ET LOGS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Stack de monitoring complète                                       ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.monitoring.yml
version: '3.8'

services:
  # Application
  app:
    build: .
    ports:
      - "8000:8000"

  # Prometheus (collecte métriques)
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'

  # Grafana (visualisation)
  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana_data:/var/lib/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    depends_on:
      - prometheus

  # Node Exporter (métriques serveur)
  node-exporter:
    image: prom/node-exporter:latest
    ports:
      - "9100:9100"
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro

  # cAdvisor (métriques containers)
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro

  # Loki (logs)
  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    volumes:
      - loki_data:/loki
    command: -config.file=/etc/loki/local-config.yaml

  # Promtail (collecte logs)
  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/log:/var/log
      - ./promtail.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml

volumes:
  prometheus_data:
  grafana_data:
  loki_data:

# Lancer :
# docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d


╔════════════════════════════════════════════════════════════════════╗
║ Centralisation des logs avec ELK Stack                             ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.elk.yml
version: '3.8'

services:
  # Elasticsearch (stockage logs)
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"

  # Kibana (visualisation)
  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

  # Logstash (traitement logs)
  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "5000:5000"
    depends_on:
      - elasticsearch

volumes:
  elasticsearch_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[VERROUILLE] SÉCURITÉ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Checklist Sécurité                                                 ║
╚════════════════════════════════════════════════════════════════════╝

1⃣  NE JAMAIS EXÉCUTER EN ROOT
   services:
     web:
       user: "1000:1000"

2⃣  NE PAS EXPOSER DE PORTS INUTILEMENT
   [OK] expose: ["5432"]              # Seulement entre containers
   [X] ports: ["5432:5432"]          # Accessible depuis internet

3⃣  UTILISER DES SECRETS (pas d'env variables)
   # Mauvais
   environment:
     - PASSWORD=secret123
   
   # Bon
   secrets:
     - db_password

4⃣  LIMITER LES RESSOURCES
   services:
     web:
       deploy:
         resources:
           limits:
             cpus: '0.5'
             memory: 512M

5⃣  READ-ONLY FILESYSTEMS QUAND POSSIBLE
   services:
     web:
       read_only: true
       tmpfs:
         - /tmp

6⃣  UTILISER DES IMAGES OFFICIELLES ET VÉRIFIÉES
   [OK] image: postgres:15-alpine
   [X] image: random-user/postgres

7⃣  SCANNER LES IMAGES POUR VULNÉRABILITÉS
   docker scan myapp:latest

8⃣  NE PAS INCLURE DE DONNÉES SENSIBLES DANS LES IMAGES
   # .dockerignore
   .env
   secrets/
   *.key
   *.pem

9⃣  UTILISER HTTPS EN PRODUCTION
   nginx:
     volumes:
       - ./certs:/etc/nginx/certs:ro
     ports:
       - "443:443"

[10]  DÉSACTIVER DEBUG EN PRODUCTION
   environment:
     - DEBUG=0
     - FLASK_ENV=production


╔════════════════════════════════════════════════════════════════════╗
║ Exemple : Configuration sécurisée                                  ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  web:
    build: .
    user: "1000:1000"                   # Non-root
    read_only: true                     # Filesystem read-only
    tmpfs:
      - /tmp                            # Seulement /tmp en écriture
    expose:
      - 8000                            # Pas de ports publics
    environment:
      - DEBUG=0
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
    networks:
      - backend                         # Réseau isolé
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"                       # Seulement HTTPS
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - web
    networks:
      - backend
      - frontend
    restart: always

  db:
    image: postgres:15-alpine
    user: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    expose:
      - 5432                            # Pas accessible publiquement
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    networks:
      - backend                         # Isolé dans backend
    restart: always

secrets:
  db_password:
    file: ./secrets/db_password.txt

networks:
  backend:
    internal: true                      # Réseau interne seulement
  frontend:

volumes:
  postgres_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[RAPIDE] OPTIMISATION DES PERFORMANCES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Limiter les ressources                                             ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  web:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '2'              # Max 2 CPUs
          memory: 2G             # Max 2 GB RAM
        reservations:
          cpus: '0.5'            # Min 0.5 CPU
          memory: 512M           # Min 512 MB RAM


╔════════════════════════════════════════════════════════════════════╗
║ Optimiser les volumes (performance)                                ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    volumes:
      # Pour Mac : utiliser cached ou delegated
      - ./code:/app:cached           # Meilleure performance lecture
      - ./logs:/app/logs:delegated   # Meilleure performance écriture
      
      # Pour production : utiliser des volumes nommés
      - app_data:/app/data


╔════════════════════════════════════════════════════════════════════╗
║ Build cache et optimisation                                        ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    build:
      context: .
      cache_from:
        - myapp:latest              # Utiliser cache
      args:
        BUILDKIT_INLINE_CACHE: 1    # BuildKit cache


╔════════════════════════════════════════════════════════════════════╗
║ Logging optimisé                                                   ║
╚════════════════════════════════════════════════════════════════════╝

services:
  web:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"       # Max 10 MB par fichier
        max-file: "3"         # Max 3 fichiers (30 MB total)
        compress: "true"      # Compresser


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OBJECTIF] PATTERNS COURANTS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Pattern 1 : Application web + DB + Cache + Worker                  ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Frontend
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    depends_on:
      - api

  # API Backend
  api:
    build: ./backend
    expose:
      - 8000
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - db
      - redis

  # Worker (Celery)
  worker:
    build: ./backend
    command: celery -A app worker
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - db
      - redis

  # Database
  db:
    image: postgres:15-alpine
    volumes:
      - db_data:/var/lib/postgresql/data

  # Cache
  redis:
    image: redis:7-alpine

volumes:
  db_data:


╔════════════════════════════════════════════════════════════════════╗
║ Pattern 2 : Microservices avec API Gateway                         ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # API Gateway (Kong/Nginx)
  gateway:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./gateway.conf:/etc/nginx/nginx.conf
    depends_on:
      - user-service
      - product-service
      - order-service

  # Service utilisateurs
  user-service:
    build: ./services/users
    expose:
      - 8001
    environment:
      DATABASE_URL: postgresql://user:pass@user-db:5432/users
    depends_on:
      - user-db

  # Service produits
  product-service:
    build: ./services/products
    expose:
      - 8002
    environment:
      DATABASE_URL: postgresql://user:pass@product-db:5432/products
    depends_on:
      - product-db

  # Service commandes
  order-service:
    build: ./services/orders
    expose:
      - 8003
    environment:
      DATABASE_URL: postgresql://user:pass@order-db:5432/orders
    depends_on:
      - order-db

  # Base de données par service
  user-db:
    image: postgres:15-alpine
    volumes:
      - user_db:/var/lib/postgresql/data

  product-db:
    image: postgres:15-alpine
    volumes:
      - product_db:/var/lib/postgresql/data

  order-db:
    image: postgres:15-alpine
    volumes:
      - order_db:/var/lib/postgresql/data

volumes:
  user_db:
  product_db:
  order_db:


╔════════════════════════════════════════════════════════════════════╗
║ Pattern 3 : Application avec reverse proxy SSL                     ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Nginx avec SSL
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
      - static:/static:ro
    depends_on:
      - web

  # Application web
  web:
    build: .
    expose:
      - 8000
    volumes:
      - static:/app/static
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb

  # Base de données
  db:
    image: postgres:15-alpine
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  static:
  db_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[LISTE] AIDE-MÉMOIRE : COMMANDES ESSENTIELLES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# DÉMARRAGE
docker compose up                      # Démarrer (interactif)
docker compose up -d                   # Démarrer (détaché)
docker compose up --build              # Rebuild + démarrer
docker compose up web                  # Service spécifique

# ARRÊT
docker compose down                    # Arrêter et supprimer
docker compose down -v                 # + supprimer volumes
docker compose stop                    # Arrêter (garder containers)

# LOGS
docker compose logs                    # Tous les logs
docker compose logs -f                 # Suivre en temps réel
docker compose logs web                # Service spécifique
docker compose logs --tail=100 web     # 100 dernières lignes

# EXÉCUTION
docker compose exec web bash           # Shell interactif
docker compose exec web python script  # Commande spécifique
docker compose run --rm web pytest     # One-off command

# ÉTAT
docker compose ps                      # Lister services
docker compose top                     # Voir processus
docker compose config                  # Valider config

# BUILD
docker compose build                   # Builder toutes images
docker compose build --no-cache        # Sans cache
docker compose pull                    # Pull images

# GESTION
docker compose restart                 # Redémarrer
docker compose restart web             # Service spécifique
docker compose pause                   # Pause
docker compose unpause                 # Unpause

# SCALING
docker compose up -d --scale web=3     # 3 instances

# FICHIERS MULTIPLES
docker compose -f file1.yml -f file2.yml up


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[COURS] EXERCICES PRATIQUES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Exercice 1 : Blog avec WordPress (Facile)                          ║
╚════════════════════════════════════════════════════════════════════╝

# Créer un blog WordPress avec MySQL

# 1. Créer docker-compose.yml avec :
#    - Service wordpress (image: wordpress:latest, port 8000)
#    - Service mysql (image: mysql:8.0)
#    - Variables d'environnement appropriées
#    - Volumes pour persistance

# 2. Lancer avec docker compose up -d
# 3. Ouvrir http://localhost:8000
# 4. Configurer WordPress


╔════════════════════════════════════════════════════════════════════╗
║ Exercice 2 : API Flask + PostgreSQL + Redis (Moyen)                ║
╚════════════════════════════════════════════════════════════════════╝

# Créer une API REST avec cache

# 1. Créer docker-compose.yml avec 3 services
# 2. Créer Dockerfile pour l'API Flask
# 3. Configurer les dépendances entre services
# 4. Ajouter healthcheck sur PostgreSQL
# 5. Utiliser des volumes pour le code (hot reload)


╔════════════════════════════════════════════════════════════════════╗
║ Exercice 3 : Stack complète avec monitoring (Avancé)               ║
╚════════════════════════════════════════════════════════════════════╝

# Application complète avec monitoring

# 1. Application web (Django/Flask)
# 2. Base de données (PostgreSQL)
# 3. Cache (Redis)
# 4. Worker (Celery)
# 5. Nginx (reverse proxy)
# 6. Prometheus (métriques)
# 7. Grafana (dashboards)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[DOCS] RESSOURCES ET DOCUMENTATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Documentation officielle
https://docs.docker.com/compose/

# Référence docker-compose.yml
https://docs.docker.com/compose/compose-file/

# Exemples officiels
https://github.com/docker/awesome-compose

# Versions de Compose
https://docs.docker.com/compose/compose-file/compose-versioning/

# Networking
https://docs.docker.com/compose/networking/

# Environment variables
https://docs.docker.com/compose/environment-variables/


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OK] CHECKLIST FINALE (suite)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[WHITE_SQUARE] Healthchecks configurés
[WHITE_SQUARE] Restart policy configuré (unless-stopped)
[WHITE_SQUARE] Logs limités (max-size, max-file)
[WHITE_SQUARE] Ressources limitées (CPU, RAM)
[WHITE_SQUARE] Réseaux isolés si nécessaire
[WHITE_SQUARE] Backup automatique configuré
[WHITE_SQUARE] Monitoring en place
[WHITE_SQUARE] Documentation à jour
[WHITE_SQUARE] Tests effectués en staging


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OUTIL] EXTENSIONS ET FRAGMENTS YAML (Réutilisation)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Les extensions YAML permettent de réutiliser des configurations

╔════════════════════════════════════════════════════════════════════╗
║ Méthode 1 : Anchors YAML (&) et Aliases (*)                        ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

# Définir un template (anchor)
x-common-variables: &common-env
  DATABASE_URL: postgresql://user:pass@db:5432/mydb
  REDIS_URL: redis://redis:6379/0
  LOG_LEVEL: info

x-common-healthcheck: &common-healthcheck
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

services:
  web:
    build: .
    environment:
      <<: *common-env          # Réutiliser les variables
      DEBUG: 1
    healthcheck:
      <<: *common-healthcheck
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
  
  worker:
    build: .
    environment:
      <<: *common-env          # Même configuration
      DEBUG: 0
    healthcheck:
      <<: *common-healthcheck
      test: ["CMD", "python", "healthcheck.py"]


╔════════════════════════════════════════════════════════════════════╗
║ Méthode 2 : Extension fields (x-)                                  ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

# Définir des fragments réutilisables
x-logging: &default-logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"
    compress: "true"

x-deploy: &default-deploy
  resources:
    limits:
      cpus: '1'
      memory: 1G
    reservations:
      cpus: '0.25'
      memory: 256M

services:
  web:
    image: myapp:latest
    logging: *default-logging
    deploy: *default-deploy
  
  api:
    image: myapi:latest
    logging: *default-logging
    deploy: *default-deploy
  
  worker:
    image: myworker:latest
    logging: *default-logging
    deploy: *default-deploy


╔════════════════════════════════════════════════════════════════════╗
║ Exemple complet avec extensions                                    ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

# === TEMPLATES RÉUTILISABLES ===

x-common-variables: &common-variables
  DATABASE_URL: postgresql://postgres:password@db:5432/mydb
  REDIS_URL: redis://redis:6379/0
  SECRET_KEY: ${SECRET_KEY}

x-logging: &logging
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

x-healthcheck-defaults: &healthcheck-defaults
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

x-deploy-defaults: &deploy-defaults
  resources:
    limits:
      memory: 1G
    reservations:
      memory: 256M

# === SERVICES ===

services:
  web:
    build: .
    command: gunicorn app:app --bind 0.0.0.0:8000
    environment:
      <<: *common-variables
      WORKERS: 4
    logging: *logging
    deploy: *deploy-defaults
    healthcheck:
      <<: *healthcheck-defaults
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
    depends_on:
      - db
      - redis

  worker:
    build: .
    command: celery -A app worker
    environment:
      <<: *common-variables
      CELERY_CONCURRENCY: 4
    logging: *logging
    deploy: *deploy-defaults
    depends_on:
      - db
      - redis

  beat:
    build: .
    command: celery -A app beat
    environment:
      <<: *common-variables
    logging: *logging
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    logging: *logging
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    logging: *logging
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  postgres_data:
  redis_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[WEB] DEPLOIEMENT AVEC DOCKER SWARM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Docker Compose peut aussi déployer sur Docker Swarm pour la haute
disponibilité et le scaling automatique.

╔════════════════════════════════════════════════════════════════════╗
║ docker-compose.yml pour Swarm                                      ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  web:
    image: myapp:latest
    deploy:
      replicas: 3                      # 3 instances
      update_config:
        parallelism: 1                 # Update 1 à la fois
        delay: 10s
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker        # Seulement sur workers
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
    ports:
      - "8000:8000"
    networks:
      - backend

  db:
    image: postgres:15-alpine
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.database == true
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - backend

networks:
  backend:
    driver: overlay

volumes:
  db_data:

# Initialiser Swarm
# docker swarm init

# Déployer le stack
# docker stack deploy -c docker-compose.yml myapp

# Lister les services
# docker service ls

# Scaler un service
# docker service scale myapp_web=5

# Supprimer le stack
# docker stack rm myapp


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[SYNC] CI/CD AVEC DOCKER COMPOSE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ GitHub Actions                                                     ║
╚════════════════════════════════════════════════════════════════════╝

# .github/workflows/docker-compose.yml
name: Docker Compose CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build and run tests
      run: |
        docker compose -f docker-compose.test.yml up --build --abort-on-container-exit
    
    - name: Clean up
      run: docker compose -f docker-compose.test.yml down -v

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to production
      run: |
        docker compose -f docker-compose.yml -f docker-compose.prod.yml build
        docker compose -f docker-compose.yml -f docker-compose.prod.yml push


╔════════════════════════════════════════════════════════════════════╗
║ GitLab CI                                                          ║
╚════════════════════════════════════════════════════════════════════╝

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2

test:
  stage: test
  image: docker/compose:latest
  services:
    - docker:dind
  script:
    - docker compose -f docker-compose.test.yml up --build --abort-on-container-exit
    - docker compose -f docker-compose.test.yml down -v

build:
  stage: build
  image: docker/compose:latest
  services:
    - docker:dind
  script:
    - docker compose build
    - docker compose push
  only:
    - main

deploy:
  stage: deploy
  script:
    - docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
    - docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
  only:
    - main


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[TEST] TESTS AVEC DOCKER COMPOSE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ docker-compose.test.yml                                            ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Service de test
  test:
    build:
      context: .
      target: test                     # Multi-stage build
    command: pytest -v --cov=app tests/
    environment:
      - TESTING=1
      - DATABASE_URL=postgresql://test:test@test-db:5432/test_db
    depends_on:
      test-db:
        condition: service_healthy
    volumes:
      - ./tests:/app/tests
      - ./coverage:/app/coverage

  # Base de données de test
  test-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test"]
      interval: 2s
      timeout: 5s
      retries: 5

# Lancer les tests
# docker compose -f docker-compose.test.yml up --build --abort-on-container-exit
# docker compose -f docker-compose.test.yml down -v


╔════════════════════════════════════════════════════════════════════╗
║ Tests d'intégration                                                ║
╚════════════════════════════════════════════════════════════════════╝

# docker-compose.integration.yml
version: '3.8'

services:
  # API
  api:
    build: ./api
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/testdb
    depends_on:
      db:
        condition: service_healthy

  # Base de données
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: pass
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 2s

  # Tests d'intégration
  integration-tests:
    build: ./tests
    command: pytest tests/integration/
    environment:
      - API_URL=http://api:8000
    depends_on:
      - api

# Lancer
# docker compose -f docker-compose.integration.yml up --abort-on-container-exit


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[GRAPHIQUE] TEMPLATES DE PROJETS COMPLETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Template 1 : Full Stack Django + React + PostgreSQL                ║
╚════════════════════════════════════════════════════════════════════╝

# Structure du projet
project/
├── backend/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app/
├── frontend/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
├── nginx/
│   ├── Dockerfile
│   └── nginx.conf
├── docker-compose.yml
├── docker-compose.prod.yml
└── .env.example

# docker-compose.yml
version: '3.8'

services:
  # Backend Django
  backend:
    build: ./backend
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./backend:/app
    expose:
      - 8000
    environment:
      - DEBUG=1
      - DATABASE_URL=postgresql://django:password@db:5432/django_db
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy

  # Frontend React
  frontend:
    build: ./frontend
    command: npm start
    volumes:
      - ./frontend/src:/app/src
      - /app/node_modules
    expose:
      - 3000
    environment:
      - REACT_APP_API_URL=http://localhost:8000

  # Nginx (Reverse Proxy)
  nginx:
    build: ./nginx
    ports:
      - "80:80"
    depends_on:
      - backend
      - frontend
    volumes:
      - static_volume:/static
      - media_volume:/media

  # PostgreSQL
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: django_db
      POSTGRES_USER: django
      POSTGRES_PASSWORD: password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U django"]
      interval: 5s
      timeout: 5s
      retries: 5

  # Redis
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

  # Celery Worker
  celery:
    build: ./backend
    command: celery -A config worker -l info
    environment:
      - DATABASE_URL=postgresql://django:password@db:5432/django_db
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis

  # Celery Beat
  celery-beat:
    build: ./backend
    command: celery -A config beat -l info
    environment:
      - DATABASE_URL=postgresql://django:password@db:5432/django_db
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:


╔════════════════════════════════════════════════════════════════════╗
║ Template 2 : Microservices avec Message Queue                      ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # API Gateway
  gateway:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./gateway/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - auth-service
      - user-service
      - notification-service

  # Service d'authentification
  auth-service:
    build: ./services/auth
    expose:
      - 8001
    environment:
      - DATABASE_URL=postgresql://auth:pass@auth-db:5432/auth_db
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - auth-db
      - rabbitmq

  # Service utilisateurs
  user-service:
    build: ./services/users
    expose:
      - 8002
    environment:
      - DATABASE_URL=postgresql://users:pass@user-db:5432/user_db
      - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
    depends_on:
      - user-db
      - rabbitmq

  # Service notifications
  notification-service:
    build: ./services/notifications
    expose:
      - 8003
    environment:
      - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
      - SMTP_HOST=${SMTP_HOST}
      - SMTP_PORT=${SMTP_PORT}
    depends_on:
      - rabbitmq

  # RabbitMQ (Message Broker)
  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"          # Management UI
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq

  # Bases de données
  auth-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: auth_db
      POSTGRES_USER: auth
      POSTGRES_PASSWORD: pass
    volumes:
      - auth_db:/var/lib/postgresql/data

  user-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: user_db
      POSTGRES_USER: users
      POSTGRES_PASSWORD: pass
    volumes:
      - user_db:/var/lib/postgresql/data

  # Redis (Cache partagé)
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

volumes:
  rabbitmq_data:
  auth_db:
  user_db:
  redis_data:


╔════════════════════════════════════════════════════════════════════╗
║ Template 3 : E-commerce complet                                    ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Frontend
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_API_URL=http://localhost:8000

  # API Backend
  api:
    build: ./backend
    expose:
      - 8000
    environment:
      - DATABASE_URL=postgresql://shop:pass@postgres:5432/shop_db
      - REDIS_URL=redis://redis:6379/0
      - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
      - AWS_ACCESS_KEY=${AWS_ACCESS_KEY}
    depends_on:
      - postgres
      - redis
      - elasticsearch

  # Worker (Celery)
  worker:
    build: ./backend
    command: celery -A app worker
    environment:
      - DATABASE_URL=postgresql://shop:pass@postgres:5432/shop_db
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - postgres
      - redis

  # PostgreSQL
  postgres:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: shop_db
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: pass

  # Redis (Cache + Celery)
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

  # Elasticsearch (Search)
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    volumes:
      - es_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"

  # MinIO (S3-compatible storage)
  minio:
    image: minio/minio:latest
    command: server /data --console-address ":9001"
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - minio_data:/data

  # Nginx
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/certs:/etc/nginx/certs
    depends_on:
      - api
      - frontend

volumes:
  postgres_data:
  redis_data:
  es_data:
  minio_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OUTILS] OUTILS ET UTILITAIRES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Makefile pour simplifier les commandes                             ║
╚════════════════════════════════════════════════════════════════════╝

# Makefile
.PHONY: help build up down restart logs shell test clean

help:  ## Afficher cette aide
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

build:  ## Builder les images
	docker compose build

up:  ## Démarrer les services
	docker compose up -d

down:  ## Arrêter les services
	docker compose down

restart:  ## Redémarrer les services
	docker compose restart

logs:  ## Voir les logs
	docker compose logs -f

shell:  ## Ouvrir un shell dans web
	docker compose exec web bash

test:  ## Lancer les tests
	docker compose exec web pytest

clean:  ## Tout nettoyer
	docker compose down -v --rmi all

migrate:  ## Lancer les migrations
	docker compose exec web python manage.py migrate

collectstatic:  ## Collecter les fichiers statiques
	docker compose exec web python manage.py collectstatic --noinput

backup:  ## Backup de la base de données
	docker compose exec -T db pg_dump -U postgres mydb > backup_$(shell date +%Y%m%d_%H%M%S).sql

prod-up:  ## Démarrer en production
	docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

prod-down:  ## Arrêter la production
	docker compose -f docker-compose.yml -f docker-compose.prod.yml down

# Utilisation :
# make help          # Voir toutes les commandes
# make up            # Démarrer
# make logs          # Voir logs
# make test          # Lancer tests
# make clean         # Tout nettoyer


╔════════════════════════════════════════════════════════════════════╗
║ Scripts Bash utiles                                                ║
╚════════════════════════════════════════════════════════════════════╝

# scripts/dev-setup.sh
#!/bin/bash
# Setup complet pour développement

echo "[RAPIDE] Setup de l'environnement de développement..."

# Vérifier Docker
if ! command -v docker &> /dev/null; then
    echo "[X] Docker n'est pas installé"
    exit 1
fi

# Créer .env depuis .env.example
if [ ! -f .env ]; then
    echo "[NOTE] Création du fichier .env..."
    cp .env.example .env
    echo "[OK] .env créé - Modifiez-le avec vos valeurs"
fi

# Builder les images
echo "[OUTIL] Build des images Docker..."
docker compose build

# Démarrer les services
echo "[BLACK_RIGHT-POINTING_TRIANGLE]  Démarrage des services..."
docker compose up -d

# Attendre que la DB soit prête
echo "[HOURGLASS_WITH_FLOWING_SAND] Attente de la base de données..."
sleep 10

# Migrations
echo "[SYNC] Migrations de la base de données..."
docker compose exec -T web python manage.py migrate

# Créer superuser (optionnel)
echo "[UTILISATEUR] Voulez-vous créer un superuser ? (y/n)"
read -r create_super
if [ "$create_super" = "y" ]; then
    docker compose exec web python manage.py createsuperuser
fi

echo "[OK] Setup terminé!"
echo "[WEB] Application : http://localhost:8000"
echo "[GRAPHIQUE] Logs : docker compose logs -f"


# scripts/backup.sh
#!/bin/bash
# Backup automatique

BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DIR

echo "[PACKAGE] Backup de la base de données..."
docker compose exec -T db pg_dump -U postgres mydb > "$BACKUP_DIR/db_$DATE.sql"

echo "[PACKAGE] Backup des volumes..."
docker run --rm \
  -v myproject_postgres_data:/data \
  -v $(pwd)/$BACKUP_DIR:/backup \
  alpine tar czf /backup/volumes_$DATE.tar.gz /data

echo "[OK] Backup terminé : $BACKUP_DIR/"
ls -lh $BACKUP_DIR/


# scripts/restore.sh
#!/bin/bash
# Restore depuis backup

if [ -z "$1" ]; then
    echo "Usage: ./restore.sh <backup_file.sql>"
    exit 1
fi

BACKUP_FILE=$1

if [ ! -f "$BACKUP_FILE" ]; then
    echo "[X] Fichier non trouvé : $BACKUP_FILE"
    exit 1
fi

echo "[ATTENTION]  Cette opération va REMPLACER la base de données actuelle"
echo "Continuer ? (y/n)"
read -r confirm

if [ "$confirm" != "y" ]; then
    echo "Annulé"
    exit 0
fi

echo "[SYNC] Restore de la base de données..."
docker compose exec -T db psql -U postgres mydb < "$BACKUP_FILE"

echo "[OK] Restore terminé"


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[IDEE] ASTUCES ET TIPS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1⃣  UTILISER DES ALIAS BASH

# Ajouter dans ~/.bashrc ou ~/.zshrc
alias dc='docker compose'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
alias dcl='docker compose logs -f'
alias dce='docker compose exec'
alias dcr='docker compose restart'

# Utilisation :
# dc up -d
# dcl web
# dce web bash


2⃣  VARIABLES D'ENVIRONNEMENT AVEC VALEURS PAR DÉFAUT

# docker-compose.yml
version: '3.8'

services:
  web:
    image: myapp:${TAG:-latest}        # Défaut: latest
    ports:
      - "${PORT:-8000}:8000"           # Défaut: 8000
    environment:
      - DEBUG=${DEBUG:-0}              # Défaut: 0
      - WORKERS=${WORKERS:-4}          # Défaut: 4

# Sans .env, utilise les valeurs par défaut
# Avec .env, utilise les valeurs du fichier


3⃣  CONDITION D'EXÉCUTION SELON L'ENVIRONNEMENT

# docker-compose.yml
version: '3.8'

services:
  web:
    image: myapp
    environment:
      - ENV=${ENV:-dev}

  # Service seulement en développement
  debug-tools:
    image: debug-image
    profiles:
      - debug
    # Lancer : docker compose --profile debug up

  # Service seulement en production
  monitoring:
    image: prometheus
    profiles:
      - production
    # Lancer : docker compose --profile production up


4⃣  ATTENDRE QUE LES SERVICES SOIENT VRAIMENT PRÊTS

# Script wait-for-it.sh (inclure dans l'image)
services:
  web:
    build: .
    command: >
      sh -c "
        ./wait-for-it.sh db:5432 --timeout=30 --strict -- 
        python manage.py migrate &&
        python manage.py runserver 0.0.0.0:8000
      "
    depends_on:
      - db

# Ou avec healthcheck
services:
  web:
    depends_on:
      db:
        condition: service_healthy
  
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s


5⃣  VISUALISER LA CONFIGURATION FINALE

# Voir la config après interpolation des variables
docker compose config

# Sauvegarder la config finale
docker compose config > docker-compose.resolved.yml


6⃣  NETTOYER RÉGULIÈREMENT

# Supprimer volumes non utilisés
docker volume prune

# Supprimer réseaux non utilisés
docker network prune

# Supprimer images non utilisées
docker image prune -a

# Tout nettoyer ([ATTENTION]  ATTENTION)
docker system prune -a --volumes


7⃣  DEBUGGER LES PROBLÈMES DE RÉSEAU

# Voir les réseaux
docker network ls

# Inspecter un réseau
docker network inspect myproject_default

# Tester la connectivité
docker compose exec web ping db
docker compose exec web nslookup db
docker compose exec web curl http://api:8000


8⃣  OVERRIDE PONCTUEL SANS MODIFIER LE FICHIER

# Changer le port temporairement
docker compose run -p 3000:3000 web

# Changer la commande
docker compose run web python script.py

# Changer une variable
docker compose run -e DEBUG=1 web


9⃣  VOIR LA CONSOMMATION DE RESSOURCES

# Statistiques en temps réel
docker stats

# Pour les services Compose
docker compose ps -q | xargs docker stats


[10]  COPIER FICHIERS DEPUIS/VERS UN CONTAINER

# Copier du container vers votre PC
docker compose cp web:/app/logs/app.log ./logs/

# Copier de votre PC vers le container
docker compose cp ./config.json web:/app/config/


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[RECHERCHE] DEBUGGING AVANCÉ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Problème : Service se comporte différemment qu'en local            ║
╚════════════════════════════════════════════════════════════════════╝

# 1. Vérifier les variables d'environnement
docker compose exec web env

# 2. Vérifier les volumes montés
docker compose exec web ls -la /app

# 3. Vérifier les permissions
docker compose exec web ls -la /app
docker compose exec web whoami

# 4. Comparer avec l'image
docker run --rm myapp:latest env


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Performance lente                                       ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier la consommation de ressources
docker stats

# Vérifier les logs pour erreurs
docker compose logs --tail=1000

# Vérifier les volumes (bind mounts lents sur Mac/Windows)
# Solution : Utiliser volumes nommés ou :cached

services:
  web:
    volumes:
      - ./code:/app:cached    # Meilleure performance


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Container s'arrête sans erreur claire                   ║
╚════════════════════════════════════════════════════════════════════╝

# Voir tous les logs depuis le début
docker compose logs web

# Voir le code de sortie
docker compose ps -a

# Lancer en mode debug
docker compose run --rm web sh

# Désactiver healthcheck temporairement
services:
  web:
    healthcheck:
      disable: true


╔════════════════════════════════════════════════════════════════════╗
║ Problème : Cannot connect to Docker daemon                         ║
╚════════════════════════════════════════════════════════════════════╝

# Vérifier que Docker est lancé
docker ps

# Vérifier les permissions (Linux)
sudo usermod -aG docker $USER
# Puis logout/login

# Redémarrer Docker
sudo systemctl restart docker


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[DOCS] CAS D'USAGE SPÉCIFIQUES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Cas 1 : Développement avec hot reload                              ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Python/Django avec hot reload
  backend:
    build: ./backend
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./backend:/app          # Code synchronisé
    environment:
      - DEBUG=1
      - PYTHONUNBUFFERED=1      # Logs en temps réel

  # Node.js avec hot reload
  frontend:
    build: ./frontend
    command: npm start
    volumes:
      - ./frontend/src:/app/src
      - /app/node_modules       # Ne pas écraser node_modules
    environment:
      - CHOKIDAR_USEPOLLING=true  # Hot reload sur Docker


╔════════════════════════════════════════════════════════════════════╗
║ Cas 2 : Multiple bases de données (Master-Slave)                   ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Master (écriture)
  db-master:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_REPLICATION_MODE: master
      POSTGRES_REPLICATION_USER: replicator
      POSTGRES_REPLICATION_PASSWORD: rep_password
    volumes:
      - db_master_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  # Slave (lecture)
  db-slave:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_REPLICATION_MODE: slave
      POSTGRES_MASTER_HOST: db-master
      POSTGRES_MASTER_PORT: 5432
      POSTGRES_REPLICATION_USER: replicator
      POSTGRES_REPLICATION_PASSWORD: rep_password
    volumes:
      - db_slave_data:/var/lib/postgresql/data
    ports:
      - "5433:5432"
    depends_on:
      - db-master

volumes:
  db_master_data:
  db_slave_data:


╔════════════════════════════════════════════════════════════════════╗
║ Cas 3 : Application avec SSL/TLS (Let's Encrypt)                   ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  # Nginx avec Certbot
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    command: >
      sh -c "while :; do sleep 6h & wait ${!}; nginx -s reload; done & nginx -g 'daemon off;'"

  # Certbot (renouvellement auto)
  certbot:
    image: certbot/certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    entrypoint: >
      sh -c "trap exit TERM; while :; do certbot renew; sleep 12h & wait ${!}; done;"


╔════════════════════════════════════════════════════════════════════╗
║ Cas 4 : Plusieurs environnements sur la même machine               ║
╚════════════════════════════════════════════════════════════════════╝

# Projet 1 : project1/docker-compose.yml
version: '3.8'

services:
  web:
    container_name: project1_web
    ports:
      - "8001:8000"
    networks:
      - project1_network

networks:
  project1_network:
    name: project1_network

# Projet 2 : project2/docker-compose.yml
version: '3.8'

services:
  web:
    container_name: project2_web
    ports:
      - "8002:8000"
    networks:
      - project2_network

networks:
  project2_network:
    name: project2_network

# Les deux projets peuvent tourner en parallèle
# cd project1 && docker compose up -d
# cd project2 && docker compose up -d


╔════════════════════════════════════════════════════════════════════╗
║ Cas 5 : Seed de données au démarrage                               ║
╚════════════════════════════════════════════════════════════════════╝

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # Exécuté au 1er démarrage
    environment:
      POSTGRES_PASSWORD: password

  web:
    build: .
    command: >
      sh -c "
        python manage.py migrate &&
        python manage.py loaddata initial_data.json &&
        python manage.py runserver 0.0.0.0:8000
      "
    depends_on:
      db:
        condition: service_healthy

volumes:
  postgres_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OBJECTIF] COMPARAISON : Docker Compose vs Alternatives
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

╔════════════════════════════════════════════════════════════════════╗
║ Docker Compose vs Kubernetes                                      ║
╚════════════════════════════════════════════════════════════════════╝

DOCKER COMPOSE :
[OK] Simple à apprendre et utiliser
[OK] Parfait pour développement local
[OK] Bon pour petites applications (1-10 containers)
[OK] Configuration en YAML simple
[OK] Démarrage rapide
[X] Pas de scaling automatique
[X] Pas de self-healing
[X] Limité à une seule machine (sauf Swarm)

KUBERNETES :
[OK] Production-ready pour grandes applications
[OK] Scaling automatique
[OK] Self-healing (redémarre containers automatiquement)
[OK] Multi-machines (cluster)
[OK] Load balancing avancé
[X] Complexe à apprendre
[X] Overhead important
[X] Trop puissant pour petits projets

QUAND UTILISER QUOI :
- Développement local : Docker Compose [OK]
- Petite app en production (< 10 containers) : Docker Compose [OK]
- App avec < 1000 utilisateurs : Docker Compose [OK]
- App avec > 10 services : Kubernetes
- App avec > 10000 utilisateurs : Kubernetes
- Besoin de scaling automatique : Kubernetes


╔════════════════════════════════════════════════════════════════════╗
║ Docker Compose vs Docker Swarm                                     ║
╚════════════════════════════════════════════════════════════════════╝

DOCKER SWARM :
[OK] Compatible avec docker-compose.yml
[OK] Simple (plus que Kubernetes)
[OK] Multi-machines
[OK] Scaling et load balancing
[X] Moins de features que Kubernetes
[X] Moins populaire (communauté plus petite)

DOCKER COMPOSE :
[OK] Plus simple
[OK] Parfait pour dev
[X] Une seule machine

QUAND UTILISER SWARM :
- Besoin de multi-machines mais Kubernetes trop complexe
- Transition simple depuis Compose
- Production avec besoins modérés


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[GUIDE] GLOSSAIRE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

SERVICE
  Définition d'un container dans docker-compose.yml
  Exemple : web, db, redis sont des services

STACK
  Ensemble de services qui fonctionnent ensemble
  Exemple : web + db + redis = stack complète

VOLUME
  Espace de stockage persistant
  Les données survivent à l'arrêt des containers

BIND MOUNT
  Lier un dossier de votre PC au container
  Changements synchronisés en temps réel

NETWORK
  Réseau virtuel permettant aux containers de communiquer

HEALTHCHECK
  Vérification automatique qu'un service fonctionne

DEPENDS_ON
  Définit l'ordre de démarrage des services

PROFILE
  Groupe de services optionnels

SCALING
  Lancer plusieurs instances du même service

ORCHESTRATION
  Gestion automatique de plusieurs containers


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[COURS] POUR ALLER PLUS LOIN
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1⃣  PRATIQUE
   - Créez un projet complet avec Docker Compose
   - Ajoutez monitoring (Prometheus + Grafana)
   - Configurez CI/CD
   - Déployez en production

2⃣  DOCKER SWARM
   - Apprenez Docker Swarm pour multi-machines
   - Déployez votre stack sur plusieurs serveurs

3⃣  KUBERNETES
   - Transition vers Kubernetes pour projets plus grands
   - Utilisez Kompose pour convertir compose -> k8s

4⃣  SÉCURITÉ
   - Approfondissez la sécurité Docker
   - Utilisez Docker secrets
   - Scannez les images pour vulnérabilités

5⃣  PERFORMANCE
   - Optimisez les images (multi-stage builds)
   - Configurez le cache correctement
   - Utilisez des images légères (alpine)


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[OK] RÉSUMÉ FINAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

DOCKER COMPOSE EN 10 POINTS :

1. Fichier YAML pour définir plusieurs containers
2. Une commande pour tout démarrer : docker compose up
3. Networking automatique entre services
4. Volumes pour données persistantes
5. Variables d'environnement avec .env
6. Plusieurs fichiers pour dev/prod
7. Healthchecks pour attendre que services soient prêts
8. Scaling facile avec --scale
9. Logs centralisés avec docker compose logs
10. Parfait pour dev local et petites apps en prod

COMMANDES ESSENTIELLES À RETENIR :

docker compose up -d              # Démarrer
docker compose down               # Arrêter et supprimer
docker compose logs -f            # Voir logs
docker compose exec web bash      # Ouvrir shell
docker compose restart            # Redémarrer
docker compose ps                 # Voir status

STRUCTURE MINIMALE :

version: '3.8'
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres:15-alpine
volumes:
  db_data:


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[BRAVO] FÉLICITATIONS !
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Vous maîtrisez maintenant Docker Compose ! [RAPIDE]

Vous savez :
[OK] Créer un fichier docker-compose.yml
[OK] Gérer plusieurs services (web, db, cache, etc.)
[OK] Configurer networks et volumes
[OK] Utiliser des variables d'environnement
[OK] Séparer dev et prod
[OK] Déboguer les problèmes
[OK] Déployer en production
[OK] Monitorer vos applications
[OK] Optimiser les performances
[OK] Sécuriser votre stack

PROCHAINES ÉTAPES :
1. Créez votre premier projet avec Compose
2. Ajoutez du monitoring
3. Déployez en production
4. Explorez Docker Swarm ou Kubernetes

Bon courage dans vos projets Docker ! [DOCKER]


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[TEL] AIDE ET SUPPORT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Documentation officielle :
https://docs.docker.com/compose/

Référence complète :
https://docs.docker.com/compose/compose-file/

Forum communautaire :
https://forums.docker.com/

Stack Overflow :
https://stackoverflow.com/questions/tagged/docker-compose

Exemples de projets :
https://github.com/docker/awesome-compose

GitHub Docker Compose :
https://github.com/docker/compose



[OK] DOCKER AVANCÉ - EXPERT


# Fichier: docker_cheats/cheatsheets/docker_advanced.txt
# Cheatsheet Docker Avancé - Guide Complet pour Débutants


[OK] DOCKER BUILDKIT (MOTEUR DE BUILD MODERNE)


════════════════════════════════════════════════════════════════════════════════
QU'EST-CE QUE BUILDKIT?
════════════════════════════════════════════════════════════════════════════════

BuildKit est le nouveau moteur de build Docker introduit avec Docker 18.09.
C'est une refonte complète du système de construction d'images qui remplace
l'ancien builder par défaut.

AVANTAGES DE BUILDKIT:
  • Builds parallélisés: Les étapes indépendantes s'exécutent en parallèle
  • Cache amélioré: Détection plus intelligente de ce qui doit être reconstruit
  • Nouvelles fonctionnalités: Secrets, SSH forwarding, cache mounts
  • Sécurité renforcée: Les secrets ne sont jamais sauvegardés dans l'image
  • Meilleure performance: Jusqu'à 2-3x plus rapide que l'ancien builder
  • Output amélioré: Affichage plus clair de la progression

QUAND UTILISER BUILDKIT:
  • Pour tous les nouveaux projets (devrait être le défaut)
  • Quand vous avez besoin de secrets pendant le build
  • Pour optimiser les temps de build en production
  • Quand vous clonez des repos privés pendant le build


════════════════════════════════════════════════════════════════════════════════
ACTIVATION DE BUILDKIT
════════════════════════════════════════════════════════════════════════════════

# Activer BuildKit pour une seule commande
DOCKER_BUILDKIT=1 docker build -t myapp .

# Activer BuildKit globalement (Linux/Mac - ajouter à ~/.bashrc ou ~/.zshrc)
export DOCKER_BUILDKIT=1

# Activer BuildKit globalement (Windows CMD)
set DOCKER_BUILDKIT=1

# Activer BuildKit globalement (Windows PowerShell)
$env:DOCKER_BUILDKIT=1

# Activer BuildKit de façon permanente (/etc/docker/daemon.json ou %programdata%\docker\config\daemon.json)
{
  "features": {
    "buildkit": true
  }
}
# Puis redémarrer Docker
sudo systemctl restart docker     # Linux
# Ou redémarrer Docker Desktop    # Windows/Mac

# Build avec affichage détaillé (utile pour debug)
docker build --progress=plain -t myapp .

# Build avec affichage auto (par défaut)
docker build --progress=auto -t myapp .

# === DOCKERFILE AVEC BUILDKIT ===

# Spécifier la syntaxe BuildKit (TOUJOURS mettre en première ligne)
# syntax=docker/dockerfile:1.4

FROM python:3.11-slim

WORKDIR /app

# === CACHE MOUNT ===
# Permet de garder un cache entre les builds
# Très utile pour pip, npm, apt, etc.
# Le cache persiste même si on supprime l'image!

# Exemple avec pip (Python)
# Le dossier /root/.cache/pip est réutilisé entre builds
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

# Exemple avec apt (Ubuntu/Debian)
RUN --mount=type=cache,target=/var/cache/apt \
    --mount=type=cache,target=/var/lib/apt \
    apt-get update && apt-get install -y curl

# Exemple avec npm (Node.js)
RUN --mount=type=cache,target=/root/.npm \
    npm install

# === SECRET MOUNT ===
# Permet d'utiliser des secrets (tokens, mots de passe) pendant le build
# Les secrets ne sont JAMAIS sauvegardés dans l'image finale!
# Sécurisé: le secret n'apparaît pas dans l'historique des layers

# Dans le Dockerfile
RUN --mount=type=secret,id=github_token \
    git clone https://$(cat /run/secrets/github_token)@github.com/user/repo.git

# Les secrets sont montés dans /run/secrets/<id>
RUN --mount=type=secret,id=api_key \
    curl -H "Authorization: $(cat /run/secrets/api_key)" https://api.example.com

# Build avec le secret
docker build --secret id=github_token,src=token.txt -t myapp .
docker build --secret id=api_key,src=~/.secrets/api.key -t myapp .

# Ou depuis variable d'environnement
docker build --secret id=github_token,env=GITHUB_TOKEN -t myapp .

# === SSH MOUNT ===
# Permet d'utiliser vos clés SSH pendant le build
# Utile pour cloner des repos privés

# Dans le Dockerfile
RUN --mount=type=ssh \
    git clone git@github.com:user/private-repo.git

# Build (utilise automatiquement votre agent SSH)
docker build --ssh default -t myapp .

# Ou spécifier une clé spécifique
docker build --ssh default=$HOME/.ssh/id_rsa -t myapp .

# === EXEMPLE COMPLET DOCKERFILE AVEC BUILDKIT ===

# syntax=docker/dockerfile:1.4

FROM python:3.11-slim AS base

WORKDIR /app

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

# SSH pour cloner repo privé
RUN --mount=type=ssh \
    git clone git@github.com:mycompany/private-lib.git

# Secret pour API
RUN --mount=type=secret,id=api_token \
    curl -H "Authorization: Bearer $(cat /run/secrets/api_token)" \
    https://api.example.com/download > data.json

COPY . .

CMD ["python", "app.py"]


[OK] DOCKER BUILDX (BUILDS MULTI-PLATEFORMES)

# === QU'EST-CE QUE BUILDX? ===
# Buildx étend les capacités de build de Docker
# Fonctionnalités principales:
# - Build pour plusieurs architectures (AMD64, ARM64, ARM/v7, etc.)
# - Builds distribués
# - Export vers plusieurs formats
# - Utilise BuildKit par défaut

# Vérifier si buildx est installé
docker buildx version

# === CRÉER UN BUILDER ===

# Créer un nouveau builder (nécessaire pour multi-platform)
docker buildx create --name mybuilder --use

# Options utiles:
# --use : utilise le builder immédiatement
# --driver docker-container : utilise un conteneur (recommandé)
# --platform linux/amd64,linux/arm64 : plateformes supportées

# Exemple complet
docker buildx create \
  --name multiarch \
  --driver docker-container \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  --use

# Initialiser le builder (télécharge images nécessaires)
docker buildx inspect --bootstrap

# Lister tous les builders
docker buildx ls
# Output exemple:
# NAME/NODE    DRIVER/ENDPOINT      STATUS   PLATFORMS
# mybuilder*   docker-container                        
#   mybuilder0 unix:///var/run/...  running  linux/amd64, linux/arm64
# default      docker                                   
#   default    default              running  linux/amd64

# Changer de builder
docker buildx use mybuilder
docker buildx use default

# Supprimer un builder
docker buildx rm mybuilder

# === BUILDS MULTI-PLATEFORMES ===

# Build pour AMD64 et ARM64 (serveurs + Raspberry Pi)
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

# Build pour AMD64, ARM64 et ARM/v7 (plus de compatibilité)
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t myuser/myapp:latest \
  --push \
  .

# Options importantes:
# --platform : plateformes cibles (séparées par virgules)
# --push : pousse directement vers registry (OBLIGATOIRE pour multi-platform)
# --load : charge dans Docker local (UN SEUL platform à la fois)
# -t : tag de l'image

# Build pour une seule plateforme et charger localement
docker buildx build --platform linux/amd64 -t myapp:latest --load .

# Build et exporter vers fichier tar
docker buildx build --platform linux/amd64 -o type=tar,dest=myapp.tar .

# Build et exporter vers OCI format
docker buildx build --platform linux/amd64 -o type=oci,dest=myapp-oci.tar .

# === PLATEFORMES COMMUNES ===

# linux/amd64        - x86_64 (Intel/AMD 64-bit) - Serveurs, PCs
# linux/arm64        - ARM 64-bit - Raspberry Pi 4, Apple Silicon, serveurs ARM
# linux/arm/v7       - ARM 32-bit - Raspberry Pi 2/3
# linux/arm/v6       - ARM ancien - Raspberry Pi 1
# linux/386          - x86 32-bit - Vieux PCs
# linux/ppc64le      - PowerPC 64-bit little-endian
# linux/s390x        - IBM mainframes
# windows/amd64      - Windows 64-bit

# Lister plateformes supportées par le builder
docker buildx inspect mybuilder

# === EXEMPLE MULTI-STAGE MULTI-PLATFORM ===

# syntax=docker/dockerfile:1.4

FROM --platform=$BUILDPLATFORM golang:1.21 AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH

WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /app

FROM alpine:latest
COPY --from=build /app /app
CMD ["/app"]

# Build pour toutes les plateformes
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t myuser/myapp:latest \
  --push \
  .

# === CACHE POUR BUILDX ===

# Utiliser cache registry (partage cache entre machines)
docker buildx build \
  --cache-from type=registry,ref=myuser/myapp:cache \
  --cache-to type=registry,ref=myuser/myapp:cache,mode=max \
  -t myuser/myapp:latest \
  --push \
  .

# Utiliser cache local
docker buildx build \
  --cache-from type=local,src=/tmp/cache \
  --cache-to type=local,dest=/tmp/cache,mode=max \
  -t myapp:latest \
  .


[OK] DOCKER REGISTRY PRIVÉ

# === QU'EST-CE QU'UN REGISTRY? ===
# Un registry est un serveur qui stocke des images Docker
# Docker Hub est le registry public par défaut
# Un registry privé permet de:
# - Garder vos images privées
# - Avoir un contrôle total
# - Éviter les limites de rate de Docker Hub
# - Héberger en interne (pas besoin d'internet)

# === REGISTRY LOCAL BASIQUE ===

# Démarrer un registry local (sans authentification)
docker run -d -p 5000:5000 --name registry registry:2

# Options:
# -d : détaché (background)
# -p 5000:5000 : port 5000 exposé
# --name registry : nom du conteneur
# registry:2 : image officielle

# Tagger une image pour le registry local
docker tag myapp:latest localhost:5000/myapp:latest
# Format: <registry-host>:<port>/<nom>:<tag>

# Pousser l'image vers le registry
docker push localhost:5000/myapp:latest

# Récupérer l'image depuis le registry
docker pull localhost:5000/myapp:latest

# Lister les images dans le registry
curl http://localhost:5000/v2/_catalog
# Output: {"repositories":["myapp"]}

# Lister les tags d'une image
curl http://localhost:5000/v2/myapp/tags/list
# Output: {"name":"myapp","tags":["latest","v1.0"]}

# === REGISTRY AVEC VOLUME (PERSISTANCE) ===

# Créer un volume pour stocker les images
docker volume create registry-data

# Démarrer registry avec volume
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v registry-data:/var/lib/registry \
  registry:2

# Ou avec dossier local
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v /opt/registry:/var/lib/registry \
  registry:2

# === REGISTRY AVEC AUTHENTIFICATION ===

# 1. Créer dossier pour auth
mkdir -p /opt/docker-registry/auth

# 2. Installer htpasswd (si pas déjà installé)
# Ubuntu/Debian:
sudo apt-get install apache2-utils
# Mac:
brew install httpd
# Windows: télécharger Apache ou utiliser Docker

# 3. Créer fichier de passwords
htpasswd -Bc /opt/docker-registry/auth/htpasswd admin
# -B : bcrypt (sécurisé)
# -c : créer fichier
# Entrer le mot de passe quand demandé

# Ajouter d'autres utilisateurs (sans -c pour ne pas écraser)
htpasswd -B /opt/docker-registry/auth/htpasswd user2

# 4. Démarrer registry avec auth
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v /opt/docker-registry/auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  -v registry-data:/var/lib/registry \
  registry:2

# 5. Se connecter au registry
docker login localhost:5000
# Username: admin
# Password: (entrer le mot de passe)

# Se déconnecter
docker logout localhost:5000

# === REGISTRY AVEC HTTPS (PRODUCTION) ===

# 1. Créer certificat SSL (self-signed pour test)
mkdir -p /opt/docker-registry/certs
openssl req -newkey rsa:4096 -nodes -sha256 \
  -keyout /opt/docker-registry/certs/domain.key \
  -x509 -days 365 \
  -out /opt/docker-registry/certs/domain.crt

# 2. Démarrer registry avec HTTPS
docker run -d \
  -p 443:5000 \
  --name registry \
  -v /opt/docker-registry/certs:/certs \
  -v /opt/docker-registry/auth:/auth \
  -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" \
  -e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  -v registry-data:/var/lib/registry \
  registry:2

# 3. Configurer Docker pour accepter le certificat self-signed
# Linux: copier le certificat
sudo mkdir -p /etc/docker/certs.d/myregistry.com:443
sudo cp /opt/docker-registry/certs/domain.crt \
  /etc/docker/certs.d/myregistry.com:443/ca.crt

# Mac: Ajouter dans Keychain
# Windows: Importer dans certificats de confiance

# === CONFIGURATION AVANCÉE (config.yml) ===

# Créer fichier de config
cat > /opt/docker-registry/config.yml <<EOF
version: 0.1
log:
  level: info
storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
auth:
  htpasswd:
    realm: Registry Realm
    path: /auth/htpasswd
EOF

# Démarrer avec config custom
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v /opt/docker-registry/config.yml:/etc/docker/registry/config.yml \
  -v /opt/docker-registry/auth:/auth \
  -v registry-data:/var/lib/registry \
  registry:2

# === SUPPRIMER DES IMAGES DU REGISTRY ===

# 1. Activer la suppression dans config.yml
# storage:
#   delete:
#     enabled: true

# 2. Récupérer le digest de l'image
curl -I -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
  http://localhost:5000/v2/myapp/manifests/latest

# 3. Supprimer l'image
curl -X DELETE http://localhost:5000/v2/myapp/manifests/<digest>

# 4. Nettoyer le stockage (garbage collection)
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml


[OK] DOCKER SWARM (ORCHESTRATION NATIVE)

# === QU'EST-CE QUE SWARM? ===
# Docker Swarm est l'orchestrateur natif de Docker
# Permet de:
# - Gérer un cluster de machines Docker
# - Déployer des services répliqués
# - Load balancing automatique
# - Haute disponibilité
# - Rolling updates
# Alternative plus simple que Kubernetes pour petites/moyennes infrastructures

# === CONCEPTS CLÉS ===
# Node: Une machine dans le cluster
# Manager: Node qui gère le cluster (peut être aussi worker)
# Worker: Node qui exécute les tâches
# Service: Définition d'une application (image, replicas, etc.)
# Task: Instance d'un conteneur
# Stack: Groupe de services définis dans un fichier Compose

# === INITIALISER SWARM ===

# Initialiser sur le premier node (devient manager)
docker swarm init
# Output: token pour joindre d'autres workers

# Si plusieurs interfaces réseau, spécifier l'IP
docker swarm init --advertise-addr 192.168.1.100

# Récupérer le token pour joindre comme worker
docker swarm join-token worker
# Output: commande complète à exécuter sur les workers

# Récupérer le token pour joindre comme manager
docker swarm join-token manager

# Régénérer un token (si compromis)
docker swarm join-token --rotate worker

# === AJOUTER DES NODES ===

# Sur une autre machine, joindre comme worker
docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377

# Lister les nodes (depuis un manager)
docker node ls
# ID          HOSTNAME    STATUS  AVAILABILITY  MANAGER STATUS
# abc123 *    manager1    Ready   Active        Leader
# def456      worker1     Ready   Active        
# ghi789      worker2     Ready   Active

# Promouvoir un worker en manager (haute disponibilité)
docker node promote worker1

# Rétrograder un manager en worker
docker node demote manager2

# Inspecter un node
docker node inspect worker1
docker node inspect --pretty worker1    # Format lisible

# Mettre un node en drain (ne reçoit plus de nouvelles tâches)
docker node update --availability drain worker1
# Utile pour maintenance

# Remettre un node actif
docker node update --availability active worker1

# === DÉPLOYER DES SERVICES ===

# Créer un service simple
docker service create --name web nginx

# Créer avec replicas (plusieurs instances)
docker service create --name web --replicas 3 nginx

# Créer avec port publié
docker service create --name web --replicas 3 -p 8080:80 nginx

# Créer avec nom de réseau
docker service create --name web --network mynetwork nginx

# Créer avec variables d'environnement
docker service create --name api \
  -e DB_HOST=postgres \
  -e DB_PORT=5432 \
  myapi:latest

# Créer avec contraintes de placement
docker service create --name web \
  --constraint 'node.role==worker' \
  nginx

# Créer avec limites de ressources
docker service create --name web \
  --limit-cpu 0.5 \
  --limit-memory 512M \
  --reserve-cpu 0.25 \
  --reserve-memory 256M \
  nginx

# === GÉRER LES SERVICES ===

# Lister les services
docker service ls
# ID        NAME  MODE        REPLICAS  IMAGE
# abc123    web   replicated  3/3       nginx:latest

# Inspecter un service
docker service inspect web
docker service inspect --pretty web

# Voir les tâches (conteneurs) d'un service
docker service ps web
# ID        NAME    IMAGE         NODE      DESIRED STATE  CURRENT STATE
# xyz1      web.1   nginx:latest  manager1  Running        Running 2 min
# xyz2      web.2   nginx:latest  worker1   Running        Running 2 min
# xyz3      web.3   nginx:latest  worker2   Running        Running 2 min

# Voir les logs d'un service
docker service logs web
docker service logs -f web              # Follow
docker service logs --tail 100 web      # Dernières 100 lignes

# === METTRE À L'ÉCHELLE ===

# Augmenter/diminuer le nombre de replicas
docker service scale web=5

# Scaler plusieurs services
docker service scale web=5 api=3 db=1

# === METTRE À JOUR UN SERVICE ===

# Update de l'image
docker service update --image nginx:alpine web

# Update avec rolling update configuré
docker service update \
  --image nginx:alpine \
  --update-delay 10s \
  --update-parallelism 2 \
  web

# Options update importantes:
# --update-delay : délai entre chaque batch
# --update-parallelism : nombre de tâches mises à jour en parallèle
# --update-failure-action : pause|continue|rollback
# --update-monitor : temps de surveillance après update

# Rollback d'un service
docker service rollback web

# Update des contraintes
docker service update --constraint-add 'node.labels.type==frontend' web

# Update des ressources
docker service update --limit-memory 1G web

# === SUPPRIMER UN SERVICE ===

docker service rm web

# Supprimer plusieurs services
docker service rm web api db

# === DOCKER STACK (COMPOSE POUR SWARM) ===

# Stack = groupe de services définis dans un fichier Compose
# Similaire à docker-compose mais pour Swarm

# Exemple docker-compose.yml pour Stack
version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    networks:
      - webnet

  api:
    image: myapi:latest
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.role==worker
    environment:
      - DB_HOST=db
    networks:
      - webnet
      - dbnet

  db:
    image: postgres:15
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type==database
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - dbnet

networks:
  webnet:
  dbnet:

volumes:
  db-data:

# Déployer une stack
docker stack deploy -c docker-compose.yml mystack

# Lister les stacks
docker stack ls
# NAME      SERVICES
# mystack   3

# Lister les services d'une stack
docker stack services mystack

# Voir les tâches d'une stack
docker stack ps mystack

# Supprimer une stack (supprime tous les services)
docker stack rm mystack

# === RÉSEAUX SWARM ===

# Créer réseau overlay (span plusieurs nodes)
docker network create --driver overlay mynetwork

# Créer réseau overlay avec encryption
docker network create --driver overlay --opt encrypted mynetwork

# Lister réseaux
docker network ls

# === SECRETS SWARM ===

# Voir section dédiée plus bas

# === QUITTER SWARM ===

# Quitter comme worker
docker swarm leave

# Quitter comme manager (force nécessaire)
docker swarm leave --force


[OK] DOCKER CONTEXT (CONNEXIONS MULTIPLES)

# === QU'EST-CE QU'UN CONTEXT? ===
# Un context stocke les informations de connexion à un daemon Docker
# Permet de switcher facilement entre:
# - Docker local
# - Docker sur une VM
# - Docker sur un serveur distant
# - Docker sur un cluster Swarm

# Lister les contexts
docker context ls
# NAME      DESCRIPTION           DOCKER ENDPOINT
# default*  Current UNIX socket   unix:///var/run/docker.sock

# Créer un context pour machine distante
docker context create myserver \
  --docker "host=tcp://192.168.1.100:2376,ca=/path/to/ca.pem,cert=/path/to/cert.pem,key=/path/to/key.pem"

# Créer context SSH
docker context create myserver --docker "host=ssh://user@192.168.1.100"

# Créer context pour Swarm
docker context create swarm-prod --docker "host=tcp://swarm-manager:2376"

# Utiliser un context
docker context use myserver
# Toutes les commandes Docker vont maintenant sur myserver

# Retour au context par défaut
docker context use default

# Inspecter un context
docker context inspect myserver

# Mettre à jour un context
docker context update myserver --docker "host=tcp://192.168.1.101:2376"

# Exporter un context
docker context export myserver
# Crée fichier myserver.dockercontext

# Importer un context
docker context import myserver myserver.dockercontext

# Supprimer un context
docker context rm myserver

# === EXEMPLE D'UTILISATION ===

# Setup
docker context create prod --docker "host=ssh://user@prod-server"
docker context create staging --docker "host=ssh://user@staging-server"
docker context create dev --docker "host=unix:///var/run/docker.sock"

# Déployer sur staging
docker context use staging
docker-compose up -d

# Vérifier sur prod
docker context use prod
docker ps

# Retour local
docker context use dev


[OK] DOCKER SECRETS (SWARM)

# === QU'EST-CE QUE LES SECRETS? ===
# Les secrets sont des données sensibles (passwords, tokens, certificates)
# Caractéristiques:
# - Encrypted at rest (chiffré sur disque)
# - Encrypted in transit (chiffré pendant transfert)
# - Accessible uniquement par les services autorisés
# - Monté dans /run/secrets/ (tmpfs, jamais écrit sur disque dans conteneur)
# - SWARM MODE REQUIS

# === CRÉER DES SECRETS ===

# Depuis stdin
echo "mypassword123" | docker secret create db_password -

# Depuis fichier
docker secret create db_password ./password.txt

# Depuis fichier avec labels
docker secret create db_password ./password.txt \
  --label env=prod \
  --label app=myapp

# Créer avec pipe
cat password.txt | docker secret create db_password -

# Générer et créer secret (password aléatoire)
openssl rand -base64 32 | docker secret create db_password -

# === LISTER ET INSPECTER SECRETS ===

# Lister tous les secrets
docker secret ls
# ID          NAME           CREATED         UPDATED
# abc123      db_password    2 minutes ago   2 minutes ago

# Inspecter un secret (ne montre PAS le contenu!)
docker secret inspect db_password
docker secret inspect --pretty db_password

# Filtrer secrets
docker secret ls --filter label=env=prod

# === UTILISER SECRETS DANS SERVICES ===

# Attacher secret à un service
docker service create \
  --name myapp \
  --secret db_password \
  myimage:latest

# Attacher avec nom custom
docker service create \
  --name myapp \
  --secret source=db_password,target=password.txt \
  myimage:latest

# Attacher avec permissions custom
docker service create \
  --name myapp \
  --secret source=db_password,target=password.txt,mode=0400 \
  myimage:latest

# Dans le conteneur, lire le secret
cat /run/secrets/db_password
# ou
cat /run/secrets/password.txt    # Si target spécifié

# === EXEMPLE APPLICATION AVEC SECRET ===

# 1. Créer secret
echo "super_secret_password" | docker secret create db_pass -

# 2. Créer service qui utilise le secret
docker service create \
  --name webapp \
  --secret db_pass \
  -e DB_PASSWORD_FILE=/run/secrets/db_pass \
  myapp:latest

# 3. Dans l'application (Python)
import os

password_file = os.environ.get('DB_PASSWORD_FILE')
with open(password_file, 'r') as f:
    db_password = f.read().strip()

# Ou directement
with open('/run/secrets/db_pass', 'r') as f:
    db_password = f.read().strip()

# === METTRE À JOUR UN SECRET ===

# Les secrets sont immutables!
# Pour "mettre à jour":

# 1. Créer nouveau secret
echo "newpassword456" | docker secret create db_password_v2 -

# 2. Update service pour utiliser nouveau secret
docker service update \
  --secret-rm db_password \
  --secret-add db_password_v2 \
  myapp

# 3. (Optionnel) Supprimer ancien secret
docker secret rm db_password

# === SUPPRIMER UN SECRET ===

# Supprimer (ne marche que si aucun service ne l'utilise)
docker secret rm db_password

# Forcer suppression (arrête d'abord les services)
docker service rm myapp
docker secret rm db_password

# === SECRETS DANS STACK ===

# docker-compose.yml
version: '3.8'

services:
  db:
    image: postgres:15
    secrets:
      - db_password
      - db_user
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
      POSTGRES_USER_FILE: /run/secrets/db_user

  app:
    image: myapp:latest
    secrets:
      - source: db_password
        target: database_password
        mode: 0400

secrets:
  db_password:
    file: ./secrets/db_password.txt
  db_user:
    external: true    # Secret déjà créé

# Déployer stack
docker stack deploy -c docker-compose.yml mystack


[OK] DOCKER CONFIGS (SWARM)

# === QU'EST-CE QUE LES CONFIGS? ===
# Similaire aux secrets mais pour données non-sensibles
# Exemples: fichiers de config, certificats publics, scripts
# Différences avec secrets:
# - Pas chiffré (mais immutable)
# - Visible avec docker config inspect
# - Même usage que secrets autrement
# - SWARM MODE REQUIS

# === CRÉER DES CONFIGS ===

# Depuis fichier
docker config create nginx_config ./nginx.conf
docker config create app_config ./config.json

# Depuis stdin
cat config.yml | docker config create app_config -

# Avec labels
docker config create nginx_config ./nginx.conf \
  --label app=web \
  --label version=v1

# === LISTER ET INSPECTER ===

# Lister configs
docker config ls

# Inspecter (montre le contenu en base64)
docker config inspect nginx_config
docker config inspect --pretty nginx_config

# === UTILISER CONFIGS ===

# Attacher config à service
docker service create \
  --name web \
  --config nginx_config \
  nginx

# Dans le conteneur:
cat /nginx_config

# Avec nom custom
docker service create \
  --name web \
  --config source=nginx_config,target=/etc/nginx/nginx.conf \
  nginx

# Avec permissions
docker service create \
  --name web \
  --config source=nginx_config,target=/etc/nginx/nginx.conf,mode=0440 \
  nginx

# === METTRE À JOUR ===

# Créer nouvelle version
docker config create nginx_config_v2 ./nginx.conf

# Update service
docker service update \
  --config-rm nginx_config \
  --config-add source=nginx_config_v2,target=/etc/nginx/nginx.conf \
  web

# === SUPPRIMER ===

docker config rm nginx_config

# === CONFIGS DANS STACK ===

version: '3.8'

services:
  web:
    image: nginx
    configs:
      - source: nginx_config
        target: /etc/nginx/nginx.conf

configs:
  nginx_config:
    file: ./nginx.conf


[OK] OPTIMISATION D'IMAGES

# === POURQUOI OPTIMISER? ===
# - Builds plus rapides
# - Déploiements plus rapides
# - Moins d'espace disque
# - Moins de bande passante
# - Moins de surface d'attaque (sécurité)

# === UTILISER IMAGES ALPINE ===

# Images Alpine sont minimalistes (~5-10 MB vs ~100-900 MB)

# [X] Image standard (large)
FROM python:3.11            # ~900 MB

# [OK] Image Alpine (petite)
FROM python:3.11-alpine     # ~50 MB

# [X] Image standard
FROM node:18                # ~900 MB

# [OK] Image Alpine
FROM node:18-alpine         # ~180 MB

# Autres options slim
FROM python:3.11-slim       # ~150 MB (compromis)
FROM node:18-slim           # ~250 MB

# === MULTI-STAGE BUILDS ===

# Séparer build et runtime pour réduire taille finale

# [X] Mauvais (une seule étape)
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install             # Inclut devDependencies!
COPY . .
RUN npm run build
CMD ["node", "dist/app.js"]
# Résultat: ~1.2 GB

# [OK] Bon (multi-stage)
# Stage 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2: Runtime (seulement ce qui est nécessaire)
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
CMD ["node", "dist/app.js"]
# Résultat: ~200 MB

# Exemple Python
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]

# Exemple Go (extrême optimisation)
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o app

FROM scratch              # Image vide (0 MB!)
COPY --from=builder /app/app /app
CMD ["/app"]
# Résultat: ~10 MB pour une app Go

# === LAYER CACHING EFFICACE ===

# Docker cache chaque instruction (layer)
# Si une layer change, toutes les suivantes sont refaites

# [X] Mauvais ordre (cache invalidé souvent)
FROM python:3.11-slim
WORKDIR /app
COPY . .                                    # Change souvent!
RUN pip install -r requirements.txt         # Recalculé à chaque fois

# [OK] Bon ordre (cache optimisé)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .                     # Change rarement
RUN pip install -r requirements.txt         # Cached si requirements.txt identique
COPY . .                                    # Change souvent, mais après install

# Même principe pour Node.js
# [X] Mauvais
COPY . .
RUN npm install

# [OK] Bon
COPY package*.json ./
RUN npm install
COPY . .

# === COMBINER COMMANDES ===

# Chaque RUN crée une nouvelle layer

# [X] Mauvais (3 layers, plus gros)
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim

# [OK] Bon (1 layer, plus petit)
FROM ubuntu:22.04
RUN apt-get update && \
    apt-get install -y curl vim && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Pourquoi nettoyer dans la même layer?
# Si on nettoie dans une layer séparée, les fichiers sont toujours dans l'image!

# [X] Mauvais (cache reste dans layer précédente)
RUN apt-get update && apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*              # Inutile! Fichiers déjà dans layer précédente

# [OK] Bon (tout dans une layer)
RUN apt-get update && \
    apt-get install -y curl && \
    rm -rf /var/lib/apt/lists/*

# === UTILISER .dockerignore ===

# Similaire à .gitignore
# Évite de copier des fichiers inutiles dans l'image

# .dockerignore
# Dépendances
node_modules/
venv/
__pycache__/
*.pyc

# Git
.git/
.gitignore
.github/

# IDE
.vscode/
.idea/
*.swp

# Logs
*.log
logs/

# Tests
tests/
*.test.js

# Documentation
README.md
docs/

# CI/CD
.gitlab-ci.yml
.travis.yml
Jenkinsfile

# Docker
Dockerfile
docker-compose.yml
.dockerignore

# Environnement
.env
.env.*
!.env.example

# Build artifacts
dist/
build/
*.tar.gz

# OS
.DS_Store
Thumbs.db

# === UTILISER --no-cache-dir POUR PIP ===

# Évite de garder le cache pip dans l'image
RUN pip install --no-cache-dir -r requirements.txt

# Ou avec cache mount (BuildKit)
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

# === ORDRE DES INSTRUCTIONS ===

# Règle: du moins changeant au plus changeant

FROM python:3.11-slim

# 1. Installation système (change rarement)
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 2. Dépendances Python (change occasionnellement)
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 3. Code application (change souvent)
COPY . .

# 4. Configuration runtime
ENV FLASK_APP=app.py
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0"]

# === UTILISER USER NON-ROOT ===

FROM python:3.11-slim

# Créer utilisateur
RUN useradd -m -u 1000 appuser

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appuser . .

# Passer à l'utilisateur non-root
USER appuser

CMD ["python", "app.py"]

# === EXEMPLE COMPLET OPTIMISÉ ===

# syntax=docker/dockerfile:1.4

# Stage 1: Build
FROM node:18-alpine AS builder

WORKDIR /app

# Copier seulement package.json (cache)
COPY package*.json ./

# Installer avec cache mount
RUN --mount=type=cache,target=/root/.npm \
    npm ci --only=production

# Copier source et build
COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:18-alpine

# Créer user non-root
RUN addgroup -g 1000 appgroup && \
    adduser -D -u 1000 -G appgroup appuser

WORKDIR /app

# Copier artifacts du builder
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
COPY --from=builder --chown=appuser:appuser /app/node_modules ./node_modules
COPY --chown=appuser:appuser package*.json ./

# Passer à user non-root
USER appuser

# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js || exit 1

EXPOSE 3000

CMD ["node", "dist/app.js"]

# Résultat: ~100 MB au lieu de 1+ GB


[OK] SÉCURITÉ

# === SCANNER VULNÉRABILITÉS ===

# Docker scan (nécessite Docker Hub account)
docker scan myapp:latest

# Afficher seulement vulnérabilités critiques/hautes
docker scan --severity high myapp:latest

# Scanner avec Trivy (alternative open-source)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image myapp:latest

# Trivy avec sortie JSON
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image --format json myapp:latest

# Installer Trivy localement
# Ubuntu/Debian
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy

# Scan avec Trivy local
trivy image myapp:latest

# === UTILISATEUR NON-ROOT ===

# [X] Mauvais (root par défaut)
FROM ubuntu:22.04
COPY app /app
CMD ["/app"]
# Le conteneur tourne en tant que root!

# [OK] Bon (utilisateur dédié)
FROM ubuntu:22.04

# Créer user et groupe
RUN groupadd -r appgroup && \
    useradd -r -g appgroup -u 1000 appuser

# Créer dossiers avec bonnes permissions
RUN mkdir -p /app && chown appuser:appgroup /app

WORKDIR /app

# Copier avec ownership
COPY --chown=appuser:appgroup app .

# Switcher vers user non-root
USER appuser

CMD ["./app"]

# Pour Alpine Linux
RUN addgroup -g 1000 appgroup && \
    adduser -D -u 1000 -G appgroup appuser

# === LIMITER CAPABILITIES ===

# Linux capabilities = permissions granulaires
# Par défaut, Docker donne trop de capabilities

# Lister capabilities d'un conteneur
docker run --rm alpine sh -c 'apk add libcap && capsh --print'

# [OK] Drop toutes les capabilities sauf nécessaires
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# Capabilities communes:
# NET_BIND_SERVICE - Binder sur ports < 1024
# CHOWN - Changer ownership de fichiers
# DAC_OVERRIDE - Bypass file read/write/execute permissions
# SETUID/SETGID - Changer UID/GID
# NET_ADMIN - Opérations réseau

# Exemple: serveur web sur port 80 (besoin NET_BIND_SERVICE)
docker run -d \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  -p 80:80 \
  nginx

# === MODE READ-ONLY ===

# Filesystem read-only sauf dossiers spécifiques

# Conteneur totalement read-only
docker run --read-only nginx
# Échouera car nginx a besoin d'écrire /var/cache, /var/run

# [OK] Read-only avec tmpfs pour dossiers temporaires
docker run \
  --read-only \
  --tmpfs /var/cache/nginx \
  --tmpfs /var/run \
  nginx

# Exemple application Python
docker run \
  --read-only \
  --tmpfs /tmp \
  --tmpfs /app/logs \
  myapp:latest

# === SECURITY OPTIONS ===

# no-new-privileges: empêche escalade de privilèges
docker run --security-opt=no-new-privileges nginx

# Toujours recommandé!
docker run -d \
  --read-only \
  --tmpfs /tmp \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --security-opt=no-new-privileges \
  -p 80:80 \
  nginx

# === APPARMOR (LINUX) ===

# AppArmor = Linux security module
# Profil par défaut: docker-default

# Utiliser profil par défaut (déjà appliqué)
docker run --security-opt apparmor=docker-default nginx

# Désactiver AppArmor (DANGEREUX)
docker run --security-opt apparmor=unconfined nginx

# Créer profil custom
# /etc/apparmor.d/docker-nginx
#include <tunables/global>

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>
  
  network inet tcp,
  network inet udp,
  
  /etc/nginx/** r,
  /var/log/nginx/** w,
  /usr/sbin/nginx ix,
  
  deny /proc/** w,
  deny /sys/** w,
}

# Charger profil
sudo apparmor_parser -r /etc/apparmor.d/docker-nginx

# Utiliser profil
docker run --security-opt apparmor=docker-nginx nginx

# === SELINUX (RED HAT/CENTOS/FEDORA) ===

# Vérifier si SELinux est activé
getenforce
# Output: Enforcing, Permissive, ou Disabled

# Label par défaut
docker run --security-opt label=level:s0:c100,c200 nginx

# Désactiver SELinux pour conteneur (DANGEREUX)
docker run --security-opt label=disable nginx

# === SECCOMP (FILTRAGE SYSCALLS) ===

# Seccomp = filtre les syscalls que le conteneur peut faire
# Profil par défaut bloque ~44 syscalls dangereux

# Voir profil par défaut
curl -o default.json https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json

# Utiliser profil par défaut (déjà appliqué)
docker run --security-opt seccomp=default.json nginx

# Désactiver seccomp (DANGEREUX)
docker run --security-opt seccomp=unconfined nginx

# Profil custom (example: bloquer mkdir)
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "name": "mkdir",
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}

docker run --security-opt seccomp=custom.json nginx

# === LIMITER RESSOURCES (SÉCURITÉ) ===

# Éviter qu'un conteneur consomme toutes les ressources

# Limiter CPU
docker run --cpus=1.5 nginx              # 1.5 CPU max
docker run --cpu-shares=512 nginx        # Priorité relative

# Limiter RAM
docker run --memory=512m nginx           # 512 MB max
docker run --memory=512m --memory-swap=1g nginx  # 512 MB RAM + 512 MB swap

# Limiter I/O disque
docker run --device-read-bps=/dev/sda:1mb nginx
docker run --device-write-bps=/dev/sda:1mb nginx

# Limiter PIDs (nombre de processus)
docker run --pids-limit=100 nginx

# === RÉSEAU SÉCURISÉ ===

# Désactiver réseau
docker run --network none alpine

# Réseau interne (pas d'accès internet)
docker network create --internal internal-net
docker run --network internal-net myapp

# === SECRETS ===

# [X] Mauvais: secrets dans variables d'environnement
docker run -e API_KEY=secret123 myapp
# Visible avec docker inspect!

# [OK] Bon: utiliser Docker secrets (Swarm)
echo "secret123" | docker secret create api_key -
docker service create --secret api_key myapp

# [OK] Alternative: utiliser volumes pour secrets
docker run -v /secure/secrets:/secrets:ro myapp

# === IMAGES DE CONFIANCE ===

# Utiliser images officielles
docker pull nginx                  # [OK] Image officielle
docker pull random/nginx           # [X] Peut être malveillant

# Vérifier signatures (Docker Content Trust)
export DOCKER_CONTENT_TRUST=1
docker pull nginx
# Échouera si signature invalide

# === EXEMPLE CONTENEUR SÉCURISÉ COMPLET ===

docker run -d \
  --name secure-app \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --tmpfs /var/run:rw,noexec,nosuid,size=64m \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --security-opt=no-new-privileges \
  --security-opt=seccomp=default.json \
  --pids-limit=100 \
  --memory=512m \
  --cpus=1 \
  --user 1000:1000 \
  -p 8080:8080 \
  myapp:latest


[OK] LOGGING (GESTION DES LOGS)

# === DRIVERS DE LOGS ===

# Docker supporte plusieurs drivers de logs
# Driver par défaut: json-file

# Lister les drivers disponibles
docker info --format '{{.Plugins.Log}}'

# === JSON-FILE (DÉFAUT) ===

# Configuration par conteneur
docker run \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx

# Options:
# max-size: taille max d'un fichier de log (10m, 100m, 1g)
# max-file: nombre max de fichiers à garder
# labels: inclure labels dans logs
# env: inclure variables d'env dans logs

# Exemple complet
docker run -d \
  --name myapp \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=5 \
  --log-opt labels=com.example.app \
  --log-opt env=ENV,VERSION \
  myapp:latest

# === CONFIGURATION GLOBALE ===

# /etc/docker/daemon.json (Linux)
# %programdata%\docker\config\daemon.json (Windows)
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3",
    "labels": "production_status",
    "env": "os,customer"
  }
}

# Redémarrer Docker
sudo systemctl restart docker

# === SYSLOG ===

# Envoyer logs vers syslog local
docker run \
  --log-driver syslog \
  --log-opt syslog-address=unix:///dev/log \
  nginx

# Envoyer vers syslog distant (UDP)
docker run \
  --log-driver syslog \
  --log-opt syslog-address=udp://192.168.1.100:514 \
  --log-opt tag="myapp" \
  nginx

# Envoyer vers syslog distant (TCP avec TLS)
docker run \
  --log-driver syslog \
  --log-opt syslog-address=tcp+tls://192.168.1.100:514 \
  --log-opt syslog-tls-ca-cert=/path/to/ca.pem \
  --log-opt tag="myapp" \
  nginx

# === JOURNALD (SYSTEMD) ===

# Utiliser journald (Linux avec systemd)
docker run \
  --log-driver journald \
  --log-opt tag="myapp" \
  nginx

# Voir logs avec journalctl
journalctl CONTAINER_NAME=myapp
journalctl -f CONTAINER_NAME=myapp        # Follow
journalctl -u docker.service              # Tous les logs Docker

# === GELF (GRAYLOG) ===

# Envoyer vers Graylog
docker run \
  --log-driver gelf \
  --log-opt gelf-address=udp://192.168.1.100:12201 \
  --log-opt tag="myapp" \
  nginx

# === FLUENTD ===

# Envoyer vers Fluentd
docker run \
  --log-driver fluentd \
  --log-opt fluentd-address=192.168.1.100:24224 \
  --log-opt tag="docker.{{.Name}}" \
  nginx

# === SPLUNK ===

# Envoyer vers Splunk
docker run \
  --log-driver splunk \
  --log-opt splunk-token=<token> \
  --log-opt splunk-url=https://splunk:8088 \
  nginx

# === AWSLOGS (CLOUDWATCH) ===

# Envoyer vers AWS CloudWatch
docker run \
  --log-driver awslogs \
  --log-opt awslogs-region=us-east-1 \
  --log-opt awslogs-group=myapp \
  --log-opt awslogs-stream=mystream \
  nginx

# === DÉSACTIVER LOGS ===

# Utile pour conteneurs très verbeux ou tests
docker run --log-driver none nginx

# === VOIR LES LOGS ===

# Logs d'un conteneur
docker logs myapp

# Follow (temps réel)
docker logs -f myapp

# Dernières N lignes
docker logs --tail 100 myapp

# Depuis un timestamp
docker logs --since 2024-01-01T00:00:00 myapp
docker logs --since 1h myapp              # Dernière heure
docker logs --since 30m myapp             # Dernières 30 minutes

# Jusqu'à un timestamp
docker logs --until 2024-01-01T12:00:00 myapp

# Avec timestamps
docker logs --timestamps myapp

# === LOCALISATION DES LOGS ===

# JSON-file driver
# Linux: /var/lib/docker/containers/<container-id>/<container-id>-json.log
# Windows: C:\ProgramData\docker\containers\<container-id>\<container-id>-json.log

# Voir emplacement
docker inspect --format='{{.LogPath}}' myapp

# === ROTATION DES LOGS ===

# Avec json-file (automatique si max-size/max-file configurés)
docker run \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx
# Créera: container.log, container.log.1, container.log.2

# Avec logrotate (manuel)
# /etc/logrotate.d/docker-containers
/var/lib/docker/containers/*/*.log {
    rotate 7
    daily
    compress
    size=10M
    missingok
    delaycompress
    copytruncate
}


[OK] MONITORING (SURVEILLANCE)

# === DOCKER STATS ===

# Stats en temps réel de tous les conteneurs
docker stats

# Stats d'un conteneur spécifique
docker stats myapp

# Stats sans streaming (snapshot)
docker stats --no-stream

# Format personnalisé
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# Format JSON
docker stats --no-stream --format "{{json .}}"

# Colonnes disponibles:
# {{.Container}} - ID conteneur
# {{.Name}} - Nom conteneur
# {{.CPUPerc}} - % CPU
# {{.MemUsage}} - Usage mémoire
# {{.MemPerc}} - % mémoire
# {{.NetIO}} - I/O réseau
# {{.BlockIO}} - I/O disque
# {{.PIDs}} - Nombre de processus

# Exemple: monitoring simple
docker stats --no-stream --format \
  "table {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"

# === DOCKER EVENTS ===

# Voir tous les événements en temps réel
docker events

# Filtrer par type d'événement
docker events --filter 'event=start'
docker events --filter 'event=stop'
docker events --filter 'event=die'
docker events --filter 'event=create'

# Filtrer par conteneur
docker events --filter 'container=myapp'

# Filtrer par image
docker events --filter 'image=nginx'

# Filtrer par label
docker events --filter 'label=com.example.app=myapp'

# Depuis un timestamp
docker events --since '2024-01-01T00:00:00'
docker events --since '30m'

# Format JSON
docker events --format '{{json .}}'

# Exemple: surveiller démarrages/arrêts
docker events --filter 'event=start' --filter 'event=stop' --format \
  '{{.Time}}: {{.Actor.Attributes.name}} {{.Action}}'

# === DOCKER SYSTEM INFO ===

# Informations système complètes
docker system info
docker info

# Format spécifique
docker info --format '{{.OperatingSystem}}'
docker info --format '{{.ServerVersion}}'
docker info --format '{{.NCPU}}'
docker info --format '{{.MemTotal}}'

# === DOCKER SYSTEM DF ===

# Usage disque par Docker
docker system df

# Détail par type
docker system df -v

# Output exemple:
# TYPE           TOTAL    ACTIVE   SIZE      RECLAIMABLE
# Images         10       5        2.5GB     1.2GB (48%)
# Containers     15       8        100MB     50MB (50%)
# Local Volumes  5        3        500MB     200MB (40%)
# Build Cache    -        -        1GB       800MB (80%)

# === INSPECTIONS DÉTAILLÉES ===

# Inspecter conteneur (JSON complet)
docker inspect myapp

# Obtenir info spécifique
docker inspect --format='{{.State.Status}}' myapp
docker inspect --format='{{.NetworkSettings.IPAddress}}' myapp
docker inspect --format='{{.Config.Image}}' myapp
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# Plusieurs conteneurs
docker inspect myapp myapp2 myapp3

# === HEALTHCHECKS ===

# Définir healthcheck dans Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

# Options:
# --interval: fréquence des checks (défaut: 30s)
# --timeout: timeout par check (défaut: 30s)
# --start-period: délai avant premier check (défaut: 0s)
# --retries: nombre d'échecs avant "unhealthy" (défaut: 3)

# Exemples de healthchecks
# HTTP
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

# Base de données
HEALTHCHECK CMD pg_isready -U postgres || exit 1

# Commande custom
HEALTHCHECK CMD python /app/healthcheck.py || exit 1

# Définir au runtime
docker run -d \
  --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-retries=3 \
  --health-start-period=5s \
  nginx

# Voir statut health
docker ps
# STATUS: Up 2 minutes (healthy)

docker inspect --format='{{.State.Health.Status}}' myapp
# Output: healthy, unhealthy, ou starting

# Historique des healthchecks
docker inspect --format='{{json .State.Health}}' myapp | jq

# === PROMETHEUS + cAdvisor ===

# cAdvisor = monitoring conteneurs par Google

# Démarrer cAdvisor
docker run -d \
  --name=cadvisor \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --publish=8080:8080 \
  gcr.io/cadvisor/cadvisor:latest

# Web UI: http://localhost:8080
# Métriques Prometheus: http://localhost:8080/metrics

# === EXEMPLE DOCKER-COMPOSE AVEC MONITORING ===

version: '3.8'

services:
  app:
    image: myapp:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

  cadvisor:
    image: gcr.io/cadvisor/cadvisor
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - "8080:8080"


[OK] BACKUP & RESTORE (SAUVEGARDE ET RESTAURATION)

# === SAUVEGARDER UNE IMAGE ===

# Sauvegarder image en fichier tar
docker save -o myapp.tar myapp:latest

# Sauvegarder plusieurs images
docker save -o images.tar myapp:latest postgres:15 redis:7

# Sauvegarder avec compression (gzip)
docker save myapp:latest | gzip > myapp.tar.gz

# Sauvegarder avec compression (bzip2 - meilleure compression)
docker save myapp:latest | bzip2 > myapp.tar.bz2

# === RESTAURER UNE IMAGE ===

# Charger image depuis fichier tar
docker load -i myapp.tar

# Charger depuis stdin
docker load < myapp.tar

# Charger avec décompression
gunzip -c myapp.tar.gz | docker load
bunzip2 -c myapp.tar.bz2 | docker load

# === EXPORT/IMPORT CONTENEUR ===

# Différence save/load vs export/import:
# save/load: sauvegarde IMAGE (toutes les layers, historique)
# export/import: sauvegarde CONTENEUR (filesystem, une seule layer)

# Exporter conteneur (filesystem seulement)
docker export myapp > myapp-container.tar
docker export -o myapp-container.tar myapp

# Importer en tant que nouvelle image
docker import myapp-container.tar myapp:snapshot

# Importer avec message et auteur
docker import -m "Backup du 2024-01-15" -c "CMD python app.py" \
  myapp-container.tar myapp:snapshot

# === SAUVEGARDER UN VOLUME ===

# Méthode 1: Avec tar dans un conteneur temporaire
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/backup.tar.gz /data

# Explication:
# --rm: supprime conteneur après exécution
# -v myvolume:/data: monte le volume à sauvegarder
# -v $(pwd):/backup: monte dossier actuel pour stocker backup
# tar czf: crée archive compressée

# Méthode 2: Sans compression
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine tar cf /backup/backup.tar /data

# Sauvegarder plusieurs volumes
docker run --rm \
  -v volume1:/volume1 \
  -v volume2:/volume2 \
  -v $(pwd):/backup \
  alpine sh -c "tar czf /backup/volumes.tar.gz /volume1 /volume2"

# === RESTAURER UN VOLUME ===

# Méthode 1: Créer volume et restaurer
docker volume create myvolume-restored

docker run --rm \
  -v myvolume-restored:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/backup.tar.gz -C /

# Méthode 2: Restaurer dans volume existant (écrase données!)
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine sh -c "rm -rf /data/* && tar xzf /backup/backup.tar.gz -C /"

# === SAUVEGARDER BASE DE DONNÉES ===

# PostgreSQL
docker exec postgres pg_dump -U user dbname > backup.sql
docker exec postgres pg_dumpall -U postgres > backup-all.sql

# MySQL
docker exec mysql mysqldump -u root -p dbname > backup.sql
docker exec mysql mysqldump -u root -p --all-databases > backup-all.sql

# MongoDB
docker exec mongo mongodump --out /backup
docker cp mongo:/backup ./mongo-backup

# Redis
docker exec redis redis-cli BGSAVE
docker cp redis:/data/dump.rdb ./redis-backup.rdb

# === RESTAURER BASE DE DONNÉES ===

# PostgreSQL
docker exec -i postgres psql -U user dbname < backup.sql
cat backup.sql | docker exec -i postgres psql -U user dbname

# MySQL
docker exec -i mysql mysql -u root -p dbname < backup.sql
cat backup.sql | docker exec -i mysql mysql -u root -p dbname

# MongoDB
docker cp ./mongo-backup mongo:/backup
docker exec mongo mongorestore /backup

# Redis
docker cp ./redis-backup.rdb redis:/data/dump.rdb
docker restart redis

# === BACKUP AUTOMATISÉ (SCRIPT) ===

#!/bin/bash
# backup-docker.sh

# Configuration
BACKUP_DIR="/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)

# Créer dossier de backup
mkdir -p "$BACKUP_DIR/$DATE"

# Sauvegarder toutes les images
echo "Sauvegarde des images..."
docker images --format "{{.Repository}}:{{.Tag}}" | \
  grep -v "<none>" | \
  while read image; do
    filename=$(echo $image | tr '/:' '_')
    docker save "$image" | gzip > "$BACKUP_DIR/$DATE/$filename.tar.gz"
  done

# Sauvegarder tous les volumes
echo "Sauvegarde des volumes..."
docker volume ls -q | while read volume; do
  docker run --rm \
    -v "$volume:/data" \
    -v "$BACKUP_DIR/$DATE:/backup" \
    alpine tar czf "/backup/$volume.tar.gz" /data
done

# Sauvegarder les configs Docker Compose
echo "Sauvegarde des fichiers docker-compose..."
find /opt/docker -name "docker-compose.yml" -exec cp {} "$BACKUP_DIR/$DATE/" \;

# Nettoyer vieux backups (garder 7 jours)
find "$BACKUP_DIR" -type d -mtime +7 -exec rm -rf {} \;

echo "Backup terminé: $BACKUP_DIR/$DATE"

# === BACKUP VERS CLOUD ===

# S3 (AWS)
# Installer AWS CLI dans le script
aws s3 sync "$BACKUP_DIR/$DATE" s3://my-bucket/docker-backups/$DATE

# Azure Blob Storage
az storage blob upload-batch \
  --destination docker-backups \
  --source "$BACKUP_DIR/$DATE"

# Google Cloud Storage
gsutil -m rsync -r "$BACKUP_DIR/$DATE" gs://my-bucket/docker-backups/$DATE

# === PLANIFIER BACKUPS (CRON) ===

# Éditer crontab
crontab -e

# Backup quotidien à 2h du matin
0 2 * * * /path/to/backup-docker.sh

# Backup toutes les 6 heures
0 */6 * * * /path/to/backup-docker.sh

# Backup hebdomadaire (dimanche à 3h)
0 3 * * 0 /path/to/backup-docker.sh


[OK] COMMIT CONTENEUR EN IMAGE

# === QU'EST-CE QUE DOCKER COMMIT? ===
# Crée une nouvelle image à partir de l'état actuel d'un conteneur
# Utile pour:
# - Sauvegarder des modifications
# - Débugger (faire des tests, commit si ok)
# - Créer images rapidement (pas recommandé en production)

# === COMMIT BASIQUE ===

# Démarrer conteneur et faire modifications
docker run -it --name mycontainer ubuntu bash
# Dans le conteneur:
apt-get update
apt-get install -y curl vim
echo "Hello" > /hello.txt
exit

# Créer image depuis le conteneur
docker commit mycontainer myapp:v1

# Vérifier la nouvelle image
docker images myapp

# === COMMIT AVEC OPTIONS ===

# Avec message de commit
docker commit -m "Installed curl and vim" mycontainer myapp:v1

# Avec auteur
docker commit -a "John Doe <john@example.com>" mycontainer myapp:v1

# Avec message et auteur
docker commit -m "Added hello.txt" -a "John Doe" mycontainer myapp:v1

# === COMMIT AVEC CHANGEMENTS DE CONFIG ===

# Changer CMD
docker commit --change="CMD python app.py" mycontainer myapp:v1

# Changer ENTRYPOINT
docker commit --change="ENTRYPOINT ['python']" mycontainer myapp:v1

# Changer variables d'environnement
docker commit --change="ENV DEBUG=true" mycontainer myapp:v1

# Exposer port
docker commit --change="EXPOSE 8080" mycontainer myapp:v1

# Plusieurs changements
docker commit \
  --change="ENV APP_ENV=production" \
  --change="EXPOSE 8080" \
  --change="CMD ['python', 'app.py']" \
  mycontainer myapp:v1

# === PAUSE PENDANT COMMIT ===

# Par défaut, conteneur est pausé pendant commit (recommandé)
docker commit mycontainer myapp:v1

# Ne pas pauser (dangereux si écritures en cours)
docker commit --pause=false mycontainer myapp:v1

# === WORKFLOW DEBUG AVEC COMMIT ===

# 1. Démarrer conteneur depuis image problématique
docker run -it --name debug myapp:broken bash

# 2. Diagnostiquer et corriger
apt-get update
apt-get install -y strace gdb
# ... débugger ...
# ... appliquer fix ...

# 3. Commit une fois fixé
docker commit -m "Fixed memory leak" debug myapp:fixed

# 4. Tester la nouvelle image
docker run myapp:fixed

# 5. Si ok, créer Dockerfile propre
# (ne pas utiliser commit en production)

# === DIFFÉRENCE ENTRE LAYERS ===

# Voir différence entre conteneur et image d'origine
docker diff mycontainer

# Output:
# C /etc
# A /etc/vim
# A /etc/vim/vimrc
# C /root
# A /root/.bash_history
# A /hello.txt

# Légende:
# A = Added (ajouté)
# C = Changed (modifié)
# D = Deleted (supprimé)

# === COMPARER TAILLE ===

# Taille image originale
docker images ubuntu
# REPOSITORY   TAG       SIZE
# ubuntu       latest    77.8MB

# Taille après modifications
docker commit mycontainer myapp:v1
docker images myapp
# REPOSITORY   TAG       SIZE
# myapp        v1        125MB

# Différence: ~47 MB ajoutés

# === BONNES PRATIQUES ===

# [X] Mauvais: utiliser commit en production
# - Pas reproductible
# - Pas de versioning
# - Historique opaque
# - Difficile à maintenir

# [OK] Bon: utiliser commit pour:
# - Débugger temporairement
# - Tests rapides
# - Puis créer Dockerfile propre

# Exemple workflow:
# 1. Debug avec commit
docker run -it ubuntu bash
# ... installer paquets, tester ...
docker commit test-container myapp:test

# 2. Tester l'image
docker run myapp:test

# 3. Une fois validé, créer Dockerfile
cat > Dockerfile <<EOF
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl vim
COPY app.py /app/
CMD ["python", "/app/app.py"]
EOF

# 4. Build proprement
docker build -t myapp:v1 .

# === AUTOMATISER AVEC SCRIPT ===

#!/bin/bash
# snapshot-container.sh

CONTAINER=$1
TAG=$2

if [ -z "$CONTAINER" ] || [ -z "$TAG" ]; then
  echo "Usage: $0 <container-name> <image:tag>"
  exit 1
fi

echo "Creating snapshot of $CONTAINER..."

# Commit avec timestamp
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
docker commit \
  -m "Snapshot created on $TIMESTAMP" \
  -a "$(whoami)" \
  "$CONTAINER" \
  "$TAG-$TIMESTAMP"

echo "Snapshot created: $TAG-$TIMESTAMP"

# Usage: ./snapshot-container.sh myapp myapp:snapshot


[OK] TROUBLESHOOTING (DÉPANNAGE)

# === CONTENEUR NE DÉMARRE PAS ===

# Voir logs
docker logs myapp
docker logs --tail 50 myapp

# Démarrer en mode interactif pour débugger
docker run -it --entrypoint /bin/bash myapp:latest

# Ou override CMD
docker run -it myapp:latest /bin/bash

# Inspecter état du conteneur
docker inspect myapp
docker inspect --format='{{.State.ExitCode}}' myapp
docker inspect --format='{{.State.Error}}' myapp

# === PROBLÈMES RÉSEAU ===

# Vérifier IP du conteneur
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# Lister réseaux
docker network ls

# Inspecter réseau
docker network inspect bridge

# Tester connectivité depuis conteneur
docker exec myapp ping google.com
docker exec myapp curl http://example.com

# Vérifier DNS
docker exec myapp nslookup google.com
docker exec myapp cat /etc/resolv.conf

# === PROBLÈMES DE PERMISSION ===

# Vérifier utilisateur
docker exec myapp whoami
docker exec myapp id

# Inspecter permissions fichiers
docker exec myapp ls -la /app

# Exécuter en tant que root pour debug
docker exec -u root myapp bash

# === IMAGE TROP GRANDE ===

# Voir taille par layer
docker history myapp:latest

# Voir taille détaillée
docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" myapp:latest

# Analyser avec dive
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive:latest myapp:latest

# === PROBLÈMES DE BUILD ===

# Build avec logs détaillés
docker build --progress=plain --no-cache -t myapp .

# Débugger un Dockerfile step par step
# Ajouter après chaque RUN:
RUN echo "Debug point 1" && ls -la

# === CONTENEUR CONSOMME TROP DE RESSOURCES ===

# Stats en temps réel
docker stats myapp

# Limiter resources
docker update --memory=512m --cpus=1 myapp

# Kill processus gourmand dans conteneur
docker exec myapp ps aux
docker exec myapp kill -9 <PID>

# === NETTOYER ESPACE DISQUE ===

# Voir usage
docker system df
docker system df -v

# Nettoyer tout ([ATTENTION]  ATTENTION)
docker system prune -a --volumes

# Nettoyer seulement images non utilisées
docker image prune -a

# Nettoyer conteneurs arrêtés
docker container prune

# Nettoyer volumes non utilisés
docker volume prune

# Nettoyer réseaux non utilisés
docker network prune

# Nettoyer build cache
docker builder prune

# === CONTENEUR ZOMBIE ===

# Conteneur existe mais ne répond pas
docker ps -a | grep myapp

# Forcer arrêt
docker kill myapp

# Si ne marche pas, tuer le processus
docker inspect --format='{{.State.Pid}}' myapp
sudo kill -9 <PID>

# === CORROMPRE/RESET DOCKER ===

# Redémarrer daemon (Linux)
sudo systemctl restart docker

# Redémarrer daemon (Mac/Windows)
# Redémarrer Docker Desktop

# Reset complet ([ATTENTION] SUPPRIME TOUT)
# Linux
sudo systemctl stop docker
sudo rm -rf /var/lib/docker
sudo systemctl start docker

# Mac/Windows: Reset dans Docker Desktop settings


[OK] RESSOURCES ET RÉFÉRENCES

# === Documentation Officielle ===
# Docker Docs: https://docs.docker.com
# Docker Hub: https://hub.docker.com
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
# Docker Compose: https://docs.docker.com/compose/

# === Outils Utiles ===
# Portainer: Interface web pour gérer Docker
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce

# Lazydocker: TUI pour Docker
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock lazyteam/lazydocker

# Dive: Analyser images
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive IMAGE

# === Commandes Rapides Utiles ===

# Stop tous les conteneurs
docker stop $(docker ps -aq)

# Supprimer tous les conteneurs
docker rm $(docker ps -aq)

# Supprimer toutes les images
docker rmi $(docker images -q)

# Exec bash dans conteneur en cours
docker exec -it $(docker ps -q | head -1) bash

# Copier fichier depuis tous les conteneurs
docker ps -q | xargs -I {} docker cp {}:/app/log.txt {}-log.txt

# === Configuration Docker Daemon Complète ===

# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",
  "features": {
    "buildkit": true
  },
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 64000,
      "Soft": 64000
    }
  },
  "dns": ["8.8.8.8", "8.8.4.4"],
  "insecure-registries": ["myregistry:5000"],
  "registry-mirrors": ["https://mirror.example.com"]
}


# Fichier: docker_cheats/cheatsheets/docker_python.txt
# Docker pour Python - Guide Complet pour Débutants


[OK] DOCKER POUR PYTHON - INTRODUCTION

# === POURQUOI DOCKER AVEC PYTHON? ===

# Problèmes sans Docker:
# - "Ça marche sur ma machine" mais pas ailleurs
# - Conflits de versions Python (3.8 vs 3.11)
# - Conflits de dépendances entre projets
# - Installation compliquée pour nouveaux développeurs
# - Environnements différents (dev, staging, production)

# Avantages avec Docker:
# [OK] Même environnement partout (dev, prod, CI/CD)
# [OK] Isolation complète entre projets
# [OK] Setup en une commande: docker-compose up
# [OK] Inclut base de données, Redis, etc.
# [OK] Facile à déployer


[OK] DOCKERFILE PYTHON BASIQUE

# === VERSION SIMPLE (POUR COMMENCER) ===

# Créez un fichier nommé "Dockerfile" (pas d'extension!)
# dans le même dossier que votre application Python

# Dockerfile
FROM python:3.11-slim

# WORKDIR = dossier de travail dans le conteneur
# Comme faire "cd /app" mais permanent
WORKDIR /app

# Copier requirements.txt dans le conteneur
COPY requirements.txt .

# Installer les dépendances Python
RUN pip install --no-cache-dir -r requirements.txt

# Copier tout le code de l'application
COPY . .

# Port que l'application utilise (informatif)
EXPOSE 8000

# Commande à exécuter au démarrage
CMD ["python", "app.py"]

# === EXPLICATION DÉTAILLÉE LIGNE PAR LIGNE ===

# ========================================
# FROM python:3.11-slim
# ========================================
# 
# C'est l'IMAGE DE BASE, votre point de départ
# 
# Qu'est-ce qu'une image de base?
# - C'est une image préconstruite que vous utilisez comme fondation
# - python:3.11-slim contient déjà:
#   * Ubuntu Linux
#   * Python 3.11 installé et configuré
#   * pip installé
#   * Bibliothèques système de base
# 
# Pourquoi "slim"?
# - python:3.11 = version COMPLÈTE (~900 MB)
#   * Contient beaucoup d'outils, compilateurs, librairies
#   * Utile si vous compilez du code C/C++
# 
# - python:3.11-slim = version ALLÉGÉE (~150 MB)
#   * Contient juste Python et l'essentiel
#   * Idéal pour la plupart des applications Python
#   * 6x plus petit!
# 
# - python:3.11-alpine = version MINIMALISTE (~50 MB)
#   * Basée sur Alpine Linux (très léger)
#   * ATTENTION: peut avoir des problèmes avec certains packages
#   * Certaines bibliothèques Python (numpy, pandas) sont difficiles à installer
#   * Compilation depuis source souvent nécessaire
# 
# Recommandation pour débutants: TOUJOURS utiliser "slim"
# C'est le meilleur compromis taille/compatibilité
# 
# Exemples d'autres images de base:
# FROM python:3.11-slim         <- Recommandé
# FROM python:3.10-slim         <- Python 3.10
# FROM python:3.9-slim-bullseye <- Debian Bullseye spécifique
# FROM python:3.11              <- Version complète (grosse)
# FROM python:3.11-alpine       <- Très légère (peut avoir problèmes)

# ========================================
# WORKDIR /app
# ========================================
# 
# Définit le DOSSIER DE TRAVAIL dans le conteneur
# 
# Que fait WORKDIR?
# 1. Crée le dossier /app s'il n'existe pas
# 2. Se déplace dans ce dossier (comme "cd /app")
# 3. Tous les chemins relatifs seront basés sur /app
# 
# Pourquoi /app?
# - C'est une CONVENTION (pas obligatoire)
# - Vous pourriez utiliser /code, /src, /myproject
# - Mais /app est le standard, tout le monde l'utilise
# 
# Exemple concret:
# Sans WORKDIR:
#   Vous êtes dans / (racine du système)
#   COPY . . copierait dans /
#   RUN pip install installerait dans /
#   = Bordel dans la racine du système!
# 
# Avec WORKDIR /app:
#   Vous êtes dans /app
#   COPY . . copie dans /app
#   RUN pip install installe dans /app
#   = Tout est organisé dans un dossier dédié
# 
# Équivalent bash:
# RUN mkdir -p /app && cd /app
# 
# Mais WORKDIR est:
# - Plus lisible
# - Affecte TOUTES les instructions suivantes
# - Change aussi le dossier pour CMD/ENTRYPOINT

# ========================================
# COPY requirements.txt .
# ========================================
# 
# Copie un fichier de VOTRE MACHINE vers le CONTENEUR
# 
# Syntaxe: COPY <source> <destination>
# 
# requirements.txt = fichier source (sur votre machine)
# . = destination (dossier actuel dans conteneur = /app)
# 
# Pourquoi copier requirements.txt EN PREMIER?
# C'EST CRUCIAL pour l'OPTIMISATION!
# 
# Docker utilise un système de CACHE par layers:
# - Chaque instruction (FROM, COPY, RUN) crée une "layer"
# - Si une layer ne change pas, Docker la réutilise (CACHE)
# - Si une layer change, Docker refait cette layer ET TOUTES LES SUIVANTES
# 
# Scénario 1 (MAUVAIS):
# COPY . .                          <- Copie TOUT (change souvent)
# RUN pip install -r requirements.txt   <- Réexécuté à CHAQUE changement de code!
# 
# Vous modifiez un commentaire dans votre code
# -> Docker voit que COPY . . a changé
# -> Il refait pip install (peut prendre 5 minutes!)
# 
# Scénario 2 (BON):
# COPY requirements.txt .               <- Change rarement
# RUN pip install -r requirements.txt   <- Utilisé depuis le cache si requirements.txt n'a pas changé
# COPY . .                              <- Copie le code (change souvent)
# 
# Vous modifiez votre code
# -> Docker voit que requirements.txt n'a pas changé
# -> Il utilise le cache pour pip install (instantané!)
# -> Il refait seulement COPY . .
# 
# Gain de temps:
# Sans optimisation: 5 minutes de build à chaque changement
# Avec optimisation: 5 secondes de build
# 
# C'est une des optimisations LES PLUS IMPORTANTES!

# ========================================
# RUN pip install --no-cache-dir -r requirements.txt
# ========================================
# 
# Exécute une commande PENDANT LA CONSTRUCTION de l'image
# 
# RUN vs CMD:
# - RUN: Exécuté pendant le BUILD (docker build)
#        Résultat sauvegardé dans l'image
#        Exemple: installation de packages
# 
# - CMD: Exécuté au DÉMARRAGE du conteneur (docker run)
#        Lance l'application
#        Exemple: python app.py
# 
# pip install -r requirements.txt
# - Installe toutes les dépendances listées dans requirements.txt
# - requirements.txt contient quelque chose comme:
#   django==4.2.8
#   requests==2.31.0
#   psycopg2-binary==2.9.9
# 
# --no-cache-dir
# - Option IMPORTANTE pour Docker
# - Par défaut, pip garde un cache des packages téléchargés
# - Le cache pip prend de l'espace (parfois 500+ MB)
# - Dans Docker, ce cache est INUTILE (l'image est figée)
# - --no-cache-dir économise 200-500 MB dans l'image finale!
# 
# Avec cache: Image de 800 MB
# Sans cache: Image de 300 MB
# 
# Autres options pip utiles:
# --no-cache-dir         <- Pas de cache (économise espace)
# --disable-pip-version-check  <- Pas de vérification version pip (plus rapide)
# --user                 <- Install dans home utilisateur (pour multi-stage)
# 
# Exemple complet avec toutes les optimisations:
# RUN pip install --no-cache-dir --disable-pip-version-check -r requirements.txt

# ========================================
# COPY . .
# ========================================
# 
# Copie TOUT le contenu du dossier actuel vers /app
# 
# Syntaxe: COPY <source> <destination>
# 
# Premier "." = SOURCE (dossier actuel sur VOTRE machine)
# Deuxième "." = DESTINATION (dossier actuel dans CONTENEUR = /app)
# 
# Qu'est-ce qui est copié?
# TOUT ce qui est dans le dossier:
# - app.py
# - models.py
# - templates/
# - static/
# - requirements.txt (déjà copié avant, mais pas grave)
# - etc.
# 
# Qu'est-ce qui N'est PAS copié?
# Les fichiers listés dans .dockerignore (voir section dédiée)
# 
# Pourquoi COPY . . est à la FIN?
# - Le code source change SOUVENT
# - En le mettant à la fin, on invalide moins le cache
# - Les étapes précédentes (pip install) restent en cache
# 
# Alternatives:
# COPY . /app          <- Même chose (explicite)
# COPY app.py .        <- Copie seulement app.py
# COPY src/ /app/src/  <- Copie seulement le dossier src
# 
# Pour projets complexes:
# COPY app/ /app/app/
# COPY tests/ /app/tests/
# COPY config/ /app/config/
# = Plus de contrôle sur ce qui est copié

# ========================================
# EXPOSE 8000
# ========================================
# 
# Documente que le conteneur écoute sur le port 8000
# 
# IMPORTANT: C'est juste de la DOCUMENTATION!
# EXPOSE ne fait RIEN de concret:
# - Ne publie PAS le port
# - N'ouvre PAS le port
# - Ne configure RIEN
# 
# C'est comme un commentaire qui dit:
# "Hey, cette application utilise le port 8000"
# 
# Pourquoi l'utiliser alors?
# 1. Documentation pour les développeurs
#    - En lisant le Dockerfile, on sait quel port utiliser
# 
# 2. Détection automatique par certains outils
#    - docker-compose peut détecter le port
#    - Certains orchestrateurs (Kubernetes) l'utilisent
# 
# 3. Utilisé par docker run -P
#    docker run -P myapp
#    <- Publie automatiquement les ports EXPOSE
# 
# Pour VRAIMENT publier le port, utilisez -p:
# docker run -p 8000:8000 myapp
#   ^         ^
#   |         └─ Port DANS le conteneur
#   └─ Port sur VOTRE machine
# 
# Exemples:
# EXPOSE 8000              <- FastAPI, Django
# EXPOSE 5000              <- Flask
# EXPOSE 80                <- Nginx
# EXPOSE 5432              <- PostgreSQL
# EXPOSE 6379              <- Redis
# 
# Plusieurs ports:
# EXPOSE 8000 8001 8002
# EXPOSE 80 443            <- HTTP et HTTPS

# ========================================
# CMD ["python", "app.py"]
# ========================================
# 
# Commande exécutée au DÉMARRAGE du conteneur
# 
# CMD vs RUN (encore):
# - RUN: Pendant le BUILD, résultat sauvegardé dans l'image
# - CMD: Au DÉMARRAGE du conteneur, exécuté à chaque fois
# 
# Formats de CMD:
# 
# 1. Format EXEC (recommandé):
#    CMD ["executable", "param1", "param2"]
#    CMD ["python", "app.py"]
#    CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]
#    
#    Avantages:
#    - Pas de shell intermédiaire
#    - Signaux (SIGTERM, SIGINT) reçus directement
#    - Arrêt propre du conteneur
#    - Plus rapide
# 
# 2. Format SHELL:
#    CMD python app.py
#    CMD gunicorn -b 0.0.0.0:8000 app:app
#    
#    Problèmes:
#    - Lance /bin/sh -c "votre commande"
#    - Shell intermédiaire = PID 1 n'est pas votre app
#    - Signaux mal transmis
#    - Arrêt sale du conteneur
# 
# Recommandation: TOUJOURS utiliser format EXEC!
# 
# CMD vs ENTRYPOINT:
# - CMD: Commande par défaut, peut être overridée
#   docker run myapp python other.py  <- Override CMD
# 
# - ENTRYPOINT: Commande fixe, les arguments s'ajoutent
#   ENTRYPOINT ["python"]
#   CMD ["app.py"]
#   docker run myapp other.py  <- Exécute "python other.py"
# 
# Il ne peut y avoir qu'UN SEUL CMD dans un Dockerfile!
# Si plusieurs CMD, seul le dernier compte.
# 
# Exemples courants:
# CMD ["python", "app.py"]                    <- App simple
# CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]  <- Django dev
# CMD ["gunicorn", "app:app", "-b", "0.0.0.0:8000"]  <- Flask production
# CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]   <- FastAPI
# CMD ["celery", "-A", "myapp", "worker"]     <- Celery worker
# CMD ["python", "-m", "pytest"]              <- Tests

# === BUILD ET RUN ===

# Structure de projet typique:
# myproject/
# ├── Dockerfile              <- Le fichier qu'on vient de créer
# ├── requirements.txt        <- Liste des dépendances Python
# ├── app.py                  <- Code de votre application
# ├── models.py               <- (optionnel) Vos modèles
# └── templates/              <- (optionnel) Vos templates HTML

# ========================================
# ÉTAPE 1: BUILD (Construire l'image)
# ========================================

# Commande de base:
docker build -t myapp:latest .

# Décomposition:
# docker build     <- Commande pour construire une image
# -t myapp:latest  <- "Tag" (nom) de l'image
# .                <- Contexte de build (dossier actuel)

# Qu'est-ce qu'un TAG?
# Format: nom:version
# - myapp = nom de l'image
# - latest = version (tag)
# - "latest" est une CONVENTION pour "dernière version"
# 
# Exemples de tags:
# myapp:latest     <- Version de développement
# myapp:1.0.0      <- Version 1.0.0
# myapp:v2-prod    <- Version 2 production
# myapp:dev        <- Version développement
# myuser/myapp:latest  <- Avec nom d'utilisateur (pour Docker Hub)

# Qu'est-ce que le CONTEXTE (.)?
# - Le "." signifie "dossier actuel"
# - Docker envoie TOUT ce dossier au daemon Docker
# - Le Dockerfile peut accéder à ces fichiers avec COPY
# 
# Exemple:
# Si vous êtes dans /home/user/myproject
# docker build -t myapp .
# -> Docker envoie tout /home/user/myproject
# -> COPY . . va copier tout ce contenu
# 
# Attention:
# Si le contexte est gros (node_modules, .git, etc.)
# -> Build sera LENT (envoi de Gigaoctets)
# -> Solution: créer un .dockerignore (voir section dédiée)

# Que se passe-t-il pendant le build?
# Docker exécute CHAQUE instruction du Dockerfile:
# 
# Output typique:
# [+] Building 45.2s (10/10) FINISHED
# => [1/6] FROM python:3.11-slim           5.2s
# => [2/6] WORKDIR /app                    0.1s
# => [3/6] COPY requirements.txt .         0.1s
# => [4/6] RUN pip install ...            35.4s  <- Le plus long!
# => [5/6] COPY . .                        0.3s
# => [6/6] CMD ["python", "app.py"]        0.0s
# => exporting to image                    4.1s
# 
# Chaque => est une "layer" (couche)
# Les layers sont CACHÉES si elles n'ont pas changé

# Rebuild avec cache:
# Si vous relancez docker build sans rien changer:
# [+] Building 2.1s (10/10) FINISHED
# => [1/6] FROM python:3.11-slim           CACHED
# => [2/6] WORKDIR /app                    CACHED
# => [3/6] COPY requirements.txt .         CACHED
# => [4/6] RUN pip install ...            CACHED <- Instantané!
# => [5/6] COPY . .                        CACHED
# => [6/6] CMD ["python", "app.py"]        CACHED
# 
# Build en 2 secondes au lieu de 45 secondes!

# Forcer rebuild sans cache:
docker build --no-cache -t myapp:latest .
# Utile si:
# - Vous avez des problèmes bizarres
# - Vous voulez être sûr que tout est frais

# Build avec nom différent:
docker build -t myapp:dev .
docker build -t myapp:1.0.0 .
docker build -t username/myapp:latest .

# Voir toutes vos images:
docker images
# Output:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# myapp        latest    abc123def456   5 minutes ago   250MB
# python       3.11-slim xyz789abc123   2 weeks ago     150MB

# ========================================
# ÉTAPE 2: RUN (Lancer un conteneur)
# ========================================

# Commande de base:
docker run -p 8000:8000 myapp:latest

# Décomposition:
# docker run       <- Commande pour lancer un conteneur
# -p 8000:8000     <- Mapping de port
# myapp:latest     <- Image à utiliser

# Qu'est-ce que -p (port mapping)?
# Format: -p HOST_PORT:CONTAINER_PORT
# 
# -p 8000:8000
#    ^     ^
#    |     └─ Port DANS le conteneur (votre app écoute sur 8000)
#    └─ Port sur VOTRE machine (vous accédez via localhost:8000)
# 
# Le conteneur est ISOLÉ du réseau de votre machine
# Sans -p, vous ne pouvez PAS accéder à l'application!
# 
# Exemples:
# -p 8000:8000     <- Port identique (simple)
# -p 80:8000       <- Port 80 externe, 8000 interne (comme nginx)
# -p 3000:8000     <- Port 3000 externe, 8000 interne
# -p 127.0.0.1:8000:8000  <- Seulement localhost (plus sécurisé)
# 
# Plusieurs ports:
# docker run -p 8000:8000 -p 8001:8001 myapp

# Lancer en ARRIÈRE-PLAN (detached):
docker run -d -p 8000:8000 --name myapp myapp:latest

# Options:
# -d               <- Détaché (background), terminal libre
# --name myapp     <- Nom du conteneur (plus facile à gérer)

# Sans -d:
# - Le conteneur tourne au premier plan
# - Les logs s'affichent dans le terminal
# - CTRL+C arrête le conteneur
# - Terminal bloqué
# 
# Avec -d:
# - Le conteneur tourne en arrière-plan
# - Terminal libre immédiatement
# - Conteneur continue même si vous fermez le terminal

# Accéder à l'application:
# Si tout va bien, votre app est accessible sur:
# http://localhost:8000

# Vérifier que le conteneur tourne:
docker ps
# Output:
# CONTAINER ID   IMAGE          COMMAND           STATUS         PORTS                    NAMES
# abc123def456   myapp:latest   "python app.py"   Up 2 minutes   0.0.0.0:8000->8000/tcp   myapp

# Voir les logs:
docker logs myapp
# Affiche tous les logs depuis le démarrage

docker logs -f myapp
# -f = follow (suit les logs en temps réel, comme tail -f)

docker logs --tail 50 myapp
# Affiche seulement les 50 dernières lignes

# Arrêter le conteneur:
docker stop myapp
# Arrêt propre (envoie SIGTERM puis SIGKILL après 10s)

docker stop -t 30 myapp
# -t 30 = attend 30 secondes avant SIGKILL

# Redémarrer:
docker start myapp
# Redémarre un conteneur arrêté

docker restart myapp
# Équivalent à stop + start

# Supprimer le conteneur:
docker rm myapp
# Supprime le conteneur (doit être arrêté)

docker rm -f myapp
# Force la suppression (même si en cours d'exécution)

# Entrer dans le conteneur (shell interactif):
docker exec -it myapp bash
# -i = interactif
# -t = TTY (pseudo-terminal)
# bash = shell à lancer

# Une fois dedans:
root@abc123:/app# ls
app.py  requirements.txt  ...
root@abc123:/app# python
>>> import django
>>> django.__version__
'4.2.8'
root@abc123:/app# exit

# Exécuter une commande sans entrer:
docker exec myapp python -c "print('Hello from container')"
docker exec myapp ls -la
docker exec myapp pip list


[OK] DOCKERFILE PYTHON OPTIMISÉ

# === VERSION OPTIMISÉE (PRODUCTION) ===

# Cette version est plus complexe mais BEAUCOUP plus efficace
# Elle utilise le "multi-stage build" - une technique avancée

# syntax=docker/dockerfile:1.4
# ^ Active BuildKit (moteur moderne de Docker)
# Optionnel mais donne accès à features avancées

# ========================================
# STAGE 1: BASE (Configuration commune)
# ========================================

FROM python:3.11-slim AS base
# ^ "AS base" donne un NOM à cette étape
# On pourra la référencer plus tard

# === VARIABLES D'ENVIRONNEMENT PYTHON ===

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# Explication de CHAQUE variable:

# ========================================
# PYTHONUNBUFFERED=1
# ========================================
# 
# PAR DÉFAUT, Python bufferise (met en mémoire) stdout/stderr
# Problème en Docker:
# - Vous faites print("Hello")
# - Le texte reste en mémoire
# - Il s'affiche 10 secondes plus tard (ou jamais si crash)
# - docker logs montre les logs avec RETARD
# 
# Avec PYTHONUNBUFFERED=1:
# - print("Hello") s'affiche IMMÉDIATEMENT
# - docker logs montre tout en temps réel
# - ESSENTIEL pour débugger
# 
# Exemple concret:
# Sans: print("Starting...") puis crash -> vous ne voyez RIEN
# Avec: print("Starting...") puis crash -> vous voyez où ça a planté
# 
# Cette variable est CRITIQUE en Docker!

# ========================================
# PYTHONDONTWRITEBYTECODE=1
# ========================================
# 
# PAR DÉFAUT, Python compile vos .py en .pyc (bytecode)
# Il crée des dossiers __pycache__/ partout:
# 
# myproject/
# ├── app.py
# ├── __pycache__/
# │   └── app.cpython-311.pyc    <- Fichier compilé
# ├── models.py
# └── __pycache__/
#     └── models.cpython-311.pyc
# 
# Pourquoi c'est un problème en Docker?
# 1. Ça prend de l'espace (peut ajouter 50-100 MB)
# 2. Les .pyc sont recréés à chaque démarrage de toute façon
# 3. Dans un conteneur, la compilation initiale est négligeable
# 4. Ça pollue l'image avec des fichiers inutiles
# 
# Avec PYTHONDONTWRITEBYTECODE=1:
# - Pas de __pycache__/
# - Image plus petite
# - Plus propre
# 
# Note: Ça peut ralentir le démarrage de 0.1 seconde
# Mais en production avec gunicorn, c'est invisible

# ========================================
# PIP_NO_CACHE_DIR=1
# ========================================
# 
# PAR DÉFAUT, pip garde un cache des packages téléchargés:
# ~/.cache/pip/
# ├── wheels/          <- Packages compilés
# └── http/            <- Packages téléchargés
# 
# Sur votre machine: UTILE (reinstall rapide)
# Dans Docker: INUTILE (l'image est figée)
# 
# Le cache peut prendre 200-500 MB!
# 
# Exemple:
# Sans PIP_NO_CACHE_DIR:
# - pip install numpy
# - numpy téléchargé + mis en cache
# - Image finale: numpy installé (100 MB) + cache (150 MB) = 250 MB
# 
# Avec PIP_NO_CACHE_DIR:
# - pip install numpy
# - numpy téléchargé mais PAS mis en cache
# - Image finale: numpy installé (100 MB) = 100 MB
# 
# Économie: 150 MB dans cet exemple
# Sur un projet réel: 300-500 MB économisés!

# ========================================
# PIP_DISABLE_PIP_VERSION_CHECK=1
# ========================================
# 
# PAR DÉFAUT, pip vérifie s'il y a une nouvelle version:
# "You should consider upgrading pip..."
# 
# Problèmes:
# 1. Ralentit chaque commande pip (1-2 secondes)
# 2. Logs pollués avec des avertissements inutiles
# 3. En Docker, la version de pip est fixée dans l'image
# 
# Avec PIP_DISABLE_PIP_VERSION_CHECK=1:
# - Pas de vérification
# - pip install plus rapide
# - Logs plus propres
# 
# Build sans: 45 secondes
# Build avec: 43 secondes
# Petit gain mais agréable!

WORKDIR /app

# ========================================
# STAGE 2: BUILDER (Installation dépendances)
# ========================================

FROM base AS builder
# On REPART de l'étape "base"
# On hérite de toutes les config (ENV, WORKDIR)

COPY requirements.txt .

# Installation avec --user
RUN pip install --user --no-cache-dir -r requirements.txt

# ========================================
# Pourquoi --user ?
# ========================================
# 
# --user installe dans /root/.local/ au lieu de /usr/local/
# 
# Installation normale (sans --user):
# /usr/local/
# ├── lib/
# │   └── python3.11/
# │       └── site-packages/
# │           ├── django/
# │           ├── requests/
# │           └── ... (vos packages)
# ├── bin/
# │   ├── django-admin
# │   └── ...
# └── ... (beaucoup d'autres choses)
# 
# Installation avec --user:
# /root/.local/
# ├── lib/
# │   └── python3.11/
# │       └── site-packages/
# │           ├── django/
# │           └── requests/
# └── bin/
#     └── django-admin
# 
# Avantage: On peut copier JUSTE /root/.local
# Sans trainer pip, setuptools, wheel, etc.

# ========================================
# STAGE 3: PRODUCTION (Image finale)
# ========================================

FROM base
# On REPART de "base" (pas de builder!)
# Cette image ne contient PAS pip, setuptools, etc.

# Copier SEULEMENT les packages installés
COPY --from=builder /root/.local /root/.local

# ========================================
# COPY --from=builder
# ========================================
# 
# La MAGIE du multi-stage build!
# 
# --from=builder : copie depuis l'étape "builder"
# /root/.local : dossier source dans builder
# /root/.local : dossier destination dans cette étape
# 
# Qu'est-ce qui est copié?
# SEULEMENT vos packages Python installés!
# 
# Qu'est-ce qui N'est PAS copié?
# - pip (économise 20 MB)
# - setuptools (économise 10 MB)
# - wheel (économise 5 MB)
# - Cache de compilation (économise 50+ MB)
# - Headers (.h files) (économise 30 MB)
# 
# Résultat:
# Image builder: 500 MB
# Image finale: 250 MB
# Économie: 250 MB (50%!)

# Ajouter au PATH pour trouver les executables
ENV PATH=/root/.local/bin:$PATH

# ========================================
# Pourquoi modifier PATH?
# ========================================
# 
# Les executables Python sont dans /root/.local/bin/
# Exemples: django-admin, gunicorn, celery, pytest
# 
# Sans modifier PATH:
# django-admin  <- Error: command not found
# /root/.local/bin/django-admin  <- Fonctionne mais pénible
# 
# Avec PATH=/root/.local/bin:$PATH:
# django-admin  <- Fonctionne!
# 
# PATH est une variable d'environnement qui liste
# les dossiers où chercher les executables:
# PATH=/usr/local/bin:/usr/bin:/bin
# 
# On ajoute /root/.local/bin AU DÉBUT:
# PATH=/root/.local/bin:/usr/local/bin:/usr/bin:/bin
# 
# Maintenant, tous les executables Python sont trouvés!

# Copier le code de l'application
COPY . .

# ========================================
# SÉCURITÉ: Utilisateur non-root
# ========================================

RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser /app

# ========================================
# Pourquoi créer un utilisateur?
# ========================================
# 
# PAR DÉFAUT, les conteneurs tournent en ROOT!
# C'est un PROBLÈME DE SÉCURITÉ majeur.
# 
# Scénario d'attaque:
# 1. Votre application a une faille (injection, RCE, etc.)
# 2. L'attaquant exécute du code dans le conteneur
# 3. Il est ROOT dans le conteneur
# 4. Il peut potentiellement accéder à l'hôte
# 5. Il a un accès complet au système
# 
# Avec un utilisateur non-root:
# 1. L'attaquant exécute du code
# 2. Il est "appuser" (pas de privilèges)
# 3. Il ne peut pas installer de packages
# 4. Il ne peut pas modifier les fichiers système
# 5. Les dégâts sont LIMITÉS
# 
# C'est une BEST PRACTICE de sécurité essentielle!

# ========================================
# Explication de la commande
# ========================================
# 
# useradd -m -u 1000 appuser
# 
# useradd : crée un nouvel utilisateur Linux
# -m : crée le home directory (/home/appuser)
# -u 1000 : UID (User ID) = 1000
# appuser : nom de l'utilisateur
# 
# Pourquoi UID 1000?
# - Sur la plupart des systèmes Linux, le premier utilisateur a UID 1000
# - En utilisant 1000, les permissions sont cohérentes
# - Si vous montez un volume, les fichiers créés auront UID 1000
# - Votre utilisateur local peut probablement les lire/écrire
# 
# chown -R appuser:appuser /app
# 
# chown : change le propriétaire (owner) des fichiers
# -R : récursif (tous les sous-dossiers)
# appuser:appuser : utilisateur:groupe
# /app : dossier à changer
# 
# Pourquoi chown?
# - COPY a été exécuté en tant que root
# - Les fichiers appartiennent à root
# - Si on passe à appuser, il ne pourra pas les lire!
# - chown donne la propriété à appuser

# Passer à l'utilisateur non-root
USER appuser

# ========================================
# USER appuser
# ========================================
# 
# Toutes les commandes SUIVANTES s'exécutent en tant que appuser
# Cela inclut CMD!
# 
# Avant USER appuser:
# root@abc123:/app# whoami
# root
# 
# Après USER appuser:
# appuser@abc123:/app$ whoami
# appuser
# 
# Vérification dans un conteneur en cours:
# docker exec myapp whoami
# -> appuser
# 
# Si vous avez besoin de root temporairement:
# docker exec -u root myapp bash

EXPOSE 8000

CMD ["python", "app.py"]

# ========================================
# COMPARAISON: Simple vs Optimisé
# ========================================
# 
# Dockerfile simple:
# - Image: 500 MB
# - Build time: 60 secondes
# - Sécurité: conteneur root (dangereux)
# - Logs: avec délai
# - Simplicité: +++
# 
# Dockerfile optimisé:
# - Image: 250 MB (50% plus petit!)
# - Build time: 45 secondes (15 sec plus rapide)
# - Sécurité: utilisateur non-root (sécurisé)
# - Logs: temps réel
# - Complexité: ++
# 
# Recommandation:
# - Prototypes, apprentissage: version simple
# - Production, projets sérieux: version optimisée


[OK] .DOCKERIGNORE (ESSENTIEL!)

# === QU'EST-CE QUE .dockerignore? ===

# C'est comme .gitignore mais pour Docker
# Liste les fichiers à NE PAS copier dans l'image

# ========================================
# Pourquoi c'est IMPORTANT?
# ========================================
# 
# Sans .dockerignore:
# docker build -t myapp .
# 
# Docker envoie TOUT le dossier au daemon:
# - Votre code (10 MB)
# - node_modules/ (500 MB)
# - venv/ (300 MB)
# - .git/ (200 MB)
# - __pycache__/ (50 MB)
# - logs/ (100 MB)
# Total: 1.16 GB envoyé!
# 
# Résultat:
# - Build prend 5 minutes (envoi des données)
# - Image finale est énorme
# - Risque de secrets exposés (.env, .git)
# 
# Avec .dockerignore:
# Docker envoie SEULEMENT:
# - Votre code (10 MB)
# Total: 10 MB envoyé!
# 
# Résultat:
# - Build prend 30 secondes
# - Image finale est petite
# - Secrets protégés

# === .dockerignore COMPLET ===

# Créer un fichier ".dockerignore" à côté de Dockerfile

# .dockerignore

# ========================================
# Python
# ========================================

__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info/
dist/
build/

# Explication:
# __pycache__/ : bytecode compilé (inutile, recréé automatiquement)
# *.pyc, *.pyo : fichiers compilés (inutiles)
# *.egg-info/ : metadata de packages (inutile dans conteneur)
# dist/, build/ : dossiers de build (ne doivent PAS être dans conteneur)

# ========================================
# Environnements virtuels
# ========================================

venv/
env/
.venv/
ENV/
env.bak/
venv.bak/

# Explication:
# Ces dossiers contiennent un environnement virtuel Python
# Ils peuvent faire 200-500 MB!
# COMPLÈTEMENT inutiles dans Docker car:
# - Docker ISOLE déjà l'environnement
# - Les dépendances sont installées via pip dans l'image

# Erreur courante de débutant:
# Créer un venv/ puis le copier dans Docker
# = Ajoute 300 MB inutiles + peut causer des bugs!

# ========================================
# IDE et éditeurs
# ========================================

.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Explication:
# Fichiers de configuration de votre éditeur
# Totalement inutiles dans le conteneur
# Peuvent contenir des chemins locaux à votre machine

# ========================================
# Git
# ========================================

.git/
.gitignore
.gitattributes

# Explication:
# .git/ peut faire 100-500 MB (historique complet)
# Contient TOUT l'historique du projet
# Risque de sécurité: peut contenir des secrets dans l'historique
# 
# IMPORTANT: Vous ne versionnez PAS avec git DANS le conteneur
# Vous versionnez le Dockerfile et le code source
# L'image Docker elle-même n'a pas besoin de .git/

# ========================================
# Tests
# ========================================

.pytest_cache/
.coverage
htmlcov/
.tox/
.hypothesis/
tests/
*_test.py
test_*.py

# Explication:
# Cache de pytest (recréé à chaque run)
# Rapports de coverage (inutiles en production)
# Tests eux-mêmes (ne vont PAS en production!)
# 
# Note: Pour une image de test, NE PAS ignorer tests/
# Créer un .dockerignore.test séparé

# ========================================
# Documentation
# ========================================

docs/
*.md
LICENSE
CHANGELOG

# Explication:
# Documentation pour humains, pas pour conteneur
# README.md, CONTRIBUTING.md, etc.
# Économise quelques MB

# ========================================
# Environnement et secrets
# ========================================

.env
.env.*
.env.local
.env.production
*.pem
*.key
secrets/

# Explication:
# CRITIQUE POUR LA SÉCURITÉ!
# Ces fichiers contiennent:
# - Mots de passe de base de données
# - Clés API
# - Tokens secrets
# - Certificats privés
# 
# ILS NE DOIVENT JAMAIS être dans l'image!
# 
# Pourquoi?
# 1. Si vous poussez l'image sur Docker Hub -> secrets publics!
# 2. Impossible de changer les secrets sans rebuild
# 3. Mêmes secrets en dev/prod (mauvaise pratique)
# 
# Comment passer des secrets?
# - Variables d'environnement au docker run
# - Docker secrets (en Swarm)
# - Fichiers montés via volumes
# - Services de secrets (AWS Secrets Manager, etc.)

# ========================================
# Logs et données temporaires
# ========================================

*.log
logs/
tmp/
temp/
*.tmp
.cache/

# Explication:
# Logs de développement (ne vont pas en production)
# Peuvent être gros (100+ MB)
# En production, logs vont vers stdout (docker logs)

# ========================================
# CI/CD
# ========================================

.github/
.gitlab-ci.yml
.travis.yml
Jenkinsfile
azure-pipelines.yml

# Explication:
# Configuration CI/CD pour GitHub Actions, GitLab CI, etc.
# Inutile dans le conteneur
# Le conteneur EST le produit du CI/CD

# ========================================
# Docker lui-même
# ========================================

Dockerfile
Dockerfile.*
docker-compose.yml
docker-compose.*.yml
.dockerignore

# Explication:
# Ces fichiers servent à CONSTRUIRE l'image
# Pas besoin d'être DANS l'image
# 
# Exception: Certains aiment garder Dockerfile dans l'image
# pour traçabilité (savoir comment l'image a été construite)
# Dans ce cas, commentez ces lignes

# ========================================
# Node.js (si projet mixte Python/JS)
# ========================================

node_modules/
npm-debug.log
yarn-error.log
package-lock.json
yarn.lock

# Explication:
# Si vous avez du frontend (React, Vue, etc.)
# node_modules/ peut faire 500 MB - 2 GB!
# JAMAIS copier node_modules/ dans Docker

# === EXEMPLE .dockerignore COMPLET ===

# .dockerignore (version complète)

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual environments
venv/
env/
.venv/
ENV/
env.bak/
venv.bak/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
Thumbs.db

# Git
.git/
.gitignore
.gitattributes
.github/

# Tests
.pytest_cache/
.coverage
htmlcov/
.tox/
tests/
test_*.py
*_test.py

# Documentation
docs/
*.md
LICENSE

# Secrets & Environment
.env
.env.*
*.pem
*.key
secrets/

# Logs
*.log
logs/

# CI/CD
.gitlab-ci.yml
Jenkinsfile

# Docker
Dockerfile
docker-compose*.yml
.dockerignore

# Node (si applicable)
node_modules/

# === VÉRIFIER CE QUI EST COPIÉ ===

# Avant de build, vérifier ce qui sera copié:
docker build --no-cache -t myapp . 2>&1 | grep "COPY"

# Ou utiliser un truc:
# Créer un Dockerfile temporaire:
FROM alpine
WORKDIR /app
COPY . .
RUN find . -type f | sort

# Build et voir:
docker build -f Dockerfile.check -t check .
docker run --rm check

# Vous verrez TOUS les fichiers copiés
# Si vous voyez venv/, .git/, .env -> problème!


[OK] REQUIREMENTS.TXT POUR DOCKER

# === STRUCTURE RECOMMANDÉE ===

# Votre projet devrait avoir plusieurs fichiers requirements

# requirements/
# ├── base.txt          # Dépendances de base
# ├── dev.txt           # Dépendances développement
# ├── prod.txt          # Dépendances production
# └── test.txt          # Dépendances tests

# === base.txt (commun à tous) ===
# requirements/base.txt

# Web framework
django==4.2.8
djangorestframework==3.14.0

# Base de données
psycopg2-binary==2.9.9

# Outils
python-dotenv==1.0.0
requests==2.31.0

# === dev.txt ===
# requirements/dev.txt

-r base.txt

# Debug
ipython==8.18.1
django-debug-toolbar==4.2.0

# Code quality
black==23.12.1
flake8==6.1.0
mypy==1.7.1

# === prod.txt ===
# requirements/prod.txt

-r base.txt

# Production server
gunicorn==21.2.0

# Monitoring
sentry-sdk==1.39.1

# === test.txt ===
# requirements/test.txt

-r base.txt

pytest==7.4.3
pytest-django==4.7.0
pytest-cov==4.1.0
faker==21.0.0

# === PINNING DES VERSIONS ===

# [X] Mauvais (version non spécifiée)
django
requests

# [ATTENTION]  Acceptable (développement)
django>=4.2,<5.0
requests>=2.31

# [OK] Bon (production)
django==4.2.8
requests==2.31.0

# === GÉNÉRER REQUIREMENTS.TXT ===

# Depuis un venv actif
pip freeze > requirements.txt

# Nettoyer (seulement packages top-level)
pip install pipreqs
pipreqs . --force

# === DOCKERFILE AVEC REQUIREMENTS MULTIPLES ===

# Dockerfile pour développement
FROM python:3.11-slim

WORKDIR /app

COPY requirements/base.txt requirements/dev.txt ./
RUN pip install --no-cache-dir -r dev.txt

COPY . .

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

# Dockerfile pour production
FROM python:3.11-slim

WORKDIR /app

COPY requirements/base.txt requirements/prod.txt ./
RUN pip install --no-cache-dir -r prod.txt

COPY . .

RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]


[OK] FLASK AVEC DOCKER

# === STRUCTURE PROJET FLASK ===

# myflaskapp/
# ├── app.py
# ├── requirements.txt
# ├── Dockerfile
# ├── docker-compose.yml
# └── .dockerignore

# === app.py (exemple simple) ===

# app.py
from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello from Flask in Docker!"

@app.route('/health')
def health():
    return {"status": "healthy"}, 200

if __name__ == '__main__':
    # 0.0.0.0 permet connexions depuis l'extérieur du conteneur
    app.run(host='0.0.0.0', port=5000, debug=True)

# === requirements.txt ===

# requirements.txt
Flask==3.0.0
gunicorn==21.2.0        # Serveur production
redis==5.0.1            # Si besoin de Redis
python-dotenv==1.0.0

# === Dockerfile DÉVELOPPEMENT ===

# Dockerfile.dev
FROM python:3.11-slim

WORKDIR /app

# Installation dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Code
COPY . .

# Port Flask par défaut
EXPOSE 5000

# Mode développement avec rechargement automatique
CMD ["flask", "run", "--host=0.0.0.0", "--reload"]

# Ou avec python directement
# CMD ["python", "app.py"]

# === Dockerfile PRODUCTION ===

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Créer user non-root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 5000

# Gunicorn pour production (pas flask run!)
CMD ["gunicorn", "-b", "0.0.0.0:5000", "-w", "4", "app:app"]

# Options gunicorn:
# -b 0.0.0.0:5000 : bind sur toutes interfaces, port 5000
# -w 4 : 4 workers (processus)
# app:app : module:application

# === docker-compose.yml ===

version: '3.8'

services:
  flask:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "5000:5000"
    volumes:
      - .:/app                    # Hot reload
    environment:
      - FLASK_ENV=development
      - FLASK_DEBUG=1
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

# === EXPLICATION DOCKER-COMPOSE ===

# version: '3.8'
# - Version du format docker-compose
# - 3.8 est une version stable et récente

# services:
# - Liste des conteneurs à créer

# flask: (nom du service)
#   build:
#     context: .
#       - Contexte de build = dossier actuel
#     dockerfile: Dockerfile.dev
#       - Utilise Dockerfile.dev au lieu de Dockerfile
#
#   ports:
#     - "5000:5000"
#       - Expose port 5000 du conteneur sur port 5000 de l'host
#       - Format: "HOST_PORT:CONTAINER_PORT"
#
#   volumes:
#     - .:/app
#       - Monte le dossier actuel dans /app du conteneur
#       - Changements dans le code = immédiatement visibles dans conteneur
#       - ESSENTIEL pour hot reload en développement
#
#   environment:
#     - Variables d'environnement pour le conteneur
#     - Accessible en Python via os.environ['FLASK_ENV']
#
#   depends_on:
#     - redis
#       - Démarre redis AVANT flask
#       - N'attend PAS que redis soit "ready", juste "started"

# redis:
#   image: redis:7-alpine
#     - Utilise image officielle Redis (pas de build)
#     - alpine = version légère

# === COMMANDES ===

# Démarrer tout
docker-compose up

# Démarrer en background
docker-compose up -d

# Voir les logs
docker-compose logs -f flask

# Rebuild si Dockerfile change
docker-compose up --build

# Arrêter tout
docker-compose down

# Arrêter et supprimer volumes
docker-compose down -v

# Exécuter commande dans conteneur
docker-compose exec flask python
docker-compose exec flask flask shell

# === UTILISER REDIS DANS FLASK ===

# app.py avec Redis
from flask import Flask
import redis
import os

app = Flask(__name__)

# Connexion Redis
redis_client = redis.Redis(
    host='redis',                    # Nom du service docker-compose
    port=6379,
    decode_responses=True
)

@app.route('/')
def hello():
    visits = redis_client.incr('visits')
    return f"Hello! Visits: {visits}"

@app.route('/reset')
def reset():
    redis_client.set('visits', 0)
    return "Counter reset!"

# === VARIABLES D'ENVIRONNEMENT ===

# .env (ne PAS commit dans Git!)
FLASK_ENV=development
FLASK_DEBUG=1
DATABASE_URL=postgresql://user:pass@db:5432/mydb
SECRET_KEY=your-secret-key-here
REDIS_URL=redis://redis:6379/0

# docker-compose.yml
version: '3.8'

services:
  flask:
    build: .
    env_file:
      - .env                        # Charge variables depuis .env
    # Ou directement:
    environment:
      - FLASK_ENV=${FLASK_ENV}
      - SECRET_KEY=${SECRET_KEY}

# Dans Flask
# config.py
import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY')
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
    REDIS_URL = os.environ.get('REDIS_URL')

# === EXEMPLE COMPLET AVEC BASE DE DONNÉES ===

# docker-compose.yml
version: '3.8'

services:
  flask:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/app
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

# app.py avec PostgreSQL
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL')
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)

@app.route('/')
def hello():
    return "Flask + PostgreSQL + Redis in Docker!"

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(host='0.0.0.0', port=5000)


[OK] DJANGO AVEC DOCKER

# === STRUCTURE PROJET DJANGO ===

# myproject/
# ├── myproject/
# │   ├── __init__.py
# │   ├── settings.py
# │   ├── urls.py
# │   └── wsgi.py
# ├── app/
# │   ├── migrations/
# │   ├── __init__.py
# │   ├── models.py
# │   └── views.py
# ├── manage.py
# ├── requirements.txt
# ├── Dockerfile
# ├── docker-compose.yml
# └── .env

# === requirements.txt ===

# requirements.txt
Django==4.2.8
psycopg2-binary==2.9.9        # PostgreSQL
gunicorn==21.2.0              # Production
django-environ==0.11.2        # Variables d'env
celery==5.3.4                 # Tasks asynchrones (optionnel)
redis==5.0.1                  # Cache/Celery (optionnel)

# === Dockerfile DÉVELOPPEMENT ===

# Dockerfile.dev
FROM python:3.11-slim

# Installer dépendances système pour PostgreSQL
RUN apt-get update && apt-get install -y \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

# Commande pour développement
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

# === Dockerfile PRODUCTION ===

# Dockerfile
FROM python:3.11-slim AS base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

RUN apt-get update && apt-get install -y \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Builder stage
FROM base AS builder

COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Production stage
FROM base

COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

COPY . .

# Collecter fichiers statiques
RUN python manage.py collectstatic --noinput

RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]

# === docker-compose.yml COMPLET ===

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/app                      # Hot reload
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ${DB_NAME:-mydb}
      POSTGRES_USER: ${DB_USER:-postgres}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres}"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  celery:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: celery -A myproject worker -l info
    volumes:
      - .:/app
    env_file:
      - .env
    depends_on:
      - db
      - redis

  celery-beat:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: celery -A myproject beat -l info
    volumes:
      - .:/app
    env_file:
      - .env
    depends_on:
      - db
      - redis

volumes:
  postgres_data:

# === EXPLICATION DÉTAILLÉE ===

# web: Service Django principal
#   command: Override CMD du Dockerfile
#     - Utilise runserver (développement uniquement!)
#   volumes:
#     - .:/app
#       - Monte code source pour hot reload
#       - Changements de code = immédiatement visibles
#   depends_on avec condition:
#     - db: service_healthy
#       - Attend que PostgreSQL soit VRAIMENT prêt (healthcheck OK)
#     - redis: service_started
#       - Attend seulement que Redis démarre

# db: Service PostgreSQL
#   environment:
#     - ${DB_NAME:-mydb}
#       - Utilise variable d'env DB_NAME, ou "mydb" par défaut
#   volumes:
#     - postgres_data:/var/lib/postgresql/data
#       - Volume nommé (persiste les données!)
#       - Données conservées même si conteneur supprimé
#   healthcheck:
#     - Vérifie si PostgreSQL est prêt à accepter connexions
#     - pg_isready = commande PostgreSQL

# celery: Workers pour tâches asynchrones
#   command: celery -A myproject worker
#     - Démarre un worker Celery
#     - myproject = nom du projet Django
#   Partage même build, volumes, et env que web

# celery-beat: Scheduler pour tâches périodiques
#   command: celery -A myproject beat
#     - Démarre le scheduler
#     - Lance tâches périodiques (comme cron)

# === .env ===

# .env
DEBUG=1
SECRET_KEY=your-secret-key-change-this-in-production
ALLOWED_HOSTS=localhost,127.0.0.1

# Database
DB_NAME=mydb
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=db
DB_PORT=5432

# Redis
REDIS_URL=redis://redis:6379/0

# Celery
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

# === settings.py (configuration Django) ===

# myproject/settings.py
import os
import environ

# Initialize environ
env = environ.Env(
    DEBUG=(bool, False)
)

# Read .env file
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

# Security
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG')
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['localhost'])

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': env('DB_NAME'),
        'USER': env('DB_USER'),
        'PASSWORD': env('DB_PASSWORD'),
        'HOST': env('DB_HOST'),
        'PORT': env('DB_PORT'),
    }
}

# Cache avec Redis
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': env('REDIS_URL'),
    }
}

# Celery Configuration
CELERY_BROKER_URL = env('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND')

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

# === COMMANDES UTILES ===

# Démarrer tous les services
docker-compose up

# Rebuild si dépendances changent
docker-compose up --build

# Exécuter migrations
docker-compose exec web python manage.py migrate

# Créer superuser
docker-compose exec web python manage.py createsuperuser

# Collecter fichiers statiques
docker-compose exec web python manage.py collectstatic --noinput

# Shell Django
docker-compose exec web python manage.py shell

# Shell PostgreSQL
docker-compose exec db psql -U postgres -d mydb

# Voir logs
docker-compose logs -f web
docker-compose logs -f celery

# Arrêter tout
docker-compose down

# Arrêter et supprimer volumes ([ATTENTION]  PERTE DE DONNÉES)
docker-compose down -v

# === SCRIPT D'INITIALISATION ===

# entrypoint.sh
#!/bin/bash

# Attendre que PostgreSQL soit prêt
echo "Waiting for PostgreSQL..."
while ! nc -z db 5432; do
  sleep 0.1
done
echo "PostgreSQL started"

# Exécuter migrations
python manage.py migrate

# Collecter fichiers statiques
python manage.py collectstatic --noinput

# Créer superuser si n'existe pas
python manage.py shell << END
from django.contrib.auth import get_user_model
User = get_user_model()
if not User.objects.filter(username='admin').exists():
    User.objects.create_superuser('admin', 'admin@example.com', 'admin')
    print('Superuser created')
END

# Démarrer l'application
exec "$@"

# Dans Dockerfile
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

# === PRODUCTION avec Nginx ===

# docker-compose.prod.yml
version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - static_volume:/app/staticfiles
    expose:
      - 8000
    env_file:
      - .env.prod
    depends_on:
      - db

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - static_volume:/app/staticfiles
    depends_on:
      - web

  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env.prod

volumes:
  postgres_data:
  static_volume:

# nginx.conf
upstream django {
    server web:8000;
}

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    location /static/ {
        alias /app/staticfiles/;
    }
}

# Démarrer en production
docker-compose -f docker-compose.prod.yml up -d


[OK] FASTAPI AVEC DOCKER

# === STRUCTURE PROJET FASTAPI ===

# myfastapi/
# ├── main.py
# ├── models.py
# ├── schemas.py
# ├── database.py
# ├── requirements.txt
# ├── Dockerfile
# └── docker-compose.yml

# === main.py (exemple simple) ===

# main.py
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from database import engine, get_db, Base
import models

# Créer tables
Base.metadata.create_all(bind=engine)

app = FastAPI(title="My FastAPI App")

@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI in Docker!"}

@app.get("/health")
def health_check():
    return {"status": "healthy"}

@app.get("/items/")
def read_items(db: Session = Depends(get_db)):
    items = db.query(models.Item).all()
    return items

@app.post("/items/")
def create_item(name: str, db: Session = Depends(get_db)):
    item = models.Item(name=name)
    db.add(item)
    db.commit()
    db.refresh(item)
    return item

# === models.py ===

# models.py
from sqlalchemy import Column, Integer, String
from database import Base

class Item(Base):
    __tablename__ = "items"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

# === database.py ===

# database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os

DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# === requirements.txt ===

# requirements.txt
fastapi==0.108.0
uvicorn[standard]==0.25.0      # Serveur ASGI
sqlalchemy==2.0.23
psycopg2-binary==2.9.9
pydantic==2.5.3
python-dotenv==1.0.0
redis==5.0.1                   # Optionnel

# === Dockerfile DÉVELOPPEMENT ===

# Dockerfile.dev
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

# Uvicorn avec hot reload
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

# === Dockerfile PRODUCTION ===

# Dockerfile
FROM python:3.11-slim AS base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

# Builder stage
FROM base AS builder

COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Production stage
FROM base

COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

COPY . .

RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

# Uvicorn production (plusieurs workers)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

# === docker-compose.yml ===

version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "8000:8000"
    volumes:
      - .:/app                      # Hot reload
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/fastapi_db
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    # Commande avec reload automatique
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: fastapi_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

# === EXPLICATION OPTIONS UVICORN ===

# uvicorn main:app
# - main = fichier main.py
# - app = instance FastAPI dans main.py

# --host 0.0.0.0
# - Écoute sur toutes les interfaces réseau
# - Nécessaire pour Docker (sinon seulement localhost)

# --port 8000
# - Port d'écoute

# --reload
# - Redémarre automatiquement si code change
# - DÉVELOPPEMENT UNIQUEMENT (ralentit les perfs)

# --workers 4
# - Nombre de processus workers
# - Production: généralement (2 x CPU cores) + 1
# - NE PAS utiliser avec --reload

# --log-level info
# - Niveau de logs: critical, error, warning, info, debug, trace

# === COMMANDES ===

# Démarrer
docker-compose up

# Rebuild
docker-compose up --build

# Voir logs
docker-compose logs -f api

# Accéder à l'API
# http://localhost:8000
# Documentation automatique: http://localhost:8000/docs
# Documentation alternative: http://localhost:8000/redoc

# Exécuter commande Python
docker-compose exec api python
docker-compose exec api python -c "print('Hello')"

# Shell dans conteneur
docker-compose exec api bash

# Arrêter
docker-compose down

# === FASTAPI AVEC BACKGROUND TASKS ===

# main.py avec tasks
from fastapi import FastAPI, BackgroundTasks
import time

app = FastAPI()

def send_email(email: str, message: str):
    # Tâche longue
    time.sleep(5)
    print(f"Sending email to {email}: {message}")

@app.post("/send-notification/")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks
):
    # Lance la tâche en arrière-plan
    background_tasks.add_task(send_email, email, "Welcome!")
    # Retourne immédiatement
    return {"message": "Notification will be sent"}

# === FASTAPI AVEC CELERY (TÂCHES LOURDES) ===

# celery_app.py
from celery import Celery
import os

celery_app = Celery(
    "worker",
    broker=os.getenv("CELERY_BROKER_URL", "redis://redis:6379/0"),
    backend=os.getenv("CELERY_RESULT_BACKEND", "redis://redis:6379/0")
)

@celery_app.task
def process_data(data):
    # Traitement lourd
    time.sleep(10)
    return {"result": "processed"}

# main.py avec Celery
from fastapi import FastAPI
from celery_app import celery_app, process_data

app = FastAPI()

@app.post("/process/")
async def process(data: dict):
    # Lance tâche Celery
    task = process_data.delay(data)
    return {"task_id": task.id, "status": "processing"}

@app.get("/task/{task_id}")
async def get_task(task_id: str):
    task = celery_app.AsyncResult(task_id)
    return {
        "task_id": task_id,
        "status": task.status,
        "result": task.result
    }

# docker-compose.yml avec Celery
version: '3.8'

services:
  api:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/app
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/fastapi_db
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/0
    depends_on:
      - db
      - redis

  celery_worker:
    build: .
    command: celery -A celery_app worker --loglevel=info
    volumes:
      - .:/app
    environment:
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/0
    depends_on:
      - redis

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: fastapi_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

volumes:
  postgres_data:

# === FASTAPI AVEC WEBSOCKETS ===

# main.py avec WebSocket
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List

app = FastAPI()

class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)

manager = ConnectionManager()

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            await manager.broadcast(f"Message: {data}")
    except WebSocketDisconnect:
        manager.disconnect(websocket)
        await manager.broadcast("Client disconnected")


[OK] JUPYTER NOTEBOOK AVEC DOCKER

# === STRUCTURE PROJET ===

# mynotebook/
# ├── notebooks/
# │   └── analysis.ipynb
# ├── data/
# │   └── dataset.csv
# ├── requirements.txt
# ├── Dockerfile
# └── docker-compose.yml

# === requirements.txt ===

# requirements.txt
jupyter==1.0.0
jupyterlab==4.0.10
pandas==2.1.4
numpy==1.26.2
matplotlib==3.8.2
seaborn==0.13.0
scikit-learn==1.3.2
plotly==5.18.0

# === Dockerfile ===

# Dockerfile
FROM python:3.11-slim

WORKDIR /workspace

# Installation dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Créer dossiers
RUN mkdir -p /workspace/notebooks /workspace/data

# Exposer port Jupyter
EXPOSE 8888

# Démarrer JupyterLab
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root"]

# === docker-compose.yml ===

version: '3.8'

services:
  jupyter:
    build: .
    ports:
      - "8888:8888"
    volumes:
      - ./notebooks:/workspace/notebooks
      - ./data:/workspace/data
    environment:
      - JUPYTER_ENABLE_LAB=yes

# === EXPLICATION ===

# volumes:
#   - ./notebooks:/workspace/notebooks
#     - Monte dossier local "notebooks" dans conteneur
#     - Notebooks sauvegardés sur votre machine!
#     - Persistent même si conteneur supprimé

# CMD ["jupyter", "lab", ...]
# - jupyter lab: lance JupyterLab (interface moderne)
# - --ip=0.0.0.0: accessible depuis l'extérieur
# - --no-browser: ne lance pas de navigateur (impossible dans Docker)
# - --allow-root: permet d'exécuter en root (nécessaire dans Docker)

# === COMMANDES ===

# Démarrer
docker-compose up

# Le terminal affiche:
# http://127.0.0.1:8888/lab?token=xxxxxxxxxxxxx
# Copier cette URL dans votre navigateur

# Ou sans token (moins sécurisé)
# Dockerfile
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root", "--NotebookApp.token=''", "--NotebookApp.password=''"]

# Arrêter
docker-compose down

# === AVEC BASE DE DONNÉES ===

# docker-compose.yml
version: '3.8'

services:
  jupyter:
    build: .
    ports:
      - "8888:8888"
    volumes:
      - ./notebooks:/workspace/notebooks
      - ./data:/workspace/data
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:

# Dans un notebook
import pandas as pd
from sqlalchemy import create_engine
import os

# Connexion base de données
engine = create_engine(os.environ['DATABASE_URL'])

# Lire depuis DB
df = pd.read_sql("SELECT * FROM users", engine)

# Écrire vers DB
df.to_sql('results', engine, if_exists='replace')


[OK] STREAMLIT AVEC DOCKER

# === STRUCTURE PROJET ===

# mystreamlit/
# ├── app.py
# ├── requirements.txt
# ├── Dockerfile
# └── docker-compose.yml

# === app.py ===

# app.py
import streamlit as st
import pandas as pd

st.title("My Streamlit App in Docker")

# Sidebar
option = st.sidebar.selectbox(
    "Choose a page:",
    ["Home", "Data", "About"]
)

if option == "Home":
    st.write("Welcome!")
    
elif option == "Data":
    # Upload file
    uploaded_file = st.file_uploader("Choose a CSV file")
    if uploaded_file is not None:
        df = pd.read_csv(uploaded_file)
        st.dataframe(df)

elif option == "About":
    st.write("This is a Streamlit app running in Docker")

# === requirements.txt ===

# requirements.txt
streamlit==1.29.0
pandas==2.1.4
plotly==5.18.0

# === Dockerfile ===

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8501

# Healthcheck
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health

CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

# === docker-compose.yml ===

version: '3.8'

services:
  streamlit:
    build: .
    ports:
      - "8501:8501"
    volumes:
      - .:/app

# === COMMANDES ===

# Démarrer
docker-compose up

# Accéder: http://localhost:8501

# Arrêter
docker-compose down


[OK] TESTS AVEC DOCKER

# === STRUCTURE PROJET AVEC TESTS ===

# myproject/
# ├── app/
# │   ├── __init__.py
# │   └── main.py
# ├── tests/
# │   ├── __init__.py
# │   └── test_main.py
# ├── requirements.txt
# ├── requirements-test.txt
# ├── Dockerfile
# ├── Dockerfile.test
# └── docker-compose.test.yml

# === requirements-test.txt ===

# requirements-test.txt
-r requirements.txt

pytest==7.4.3
pytest-cov==4.1.0
pytest-mock==3.12.0
httpx==0.25.2                  # Pour FastAPI tests
faker==21.0.0

# === tests/test_main.py ===

# tests/test_main.py
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello"}

def test_create_item():
    response = client.post("/items/", json={"name": "test"})
    assert response.status_code == 200
    assert response.json()["name"] == "test"

# === Dockerfile.test ===

# Dockerfile.test
FROM python:3.11-slim

WORKDIR /app

# Installer dépendances de test
COPY requirements-test.txt .
RUN pip install --no-cache-dir -r requirements-test.txt

# Copier code
COPY . .

# Commande de test
CMD ["pytest", "-v", "--cov=app", "--cov-report=term-missing"]

# === docker-compose.test.yml ===

version: '3.8'

services:
  test:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/test_db
      - TESTING=1
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

# === COMMANDES TESTS ===

# Exécuter tests
docker-compose -f docker-compose.test.yml up --abort-on-container-exit

# Options utiles:
# --abort-on-container-exit: arrête tout si un conteneur se termine
# --exit-code-from test: utilise exit code du service "test"

# Exécuter tests avec coverage
docker-compose -f docker-compose.test.yml run --rm test pytest --cov=app --cov-report=html

# Tests spécifiques
docker-compose -f docker-compose.test.yml run --rm test pytest tests/test_main.py

# Tests avec verbose
docker-compose -f docker-compose.test.yml run --rm test pytest -v

# Tests avec pdb (debugger)
docker-compose -f docker-compose.test.yml run --rm test pytest --pdb

# === INTÉGRATION CI/CD ===

# .gitlab-ci.yml
test:
  stage: test
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker-compose -f docker-compose.test.yml build
  script:
    - docker-compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from test
  after_script:
    - docker-compose -f docker-compose.test.yml down

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build test image
        run: docker-compose -f docker-compose.test.yml build
      
      - name: Run tests
        run: docker-compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from test
      
      - name: Cleanup
        if: always()
        run: docker-compose -f docker-compose.test.yml down


[OK] BONNES PRATIQUES PYTHON + DOCKER

# === 1. ORDRE DES LAYERS (CACHE OPTIMAL) ===

# [X] Mauvais (cache invalidé à chaque changement de code)
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt

# [OK] Bon (cache utilisé si requirements.txt ne change pas)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# === 2. MULTI-STAGE POUR RÉDUIRE TAILLE ===

# [OK] Image finale plus petite
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
CMD ["python", "app.py"]

# === 3. VARIABLES D'ENVIRONNEMENT PYTHON ===

# Dans Dockerfile
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# === 4. UTILISATEUR NON-ROOT ===

# [OK] Toujours créer un utilisateur
RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser /app
USER appuser

# === 5. .dockerignore COMPLET ===

# .dockerignore
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so

venv/
env/
.venv/

.pytest_cache/
.coverage
htmlcov/

.git/
.gitignore
.vscode/
.idea/

*.md
docs/

.env
.env.*

# === 6. HEALTHCHECK ===

# Dans Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1

# Ou avec curl
HEALTHCHECK CMD curl --fail http://localhost:8000/health || exit 1

# === 7. LOGGING ===

# Configurer logging dans app Python
import logging
import sys

# Log vers stdout (visible dans docker logs)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout)
    ]
)

logger = logging.getLogger(__name__)
logger.info("Application started")

# === 8. SECRETS ===

# [X] Mauvais: secrets dans Dockerfile
ENV API_KEY=secret123

# [X] Mauvais: secrets dans code
api_key = "secret123"

# [OK] Bon: variables d'environnement
import os
api_key = os.environ.get('API_KEY')

# [OK] Bon: fichier de secrets
with open('/run/secrets/api_key', 'r') as f:
    api_key = f.read().strip()

# === 9. GRACEFUL SHUTDOWN ===

# app.py avec signal handling
import signal
import sys

def signal_handler(sig, frame):
    print('Shutting down gracefully...')
    # Fermer connexions DB, etc.
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# Dans docker-compose.yml
services:
  app:
    stop_grace_period: 30s

# === 10. DÉPENDANCES SYSTÈME ===

# Si besoin de packages système (ex: pour psycopg2, Pillow, etc.)
FROM python:3.11-slim

RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-client \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Ou utiliser wheels précompilés (psycopg2-binary)


[OK] DÉPANNAGE COURANT PYTHON + DOCKER

# === PROBLÈME: Module introuvable ===

# Erreur: ModuleNotFoundError: No module named 'mymodule'

# Solution 1: Vérifier que pip install a fonctionné
docker-compose logs app

# Solution 2: Vérifier que COPY . . est après pip install
# Dockerfile devrait avoir:
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# Solution 3: Rebuild l'image
docker-compose up --build

# Solution 4: Vérifier .dockerignore n'ignore pas le module

# === PROBLÈME: Logs non visibles ===

# Solution: PYTHONUNBUFFERED=1
ENV PYTHONUNBUFFERED=1

# Ou au runtime
docker run -e PYTHONUNBUFFERED=1 myapp

# === PROBLÈME: Permission denied ===

# Solution: Ownership correct
RUN useradd -m appuser && \
    chown -R appuser:appuser /app
USER appuser

# === PROBLÈME: Connexion DB échoue ===

# Solution 1: Utiliser healthcheck
depends_on:
  db:
    condition: service_healthy

# Solution 2: Retry dans code Python
import time
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError

def get_db_engine(max_retries=5):
    for i in range(max_retries):
        try:
            engine = create_engine(DATABASE_URL)
            engine.connect()
            return engine
        except OperationalError:
            if i < max_retries - 1:
                time.sleep(2)
            else:
                raise

# === PROBLÈME: Hot reload ne marche pas ===

# Solution: Volume correctement monté
volumes:
  - .:/app

# Et reload activé
command: uvicorn main:app --reload

# === PROBLÈME: Image trop grosse ===

# Solution: Multi-stage + slim + .dockerignore
# Voir section optimisation ci-dessus

# Vérifier taille
docker images myapp

# Analyser layers
docker history myapp:latest


[OK] EXEMPLES COMPLETS PAR CAS D'USAGE

# === MICROSERVICE API SIMPLE ===

# Structure:
# api/
# ├── main.py
# ├── requirements.txt
# ├── Dockerfile
# └── docker-compose.yml

# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER 1000
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]

# docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"

# === APPLICATION WEB COMPLÈTE (Django + PostgreSQL + Redis) ===

# Voir section Django ci-dessus

# === DATA SCIENCE PIPELINE ===

# Structure:
# pipeline/
# ├── notebooks/
# ├── scripts/
# │   └── process.py
# ├── data/
# ├── requirements.txt
# └── docker-compose.yml

# docker-compose.yml
version: '3.8'
services:
  jupyter:
    image: jupyter/datascience-notebook
    ports:
      - "8888:8888"
    volumes:
      - ./notebooks:/home/jovyan/work
      - ./data:/home/jovyan/data
  
  processor:
    build: .
    volumes:
      - ./data:/app/data
    command: python scripts/process.py

# === BOT / SCRIPT CRON ===

# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY bot.py .
CMD ["python", "bot.py"]

# docker-compose.yml
version: '3.8'
services:
  bot:
    build: .
    restart: always
    environment:
      - TOKEN=${BOT_TOKEN}


[OK] RESSOURCES ET AIDE

# Documentation officielle:
# Docker Python: https://docs.docker.com/language/python/
# FastAPI: https://fastapi.tiangolo.com/deployment/docker/
# Django: https://docs.djangoproject.com/en/stable/howto/deployment/

# Images officielles:
# Python: https://hub.docker.com/_/python
# PostgreSQL: https://hub.docker.com/_/postgres
# Redis: https://hub.docker.com/_/redis

# Commandes utiles de debug:
docker-compose logs -f app
docker-compose exec app python
docker-compose exec app bash
docker-compose exec db psql -U postgres


[OK] DOCKER POUR NODE.JS
______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________

# === Dockerfile Node.js Optimisé ===

FROM node:18-alpine AS base

WORKDIR /usr/src/app

# Stage de dépendances
FROM base AS dependencies

COPY package*.json ./
RUN npm ci --only=production

# Stage de build
FROM base AS build

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Stage de production
FROM base

COPY package*.json ./
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/dist ./dist

USER node

EXPOSE 3000

CMD ["node", "dist/index.js"]


# === Docker Compose Node.js + MongoDB ===

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      - NODE_ENV=development
      - MONGODB_URI=mongodb://mongo:27017/mydb
    depends_on:
      - mongo

  mongo:
    image: mongo:6
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password

volumes:
  mongo_data:


[OK] PATTERNS ET BONNES PRATIQUES


# === Pattern: Healthcheck ===

# Dockerfile avec healthcheck
FROM nginx:alpine

COPY nginx.conf /etc/nginx/nginx.conf

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1

# Python healthcheck
FROM python:3.11-slim

COPY . /app
WORKDIR /app

RUN pip install flask

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"

EXPOSE 5000
CMD ["python", "app.py"]


# === Pattern: Entrypoint Script ===

# docker-entrypoint.sh
#!/bin/bash
set -e

# Attendre que la base de données soit prête
echo "Waiting for database..."
while ! nc -z db 5432; do
  sleep 0.1
done
echo "Database started"

# Migrations
python manage.py migrate

# Collecte des fichiers statiques
python manage.py collectstatic --noinput

# Démarrer l'application
exec "$@"

# Dockerfile
FROM python:3.11-slim

COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]


# === Pattern: Configuration Dynamique ===

# envsubst pour remplacer variables dans config
FROM nginx:alpine

COPY nginx.conf.template /etc/nginx/templates/default.conf.template

# Les variables $VAR dans le template seront remplacées
ENV BACKEND_HOST=backend
ENV BACKEND_PORT=8000


# === Pattern: Development vs Production ===

# Dockerfile
FROM python:3.11-slim AS base

WORKDIR /app
COPY requirements.txt .

# Development
FROM base AS development
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

# Production
FROM base AS production
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]

# Build
docker build --target development -t myapp:dev .
docker build --target production -t myapp:prod .


# === Pattern: Init Containers ===

# docker-compose.yml avec init
version: '3.8'

services:
  init:
    image: myapp:latest
    command: python manage.py migrate
    depends_on:
      db:
        condition: service_healthy

  web:
    image: myapp:latest
    depends_on:
      init:
        condition: service_completed_successfully
    ports:
      - "8000:8000"

  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s


# === Pattern: Sidecar Container ===

# Nginx comme sidecar pour servir fichiers statiques
version: '3.8'

services:
  app:
    build: .
    volumes:
      - static:/app/static

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - static:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app

volumes:
  static:


[OK] DÉPANNAGE ET DEBUG


# === Debug Conteneur qui Crash ===

# Voir logs du dernier conteneur
docker logs $(docker ps -lq)

# Voir pourquoi un conteneur s'est arrêté
docker inspect --format='{{.State.ExitCode}}' container_name
docker inspect --format='{{.State.Error}}' container_name

# Démarrer conteneur avec shell alternatif
docker run -it --entrypoint /bin/sh myapp

# Override CMD
docker run -it myapp /bin/bash

# Exécuter en mode interactif
docker run -it myapp

# Garder conteneur vivant pour debug
docker run -d myapp tail -f /dev/null
docker exec -it container_name bash


# === Debug Réseau ===

# Inspecter réseau d'un conteneur
docker inspect --format='{{.NetworkSettings.Networks}}' container_name

# Tester connectivité entre conteneurs
docker exec container1 ping container2
docker exec container1 curl http://container2

# DNS lookup
docker exec container_name nslookup other_container

# Netcat pour tester ports
docker exec container_name nc -zv hostname 80

# Voir tous les ports ouverts
docker exec container_name netstat -tuln


# === Debug Volumes ===

# Voir où est monté un volume
docker volume inspect volume_name

# Voir contenu d'un volume
docker run --rm -v myvolume:/data alpine ls -la /data

# Copier données depuis volume
docker run --rm -v myvolume:/data -v $(pwd):/backup alpine \
  cp -r /data /backup/

# Vérifier permissions
docker run --rm -v myvolume:/data alpine ls -la /data


# === Debug Build ===

# Build avec tous les détails
docker build --progress=plain --no-cache .

# Arrêter à un stage spécifique
docker build --target builder -t debug .

# Run stage intermédiaire
docker run -it debug /bin/sh

# Voir taille de chaque layer
docker history myapp:latest


# === Debug Performance ===

# Stats détaillées
docker stats --no-stream --format \
  "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"

# Top des processus dans conteneur
docker top container_name aux

# Voir les changements filesystem
docker diff container_name


# === Debug Compose ===

# Valider docker-compose.yml
docker compose config

# Voir services qui ne démarrent pas
docker compose ps -a

# Logs d'un service spécifique
docker compose logs -f service_name

# Recréer service
docker compose up -d --force-recreate service_name

# Rebuild et redémarrer
docker compose up -d --build service_name


# === Commandes Utiles pour Debug ===

# Shell dans conteneur Alpine
docker exec -it container_name /bin/sh

# Installer outils de debug dans Alpine
docker exec -it container_name apk add --no-cache curl wget netcat-openbsd

# Installer outils dans Debian/Ubuntu
docker exec -it container_name bash -c \
  "apt-get update && apt-get install -y curl wget netcat iputils-ping dnsutils"

# Copier fichier pour examiner
docker cp container_name:/path/to/file ./file

# Voir variables d'environnement
docker exec container_name env

# Voir processus
docker exec container_name ps aux


[OK] NETTOYAGE ET MAINTENANCE


# === Nettoyage Complet ===

# Tout nettoyer (ATTENTION: supprime tout)
docker system prune -a --volumes

# Nettoyer avec confirmation
docker system prune

# Nettoyer sans confirmation
docker system prune -f

# Nettoyer images non utilisées
docker image prune -a

# Nettoyer conteneurs arrêtés
docker container prune

# Nettoyer volumes non utilisés
docker volume prune

# Nettoyer réseaux non utilisés
docker network prune

# Nettoyer build cache
docker builder prune


# === Nettoyage Sélectif ===

# Supprimer conteneurs de plus de 24h
docker container prune --filter "until=24h"

# Supprimer images sans tag
docker rmi $(docker images -f "dangling=true" -q)

# Supprimer tous les conteneurs arrêtés
docker rm $(docker ps -a -q -f status=exited)

# Supprimer images d'un repository
docker rmi $(docker images 'myapp' -a -q)

# Supprimer volumes non attachés
docker volume rm $(docker volume ls -q -f dangling=true)


# === Voir Utilisation Disque ===

# Espace utilisé par Docker
docker system df

# Détails par composant
docker system df -v

# Taille des images
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# Taille des conteneurs
docker ps -s

# Taille des volumes
docker volume ls


# === Maintenance Régulière ===

# Script de nettoyage quotidien
#!/bin/bash
echo "Nettoyage Docker..."
docker container prune -f
docker image prune -f
docker volume prune -f
docker network prune -f
echo "Espace libéré:"
docker system df

# Cron job (exécuter à 3h du matin)
0 3 * * * /path/to/cleanup-docker.sh


[OK] CI/CD AVEC DOCKER


# === GitHub Actions ===

# .github/workflows/docker-build.yml
name: Docker Build and Push

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
    
    - name: Login to DockerHub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}
    
    - name: Build and push
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: username/myapp:latest
        cache-from: type=registry,ref=username/myapp:latest
        cache-to: type=inline
    
    - name: Run tests
      run: |
        docker run --rm username/myapp:latest pytest


# === GitLab CI ===

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

test:
  stage: test
  script:
    - docker run --rm $IMAGE_TAG pytest

deploy:
  stage: deploy
  only:
    - main
  script:
    - docker pull $IMAGE_TAG
    - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest


# === Jenkins Pipeline ===

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        DOCKER_IMAGE = "myapp"
        DOCKER_TAG = "${BUILD_NUMBER}"
    }
    
    stages {
        stage('Build') {
            steps {
                script {
                    docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                }
            }
        }
        
        stage('Test') {
            steps {
                script {
                    docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").inside {
                        sh 'pytest'
                    }
                }
            }
        }
        
        stage('Push') {
            steps {
                script {
                    docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-credentials') {
                        docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push()
                        docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push('latest')
                    }
                }
            }
        }
    }
    
    post {
        always {
            sh "docker rmi ${DOCKER_IMAGE}:${DOCKER_TAG} || true"
        }
    }
}


[OK] DOCKER EN PRODUCTION


# === Configuration Daemon (/etc/docker/daemon.json) ===

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3",
    "labels": "production"
  },
  "storage-driver": "overlay2",
  "live-restore": true,
  "userland-proxy": false,
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 64000,
      "Soft": 64000
    }
  },
  "max-concurrent-downloads": 3,
  "max-concurrent-uploads": 5,
  "metrics-addr": "127.0.0.1:9323",
  "experimental": false
}

# Redémarrer Docker
sudo systemctl restart docker


# === Limites de Ressources Production ===

# docker-compose.yml production
version: '3.8'

services:
  web:
    image: myapp:latest
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s


# === Rolling Updates ===

# Docker Swarm rolling update
docker service update \
  --image myapp:v2 \
  --update-parallelism 1 \
  --update-delay 10s \
  myapp

# Compose rolling update
docker compose up -d --no-deps --scale web=4 web
docker compose up -d --no-deps --scale old_web=0 old_web


# === Monitoring Production ===

# Prometheus metrics
docker run -d -p 9090:9090 \
  -v /path/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus

# cAdvisor pour métriques conteneurs
docker run -d \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --publish=8080:8080 \
  --name=cadvisor \
  gcr.io/cadvisor/cadvisor

# Grafana
docker run -d -p 3000:3000 grafana/grafana


# === Backup Automatisé ===

# Script de backup
#!/bin/bash
BACKUP_DIR="/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)

# Backup volumes
for volume in $(docker volume ls -q); do
    docker run --rm \
        -v $volume:/data \
        -v $BACKUP_DIR:/backup \
        alpine tar czf /backup/${volume}_${DATE}.tar.gz /data
done

# Backup images
docker images --format "{{.Repository}}:{{.Tag}}" | \
    while read image; do
        filename=$(echo $image | tr '/:' '_')
        docker save $image | gzip > $BACKUP_DIR/${filename}_${DATE}.tar.gz
    done


[OK] SÉCURITÉ AVANCÉE


# === Docker Bench Security ===

# Audit de sécurité
docker run -it --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /var/lib:/var/lib \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /usr/lib/systemd:/usr/lib/systemd \
    -v /etc:/etc --label docker_bench_security \
    docker/docker-bench-security


# === Content Trust (Signature Images) ===

# Activer Content Trust
export DOCKER_CONTENT_TRUST=1

# Générer clés
docker trust key generate mykey

# Signer et push
docker trust sign myapp:latest


# === Scan de Vulnérabilités ===

# Docker scan (nécessite login)
docker scan myapp:latest

# Trivy (alternative)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image myapp:latest

# Clair
docker run -d --name clair-db postgres:latest
docker run -d --name clair --link clair-db:postgres \
  -p 6060:6060 quay.io/coreos/clair:latest


# === Secrets Management ===

# Docker secrets (Swarm)
echo "mypassword" | docker secret create db_password -

# HashiCorp Vault intégration
docker run -d \
  -e VAULT_ADDR=http://vault:8200 \
  -e VAULT_TOKEN=mytoken \
  myapp

# Utiliser fichiers secrets
docker run --mount type=secret,source=db_password,target=/run/secrets/db_password myapp


# === Réseau Sécurisé ===

# Créer réseau encrypted
docker network create --driver overlay --opt encrypted mynetwork

# Isoler conteneurs
docker run --network none myapp

# Custom iptables
docker run --cap-add=NET_ADMIN myapp


[OK] DOCKER COMPOSE - EXEMPLES COMPLETS


# === Stack LAMP (Linux, Apache, MySQL, PHP) ===

version: '3.8'

services:
  web:
    image: php:8.1-apache
    ports:
      - "80:80"
    volumes:
      - ./src:/var/www/html
    depends_on:
      - db
    networks:
      - lamp

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: mydb
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - lamp

  phpmyadmin:
    image: phpmyadmin:latest
    ports:
      - "8080:80"
    environment:
      PMA_HOST: db
      PMA_USER: root
      PMA_PASSWORD: rootpass
    depends_on:
      - db
    networks:
      - lamp

volumes:
  mysql_data:

networks:
  lamp:


# === Stack MEAN (MongoDB, Express, Angular, Node) ===

version: '3.8'

services:
  mongo:
    image: mongo:6
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      - mongo_data:/data/db

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    environment:
      MONGODB_URI: mongodb://admin:password@mongo:27017
    depends_on:
      - mongo
    volumes:
      - ./backend:/app
      - /app/node_modules

  frontend:
    build: ./frontend
    ports:
      - "4200:4200"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    command: npm start

volumes:
  mongo_data:


# === Stack Elasticsearch + Kibana + Logstash ===

version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.8.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
    ports:
      - "9200:9200"
    volumes:
      - es_data:/usr/share/elasticsearch/data

  kibana:
    image: docker.elastic.co/kibana/kibana:8.8.0
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
    depends_on:
      - elasticsearch

  logstash:
    image: docker.elastic.co/logstash/logstash:8.8.0
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    ports:
      - "5044:5044"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
    depends_on:
      - elasticsearch

volumes:
  es_data:


# === Stack Microservices Complet ===

version: '3.8'

services:
  # API Gateway
  api-gateway:
    build: ./api-gateway
    ports:
      - "8000:8000"
    depends_on:
      - user-service
      - product-service
      - order-service
    networks:
      - microservices

  # User Service
  user-service:
    build: ./user-service
    environment:
      DATABASE_URL: postgresql://postgres:password@user-db:5432/users
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - user-db
      - redis
    networks:
      - microservices

  user-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: users
      POSTGRES_PASSWORD: password
    volumes:
      - user_db_data:/var/lib/postgresql/data
    networks:
      - microservices

  # Product Service
  product-service:
    build: ./product-service
    environment:
      DATABASE_URL: postgresql://postgres:password@product-db:5432/products
    depends_on:
      - product-db
    networks:
      - microservices

  product-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: products
      POSTGRES_PASSWORD: password
    volumes:
      - product_db_data:/var/lib/postgresql/data
    networks:
      - microservices

  # Order Service
  order-service:
    build: ./order-service
    environment:
      MONGODB_URI: mongodb://mongo:27017/orders
      RABBITMQ_URL: amqp://rabbitmq:5672
    depends_on:
      - mongo
      - rabbitmq
    networks:
      - microservices

  # Message Queue
  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - microservices

  # Cache
  redis:
    image: redis:7-alpine
    networks:
      - microservices

  # MongoDB
  mongo:
    image: mongo:6
    volumes:
      - mongo_data:/data/db
    networks:
      - microservices

  # Monitoring
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - microservices

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    depends_on:
      - prometheus
    networks:
      - microservices

volumes:
  user_db_data:
  product_db_data:
  mongo_data:

networks:
  microservices:
    driver: bridge


[OK] RESSOURCES ET DOCUMENTATION


# Documentation officielle
# https://docs.docker.com/

# Docker Hub (images publiques)
# https://hub.docker.com/

# Awesome Docker (ressources communautaires)
# https://github.com/veggiemonk/awesome-docker

# Play with Docker (environnement de test en ligne)
# https://labs.play-with-docker.com/

# Docker Cheat Sheet officiel
# https://docs.docker.com/get-started/docker_cheatsheet.pdf

# Best practices
# https://docs.docker.com/develop/dev-best-practices/

# Security best practices
# https://docs.docker.com/engine/security/

# Dockerfile best practices
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/