Un Deployment traite ses pods comme strictement interchangeables : peu importe lequel meurt, un autre le remplace avec un nom généré aléatoirement, et rien ne garantit qu’il retrouve le même stockage que son prédécesseur. Ce modèle convient parfaitement à une API stateless. Il casse immédiatement pour une base de données : chaque nœud d’un cluster PostgreSQL ou d’un cluster Kafka a une identité et des données qui lui sont propres, pas interchangeables avec celles du pod voisin.

Une identité réseau stable, pas un nom aléatoire

Un StatefulSet nomme ses pods par index ordinal prévisible, pas par suffixe aléatoire :

postgres-0
postgres-1
postgres-2

Ce nom reste identique à travers les redémarrages : postgres-1 qui redémarre revient sous le nom postgres-1, jamais sous un nouveau nom généré. Combiné à un Service headless, chaque pod obtient une adresse DNS stable et prévisible (postgres-1.postgres.default.svc.cluster.local), ce qu’aucun Deployment ne garantit.

Un volume dédié par replica, pas partagé

C’est le point qui distingue le plus concrètement un StatefulSet : chaque replica reçoit son propre PersistentVolumeClaim, généré depuis un template, pas un volume partagé entre toutes les instances.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        storageClassName: longhorn
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi

postgres-0 reçoit son propre PVC data-postgres-0, postgres-1 reçoit data-postgres-1, chacun provisionné automatiquement (via un StorageClass comme Longhorn sur un cluster on-premise). Un redémarrage de postgres-1 le rattache à son propre volume, jamais à celui d’un autre pod : c’est exactement la garantie qu’un Deployment, avec un volume partagé ou aucun volume persistant, ne peut pas offrir.

Un déploiement et un scaling ordonnés, pas parallèles

Par défaut, un StatefulSet crée et met à jour ses pods dans l’ordre strict des index (postgres-0 avant postgres-1, qui doit être prêt avant postgres-2), et les supprime dans l’ordre inverse. Cette séquentialité compte pour des systèmes où l’ordre d’initialisation a un sens réel (un nœud primaire qui doit exister avant que des répliques ne le rejoignent). podManagementPolicy: Parallel désactive cette contrainte quand l’application gérée n’en a pas besoin, au bénéfice d’un déploiement plus rapide.

Ce qu’un StatefulSet ne fait pas à votre place

Un StatefulSet organise l’identité et le stockage ; il ne configure ni la réplication entre les instances, ni l’élection d’un primaire, ni la reprise après panne applicative. Ces responsabilités restent celles de l’application elle-même ou d’un opérateur dédié (un Operator PostgreSQL, par exemple, qui pilote un StatefulSet en dessous tout en gérant la logique métier de réplication). Poser un StatefulSet nu pour une base de données sans cette couche applicative laisse trois instances isolées, chacune avec ses propres données, sans jamais se synchroniser entre elles.

Le vrai critère : identité et stockage individuels, pas juste « c’est une base de données »

La question qui décide n’est pas « est-ce que ça stocke des données » mais « chaque instance a-t-elle besoin de sa propre identité stable et de son propre stockage, distinct des autres ». Une application stateless qui lit et écrit dans une base externe (elle-même potentiellement un StatefulSet) reste un Deployment classique, quelle que soit la quantité de données qu’elle manipule au final.

À retenir

Un Deployment convient à tout ce qui est interchangeable ; un StatefulSet existe précisément pour ce qui ne l’est pas, via une identité réseau stable et un volume dédié par replica. Le déploiement ordonné par défaut respecte les dépendances d’initialisation réelles, désactivable si l’application n’en a pas besoin. Un StatefulSet organise l’infrastructure, jamais la logique de réplication applicative, qui reste la responsabilité de l’application ou d’un Operator dédié. Ce socle de stockage stateful complète directement ce que couvre Longhorn côté provisionnement dans une migration Kubernetes.