Retour au cours

infra / kubernetes

StatefulSets

Leçon 161 exercice

Explication

Ce que vous allez apprendre

  • Comprendre la différence entre des Pods interchangeables (Deployment) et une identité stable (StatefulSet)
  • Identifier les trois garanties qu'apporte un StatefulSet (noms, stockage, ordre)
  • Comprendre pourquoi un Service headless est indispensable au fonctionnement d'un StatefulSet
  • Reconnaître les cas d'usage réels d'un StatefulSet (bases de données, Kafka, etcd)
  • Savoir quand un Deployment classique reste préférable à un StatefulSet

Dans quel contexte ?

Une équipe déploie un cluster PostgreSQL avec un simple Deployment à 3 replicas, comme elle le ferait pour une API web classique. Résultat : à chaque redémarrage, les Pods reçoivent de nouveaux noms aléatoires et perdent leur volume de stockage précédent, ce qui casse la réplication entre les instances de la base — chaque nouvelle instance repart de zéro sans données. Le StatefulSet existe précisément pour ce genre de charge : il garantit des noms stables (postgres-0, postgres-1, postgres-2), un volume dédié qui suit chaque instance, et un ordre de création prévisible.

Ce que les Pods interchangeables ne permettent pas

D'abord, tout le cours jusqu'ici a valorisé des Pods interchangeables et sans identité propre : peu importe lequel répond, un Deployment peut les remplacer librement.

Un besoin exactement inverse

Mais certaines applications, en particulier les bases de données distribuées ou les systèmes de clustering, ont besoin exactement du contraire : une identité stable. Chaque instance doit garder le même nom, le même stockage, et parfois connaître son rang dans le groupe.

L'objet conçu pour ce besoin

C'est le rôle du StatefulSet. Il apporte trois garanties qu'un Deployment classique ne fournit pas.

Garantie du StatefulSetCe que ça évite
Noms de Pods stables (postgres-0, postgres-1...)Des noms aléatoires à chaque redémarrage
Stockage dédié et persistant par instanceLa perte de données lors d'une recréation
Ordre garanti de création/suppressionUne instance secondaire créée avant la primaire

Première garantie : des noms prévisibles

D'abord, des noms de Pods prévisibles et stables, comme postgres-0, postgres-1, postgres-2, qui restent les mêmes même après un redémarrage, contrairement aux noms générés aléatoirement d'un Deployment.

Deuxième garantie : un stockage dédié

Ensuite, un stockage dédié et persistant par instance : chaque replica garde son propre volume, jamais partagé, et le retrouve même après recréation.

Troisième garantie : un ordre garanti

Enfin, un ordre garanti de création et de suppression, essentiel pour des systèmes où l'instance numéro 0 doit exister avant les suivantes, un cas fréquent dans les bases distribuées.

Piège fréquent

Utiliser un StatefulSet "par défaut" pour une application web classique sans état ajoute de la complexité pour rien : mises à jour plus lentes, gestion plus délicate, sans aucun bénéfice réel. Réserve le StatefulSet aux charges qui ont vraiment besoin d'une identité stable.

Une brique réseau facile à oublier

Pour que chaque Pod ait sa propre adresse DNS individuelle, et non une adresse partagée par le groupe, un StatefulSet a besoin d'un Service particulier dit "headless", sans IP virtuelle propre.

Ce qui casse sans cette brique

Sans ce Service headless, l'identité réseau individuelle promise par le StatefulSet ne fonctionne tout simplement pas.

Bonne pratique

Avant de choisir un StatefulSet, pose-toi la question inverse : "mon application a-t-elle vraiment besoin qu'une instance précise garde son identité et son stockage ?" Si la réponse est non, un Deployment classique reste plus simple à opérer et suffit largement.

Un outil à réserver aux vrais besoins d'état

Le StatefulSet est plus complexe à opérer qu'un Deployment, avec des mises à jour plus lentes et une gestion plus délicate. Réserve-le aux charges réellement stateful, comme les bases de données, Kafka ou etcd ; pour une application web classique, un Deployment simple reste le bon choix.

La leçon suivante quitte les charges permanentes pour un besoin différent : des tâches qui doivent s'exécuter une fois puis s'arrêter, avec les Jobs et CronJobs.

Commandes & code

StatefulSets

Pour les workloads avec état qui ont besoin d'une identité stable (nom, stockage, ordre) : bases de données, clusters distribués.

yaml
# headless service - requis par un StatefulSet pour l'identité réseau stable de chaque Pod
apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
spec:
  clusterIP: None       # "headless" : pas de load balancing, DNS pointe direct vers chaque Pod
  selector:
    app: postgres
  ports:
    - port: 5432
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 3
  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
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
  # Chaque replica obtient SON PROPRE PVC, créé à partir de ce template
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 20Gi
bash
kubectl apply -f postgres-statefulset.yaml
kubectl get pods -l app=postgres
# Pods nommés de façon PRÉVISIBLE et STABLE : postgres-0, postgres-1, postgres-2
# (contrairement à un Deployment où les noms sont aléatoires)

# DNS individuel et stable par Pod (grâce au service headless)
# postgres-0.postgres-headless.default.svc.cluster.local

kubectl get pvc
# data-postgres-0, data-postgres-1, data-postgres-2 : un PVC dédié PAR Pod, jamais partagé
bash
# Ordre garanti : création/suppression séquentielle (postgres-0 avant postgres-1, etc.)
kubectl delete pod postgres-1
# Kubernetes recrée "postgres-1" avec la MÊME identité et le MÊME PVC (pas un nouveau volume vide)

Résumé

  • StatefulSet = identité stable (nom, DNS, PVC dédié) + ordre de création/suppression garanti.
  • Nécessite un Service headless (clusterIP: None) pour le DNS individuel par Pod.
  • volumeClaimTemplates crée un PVC unique et persistant par replica, jamais partagé.
  • À réserver aux vraies charges avec état (bases, Kafka, etcd) ; sinon un Deployment suffit.

Exercices pratiques

1 disponible
1

Mission : réparer un cluster Postgres qui a perdu son DNS interne

Objectif : Diagnostiquer et corriger un StatefulSet Postgres dont le Service headless manque, puis vérifier que l'identité et le stockage de chaque replica survivent à une recréation.

Contexte

Un ancien Deployment Postgres a été converti en StatefulSet postgres à 3 replicas, mais le Service postgres-headless référencé dans spec.serviceName n'a jamais été créé. Résultat : postgres-1 ne parvient pas à joindre postgres-0 pour la réplication, alors que les noms de Pods (postgres-0, postgres-1, postgres-2) semblent pourtant corrects dans kubectl get pods.

Résoudre l’exercice →