Skip to content

Latest commit

 

History

History
1172 lines (927 loc) · 40.5 KB

File metadata and controls

1172 lines (927 loc) · 40.5 KB

🔝 Retour au Sommaire

20bis.4.1. StatefulSets : Persistance et Identité

Introduction

Déployer PostgreSQL sur Kubernetes représente un défi particulier. Contrairement à une application web stateless (sans état) qui peut être répliquée et détruite à volonté, une base de données a besoin de persistance et d'identité stable.

C'est exactement ce que les StatefulSets apportent à Kubernetes. Dans ce chapitre, nous allons comprendre ce concept fondamental et voir comment il permet de faire fonctionner PostgreSQL de manière fiable dans un cluster Kubernetes.


Kubernetes en Quelques Mots

Avant de plonger dans les StatefulSets, assurons-nous de comprendre les bases de Kubernetes.

Qu'est-ce que Kubernetes ?

Kubernetes (souvent abrégé "K8s") est une plateforme d'orchestration de conteneurs. Elle permet de :

  • Déployer des applications conteneurisées
  • Les faire évoluer automatiquement (scaling)
  • Les maintenir en bonne santé (self-healing)
  • Gérer le réseau et le stockage
┌─────────────────────────────────────────────────────────────────────┐
│                        CLUSTER KUBERNETES                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐   │
│   │     Node 1      │   │     Node 2      │   │     Node 3      │   │
│   │   (Serveur)     │   │   (Serveur)     │   │   (Serveur)     │   │
│   │                 │   │                 │   │                 │   │
│   │  ┌───┐ ┌───┐    │   │  ┌───┐ ┌───┐    │   │  ┌───┐ ┌───┐    │   │
│   │  │Pod│ │Pod│    │   │  │Pod│ │Pod│    │   │  │Pod│ │Pod│    │   │
│   │  └───┘ └───┘    │   │  └───┘ └───┘    │   │  └───┘ └───┘    │   │
│   │                 │   │                 │   │                 │   │
│   └─────────────────┘   └─────────────────┘   └─────────────────┘   │
│                                                                     │
│   Kubernetes distribue les Pods sur les Nodes disponibles           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Les Concepts de Base

Concept Description
Cluster L'ensemble de l'infrastructure Kubernetes
Node Une machine (physique ou virtuelle) dans le cluster
Pod L'unité de base : un ou plusieurs conteneurs qui s'exécutent ensemble
Service Un point d'accès réseau stable vers des Pods
Volume Du stockage attaché aux Pods

Le Problème des Applications Stateless vs Stateful

Kubernetes a d'abord été conçu pour les applications stateless (sans état) :

APPLICATION STATELESS (ex: serveur web)
───────────────────────────────────────

Caractéristiques :
• Chaque instance est identique et interchangeable
• Pas de données locales à conserver
• Peut être détruite et recréée n'importe où
• Facile à répliquer

┌─────┐  ┌─────┐  ┌─────┐
│Web 1│  │Web 2│  │Web 3│   ← Tous identiques
└─────┘  └─────┘  └─────┘     Interchangeables
    │        │        │
    └────────┼────────┘
             │
    ┌────────┴────────┐
    │  Load Balancer  │
    └─────────────────┘
             │
        Utilisateurs

Si Web 2 meurt → Kubernetes le recrée ailleurs  
Aucune perte de données car pas d'état local  
APPLICATION STATEFUL (ex: PostgreSQL)
─────────────────────────────────────

Caractéristiques :
• Chaque instance a une identité unique
• Stocke des données qui doivent persister
• Ne peut PAS être détruite sans précaution
• La réplication est complexe

┌─────────────┐  ┌───────────┐  ┌───────────┐
│ PG Primary  │  │PG Replica │  │PG Replica │
│  (écriture) │  │ (lecture) │  │ (lecture) │
└─────┬───────┘  └─────┬─────┘  └─────┬─────┘
      │                │              │
