Skip to content

Latest commit

 

History

History
2233 lines (1756 loc) · 54.9 KB

File metadata and controls

2233 lines (1756 loc) · 54.9 KB

🔝 Retour au Sommaire

19.1.3. Conteneurs (Docker, Podman)

Introduction à la Conteneurisation

Qu'est-ce qu'un Conteneur ?

Un conteneur est une unité légère et portable qui regroupe une application et toutes ses dépendances (bibliothèques, configuration, binaires) dans un package isolé du système hôte.

Analogie simple :

Imaginez que vous déménagez :

  • Bare Metal : Vous emportez tous vos meubles en vrac, vous devez tout remonter dans le nouveau logement
  • VM : Vous emportez votre appartement entier (murs, électricité, plomberie) pour le reconstruire ailleurs
  • Conteneur : Vous mettez tout dans des cartons standardisés qui s'adaptent à n'importe quel logement. Vous ouvrez le carton et c'est prêt !

Vocabulaire de Base

Terme Définition Exemple
Conteneur Instance en cours d'exécution d'une image PostgreSQL tournant dans un conteneur
Image Template immuable contenant l'application postgres:18
Dockerfile Recette pour construire une image Instructions pour créer image PostgreSQL personnalisée
Registry Dépôt d'images Docker Hub, Quay.io
Volume Stockage persistant pour conteneurs Données PostgreSQL PGDATA
Network Réseau virtuel pour connecter conteneurs Bridge, host, overlay
Orchestrateur Gère déploiement à grande échelle Kubernetes, Docker Swarm

Architecture : Conteneurs vs VMs

┌────────────────────────────────────────────────────────────┐
│                    VIRTUAL MACHINES                        │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                  │
│  │App + Libs│  │App + Libs│  │App + Libs│                  │
│  │Guest OS  │  │Guest OS  │  │Guest OS  │  ← OS complet    │
│  │(2-4 GB)  │  │(2-4 GB)  │  │(2-4 GB)  │     dans chaque  │
│  └──────────┘  └──────────┘  └──────────┘                  │
│  ├────────────── Hyperviseur ──────────────┤               │
│  │           Host OS + Hardware            │               │
│  └─────────────────────────────────────────┘               │
│  Overhead : 5-10% | Démarrage : 30-60s                     │
└────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────┐
│                      CONTENEURS                            │
│  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐               │
│  │App+L│  │App+L│  │App+L│  │App+L│  │App+L│ ← Seulement   │
│  │50 MB│  │50 MB│  │50 MB│  │50 MB│  │50 MB│    app+libs   │
│  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘               │
│  ├───────── Runtime Conteneur (Docker/Podman) ─────┤       │
│  │           Host OS + Kernel Linux                │       │
│  │                 Hardware                        │       │
│  └─────────────────────────────────────────────────┘       │
│  Overhead : 2-5% | Démarrage : <5s                         │
└────────────────────────────────────────────────────────────┘

Différences clés :

Caractéristique VM Conteneur
Isolation Forte (OS complet) Moyenne (partage kernel)
Taille 2-20 GB 50-500 MB
Démarrage 30-60 secondes 1-5 secondes
Overhead 5-10% 2-5%
Densité 10-20 par hôte 50-1000+ par hôte
Portabilité Moyenne Excellente
Sécurité Très forte Bonne (dépend config)

Comment Fonctionnent les Conteneurs ?

Les conteneurs utilisent des fonctionnalités du kernel Linux :

Conteneur PostgreSQL
├─ Namespaces : Isolation (PID, network, mount, IPC, UTS)
│   └─ Le conteneur "pense" être seul sur la machine
├─ Cgroups : Limitation ressources (CPU, RAM, I/O)
│   └─ "Tu peux utiliser max 2 CPU et 4 GB RAM"
├─ Union Filesystem : Système de fichiers en couches
│   └─ Layers empilées (base OS → PostgreSQL → config)
└─ Capabilities : Permissions granulaires
    └─ Moins de privilèges que root complet

Exemple concret :

# Lancer PostgreSQL en conteneur
docker run -d \
  --name postgres18 \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:18

# En quelques secondes :
# ✅ PostgreSQL 18 démarre
# ✅ Isolé du système hôte
# ✅ Données persistantes dans volume
# ✅ Portable : même commande sur n'importe quel OS

Docker vs Podman : Quel Outil Choisir ?

Docker

Présentation :

  • Créé en 2013, leader du marché
  • Architecture client-serveur (daemon)
  • Écosystème mature et riche
  • Docker Hub : registre officiel avec millions d'images

Architecture Docker :

┌─────────────────────────────────────┐
│        Utilisateur                  │
└───────────┬─────────────────────────┘
            │ docker run postgres:18
            ↓
┌─────────────────────────────────────┐
│      Docker Client (CLI)            │
└───────────┬─────────────────────────┘
            │ API REST
            ↓
┌─────────────────────────────────────┐
│      Docker Daemon (dockerd)        │
│  - Gestion conteneurs               │
│  - Gestion images                   │
│  - Gestion réseau                   │
│  - Gestion volumes                  │
└───────────┬─────────────────────────┘
            │
            ↓
┌─────────────────────────────────────┐
│      containerd + runc              │
│  (runtime bas-niveau)               │
└───────────┬─────────────────────────┘
            │
            ↓
      [Conteneurs]

Avantages Docker :

  • ✅ Documentation très complète
  • ✅ Énorme communauté (Stack Overflow, forums)
  • ✅ Écosystème riche (Docker Compose, Swarm, Desktop)
  • ✅ Support commercial (Docker Inc.)
  • ✅ Compatible Windows et macOS (Docker Desktop)
  • ✅ Intégration CI/CD mature

Inconvénients Docker :

  • ❌ Nécessite daemon root (problème sécurité)
  • ❌ Architecture centralisée (SPOF)
  • ❌ Licence restrictive pour entreprises (Docker Desktop payant)
  • ❌ Complexité pour environnements multi-utilisateurs

