Aller au contenu
Conteneurs & Orchestration medium

StatefulSets Kubernetes : identité stable et stockage persistant

70 min de lecture

logo kubernetes

Un StatefulSet gère des Pods avec une identité stable, un stockage persistant dédié et un ordre de déploiement garanti. Contrairement à un Deployment où les Pods sont interchangeables, chaque Pod d'un StatefulSet conserve son nom, son réseau et son stockage même après un redémarrage.

Ce guide couvre la création, le headless Service, les volumeClaimTemplates, les stratégies de mise à jour et le scaling, tout ce qu'il faut pour la certification CKAD.

  • Créer un StatefulSet avec un headless Service
  • Configurer le stockage persistant avec volumeClaimTemplates
  • Comprendre le DNS stable des Pods StatefulSet
  • Mettre à jour un StatefulSet avec RollingUpdate et partitions
  • Choisir entre Deployment, StatefulSet et DaemonSet

Un StatefulSet est conçu pour les applications qui nécessitent :

BesoinExemple
Identité réseau stableChaque Pod garde son nom (app-0, app-1)
Stockage persistant dédiéChaque Pod a son propre PVC
Ordre de déploiementapp-0 démarre avant app-1
Scaling ordonnéScale down en ordre inverse

Le point commun de ces quatre familles est qu'un Pod n'y est pas interchangeable avec un autre : une réplique de base de données détient des données que ses pairs n'ont pas, un nœud etcd participe à une élection de leader qui suppose une identité fixe. C'est précisément ce que le Deployment ne sait pas garantir, et ce qui justifie le StatefulSet. Si votre application ne figure pas dans ce tableau et que ses instances sont parfaitement équivalentes, le comparatif en fin de guide vous orientera vers un Deployment, plus simple.

ApplicationPourquoi StatefulSet
Bases de donnéesPostgreSQL, MySQL, MongoDB, chaque instance a ses données
Clusters distribuésetcd, Zookeeper, Kafka, identité stable pour l'élection
Caches répliquésRedis Cluster, nœuds avec rôles spécifiques
Queues de messagesRabbitMQ, identité stable pour le clustering

Un StatefulSet nécessite un headless Service pour fournir le DNS stable de chaque Pod.

Un headless Service se distingue d'un Service classique par clusterIP: None. Au lieu de load-balancer vers les Pods, il crée une entrée DNS pour chaque Pod.

headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
clusterIP: None # Headless Service
selector:
app: nginx
ports:
- port: 80
name: web

Appliquer :

Fenêtre de terminal
kubectl apply -f headless-service.yaml

Chaque Pod d'un StatefulSet obtient un nom DNS prévisible :

<nom-pod>.<service-headless>.<namespace>.svc.cluster.local

Exemple avec un StatefulSet web et un Service nginx dans le namespace default :

PodDNS complet
web-0web-0.nginx.default.svc.cluster.local
web-1web-1.nginx.default.svc.cluster.local
web-2web-2.nginx.default.svc.cluster.local

Ce DNS stable permet aux applications distribuées de joindre un pair précis, même après un redémarrage du Pod.

Voici un StatefulSet simple avec 3 réplicas Nginx et un volume persistant de 1 Go par Pod :

statefulset-nginx.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: nginx # Référence au headless Service
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi

Points clés :

ChampRôle
serviceName: nginxLie le StatefulSet au headless Service
replicas: 3Crée 3 Pods : web-0, web-1, web-2
volumeClaimTemplatesCrée un PVC dédié par Pod (www-web-0, www-web-1, etc.)

Appliquer :

Fenêtre de terminal
kubectl apply -f statefulset-nginx.yaml

Observer le déploiement ordonné :

Fenêtre de terminal
kubectl get pods -l app=nginx -w
# NAME READY STATUS RESTARTS AGE
# web-0 0/1 Pending 0 0s
# web-0 1/1 Running 0 3s
# web-1 0/1 Pending 0 0s # Démarre après web-0 Ready
# web-1 1/1 Running 0 3s
# web-2 0/1 Pending 0 0s # Démarre après web-1 Ready
# web-2 1/1 Running 0 3s