┌─────┴─────┐    ┌─────┴─────┐  ┌─────┴─────┐
│  Disque 1 │    │  Disque 2 │  │  Disque 3 │
│  (données)│    │  (données)│  │  (données)│
└───────────┘    └───────────┘  └───────────┘

Si PG Primary meurt → Catastrophe si mal géré !  
Les données doivent être préservées  
L'identité (primary vs replica) compte  

Le Deployment : Inadapté pour les Bases de Données

Comment Fonctionne un Deployment

Un Deployment est la ressource standard pour déployer des applications sur Kubernetes :

# Exemple de Deployment (pour une app stateless)
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:latest

Ce Deployment crée 3 Pods identiques avec des noms aléatoires :

web-app-7d9f8b6c4d-x2k9p  ← Nom généré aléatoirement  
web-app-7d9f8b6c4d-m3n7q  ← Nom généré aléatoirement  
web-app-7d9f8b6c4d-h8j2w  ← Nom généré aléatoirement  

Pourquoi C'est Problématique pour PostgreSQL

Comportement du Deployment Problème pour PostgreSQL
Noms de Pods aléatoires Impossible de savoir qui est primary/replica
Pas d'ordre de démarrage Le replica pourrait démarrer avant le primary
Stockage partagé ou éphémère Les données seraient perdues ou corrompues
Remplacement arbitraire Un nouveau Pod ne retrouverait pas ses données
❌ SCÉNARIO CATASTROPHE AVEC DEPLOYMENT
───────────────────────────────────────

1. Deployment crée 3 Pods PostgreSQL
   pod-abc123 (primary ?)
   pod-def456 (replica ?)
   pod-ghi789 (replica ?)

2. pod-abc123 (primary) crashe

3. Kubernetes recrée un Pod...
   pod-xyz999 (nouveau, vide !)

4. Problèmes :
   • Où sont les données de l'ancien primary ?
   • Le nouveau Pod ne sait pas qu'il était primary
   • Le disque de l'ancien Pod est peut-être perdu

   → PERTE DE DONNÉES POTENTIELLE

StatefulSet : La Solution pour les Applications Stateful

Qu'est-ce qu'un StatefulSet ?

Un StatefulSet est une ressource Kubernetes spécialement conçue pour les applications qui ont besoin de :

  1. Identité stable : Chaque Pod a un nom prévisible et permanent
  2. Stockage persistant : Chaque Pod a son propre volume de données
  3. Ordre de déploiement : Les Pods sont créés et supprimés dans un ordre défini
  4. Réseau stable : Chaque Pod a un nom DNS prévisible

Les Garanties du StatefulSet

┌─────────────────────────────────────────────────────────────────────┐
│                    GARANTIES DU STATEFULSET                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. IDENTITÉ STABLE                                                 │
│     ─────────────────                                               │
│     Noms prévisibles : postgres-0, postgres-1, postgres-2           │
│     Pas de suffixes aléatoires                                      │
│                                                                     │
│  2. STOCKAGE PERSISTANT                                             │
│     ─────────────────────                                           │
│     Chaque Pod a son propre PersistentVolumeClaim                   │
│     Le volume survit à la destruction du Pod                        │
│     postgres-0 retrouve toujours ses données                        │
│                                                                     │
│  3. DÉPLOIEMENT ORDONNÉ                                             │
│     ────────────────────                                            │
│     Création : postgres-0, puis postgres-1, puis postgres-2         │
│     Suppression : postgres-2, puis postgres-1, puis postgres-0      │
│                                                                     │
│  4. RÉSEAU STABLE                                                   │
│     ─────────────────                                               │
│     DNS : postgres-0.postgres-svc.namespace.svc.cluster.local       │
│     Toujours accessible à la même adresse                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Comparaison Deployment vs StatefulSet