Cas d'usage Docker pour PostgreSQL :

  • Développement local (avec Docker Desktop)
  • CI/CD (tests d'intégration)
  • Petites infrastructures (<50 conteneurs)
  • Équipes habituées à Docker
  • Environnement Windows/macOS

Podman

Présentation :

  • Créé par Red Hat en 2018
  • Alternative open source à Docker
  • Architecture daemonless (sans daemon)
  • Compatible API Docker (drop-in replacement)
  • Intégré à RHEL, Fedora, CentOS Stream

Architecture Podman :

┌─────────────────────────────────────┐
│        Utilisateur                  │
└───────────┬─────────────────────────┘
            │ podman run postgres:18
            ↓
┌─────────────────────────────────────┐
│       Podman CLI                    │
│  (Processus utilisateur direct)     │
└───────────┬─────────────────────────┘
            │ Fork direct
            ↓
┌─────────────────────────────────────┐
│     conmon + runc                   │
│  (runtime conteneur)                │
└───────────┬─────────────────────────┘
            │
            ↓
      [Conteneurs]

Pas de daemon central ! Plus sécurisé.

Avantages Podman :

  • Sans daemon : Pas de processus root permanent
  • Rootless : Conteneurs sans privilèges root
  • Sécurité : Isolation utilisateur native
  • Pods : Support natif groupes de conteneurs (comme Kubernetes)
  • Systemd : Intégration native services systemd
  • Licence : 100% open source Apache 2.0
  • Compatible Docker : alias docker=podman fonctionne

Inconvénients Podman :

  • ❌ Communauté plus petite que Docker
  • ❌ Documentation moins abondante
  • ❌ Quelques incompatibilités mineures avec Docker
  • ❌ Pas de Docker Desktop équivalent (Podman Desktop en beta)
  • ❌ Moins mature pour Windows/macOS

Cas d'usage Podman pour PostgreSQL :

  • Production Linux (meilleure sécurité)
  • ✅ Environnements multi-utilisateurs
  • ✅ Infrastructure Red Hat / Fedora
  • ✅ Migration vers Kubernetes facilitée
  • ✅ Conformité sécurité stricte

Comparaison Détaillée

Critère Docker Podman Recommandation
Sécurité ⭐⭐⭐ (daemon root) ⭐⭐⭐⭐⭐ (rootless) Podman
Maturité ⭐⭐⭐⭐⭐ (10 ans) ⭐⭐⭐⭐ (5 ans) Docker
Communauté ⭐⭐⭐⭐⭐ Énorme ⭐⭐⭐⭐ Grande Docker
Performance ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ (pas daemon) Podman
Compatibilité ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ (99% Docker) Docker
Production ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ Podman
Développement ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ Docker
Windows/Mac ⭐⭐⭐⭐⭐ ⭐⭐ Docker

Notre recommandation :

Si développement (Windows/macOS) :
└─ Docker Desktop

Si production Linux :
└─ Podman (sécurité supérieure)

Si équipe déjà sur Docker :
└─ Rester sur Docker (sauf besoin sécurité critique)

Si infrastructure Red Hat :
└─ Podman (natif)

Note : Le reste du document utilise principalement Docker pour les exemples, mais les commandes Podman sont quasi-identiques (docker → podman).


Images PostgreSQL Officielles

Images Disponibles

Docker Hub : postgres

Registre : hub.docker.com/_/postgres  
Versions disponibles :  
├─ postgres:18        → Dernière 18.x
├─ postgres:18.1      → Version spécifique
├─ postgres:18-alpine → Version légère Alpine Linux
├─ postgres:17        → Précédente version
├─ postgres:latest    → ⚠️ Déconseillé production
└─ postgis/postgis:18 → PostgreSQL + PostGIS

Tags recommandés :

✅ postgres:18.1            # Version spécifique (recommandé production)
✅ postgres:18              # Dernière 18.x (dev/staging)
⚠️ postgres:18-alpine      # Léger mais différences comportement
❌ postgres:latest          # Version changeante (jamais en production !)

Pourquoi éviter :latest ?

Aujourd'hui :
  postgres:latest → 18.1

Demain (après release PG 19) :
  postgres:latest → 19.0

Votre conteneur redémarre → Upgrade surprise → Incompatibilités

Règle d'or : Toujours spécifier version exacte en production.

Anatomie d'une Image PostgreSQL

Couches (layers) de l'image :

Image postgres:18 (~370 MB)
├─ Layer 1 : Debian bookworm base     (~80 MB)
├─ Layer 2 : Dépendances système      (~50 MB)
├─ Layer 3 : PostgreSQL 18 binaires   (~200 MB)
├─ Layer 4 : Scripts d'initialisation (~5 MB)
├─ Layer 5 : Configuration par défaut (~1 MB)
└─ Layer 6 : Entrypoint et CMD        (~1 KB)

Avantages couches :
✅ Réutilisation (cache layers partagés)
✅ Mises à jour efficaces (seules layers modifiées)
✅ Stockage optimisé (déduplication)

Fichier Dockerfile de l'image officielle (simplifié) :

FROM debian:bookworm-slim

# Variables d'environnement
ENV POSTGRES_VERSION 18  
ENV PGDATA /var/lib/postgresql/data  

# Installation PostgreSQL
RUN apt-get update && apt-get install -y \
    postgresql-18 \
    postgresql-contrib-18 \
    && rm -rf /var/lib/apt/lists/*

# Volume pour données
VOLUME /var/lib/postgresql/data

# Exposition port
EXPOSE 5432

# Point d'entrée
COPY docker-entrypoint.sh /usr/local/bin/  
ENTRYPOINT ["docker-entrypoint.sh"]  
CMD ["postgres"]  

Lancer PostgreSQL en Conteneur

Commande de Base

docker run -d \
  --name postgres18-demo \
  -e POSTGRES_PASSWORD=MySecretPassword \
  -p 5432:5432 \
  postgres:18

Décortiquons cette commande :

docker run              # Créer et lancer conteneur
  -d                    # Detached (arrière-plan)
  --name postgres18-demo  # Nom du conteneur
  -e POSTGRES_PASSWORD=MySecretPassword  # Variable d'environnement
  -p 5432:5432          # Mapping port hôte:conteneur
  postgres:18           # Image à utiliser

Que se passe-t-il ?

  1. Docker télécharge l'image postgres:18 (si pas déjà en cache)
  2. Crée un conteneur nommé postgres18-demo
  3. Configure mot de passe superuser postgres
  4. Expose port 5432 (accessible depuis hôte)
  5. Démarre PostgreSQL

Vérification :

# Lister conteneurs en cours
docker ps

# Logs
docker logs postgres18-demo

# Connexion au conteneur
docker exec -it postgres18-demo psql -U postgres

Variables d'Environnement Importantes

docker run -d \
  --name postgres18-prod \

  # ===== CONFIGURATION DE BASE =====
  -e POSTGRES_PASSWORD=SecretPass123    # ✅ Obligatoire
  -e POSTGRES_USER=myapp               # Utilisateur custom (défaut: postgres)
  -e POSTGRES_DB=myappdb               # Base créée au démarrage

  # ===== INITIALISATION =====
  -e POSTGRES_INITDB_ARGS="--data-checksums --encoding=UTF8 --locale=fr_FR.UTF-8"
  # Options initdb (checksums activés par défaut PG18)

  -e POSTGRES_HOST_AUTH_METHOD=scram-sha-256  # Méthode auth (défaut: scram)
  # ⚠️ Ne JAMAIS utiliser 'trust' en production !

  # ===== TUNING =====
  -e POSTGRES_SHARED_BUFFERS=2GB       # Équivalent shared_buffers
  -e POSTGRES_WORK_MEM=64MB            # Équivalent work_mem

  # ===== CHEMINS =====
  -e PGDATA=/var/lib/postgresql/data/pgdata  # Sous-répertoire PGDATA

  postgres:18

Variables communes :

Variable Description Défaut Exemple
POSTGRES_PASSWORD Mot de passe superuser (obligatoire) MySecret123
POSTGRES_USER Nom superuser postgres admin
POSTGRES_DB Base initiale = POSTGRES_USER production
PGDATA Répertoire données /var/lib/postgresql/data /pgdata
POSTGRES_INITDB_ARGS Arguments initdb --auth-host=scram-sha-256 --data-checksums
POSTGRES_INITDB_WALDIR Répertoire WAL séparé (dans PGDATA) /pg_wal

Persistance des Données : Volumes

Problème sans Volume

# Lancer conteneur sans volume
docker run -d --name pg-temp -e POSTGRES_PASSWORD=pass postgres:18

# Créer une table
docker exec -it pg-temp psql -U postgres -c "CREATE TABLE users (id INT);"

# Arrêter et supprimer conteneur
docker stop pg-temp  
docker rm pg-temp  

# Relancer conteneur
docker run -d --name pg-temp -e POSTGRES_PASSWORD=pass postgres:18

# La table n'existe plus ! Données perdues 😱

Pourquoi ? Les données étaient stockées dans le conteneur, pas sur l'hôte.

Solution : Volumes Docker

3 types de montage :

1. Named Volume (Recommandé Production)

# Créer volume
docker volume create pgdata-prod

# Lancer conteneur avec volume
docker run -d \
  --name postgres18-prod \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata-prod:/var/lib/postgresql/data \
  postgres:18

# Avantages :
# ✅ Géré par Docker (backup, migration faciles)
# ✅ Performance optimale
# ✅ Indépendant du système de fichiers hôte
# ✅ Survit à la suppression du conteneur

Inspection :

# Lister volumes
docker volume ls

# Inspecter volume
docker volume inspect pgdata-prod
# {
#   "Mountpoint": "/var/lib/docker/volumes/pgdata-prod/_data",
#   ...
# }

# Emplacement physique (nécessite root)
sudo ls -la /var/lib/docker/volumes/pgdata-prod/_data

2. Bind Mount (Développement)

# Créer répertoire local
mkdir -p ~/postgres-data

# Lancer avec bind mount
docker run -d \
  --name postgres18-dev \
  -e POSTGRES_PASSWORD=secret \
  -v ~/postgres-data:/var/lib/postgresql/data \
  postgres:18

# Avantages :
# ✅ Accès direct fichiers depuis hôte
# ✅ Facile pour déboguer
# ✅ Édition configuration à chaud

# Inconvénients :
# ❌ Permissions complexes (problème UID/GID)
# ❌ Performance variable selon filesystem hôte
# ❌ Moins portable

Problème de permissions typique :

# L'utilisateur postgres dans conteneur a UID 999
# Vos fichiers locaux appartiennent à UID 1000 (vous)
# → Erreur permission denied

# Solution :
sudo chown -R 999:999 ~/postgres-data

3. tmpfs Mount (Temporaire)

# Volume en mémoire RAM (non persistant)
docker run -d \
  --name postgres18-test \
  -e POSTGRES_PASSWORD=secret \
  --tmpfs /var/lib/postgresql/data:rw,size=1g \
  postgres:18

# Avantages :
# ✅ Très rapide (RAM)
# ✅ Sécurité (données effacées à l'arrêt)

# Inconvénients :
# ❌ Aucune persistance
# ❌ Limité par RAM
# ❌ Usage : Tests uniquement

Architecture Multi-Volumes (Production)

# Créer volumes séparés
docker volume create pgdata-prod      # Données principales  
docker volume create pg-wal-prod      # WAL séparé  
docker volume create pg-backups-prod  # Sauvegardes  

docker run -d \
  --name postgres18-prod \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_INITDB_WALDIR=/pg_wal \
  -v pgdata-prod:/var/lib/postgresql/data \
  -v pg-wal-prod:/pg_wal \
  -v pg-backups-prod:/backups \
  postgres:18

# Architecture :
# Conteneur
# ├─ /var/lib/postgresql/data → Volume pgdata-prod (données)
# ├─ /pg_wal → Volume pg-wal-prod (WAL isolé)
# └─ /backups → Volume pg-backups-prod (sauvegardes)

Avantages :

  • Optimisation I/O (WAL séparé réduit contention)
  • Gestion sauvegardes simplifiée
  • Snapshots sélectifs possibles

Réseau et Connectivité

Modes Réseau Docker

1. Bridge (Défaut)

docker run -d \
  --name postgres18 \
  -p 5432:5432 \
  postgres:18

# Fonctionnement :
# Conteneur → Bridge docker0 → Interface hôte → Réseau externe
# IP conteneur : 172.17.0.x (réseau privé Docker)
# Accessible depuis hôte : localhost:5432

Caractéristiques :

  • ✅ Isolation réseau
  • ✅ Port mapping flexible
  • ❌ NAT = légère latence (+0.5-1ms)
  • ✅ Usage : Développement, petite infra

2. Host (Performance)

docker run -d \
  --name postgres18 \
  --network host \
  postgres:18

# Fonctionnement :
# Conteneur partage stack réseau de l'hôte
# Pas d'isolation réseau
# Écoute directement sur 5432 de l'hôte

Caractéristiques :

  • ✅ Performance maximale (pas de NAT)
  • ✅ Latence minimale
  • ❌ Perte isolation
  • ❌ Conflits de ports possibles
  • ⚠️ Usage : Production haute performance (avec précautions)

3. Custom Network (Recommandé Multi-Conteneurs)

# Créer réseau custom
docker network create pg-network

# Lancer PostgreSQL
docker run -d \
  --name postgres18 \
  --network pg-network \
  -e POSTGRES_PASSWORD=secret \
  postgres:18

# Lancer application
docker run -d \
  --name myapp \
  --network pg-network \
  -e DATABASE_URL=postgresql://postgres:secret@postgres18:5432/postgres \
  myapp:latest

# Communication :
# myapp → DNS "postgres18" → PostgreSQL
# Pas besoin port mapping (réseau isolé)

Avantages :

  • ✅ DNS automatique entre conteneurs
  • ✅ Isolation réseau par projet
  • ✅ Pas de publication ports externe nécessaire
  • ✅ Sécurité accrue

Exposition des Ports

# Différentes syntaxes port mapping

# Port unique
-p 5432:5432          # hôte:conteneur

# Port hôte différent
-p 5433:5432          # PostgreSQL sur port 5433 de l'hôte

# IP spécifique
-p 10.0.1.50:5432:5432  # Écoute uniquement sur IP spécifique

# Port aléatoire hôte
-p 5432               # Docker choisit port hôte libre

# Tous les ports exposés
-P                    # Mappe tous EXPOSE du Dockerfile

Sécurité :

# ❌ Dangereux : Exposition publique
docker run -p 0.0.0.0:5432:5432 postgres:18

# ✅ Mieux : Localhost uniquement
docker run -p 127.0.0.1:5432:5432 postgres:18

# ✅ Optimal : Réseau custom sans exposition
docker run --network pg-network postgres:18

Configuration PostgreSQL en Conteneur

Méthodes de Configuration

1. Variables d'Environnement (Simple)

docker run -d \
  --name postgres18 \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_SHARED_BUFFERS=2GB \
  -e POSTGRES_WORK_MEM=64MB \
  -e POSTGRES_MAX_CONNECTIONS=200 \
  postgres:18

# Limitations :
# ⚠️ Paramètres limités
# ⚠️ Pas toutes les options disponibles

2. Arguments de Commande (Flexible)

docker run -d \
  --name postgres18 \
  -e POSTGRES_PASSWORD=secret \
  postgres:18 \
  postgres \
    -c shared_buffers=2GB \
    -c work_mem=64MB \
    -c max_connections=200 \
    -c log_statement=all \
    -c log_min_duration_statement=1000

# Avantages :
# ✅ Tous paramètres disponibles
# ✅ Visible dans docker ps
# ✅ Facile à modifier (recréer conteneur)

3. Fichier postgresql.conf Custom (Production)

Création fichier custom :

# custom-postgresql.conf
listen_addresses = '*'  
max_connections = 200  
shared_buffers = 4GB  
effective_cache_size = 12GB  
work_mem = 64MB  
maintenance_work_mem = 1GB  
wal_level = replica  
max_wal_size = 4GB  
checkpoint_completion_target = 0.9  

# PostgreSQL 18 spécifique
io_method = 'worker'  
io_async_workers = 8  
wal_compression = zstd  

Montage dans conteneur :

docker run -d \
  --name postgres18-prod \
  -e POSTGRES_PASSWORD=secret \
  -v $(pwd)/custom-postgresql.conf:/etc/postgresql/postgresql.conf \
  -v pgdata:/var/lib/postgresql/data \
  postgres:18 \
  postgres -c config_file=/etc/postgresql/postgresql.conf

4. Dockerfile Custom (Avancé)

FROM postgres:18

# Copier configuration custom
COPY custom-postgresql.conf /etc/postgresql/postgresql.conf  
COPY custom-pg_hba.conf /etc/postgresql/pg_hba.conf  

# Scripts d'initialisation
COPY init-scripts/*.sql /docker-entrypoint-initdb.d/

# Variables par défaut
ENV POSTGRES_PASSWORD=changeme \
    POSTGRES_DB=production

# Commande personnalisée
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]

Build et run :

# Construire image
docker build -t mycompany/postgres18:1.0 .

# Lancer
docker run -d \
  --name postgres18-custom \
  -v pgdata:/var/lib/postgresql/data \
  mycompany/postgres18:1.0

Configuration pg_hba.conf

Fichier custom pg_hba.conf :

# custom-pg_hba.conf

# TYPE  DATABASE   USER      ADDRESS          METHOD

# Connexions locales (dans conteneur)
local   all        all                        scram-sha-256

# Depuis réseau Docker (172.17.0.0/16)
host    all        all       172.17.0.0/16    scram-sha-256

# Depuis réseau custom spécifique
host    all        all       10.0.1.0/24      scram-sha-256

# Réplication
host    replication  replicator  10.0.1.0/24  scram-sha-256

Montage :

docker run -d \
  --name postgres18 \
  -v $(pwd)/custom-pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf \
  -v pgdata:/var/lib/postgresql/data \
  postgres:18

Docker Compose : Orchestration Multi-Conteneurs

Introduction à Docker Compose

Docker Compose permet de définir et gérer des applications multi-conteneurs via un fichier YAML.

Avantages :

  • ✅ Configuration déclarative (Infrastructure as Code)
  • ✅ Gestion multi-conteneurs simplifiée
  • ✅ Réseau et volumes automatiques
  • ✅ Reproductibilité
  • ✅ Versioning (Git)

Exemple Simple : PostgreSQL seul

docker-compose.yml :

version: '3.8'

services:
  postgres:
    image: postgres:18
    container_name: postgres18
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
      POSTGRES_USER: postgres
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pgdata:
    driver: local

Utilisation :

# Démarrer
docker-compose up -d

# Logs
docker-compose logs -f postgres

# Arrêter
docker-compose down

# Arrêter + supprimer volumes
docker-compose down -v

Stack Complète : PostgreSQL + Application + pgAdmin

version: '3.8'

services:
  # ===== PostgreSQL =====
  postgres:
    image: postgres:18
    container_name: postgres18
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-SecretPass123}
      POSTGRES_DB: production
      POSTGRES_INITDB_ARGS: "--data-checksums"
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d  # Scripts init
      - ./postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 8G
        reservations:
          cpus: '2'
          memory: 4G

  # ===== Application =====
  app:
    image: myapp:latest
    container_name: myapp
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-SecretPass123}@postgres:5432/production
    networks:
      - backend
      - frontend
    ports:
      - "8080:8080"

  # ===== pgAdmin (Administration) =====
  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: pgadmin
    restart: unless-stopped
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@example.com
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
      - pgadmin-data:/var/lib/pgadmin
    networks:
      - backend
      - frontend
    ports:
      - "5050:80"
    depends_on:
      - postgres

networks:
  backend:
    driver: bridge
  frontend:
    driver: bridge

volumes:
  pgdata:
    driver: local
  pgadmin-data:
    driver: local

Fonctionnalités avancées :

# Healthcheck : Attendre PostgreSQL prêt
depends_on:
  postgres:
    condition: service_healthy

# Limites ressources
deploy:
  resources:
    limits:
      cpus: '4'
      memory: 8G

# Variables d'environnement depuis fichier
env_file:
  - .env

# Redémarrage automatique
restart: unless-stopped

Scripts d'Initialisation

Structure :

project/
├── docker-compose.yml
├── init-scripts/
│   ├── 01-schema.sql        # Ordre alphabétique
│   ├── 02-data.sql
│   └── 03-functions.sql
└── postgresql.conf

01-schema.sql :

-- Scripts exécutés au premier démarrage uniquement
CREATE SCHEMA IF NOT EXISTS app;

CREATE TABLE app.users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(50) UNIQUE NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_users_email ON app.users(email);

02-data.sql :

INSERT INTO app.users (username, email) VALUES
  ('admin', 'admin@example.com'),
  ('user1', 'user1@example.com');

Gestion des Ressources (Limits)

Limitation CPU

# Limiter à 2 CPU
docker run -d \
  --name postgres18 \
  --cpus="2" \
  -e POSTGRES_PASSWORD=secret \
  postgres:18

# Limiter à 50% d'un CPU
docker run -d \
  --name postgres18 \
  --cpus="0.5" \
  postgres:18

# CPU shares (poids relatif)
docker run -d \
  --name postgres18 \
  --cpu-shares=1024 \
  postgres:18

Comportement :

  • --cpus="2" : Maximum 2 cœurs utilisés
  • Si charge < 2 CPU : utilisation normale
  • Si charge > 2 CPU : limitation (throttling)

Limitation Mémoire

# Limiter à 4 GB RAM
docker run -d \
  --name postgres18 \
  --memory="4g" \
  -e POSTGRES_PASSWORD=secret \
  postgres:18

# RAM + Swap total
docker run -d \
  --memory="4g" \
  --memory-swap="6g" \  # 4 GB RAM + 2 GB swap
  postgres:18

# Réservation mémoire minimale
docker run -d \
  --memory="4g" \
  --memory-reservation="2g" \  # Soft limit
  postgres:18

⚠️ Important pour PostgreSQL :

# Désactiver swap (recommandé)
docker run -d \
  --memory="4g" \
  --memory-swap="4g" \  # Égal à memory = pas de swap
  postgres:18

# Ou via postgresql.conf
-c shared_buffers=1GB  # Adapter à la RAM allouée

Limitation I/O

# Limiter I/O disque (IOPS)
docker run -d \
  --name postgres18 \
  --device-read-iops /dev/sda:1000 \
  --device-write-iops /dev/sda:1000 \
  postgres:18

# Limiter bande passante (MB/s)
docker run -d \
  --device-read-bps /dev/sda:100mb \
  --device-write-bps /dev/sda:100mb \
  postgres:18

Cas d'usage :

  • Environnements multi-tenants
  • Éviter qu'un conteneur sature I/O
  • Tests de performance avec contraintes

Docker Compose avec Limites

services:
  postgres:
    image: postgres:18
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 8G
        reservations:  # Garanties minimales
          cpus: '2'
          memory: 4G
    volumes:
      - pgdata:/var/lib/postgresql/data

Sécurité des Conteneurs PostgreSQL

1. Ne Pas Exécuter en Root

Problème :

Par défaut, les processus dans conteneurs s'exécutent souvent en root, ce qui est un risque si le conteneur est compromis.

Solution : Utilisateur non-root

# Dockerfile custom
FROM postgres:18

# L'image officielle utilise déjà user postgres (UID 999)
# Vérification
USER postgres

Avec Podman (Rootless) :

# Podman permet conteneurs complètement sans root
podman run -d \
  --name postgres18 \
  -e POSTGRES_PASSWORD=secret \
  postgres:18

# Processus tournent sous votre UID utilisateur
# Sécurité maximale

2. Secrets Management

❌ Mauvaise pratique : Mot de passe en clair

docker run -e POSTGRES_PASSWORD=SuperSecret123 postgres:18
# Visible dans docker inspect, logs, historique

✅ Bonne pratique : Docker Secrets (Swarm)

# Créer secret
echo "SuperSecret123" | docker secret create postgres_password -

# Utiliser dans service
docker service create \
  --name postgres18 \
  --secret postgres_password \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password \
  postgres:18

✅ Alternative : Fichier .env

# .env (ne pas commiter !)
POSTGRES_PASSWORD=SuperSecret123

# docker-compose.yml
services:
  postgres:
    env_file: .env

# .gitignore
.env

✅ Avec gestionnaire secrets (Vault, AWS Secrets Manager)

services:
  postgres:
    environment:
      POSTGRES_PASSWORD: ${VAULT_POSTGRES_PASSWORD}

3. Réseau Isolé

# docker-compose.yml
services:
  postgres:
    networks:
      - backend  # Réseau privé
    # Pas de ports: exposés publiquement

  app:
    networks:
      - backend   # Accède PostgreSQL
      - frontend  # Accessible publiquement
    ports:
      - "443:443"

networks:
  backend:
    internal: true  # Pas d'accès Internet
  frontend:

4. Capabilities Linux (Réduction Privilèges)

docker run -d \
  --name postgres18 \
  --cap-drop ALL \              # Retire toutes capabilities
  --cap-add CHOWN \             # Ajoute seulement nécessaires
  --cap-add DAC_OVERRIDE \
  --cap-add SETGID \
  --cap-add SETUID \
  postgres:18

5. Read-Only Root Filesystem

docker run -d \
  --name postgres18 \
  --read-only \                           # Filesystem root en lecture seule
  --tmpfs /tmp:rw,noexec,nosuid,size=100m \  # tmpfs pour /tmp
  --tmpfs /run:rw,noexec,nosuid,size=100m \
  -v pgdata:/var/lib/postgresql/data:rw \     # Volume RW pour données
  postgres:18

Avantages :

  • Protection contre modifications filesystem malveillantes
  • Conteneur immuable
  • Conformité sécurité renforcée

6. Scanning d'Images (Vulnérabilités)

# Avec Trivy (scanner open source)
trivy image postgres:18

# Avec Snyk
snyk container test postgres:18

# Avec Docker Scout
docker scout cves postgres:18

Exemple résultat :

postgres:18 (debian bookworm)
├─ CRITICAL: 0
├─ HIGH: 2
├─ MEDIUM: 15
└─ LOW: 48

Recommandation : Mettre à jour vers postgres:18.1

7. Politiques AppArmor / SELinux

AppArmor (Ubuntu/Debian) :

# Profil AppArmor custom pour PostgreSQL
docker run -d \
  --security-opt apparmor=docker-postgresql \
  postgres:18

SELinux (RHEL/Fedora) :

# Avec contexte SELinux
docker run -d \
  --security-opt label=type:container_runtime_t \
  postgres:18

Checklist Sécurité

# docker-compose.yml sécurisé
services:
  postgres:
    image: postgres:18.1  # Version spécifique
    user: postgres        # Non-root
    read_only: true       # Filesystem RO
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - SETGID
      - SETUID
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp
      - /run
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend  # Réseau privé
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          memory: 4G

secrets:
  db_password:
    external: true

networks:
  backend:
    internal: true

Haute Disponibilité et Réplication

PostgreSQL Standalone (Single Node)

# Simple mais pas de HA
services:
  postgres:
    image: postgres:18
    volumes:
      - pgdata:/var/lib/postgresql/data

Limitations :

  • ❌ SPOF (Single Point of Failure)
  • ❌ Pas de tolérance panne
  • ❌ Downtime pendant mises à jour

Réplication Streaming (Primary-Standby)

Architecture :

┌──────────────────┐    Streaming    ┌──────────────────┐
│   Primary        │  Replication    │   Standby        │
│  (Read/Write)    │ ════════════>   │  (Read-Only)     │
│  Container 1     │                 │  Container 2     │
└──────────────────┘                 └──────────────────┘
        │                                     │
        └─────────── Shared Volume (WAL) ─────┘

docker-compose.yml (réplication manuelle) :

version: '3.8'

services:
  postgres-primary:
    image: postgres:18
    container_name: postgres-primary
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_USER: postgres
      POSTGRES_DB: production
      # Configuration réplication
      POSTGRES_INITDB_ARGS: "-c wal_level=replica -c max_wal_senders=5"
    volumes:
      - pgdata-primary:/var/lib/postgresql/data
      - ./setup-replication.sh:/docker-entrypoint-initdb.d/setup-replication.sh
    command: |
      postgres
      -c wal_level=replica
      -c max_wal_senders=5
      -c max_replication_slots=5
      -c hot_standby=on
    networks:
      - pg-network

  postgres-standby:
    image: postgres:18
    container_name: postgres-standby
    environment:
      POSTGRES_PASSWORD: secret
      PGDATA: /var/lib/postgresql/data
    volumes:
      - pgdata-standby:/var/lib/postgresql/data
    command: |
      bash -c "
      if [ ! -f /var/lib/postgresql/data/PG_VERSION ]; then
        pg_basebackup -h postgres-primary -D /var/lib/postgresql/data -U replication -Fp -Xs -R
      fi
      postgres
      "
    depends_on:
      - postgres-primary
    networks:
      - pg-network

networks:
  pg-network:

volumes:
  pgdata-primary:
  pgdata-standby:

Configuration manuelle réplication (complexe) :

# Sur primary : Créer utilisateur réplication
docker exec -it postgres-primary psql -U postgres -c \
  "CREATE USER replication WITH REPLICATION PASSWORD 'repl_pass';"

# Configurer pg_hba.conf primary
docker exec -it postgres-primary bash -c \
  "echo 'host replication replication 0.0.0.0/0 scram-sha-256' >> /var/lib/postgresql/data/pg_hba.conf"

# Reload
docker exec -it postgres-primary psql -U postgres -c "SELECT pg_reload_conf();"

# Sur standby : pg_basebackup
docker exec -it postgres-standby bash -c \
  "pg_basebackup -h postgres-primary -D /var/lib/postgresql/data -U replication -Fp -Xs -R"

⚠️ Complexité : Configuration manuelle, gestion failover compliquée.

Solution Automatisée : Patroni

Patroni = Solution HA automatisée pour PostgreSQL avec :

  • Auto-failover
  • Détection panne automatique
  • Promotion standby automatique
  • Consensus distribué (etcd, Consul, ZooKeeper)

Architecture Patroni :

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  Patroni 1  │  │  Patroni 2  │  │  Patroni 3  │
│  + PG (P)   │  │  + PG (S)   │  │  + PG (S)   │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────────────┼────────────────┘
                        │
                 ┌──────▼──────┐
                 │    etcd     │  ← Consensus
                 │  (Cluster)  │
                 └─────────────┘
                        │
                 ┌──────▼──────┐
                 │   HAProxy   │  ← Load Balancer
                 └─────────────┘

docker-compose.yml avec Patroni :

version: '3.8'

services:
  # ===== ETCD (Consensus) =====
  etcd:
    image: quay.io/coreos/etcd:v3.5
    environment:
      ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
      ETCD_ADVERTISE_CLIENT_URLS: http://etcd:2379
    networks:
      - pg-ha

  # ===== Patroni Node 1 (Initial Primary) =====
  patroni1:
    image: patroni/patroni:latest
    hostname: patroni1
    environment:
      PATRONI_NAME: patroni1
      PATRONI_SCOPE: pg-cluster
      PATRONI_ETCD3_HOSTS: "'etcd:2379'"
      PATRONI_RESTAPI_CONNECT_ADDRESS: patroni1:8008
      PATRONI_POSTGRESQL_CONNECT_ADDRESS: patroni1:5432
      PATRONI_POSTGRESQL_DATA_DIR: /var/lib/postgresql/data
      PATRONI_POSTGRESQL_PGPASS: /tmp/pgpass
      PATRONI_SUPERUSER_USERNAME: postgres
      PATRONI_SUPERUSER_PASSWORD: postgres
      PATRONI_REPLICATION_USERNAME: replicator
      PATRONI_REPLICATION_PASSWORD: replicator
    volumes:
      - pgdata1:/var/lib/postgresql/data
    networks:
      - pg-ha
    depends_on:
      - etcd

  # ===== Patroni Node 2 (Standby) =====
  patroni2:
    image: patroni/patroni:latest
    hostname: patroni2
    environment:
      PATRONI_NAME: patroni2
      PATRONI_SCOPE: pg-cluster
      PATRONI_ETCD3_HOSTS: "'etcd:2379'"
      PATRONI_RESTAPI_CONNECT_ADDRESS: patroni2:8008
      PATRONI_POSTGRESQL_CONNECT_ADDRESS: patroni2:5432
      PATRONI_POSTGRESQL_DATA_DIR: /var/lib/postgresql/data
      PATRONI_SUPERUSER_USERNAME: postgres
      PATRONI_SUPERUSER_PASSWORD: postgres
      PATRONI_REPLICATION_USERNAME: replicator
      PATRONI_REPLICATION_PASSWORD: replicator
    volumes:
      - pgdata2:/var/lib/postgresql/data
    networks:
      - pg-ha
    depends_on:
      - etcd

  # ===== HAProxy (Load Balancer) =====
  haproxy:
    image: haproxy:latest
    ports:
      - "5432:5432"  # Primary
      - "5433:5433"  # Standbys (read-only)
      - "7000:7000"  # Stats
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
    networks:
      - pg-ha
    depends_on:
      - patroni1
      - patroni2

networks:
  pg-ha:

volumes:
  pgdata1:
  pgdata2:

haproxy.cfg :

global
    maxconn 100

defaults
    mode tcp
    timeout connect 5s
    timeout client 30s
    timeout server 30s

# Primary (Read/Write)
listen primary
    bind *:5432
    option httpchk
    http-check expect status 200
    default-server inter 3s fall 3 rise 2
    server patroni1 patroni1:5432 check port 8008
    server patroni2 patroni2:5432 check port 8008 backup

# Standbys (Read-Only)
listen standbys
    bind *:5433
    balance roundrobin
    option httpchk
    http-check expect status 200
    default-server inter 3s fall 3 rise 2
    server patroni1 patroni1:5432 check port 8008
    server patroni2 patroni2:5432 check port 8008

# Stats
listen stats
    bind *:7000
    stats enable
    stats uri /

Fonctionnement Patroni :

  1. État normal :

    • patroni1 = Primary (Read/Write)
    • patroni2 = Standby (Read-Only)
    • HAProxy route écritures vers Primary
  2. Panne Primary :

    • Patroni détecte panne via etcd
    • Promotion automatique patroni2 → Primary
    • HAProxy redirige traffic automatiquement
    • Downtime : 10-30 secondes
  3. Récupération ancien Primary :

    • Redémarre automatiquement en Standby
    • Replication reprend

Production : Best Practices

1. Logging et Monitoring

Configuration logs :

services:
  postgres:
    image: postgres:18
    command: |
      postgres
      -c log_statement=all
      -c log_min_duration_statement=1000
      -c log_connections=on
      -c log_disconnections=on
      -c log_line_prefix='%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./logs:/var/log/postgresql

Exporter logs vers système centralisé :

services:
  postgres:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "3"
    # Ou vers syslog, fluentd, etc.

Monitoring avec Prometheus :

services:
  postgres:
    image: postgres:18
    # ... config ...

  postgres-exporter:
    image: prometheuscommunity/postgres-exporter:latest
    environment:
      DATA_SOURCE_NAME: "postgresql://postgres:secret@postgres:5432/postgres?sslmode=disable"
    ports:
      - "9187:9187"
    depends_on:
      - postgres

2. Health Checks

services:
  postgres:
    image: postgres:18
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

Health check avancé :

healthcheck:
  test: |
    pg_isready -U postgres && \
    psql -U postgres -c "SELECT 1" > /dev/null 2>&1
  interval: 10s
  timeout: 5s
  retries: 3

3. Backups Automatisés

Script backup avec cron :

services:
  postgres:
    # ... config ...

  postgres-backup:
    image: postgres:18
    environment:
      POSTGRES_HOST: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
      BACKUP_SCHEDULE: "0 2 * * *"  # 2h du matin
    volumes:
      - ./backups:/backups
      - ./backup-script.sh:/backup-script.sh
    command: crond -f

backup-script.sh :

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)  
pg_dump -h postgres -U postgres -Fc production > /backups/backup_${DATE}.dump  

# Rotation (garder 7 jours)
find /backups -name "backup_*.dump" -mtime +7 -delete

Avec extension pg_basebackup :

docker exec postgres-primary pg_basebackup \
  -D /backups/base_$(date +%Y%m%d) \
  -Ft -z -Xs -P

4. Mises à Jour (Upgrades)

Stratégie Blue/Green :

# docker-compose.blue.yml (Version actuelle)
services:
  postgres:
    image: postgres:17  # Ancienne version
    container_name: postgres-blue
    ports:
      - "5432:5432"

# docker-compose.green.yml (Nouvelle version)
services:
  postgres:
    image: postgres:18  # Nouvelle version
    container_name: postgres-green
    ports:
      - "5433:5432"  # Port différent

Procédure upgrade :

# 1. Démarrer green (PG 18) sur port 5433
docker-compose -f docker-compose.green.yml up -d

# 2. Restore backup dans green
docker exec postgres-green psql -U postgres < backup.sql

# 3. Tester green
psql -h localhost -p 5433 -U postgres

# 4. Basculer traffic (HAProxy ou DNS)
# ...

# 5. Arrêter blue si OK
docker-compose -f docker-compose.blue.yml down

5. Resource Limits Production

services:
  postgres:
    image: postgres:18
    deploy:
      resources:
        limits:
          cpus: '8'
          memory: 16G
        reservations:
          cpus: '4'
          memory: 8G
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc:
        soft: 65536
        hard: 65536

Troubleshooting Conteneurs

Problème 1 : Conteneur ne Démarre Pas

Symptôme :

docker ps -a
# STATUS: Exited (1) 5 seconds ago

Diagnostic :

# Voir les logs
docker logs postgres18

# Erreur typique :
# ERROR: database files are incompatible with server

Solutions courantes :

# 1. Volume avec mauvaise version PostgreSQL
# → Supprimer volume et recréer
docker volume rm pgdata  
docker run -d --name postgres18 -v pgdata:/var/lib/postgresql/data postgres:18  

# 2. Permissions volume incorrectes
docker run --rm -v pgdata:/data busybox chown -R 999:999 /data

# 3. Mot de passe manquant
docker run -e POSTGRES_PASSWORD=secret postgres:18

Problème 2 : Performances Dégradées

Diagnostic :

# CPU/RAM utilisés
docker stats postgres18

# I/O en temps réel
docker exec postgres18 iostat -x 1

# Connexions actives
docker exec postgres18 psql -U postgres -c \
  "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';"

Causes fréquentes :

  1. Shared buffers trop bas :

    # Augmenter
    docker run -e POSTGRES_SHARED_BUFFERS=2GB postgres:18
  2. Limitation CPU :

    # Vérifier limites
    docker inspect postgres18 | grep -i cpu
    
    # Augmenter
    docker update --cpus="4" postgres18
  3. Volume I/O lent :

    # Tester vitesse volume
    docker run --rm -v pgdata:/data ubuntu \
      dd if=/dev/zero of=/data/test bs=1M count=1000 oflag=direct

Problème 3 : Out of Memory (OOM)

Symptôme :

docker logs postgres18
# ... killed by OOM killer

Solutions :

# 1. Augmenter limite mémoire
docker run --memory="8g" postgres:18

# 2. Adapter shared_buffers
# Règle : shared_buffers < 25% de RAM conteneur
docker run --memory="4g" \
  postgres:18 \
  postgres -c shared_buffers=1GB

# 3. Réduire work_mem si beaucoup de connexions
postgres -c work_mem=16MB

Problème 4 : Cannot Connect from Host

Symptôme :

psql -h localhost -p 5432 -U postgres
# connection refused

Diagnostic :

# 1. Conteneur tourne-t-il ?
docker ps | grep postgres

# 2. Port bien mappé ?
docker port postgres18

# 3. PostgreSQL écoute sur bonne interface ?
docker exec postgres18 netstat -tlnp | grep 5432

# 4. Firewall hôte ?
sudo ufw status

Solutions :

# Vérifier pg_hba.conf
docker exec postgres18 cat /var/lib/postgresql/data/pg_hba.conf
# Doit contenir : host all all 0.0.0.0/0 scram-sha-256

# Vérifier listen_addresses
docker exec postgres18 psql -U postgres -c "SHOW listen_addresses;"
# Doit être '*' ou '0.0.0.0'

Comparaison : Bare Metal vs VM vs Conteneurs

Tableau Récapitulatif

Critère Bare Metal VM Conteneurs
Performance ⭐⭐⭐⭐⭐ 100% ⭐⭐⭐⭐ 90-95% ⭐⭐⭐⭐ 95-98%
Overhead 0% 5-10% 2-5%
Démarrage Minutes 30-60s 1-5s
Taille N/A 2-20 GB 50-500 MB
Densité 1 serveur 10-20/hôte 50-1000+/hôte
Isolation Totale Forte Moyenne
Portabilité Faible Moyenne ⭐⭐⭐⭐⭐ Excellente
Coût initial €€€€€ €€€€ €€
Complexité Élevée Moyenne Faible-Moyenne
HA Manuelle Automatisable Automatisable
Mises à jour Complexes Moyennes Simples
Backup Complexe Snapshots Volumes

Cas d'Usage Recommandés

Bare Metal :

  • Trading haute fréquence
  • Latence critique < 1ms
  • Conformité interdisant virtualisation
  • Infrastructure < 3 serveurs

VMs :

  • Infrastructure 5-100 serveurs
  • Besoin Live Migration
  • Charges stables longue durée
  • Isolation forte requise

Conteneurs :

  • ✅ Microservices / Architecture moderne
  • ✅ CI/CD / Environnements éphémères
  • ✅ Développement local
  • ✅ Cloud-native applications
  • ✅ Déploiements fréquents
  • ✅ Infrastructure as Code

Scénarios Hybrides

1. VMs + Conteneurs (Recommandé Production) :

Infrastructure
├─ Serveurs Physiques (3×)
│   └─ Hyperviseur (Proxmox/VMware)
│       ├─ VM 1 : Kubernetes Node 1
│       │   └─ PostgreSQL Containers (Patroni)
│       ├─ VM 2 : Kubernetes Node 2
│       │   └─ PostgreSQL Containers (Patroni)
│       └─ VM 3 : Kubernetes Node 3
│           └─ PostgreSQL Containers (Patroni)

Avantages :
✅ Isolation VM + Flexibilité Conteneurs
✅ Live Migration VMs
✅ Orchestration Kubernetes

2. Bare Metal + Conteneurs (Performance Maximale) :

Serveurs Bare Metal (2×)
├─ Server 1 : Docker/Podman
│   └─ PostgreSQL Primary Container
└─ Server 2 : Docker/Podman
    └─ PostgreSQL Standby Container

Usage : Bases critiques haute performance

Podman : Différences et Avantages

Commandes Équivalentes

# Docker → Podman (alias possible)
alias docker=podman

# Identiques
docker run → podman run  
docker ps → podman ps  
docker images → podman images  
docker build → podman build  

Fonctionnalités Uniques Podman

1. Pods (comme Kubernetes)

# Créer pod (groupe de conteneurs partageant réseau)
podman pod create --name pg-pod -p 5432:5432

# Lancer PostgreSQL dans pod
podman run -d \
  --pod pg-pod \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  postgres:18

# Lancer pgAdmin dans même pod
podman run -d \
  --pod pg-pod \
  --name pgadmin \
  dpage/pgadmin4

# Avantage : Partage localhost, comme sur Kubernetes

2. Rootless Containers (Sécurité)

# En tant qu'utilisateur normal (pas root)
podman run -d \
  --name postgres18 \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:18

# Processus sous votre UID
ps aux | grep postgres
# user 1000 ...

Sécurité :

  • Pas de daemon root
  • User namespaces
  • Isolation renforcée

3. Génération Systemd

# Générer service systemd depuis conteneur
podman generate systemd --new --name postgres18 > postgres.service

# Installer service
sudo cp postgres.service /etc/systemd/system/  
sudo systemctl enable postgres  
sudo systemctl start postgres  

# Gestion comme service natif
sudo systemctl status postgres

4. Compatible Kubernetes

# Générer YAML Kubernetes depuis pod
podman generate kube pg-pod > postgres-k8s.yaml

# Déployer sur Kubernetes
kubectl apply -f postgres-k8s.yaml

Conclusion : Quand Utiliser les Conteneurs ?

Conteneurs : Le Choix Optimal Pour

Développement Local

  • Setup en secondes
  • Environnement reproductible
  • Isolation des projets
  • Versions PostgreSQL multiples

CI/CD (Tests Automatisés)

  • Tests d'intégration rapides
  • Environnements éphémères
  • Parallélisation facile
  • Nettoyage automatique

Microservices

  • Déploiement indépendant
  • Scaling horizontal
  • Updates sans downtime (rolling)
  • Service discovery natif

Cloud-Native Applications

  • Portabilité multi-cloud
  • Infrastructure as Code
  • Auto-scaling
  • Intégration Kubernetes

Prototypage et POC

  • Démarrage instantané
  • Coût minimal
  • Flexibilité maximale

Éviter les Conteneurs Si

Données critiques sans HA robuste

  • Conteneur = Éphémère par nature
  • Nécessite orchestration (Kubernetes, Patroni)
  • Sinon, préférer VM avec réplication native

Performance absolue requise

  • Trading ultra haute fréquence
  • Latence sub-milliseconde
  • Bare metal reste optimal

Équipe sans expertise conteneurs

  • Courbe d'apprentissage
  • Préférer VM si déjà maîtrisé

Réglementations interdisant conteneurs

  • Certifications spécifiques
  • Secteurs conservateurs (finance, santé)

Points Clés à Retenir

  1. Conteneurs ≠ Moins Sécurisés : Bien configurés (rootless, read-only, secrets), ils sont très sûrs.

  2. Volumes = Critical : Toujours utiliser named volumes en production, jamais de stockage éphémère.

  3. Orchestration Requise Production : Docker Compose pour dev, Kubernetes/Patroni pour production.

  4. Performance ~95-98% : Overhead négligeable pour la majorité des cas d'usage.

  5. Podman > Docker en Production Linux : Sécurité supérieure, architecture daemonless.

  6. PostgreSQL 18 compatible : Améliorations I/O async bénéficient même en conteneur.

Recommandation finale :

Développement :
└─ Docker Desktop (Windows/macOS) ou Podman (Linux)

Staging :
└─ Docker Compose ou Kubernetes (petite échelle)

Production :
├─ Small-Medium : Podman + Systemd ou Patroni
└─ Large : Kubernetes avec Operators (CloudNativePG, Zalando)

Les conteneurs sont l'avenir du déploiement PostgreSQL pour 80% des cas d'usage. Ils combinent flexibilité, portabilité et performance dans un package simple et moderne.


Prochaines étapes suggérées :

  • 19.1.4. Kubernetes (StatefulSets, Operators)
    1. Drivers, Connexion Applicative et Bonnes Pratiques
    1. Extensions et Intégrations

⏭️ Kubernetes (StatefulSets, Operators : Zalando, CloudNativePG)