Vérifier les PVC créés :

Fenêtre de terminal
kubectl get pvc
# NAME STATUS VOLUME CAPACITY ACCESS MODES
# www-web-0 Bound pvc-xxx 1Gi RWO
# www-web-1 Bound pvc-yyy 1Gi RWO
# www-web-2 Bound pvc-zzz 1Gi RWO

Ces commandes sont essentielles pour l'examen CKAD.

Ces commandes se lisent du survol au détail. Retenez l'abréviation sts, le nom court de statefulset accepté partout, qui fait gagner un temps précieux à l'examen. La commande la plus révélatrice est kubectl rollout status sts/web : elle bloque jusqu'à la fin d'une mise à jour et affiche sa progression Pod par Pod, ce qui permet de savoir si un déploiement avance ou reste coincé sur un ordinal donné.

Fenêtre de terminal
# Vue d'ensemble des StatefulSets
kubectl get sts
# Détails complets (événements, stratégie, réplicas)
kubectl describe sts web
# Pods du StatefulSet avec leurs nœuds
kubectl get pods -l app=nginx -o wide
# PVC associés
kubectl get pvc -l app=nginx
# État du rollout en cours
kubectl rollout status sts/web
# Historique des révisions
kubectl rollout history sts/web

Depuis un Pod du cluster, testez la résolution DNS :

Fenêtre de terminal
kubectl run dns-test --rm -it --image=busybox:1.37 -- nslookup web-0.nginx
# Server: 10.96.0.10
# Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
#
# Name: web-0.nginx
# Address 1: 10.244.1.5 web-0.nginx.default.svc.cluster.local

Kubernetes supporte deux stratégies de mise à jour pour les StatefulSets.

C'est la stratégie par défaut. Kubernetes met à jour les Pods un par un, en ordre inverse (du plus grand ordinal vers le plus petit).

spec:
updateStrategy:
type: RollingUpdate

Comportement :

  1. web-2 est mis à jour et doit devenir Ready
  2. Puis web-1 est mis à jour
  3. Puis web-0