Aspect Deployment StatefulSet
Noms des Pods Aléatoires (hash) Ordonnés (0, 1, 2...)
Identité Interchangeable Unique et stable
Stockage Partagé ou éphémère Dédié et persistant
Ordre de création Parallèle Séquentiel
Ordre de suppression Arbitraire Inverse de création
DNS Via Service uniquement Hostname individuel
Cas d'usage Apps stateless Bases de données, caches

Anatomie d'un StatefulSet

Structure de Base

Voici la structure d'un StatefulSet pour PostgreSQL :

apiVersion: apps/v1  
kind: StatefulSet  
metadata:  
  name: postgres
spec:
  serviceName: "postgres-svc"      # Service headless associé
  replicas: 3                       # Nombre d'instances
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:             # Template pour les volumes
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

Les Composants Clés

1. Le serviceName

spec:
  serviceName: "postgres-svc"

Ce champ lie le StatefulSet à un Service Headless qui permet la découverte DNS des Pods individuels.

2. Les replicas

spec:
  replicas: 3

Définit le nombre d'instances. Les Pods seront nommés :

  • postgres-0
  • postgres-1
  • postgres-2

3. Le volumeClaimTemplates

volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    resources:
      requests:
        storage: 10Gi

C'est la fonctionnalité clé du StatefulSet. Pour chaque Pod, Kubernetes crée automatiquement un PersistentVolumeClaim (PVC) distinct :

StatefulSet postgres (replicas: 3)
         │
         ├── postgres-0
         │      └── PVC: data-postgres-0 ──► Volume dédié
         │
         ├── postgres-1
         │      └── PVC: data-postgres-1 ──► Volume dédié
         │
         └── postgres-2
                └── PVC: data-postgres-2 ──► Volume dédié

L'Identité Stable en Détail

Nommage Prévisible des Pods

Contrairement aux Deployments, les StatefulSets utilisent un nommage ordonné et prévisible :

Format : <nom-statefulset>-<index>

Exemples :
  postgres-0    (premier Pod, index 0)
  postgres-1    (deuxième Pod, index 1)
  postgres-2    (troisième Pod, index 2)

Cette prévisibilité est cruciale pour PostgreSQL :

CONFIGURATION POSTGRESQL AVEC STATEFULSET
─────────────────────────────────────────

postgres-0  →  Primary (maître)
               Accepte lectures ET écritures

postgres-1  →  Replica (esclave)
               Réplique depuis postgres-0
               Lectures uniquement

postgres-2  →  Replica (esclave)
               Réplique depuis postgres-0
               Lectures uniquement

L'index 0 est TOUJOURS le primary  
Les scripts de configuration peuvent s'y fier  

DNS Stable avec le Service Headless

Un Service Headless est un Service sans ClusterIP qui permet d'accéder directement aux Pods :

apiVersion: v1  
kind: Service  
metadata:  
  name: postgres-svc
spec:
  clusterIP: None          # ← Headless : pas d'IP de service
  selector:
    app: postgres
  ports:
  - port: 5432

Avec ce Service, chaque Pod obtient un enregistrement DNS individuel :

ENREGISTREMENTS DNS CRÉÉS
─────────────────────────

postgres-0.postgres-svc.default.svc.cluster.local
    │         │          │      │      │
    │         │          │      │      └── Domaine cluster
    │         │          │      └── Suffixe standard
    │         │          └── Namespace
    │         └── Nom du Service
    └── Nom du Pod (hostname)

Autres Pods :  
postgres-1.postgres-svc.default.svc.cluster.local  
postgres-2.postgres-svc.default.svc.cluster.local  

Utilisation pratique :

# Connexion au Primary (postgres-0)
conn = psycopg2.connect(
    host="postgres-0.postgres-svc.default.svc.cluster.local",
    database="mydb",
    user="postgres"
)

# Connexion à un Replica spécifique
conn_replica = psycopg2.connect(
    host="postgres-1.postgres-svc.default.svc.cluster.local",
    database="mydb",
    user="postgres"
)

Hostname dans le Pod

Chaque Pod connaît son propre hostname, ce qui permet des scripts de configuration intelligents :

# Dans le conteneur postgres-0
$ hostname
postgres-0

# Dans le conteneur postgres-1
$ hostname
postgres-1

Exemple de script d'initialisation :

#!/bin/bash

# Récupérer l'index du Pod
HOSTNAME=$(hostname)  
INDEX=${HOSTNAME##*-}  # Extrait le numéro après le dernier "-"  

if [ "$INDEX" -eq 0 ]; then
    echo "Je suis le PRIMARY (postgres-0)"
    # Configuration en tant que primary
    pg_ctl start -D /var/lib/postgresql/data
else
    echo "Je suis un REPLICA (postgres-$INDEX)"
    # Configuration en tant que replica
    # Attendre que le primary soit prêt
    until pg_isready -h postgres-0.postgres-svc; do
        echo "Attente du primary..."
        sleep 2
    done
    # Démarrer la réplication
    pg_basebackup -h postgres-0.postgres-svc -D /var/lib/postgresql/data -R
    pg_ctl start -D /var/lib/postgresql/data
fi

La Persistance des Données

Le Problème de la Persistance dans Kubernetes

Par défaut, les données dans un conteneur sont éphémères :

SANS PERSISTANCE
────────────────

1. Pod postgres-0 créé
   └── Données en mémoire/filesystem conteneur

2. Pod postgres-0 crashe ou est supprimé

3. Pod postgres-0 recréé
   └── Nouveau filesystem vide !

   → TOUTES LES DONNÉES SONT PERDUES

PersistentVolume et PersistentVolumeClaim

Kubernetes utilise deux concepts pour la persistance :

Concept Description
PersistentVolume (PV) Un espace de stockage réel (disque, NFS, cloud storage)
PersistentVolumeClaim (PVC) Une demande de stockage par une application
RELATION PV / PVC
─────────────────

┌─────────────────────────────────────────────────────────────────┐
│                        INFRASTRUCTURE                           │
│                                                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │     PV 1    │  │     PV 2    │  │     PV 3    │              │
│  │   100 Gi    │  │    50 Gi    │  │   200 Gi    │              │
│  │  SSD Fast   │  │  HDD Slow   │  │  SSD Fast   │              │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘              │
│         │                │                │                     │
└─────────┼────────────────┼────────────────┼─────────────────────┘
          │                │                │
          │    BINDING     │                │
          │   (liaison)    │                │
          ▼                ▼                ▼
┌─────────────────────────────────────────────────────────────────┐
│                        APPLICATIONS                             │
│                                                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │    PVC 1    │  │    PVC 2    │  │    PVC 3    │              │
│  │"Je veux     │  │"Je veux     │  │"Je veux     │              │
│  │ 50Gi SSD"   │  │ 20Gi"       │  │ 100Gi SSD"  │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│         │                │                │                     │
│         ▼                ▼                ▼                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │ postgres-0  │  │ postgres-1  │  │ postgres-2  │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Le volumeClaimTemplates du StatefulSet

Le StatefulSet automatise la création des PVC via volumeClaimTemplates :

volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: "fast-ssd"    # Classe de stockage
    resources:
      requests:
        storage: 10Gi

À la création du StatefulSet, Kubernetes génère automatiquement :

StatefulSet: postgres (replicas: 3)
│
├── Crée Pod: postgres-0
│   └── Crée PVC: data-postgres-0
│       └── Lie à un PV de 10Gi (fast-ssd)
│
├── Crée Pod: postgres-1
│   └── Crée PVC: data-postgres-1
│       └── Lie à un PV de 10Gi (fast-ssd)
│
└── Crée Pod: postgres-2
    └── Crée PVC: data-postgres-2
        └── Lie à un PV de 10Gi (fast-ssd)

Cycle de Vie des Volumes

Point crucial : Les PVC survivent à la suppression des Pods !

CYCLE DE VIE
────────────

1. StatefulSet créé avec replicas: 3
   → postgres-0, postgres-1, postgres-2 créés
   → data-postgres-0, data-postgres-1, data-postgres-2 créés

2. postgres-1 crashe
   → Pod supprimé
   → PVC data-postgres-1 CONSERVÉ (données intactes)

3. Kubernetes recrée postgres-1
   → Nouveau Pod avec le même nom
   → Se rattache à data-postgres-1
   → RETROUVE SES DONNÉES !

4. Scale down à replicas: 1
   → postgres-2 supprimé, PVC data-postgres-2 CONSERVÉ
   → postgres-1 supprimé, PVC data-postgres-1 CONSERVÉ
   → postgres-0 reste actif

5. Scale up à replicas: 3
   → postgres-1 recréé, retrouve data-postgres-1
   → postgres-2 recréé, retrouve data-postgres-2

StorageClass : Choisir le Type de Stockage

Une StorageClass définit le type de stockage à utiliser :

# Exemple de StorageClass pour AWS EBS
apiVersion: storage.k8s.io/v1  
kind: StorageClass  
metadata:  
  name: fast-ssd
provisioner: ebs.csi.aws.com  
parameters:  
  type: gp3
  iops: "3000"
  throughput: "125"
reclaimPolicy: Retain          # Garder les données après suppression  
allowVolumeExpansion: true     # Permettre l'agrandissement  
volumeBindingMode: WaitForFirstConsumer  

StorageClasses courantes par cloud :

Cloud Provider StorageClass Type de stockage
AWS gp3, io2 EBS SSD
GCP pd-ssd, pd-balanced Persistent Disk
Azure managed-premium Azure Disk SSD
On-premise local-storage Disques locaux

L'Ordre de Déploiement

Création Séquentielle

Le StatefulSet crée les Pods un par un, dans l'ordre :

CRÉATION DES PODS
─────────────────

Temps ──────────────────────────────────────────────────────────►

      ┌──────────────┐
      │ postgres-0   │ Créé en premier
      │   Starting   │
      └──────┬───────┘
             │ Ready ✓
             ▼
             ┌──────────────┐
             │ postgres-1   │ Créé quand postgres-0 est Ready
             │   Starting   │
             └──────┬───────┘
                    │ Ready ✓
                    ▼
                    ┌──────────────┐
                    │ postgres-2   │ Créé quand postgres-1 est Ready
                    │   Starting   │
                    └──────────────┘

Pourquoi c'est important pour PostgreSQL :

  1. postgres-0 démarre en tant que primary
  2. postgres-0 initialise le cluster et devient disponible
  3. postgres-1 démarre et peut se connecter à postgres-0 pour la réplication
  4. postgres-2 démarre et se connecte également à postgres-0

Sans cet ordre, les replicas essaieraient de se connecter à un primary qui n'existe pas encore !

Suppression en Ordre Inverse

La suppression suit l'ordre inverse :

SUPPRESSION DES PODS (scale down ou delete)
───────────────────────────────────────────

Temps ──────────────────────────────────────────────────────────►

                    ┌──────────────┐
                    │ postgres-2   │ Supprimé en premier
                    │  Terminating │
                    └──────┬───────┘
                           │ Terminé ✓
                           ▼
             ┌──────────────┐
             │ postgres-1   │ Supprimé ensuite
             │  Terminating │
             └──────┬───────┘
                    │ Terminé ✓
                    ▼
      ┌──────────────┐
      │ postgres-0   │ Supprimé en dernier (le primary)
      │  Terminating │
      └──────────────┘

Pourquoi c'est important :

  • Les replicas sont supprimés avant le primary
  • Évite les erreurs de réplication vers un primary inexistant
  • Permet une dégradation gracieuse du cluster

Contrôler l'Ordre avec podManagementPolicy

Par défaut, le StatefulSet utilise OrderedReady. Vous pouvez changer ce comportement :

spec:
  podManagementPolicy: OrderedReady  # Défaut : un par un, attend Ready
  # ou
  podManagementPolicy: Parallel      # Tous en parallèle (use case avancé)
Policy Comportement Cas d'usage
OrderedReady Séquentiel, attend que chaque Pod soit Ready Bases de données, systèmes avec dépendances
Parallel Tous les Pods en même temps Applications stateful sans dépendance d'ordre

Pour PostgreSQL, gardez toujours OrderedReady.


Mise à Jour d'un StatefulSet

Stratégies de Mise à Jour

Le StatefulSet supporte deux stratégies de mise à jour :

1. RollingUpdate (Défaut)

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0    # Mettre à jour tous les Pods

Les Pods sont mis à jour un par un, en ordre inverse (du plus grand index au plus petit) :

ROLLING UPDATE
──────────────

État initial : postgres-0 (v1), postgres-1 (v1), postgres-2 (v1)

1. postgres-2 supprimé → recréé avec v2
   postgres-0 (v1), postgres-1 (v1), postgres-2 (v2) ✓

2. postgres-1 supprimé → recréé avec v2
   postgres-0 (v1), postgres-1 (v2), postgres-2 (v2) ✓

3. postgres-0 supprimé → recréé avec v2
   postgres-0 (v2), postgres-1 (v2), postgres-2 (v2) ✓

Mise à jour terminée !

2. OnDelete

spec:
  updateStrategy:
    type: OnDelete

Les Pods ne sont mis à jour que lorsqu'ils sont manuellement supprimés :

# Mise à jour manuelle du Pod 0
kubectl delete pod postgres-0
# Kubernetes recrée postgres-0 avec la nouvelle configuration

Utile pour : Mises à jour contrôlées, tests progressifs.

Partition : Mise à Jour Partielle

Le paramètre partition permet des mises à jour "canary" :

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2    # Seuls les Pods avec index >= 2 seront mis à jour
PARTITION UPDATE (partition: 2)
───────────────────────────────

Pods existants : postgres-0 (v1), postgres-1 (v1), postgres-2 (v1)

Après changement de l'image vers v2 :

• postgres-2 (index 2 >= partition 2) → Mis à jour vers v2
• postgres-1 (index 1 < partition 2)  → Reste en v1
• postgres-0 (index 0 < partition 2)  → Reste en v1

État final : postgres-0 (v1), postgres-1 (v1), postgres-2 (v2)

Vous pouvez tester v2 sur postgres-2 avant de mettre à jour les autres !

Exemple Complet : PostgreSQL avec StatefulSet

Architecture Cible

┌────────────────────────────────────────────────────────────────┐
│                    CLUSTER POSTGRESQL                          │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│         ┌───────────────────────────────────────┐              │
│         │        Service: postgres-svc          │              │
│         │         (Headless, ClusterIP: None)   │              │
│         └───────────────────┬───────────────────┘              │
│                             │                                  │
│         ┌───────────────────┼───────────────────┐              │
│         │                   │                   │              │
│         ▼                   ▼                   ▼              │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │ postgres-0  │    │ postgres-1  │    │ postgres-2  │         │
│  │  (PRIMARY)  │    │  (REPLICA)  │    │  (REPLICA)  │         │
│  │             │    │             │    │             │         │
│  │ Port: 5432  │    │ Port: 5432  │    │ Port: 5432  │         │
│  └──────┬──────┘    └──────┬──────┘    └──────┬──────┘         │
│         │                  │                  │                │
│  ┌──────┴──────┐    ┌──────┴──────┐    ┌──────┴──────┐         │
│  │     PVC     │    │     PVC     │    │     PVC     │         │
│  │data-postgres│    │data-postgres│    │data-postgres│         │
│  │     -0      │    │     -1      │    │     -2      │         │
│  │   (10Gi)    │    │   (10Gi)    │    │   (10Gi)    │         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│                                                                │
└────────────────────────────────────────────────────────────────┘

Les Manifestes Kubernetes

1. Le Service Headless

# postgres-service.yaml
apiVersion: v1  
kind: Service  
metadata:  
  name: postgres-svc
  labels:
    app: postgres
spec:
  clusterIP: None              # Headless Service
  selector:
    app: postgres
  ports:
  - name: postgresql
    port: 5432
    targetPort: 5432

2. Le ConfigMap pour la Configuration

# postgres-configmap.yaml
apiVersion: v1  
kind: ConfigMap  
metadata:  
  name: postgres-config
data:
  POSTGRES_DB: "myapp"
  POSTGRES_USER: "appuser"
  # Configuration PostgreSQL
  postgresql.conf: |
    listen_addresses = '*'
    max_connections = 100
    shared_buffers = 256MB
    wal_level = replica
    max_wal_senders = 3
    max_replication_slots = 3
    hot_standby = on

3. Le Secret pour le Mot de Passe

# postgres-secret.yaml
apiVersion: v1  
kind: Secret  
metadata:  
  name: postgres-secret
type: Opaque  
stringData:  
  POSTGRES_PASSWORD: "your-secure-password"
  REPLICATION_PASSWORD: "replication-password"

4. Le StatefulSet

# postgres-statefulset.yaml
apiVersion: apps/v1  
kind: StatefulSet  
metadata:  
  name: postgres
spec:
  serviceName: "postgres-svc"
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        ports:
        - containerPort: 5432
          name: postgresql
        env:
        - name: POSTGRES_DB
          valueFrom:
            configMapKeyRef:
              name: postgres-config
              key: POSTGRES_DB
        - name: POSTGRES_USER
          valueFrom:
            configMapKeyRef:
              name: postgres-config
              key: POSTGRES_USER
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: POSTGRES_PASSWORD
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        readinessProbe:
          exec:
            command:
            - pg_isready
            - -U
            - $(POSTGRES_USER)
            - -d
            - $(POSTGRES_DB)
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          exec:
            command:
            - pg_isready
            - -U
            - $(POSTGRES_USER)
            - -d
            - $(POSTGRES_DB)
          initialDelaySeconds: 30
          periodSeconds: 10
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "standard"    # Adapter selon votre cluster
      resources:
        requests:
          storage: 10Gi

Commandes de Déploiement

# Appliquer les manifestes
kubectl apply -f postgres-secret.yaml  
kubectl apply -f postgres-configmap.yaml  
kubectl apply -f postgres-service.yaml  
kubectl apply -f postgres-statefulset.yaml  

# Vérifier le déploiement
kubectl get statefulset postgres  
kubectl get pods -l app=postgres  
kubectl get pvc  

# Voir les logs du primary
kubectl logs postgres-0

# Se connecter au primary
kubectl exec -it postgres-0 -- psql -U appuser -d myapp

Bonnes Pratiques

1. Toujours Utiliser des Probes

readinessProbe:
  exec:
    command: ["pg_isready", "-U", "postgres"]
  initialDelaySeconds: 5
  periodSeconds: 10

livenessProbe:
  exec:
    command: ["pg_isready", "-U", "postgres"]
  initialDelaySeconds: 30
  periodSeconds: 10

Les probes garantissent que :

  • Les Pods ne reçoivent du trafic que lorsqu'ils sont prêts
  • Les Pods défaillants sont redémarrés

2. Définir des Resources

resources:
  requests:
    memory: "1Gi"
    cpu: "500m"
  limits:
    memory: "2Gi"
    cpu: "1000m"

Évite les problèmes de :

  • Pods évincés par manque de mémoire
  • Contention CPU

3. Utiliser un StorageClass Approprié

Workload StorageClass recommandée
Production SSD avec IOPS garantis
Développement Standard/balanced
Haute performance NVMe local ou io2

4. Configurer la Rétention des PVC

# Dans le StorageClass
reclaimPolicy: Retain  # Ne jamais supprimer automatiquement les données

5. Ne Pas Gérer la Réplication Manuellement

Pour la réplication PostgreSQL en production, utilisez un Operator (voir chapitre 20bis.4.2) plutôt que de scripter la réplication vous-même.


Limitations des StatefulSets

Ce que les StatefulSets NE FONT PAS

Limitation Description
Pas de réplication automatique Vous devez configurer la réplication PostgreSQL vous-même
Pas de failover automatique Si postgres-0 meurt, pas de promotion automatique d'un replica
Pas de backup intégré Les sauvegardes sont votre responsabilité
Pas de monitoring intégré Vous devez ajouter vos propres outils

Pourquoi les Operators Existent

Les StatefulSets fournissent les briques de base (identité, persistance, ordre), mais pas la logique métier spécifique à PostgreSQL.

C'est pourquoi des Operators PostgreSQL ont été créés :

STATEFULSET SEUL                    STATEFULSET + OPERATOR
────────────────                    ──────────────────────

✓ Identité stable                   ✓ Identité stable
✓ Persistance                       ✓ Persistance
✓ Ordre de déploiement              ✓ Ordre de déploiement
                                    ✓ Réplication automatique
✗ Pas de réplication auto           ✓ Failover automatique
✗ Pas de failover                   ✓ Backups planifiés
✗ Pas de backup                     ✓ Monitoring intégré
✗ Pas de monitoring                 ✓ Scaling simplifié

Le chapitre suivant (20bis.4.2) couvre les Operators en détail.


Résumé

Points Clés à Retenir

Concept Description
StatefulSet Ressource Kubernetes pour applications stateful
Identité stable Noms prévisibles (postgres-0, postgres-1, ...)
Persistance Chaque Pod a son PVC dédié qui survit aux redémarrages
Ordre Création séquentielle, suppression en ordre inverse
DNS stable Chaque Pod accessible via <pod>.<service>.<namespace>.svc.cluster.local

Quand Utiliser un StatefulSet

Utilisez un StatefulSet pour :

  • Bases de données (PostgreSQL, MySQL, MongoDB)
  • Systèmes de cache distribués (Redis Cluster)
  • Systèmes de messaging (Kafka, RabbitMQ)
  • Toute application nécessitant identité + persistance

N'utilisez PAS un StatefulSet pour :

  • Applications web stateless
  • Workers de traitement sans état
  • Toute application où les instances sont interchangeables

Ce Qui Manque pour la Production

Un StatefulSet seul n'est pas suffisant pour PostgreSQL en production. Vous aurez besoin de :

  1. Un Operator pour la gestion automatisée (failover, backups)
  2. Du monitoring (Prometheus, Grafana)
  3. Une stratégie de backup (pg_dump, pgBackRest, WAL archiving)
  4. Une configuration réseau sécurisée (NetworkPolicies, TLS)

Ces sujets sont couverts dans les chapitres suivants.


Conclusion

Les StatefulSets sont la fondation pour exécuter PostgreSQL sur Kubernetes. Ils résolvent les problèmes fondamentaux d'identité et de persistance que les Deployments ne peuvent pas adresser.

Cependant, ils ne sont qu'une partie de la solution. Pour une configuration production-ready de PostgreSQL sur Kubernetes, vous combinerez :

  • StatefulSets : Pour l'infrastructure de base
  • Operators : Pour l'automatisation et la gestion du cycle de vie
  • Services : Pour l'accès réseau
  • ConfigMaps/Secrets : Pour la configuration
  • Monitoring : Pour l'observabilité

Le chapitre suivant sur les Operators vous montrera comment automatiser tout ce que les StatefulSets ne gèrent pas nativement.


Ressources Complémentaires

Documentation Officielle

Tutoriels

  • Kubernetes : Déployer une base de données stateful
  • Comprendre le stockage dans Kubernetes
  • StatefulSets vs Deployments : Quand utiliser quoi

Outils

  • kubectl : CLI Kubernetes
  • k9s : Interface terminal pour Kubernetes
  • Lens : IDE Kubernetes

⏭️ Operators : Zalando, CloudNativePG, Crunchy