Appliquer une mise à jour (par exemple changer l'image) :

Fenêtre de terminal
kubectl set image sts/web nginx=nginx:1.28

Suivre la progression :

Fenêtre de terminal
kubectl rollout status sts/web
# Waiting for partitioned roll out to finish: 0 out of 3 new pods have been updated...
# statefulset rolling update complete 3 pods...

Vous pouvez limiter la mise à jour aux Pods d'ordinal supérieur ou égal à une valeur avec partition. C'est utile pour les déploiements progressifs.

spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Seul web-2 sera mis à jour

Exemple : avec partition: 2 et 3 réplicas :

  • web-2 : reçoit la nouvelle spec
  • web-1 : garde l'ancienne spec
  • web-0 : garde l'ancienne spec

C'est une forme de canary deployment pour StatefulSet.

Avec OnDelete, Kubernetes ne met pas à jour automatiquement les Pods. Vous devez les supprimer manuellement pour qu'ils soient recréés avec la nouvelle spec.

spec:
updateStrategy:
type: OnDelete

Workflow :

Fenêtre de terminal
# 1. Modifier la spec du StatefulSet
kubectl apply -f statefulset-updated.yaml
# 2. Les Pods existants ne changent pas
# 3. Supprimer manuellement un Pod pour déclencher sa recréation
kubectl delete pod web-0
# 4. Le nouveau Pod est créé avec la nouvelle spec

Si une nouvelle version se comporte mal, kubectl rollout undo ramène le StatefulSet à la révision précédente enregistrée dans son historique. Le retour suit la même mécanique ordonnée qu'une mise à jour normale : les Pods reviennent à l'ancienne spec un par un, du plus grand ordinal vers le plus petit. Une limite à connaître pour la CKAD : le rollback restaure la spec des Pods, il ne restaure pas les données déjà écrites sur les volumes persistants, qui survivent au changement de version.

Fenêtre de terminal
kubectl rollout undo sts/web

Par défaut, les Pods sont créés et supprimés de manière ordonnée. Vous pouvez changer ce comportement avec podManagementPolicy.

Les Pods sont créés un par un, chaque Pod devant être Ready avant de créer le suivant. Lors du scale down, les Pods sont supprimés en ordre inverse.

spec:
podManagementPolicy: OrderedReady # Par défaut

Cas d'usage : applications où l'ordre de démarrage est critique (bases de données distribuées, clusters avec élection de leader).

Les Pods peuvent être créés ou supprimés simultanément. Kubernetes n'attend pas que chaque Pod soit Ready avant de passer au suivant.

spec:
podManagementPolicy: Parallel

Cas d'usage : applications tolérantes au démarrage simultané, où la vitesse de scaling est prioritaire sur l'ordre.

Fenêtre de terminal
kubectl scale sts web --replicas=5

Avec OrderedReady, les nouveaux Pods sont créés dans l'ordre : web-3, puis web-4.

Fenêtre de terminal
kubectl scale sts web --replicas=2

Les Pods sont supprimés en ordre inverse : web-4, web-3, web-2.

Pour supprimer le StatefulSet sans toucher aux Pods :

Fenêtre de terminal
kubectl delete sts web --cascade=orphan

Les Pods continuent de tourner, mais ne sont plus gérés par un contrôleur. Utile pour recréer un StatefulSet avec une configuration différente.

Fenêtre de terminal
kubectl delete sts web

Cela supprime le StatefulSet et ses Pods, mais pas les PVC.

Fenêtre de terminal
kubectl delete sts web
kubectl delete pvc -l app=nginx

Politique de rétention des PVC (Kubernetes 1.27+)

Section intitulée « Politique de rétention des PVC (Kubernetes 1.27+) »

Depuis Kubernetes 1.27, vous pouvez configurer le comportement des PVC lors de la suppression ou du scaling avec persistentVolumeClaimRetentionPolicy :

spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Garde les PVC quand le StatefulSet est supprimé
whenScaled: Delete # Supprime les PVC quand le Pod est supprimé par scaling
ValeurComportement
RetainLes PVC sont conservés (comportement par défaut actuel)
DeleteLes PVC sont supprimés avec le Pod/StatefulSet

La particularité du dépannage d'un StatefulSet tient à son ordre strict : un Pod n'est créé que si son prédécesseur est Ready. La deuxième ligne du tableau en découle directement, et c'est la cause la plus déroutante pour un débutant. Si web-1 reste bloqué, la vraie panne est souvent sur web-0. Le second grand foyer de problèmes est le stockage : un Pod ou un PVC coincé en Pending pointe presque toujours vers une StorageClass absente ou un provisioner qui ne répond pas, à confirmer avec kubectl describe pvc.

SymptômeCause probableSolution
Pod PendingPas de PV disponible ou StorageClass manquantekubectl describe pod + vérifier StorageClass
Pod ne démarre pasPod précédent pas ReadyVérifier l'état du Pod d'ordinal inférieur
PVC PendingProvisioner défaillant ou quota atteintkubectl describe pvc
DNS non résoluHeadless Service manquant ou mal configuréVérifier clusterIP: None
Mise à jour bloquéePod en erreurkubectl describe pod + logs

Suivez ces commandes dans l'ordre du plus large au plus ciblé. Commencez toujours par la section Events de kubectl describe sts web, car le contrôleur y consigne pourquoi il n'avance pas (attente d'un PVC, échec de programmation). Descendez ensuite au Pod bloquant, dont l'ordinal est le plus petit parmi ceux qui ne sont pas Ready, puis à son PVC. Le --timeout=60s sur rollout status évite d'attendre indéfiniment un déploiement qui ne se terminera jamais.

Fenêtre de terminal
# Voir les événements du StatefulSet
kubectl describe sts web
# Vérifier un Pod spécifique
kubectl describe pod web-0
kubectl logs web-0
# Vérifier les PVC
kubectl describe pvc www-web-0
# État du rollout
kubectl rollout status sts/web --timeout=60s

Comparatif : Deployment vs StatefulSet vs DaemonSet

Section intitulée « Comparatif : Deployment vs StatefulSet vs DaemonSet »

Choisir la bonne ressource est essentiel pour la CKAD.

CritèreDeploymentStatefulSetDaemonSet
Identité des PodsAnonymeStable (app-0, app-1)Anonyme
NommageHash aléatoireOrdinal incrémentalHash aléatoire
StockagePVC partagé ou sans étatPVC dédié par PodGénéralement hostPath
Ordre de déploiementNon garantiOrdonné (par défaut)Non garanti
DNS stableNonOui (headless Service)Non
ScalingHorizontal libreOrdonnéSuit les nœuds
Cas d'usageAPIs, web statelessBases de données, clustersAgents système

Cet arbre reprend le tableau sous forme de questions à se poser dans l'ordre. La première tranche le cas le plus net : une charge qui doit tourner sur chaque nœud (agent de logs, exportateur de métriques) relève du DaemonSet, sans discussion. Ne descendez vers le StatefulSet que si vous répondez oui à la combinaison identité stable ET stockage dédié par instance : l'un sans l'autre ne le justifie pas, et un Deployment avec un volume partagé suffit souvent.

Arbre de décision : si l'application doit tourner sur chaque nœud éligible, choisir un DaemonSet ; sinon, si elle réclame une identité stable et un stockage persistant par instance, choisir un StatefulSet ; dans tous les autres cas, choisir un Deployment

Voici un exemple plus complet avec anti-affinité pour répartir les Pods sur différents nœuds :

statefulset-distributed.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: distributed-app
spec:
serviceName: distributed-app
replicas: 3
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
selector:
matchLabels:
app: distributed-app
template:
metadata:
labels:
app: distributed-app
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: distributed-app
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 30
containers:
- name: app
image: busybox:1.37
command: ["sh", "-c", "echo 'Pod $(hostname) started'; sleep infinity"]
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
exec:
command: ["cat", "/tmp/ready"]
initialDelaySeconds: 5
periodSeconds: 5
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi

Points clés :

  • podAntiAffinity : empêche deux Pods d'être sur le même nœud
  • terminationGracePeriodSeconds: 30 : laisse le temps à l'application de se terminer proprement
  • livenessProbe et readinessProbe : essentiels pour les rolling updates ordonnés
  1. Un StatefulSet fournit une identité stable (app-0, app-1) et un stockage persistant dédié par Pod
  2. Il nécessite un headless Service (clusterIP: None) pour le DNS stable
  3. La stratégie de mise à jour par défaut est RollingUpdate, pas OnDelete
  4. volumeClaimTemplates crée automatiquement un PVC par Pod
  5. Les mises à jour se font en ordre inverse (du plus grand ordinal vers le plus petit)
  6. podManagementPolicy: Parallel accélère le scaling mais pas les updates
  7. Les PVC ne sont pas supprimés automatiquement lors d'un scale down ou d'une suppression du StatefulSet (sauf avec persistentVolumeClaimRetentionPolicy)
  8. Le DNS d'un Pod est <pod>.<service>.<namespace>.svc.cluster.local

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
90% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Scheduling avancé : La répartition des réplicas sur des nœuds et des zones distincts.
  • Introduction au stockage : Les PersistentVolumes qui se cachent derrière les volumeClaimTemplates.
  • StorageClass : Le provisionnement dynamique des volumes réclamés par chaque réplica.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn