StatefulSets et stockage persistant dans Kubernetes
Déployez des applications stateful avec un scaling ordonné et des données persistantes
Kubernetes StatefulSets sont la solution idéale pour gérer les applications stateful qui nécessitent des identités stables, un stockage persistant et des schémas de déploiement ordonnés — essentielles pour les bases de données, les systèmes distribués et les couches de mise en cache.
Si vous êtes nouveau dans Kubernetes ou que vous configurez un cluster, envisagez d’explorer des distributions Kubernetes comme k3s ou MicroK8s pour le développement, ou l’installation de Kubernetes avec Kubespray pour des clusters de production.
Cette belle image a été générée par le modèle AI Flux 1 dev.
Qu’est-ce qu’un StatefulSet ?
Les StatefulSets sont un objet API de charge de travail Kubernetes conçu spécifiquement pour gérer les applications stateful. Contrairement aux Deployments qui traitent tous les pods comme interchangeables, les StatefulSets maintiennent une identité unique pour chaque pod avec des garanties concernant l’ordre et l’unicité.
Fonctionnalités clés :
- Identifiants réseau stables : Chaque pod reçoit un hôte nommé prédéfini qui persiste après les redémarrages
- Stockage persistant : Des PersistentVolumeClaims dédiés qui suivent les pods lors des reschedules
- Déploiement ordonné : Les pods sont créés séquentiellement (0, 1, 2…) et terminés dans l’ordre inverse
- Mises à jour ordonnées : Les mises à jour en roulement se déroulent dans l’ordre, assurant la stabilité de l’application
Les StatefulSets sont essentiels pour les applications comme PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis et etcd — tout workload où l’identité du pod et la persistance des données sont importantes.
Comprendre le stockage persistant dans Kubernetes
Kubernetes fournit une couche d’abstraction de stockage sophistiquée qui découple la gestion du stockage du cycle de vie des pods :
Composants de stockage
PersistentVolume (PV) : Un morceau de stockage dans le cluster provisionné par un administrateur ou créé dynamiquement via un StorageClass. Les PV existent indépendamment des pods.
PersistentVolumeClaim (PVC) : Une demande de stockage par un pod. Les PVC se lient à des PV disponibles qui correspondent à leurs exigences (taille, mode d’accès, classe de stockage).
StorageClass : Définit différentes “classes” de stockage avec divers fournisseurs (AWS EBS, GCE PD, Azure Disk, NFS, etc.) et des paramètres comme la réplication, les niveaux de performance et les politiques de sauvegarde.
Modes d’accès
- ReadWriteOnce (RWO) : Volume monté en lecture-écriture par un seul nœud
- ReadOnlyMany (ROX) : Volume monté en lecture seule par plusieurs nœuds
- ReadWriteMany (RWX) : Volume monté en lecture-écriture par plusieurs nœuds (nécessite des backends de stockage spécifiques)
Architecture de stockage des StatefulSets
Les StatefulSets utilisent volumeClaimTemplates pour créer automatiquement des PersistentVolumeClaims pour chaque réplica de pod. Cela diffère fondamentalement des Deployments :
Fonctionnement des volumeClaimTemplates
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
Lorsque vous créez ce StatefulSet :
- Kubernetes crée le pod
mysql-0et le PVCdata-mysql-0 - Puis crée le pod
mysql-1et le PVCdata-mysql-1 - Enfin crée le pod
mysql-2et le PVCdata-mysql-2
Chaque pod reçoit son propre volume persistant de 10 Go. Si mysql-1 est supprimé ou reschedulé, Kubernetes le recrée et réattache le même PVC data-mysql-1, préservant ainsi tous les données.
Créer un Service Headless pour les StatefulSets
Les StatefulSets nécessitent un Service Headless pour fournir des identités réseau stables :
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None # Cela rend le service headless
selector:
app: mysql
ports:
- port: 3306
name: mysql
Cela crée des entrées DNS pour chaque pod :
mysql-0.mysql-service.default.svc.cluster.localmysql-1.mysql-service.default.svc.cluster.localmysql-2.mysql-service.default.svc.cluster.local
Les applications peuvent se connecter directement à des instances de pod spécifiques en utilisant ces noms DNS stables.
Schémas de déploiement des StatefulSets
Pour les équipes gérant des déploiements Kubernetes complexes, les Helm Charts offrent un moyen puissant de packager et de déployer des StatefulSets avec du templating, la gestion des versions et la gestion des dépendances. Helm simplifie la gestion des configurations des StatefulSets à travers différents environnements.
Échelle ordonnée
Lors de l’échelle de 3 à 5 réplicas :
kubectl scale statefulset mysql --replicas=5
Kubernetes crée les pods dans l’ordre : mysql-3 → attend que Ready → mysql-4
Lors de l’échelle de 5 à 3 réplicas :
kubectl scale statefulset mysql --replicas=3
Kubernetes termine dans l’ordre inverse : mysql-4 → attend la terminaison → mysql-3
Mises à jour en roulement
Les StatefulSets prennent en charge deux stratégies de mise à jour :
OnDelete : Mises à jour manuelles — les pods ne sont mis à jour que lorsqu’on les supprime RollingUpdate : Mises à jour automatiques en séquence dans l’ordre inverse des ordinaux
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Ne mettre à jour que les pods avec un ordinal >= 2
Le paramètre partition permet les déploiements canary — vous pouvez mettre à jour les pods à haut numéro d’abord et tester avant de les déployer sur tous les réplicas.
Bonnes pratiques de stockage
Provisionnement dynamique
Utilisez toujours des StorageClasses pour le provisionnement dynamique des volumes :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Retain
allowVolumeExpansion : Active la redimension des PVC sans les recréer
reclaimPolicy : Retain garde les données du PV après la suppression du PVC, Delete les supprime automatiquement
Politiques de rétention des PVC
Kubernetes 1.23+ prend en charge persistentVolumeClaimRetentionPolicy :
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Garde les PVC lors de la suppression du StatefulSet
whenScaled: Delete # Supprime les PVC lors de l'échelle
Options :
- Retain : Garde les PVC (comportement par défaut, le plus sûr)
- Delete : Supprime automatiquement les PVC (utile pour les environnements de développement)
Stratégies de sauvegarde
Snapshots de volume : Utilisez des ressources VolumeSnapshot pour créer des sauvegardes ponctuelles
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20251113
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: data-mysql-0
Sauvegardes au niveau de l’application : Utilisez des outils comme mysqldump, pg_dump ou Velero pour les sauvegardes spécifiques aux bases de données
Réplication distribuée : Configurez la réplication au niveau de l’application (réplication MySQL, réplication en flux continu PostgreSQL) comme première ligne de défense
Cas d’utilisation réels
Cluster de base de données (PostgreSQL)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 20Gi
Cache distribué (Redis)
Pour les clusters Redis, vous avez besoin à la fois d’un StatefulSet et d’une configuration soigneuse :
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis-service
replicas: 6
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- "--appendonly"
- "yes"
- "--appendfsync"
- "everysec"
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
File d’attente de messages (Kafka)
Kafka nécessite à la fois un stockage persistant pour les journaux et des identités réseau stables pour la coordination des brokers :
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka-service
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5.0
ports:
- containerPort: 9092
name: kafka
env:
- name: KAFKA_BROKER_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: data
mountPath: /var/lib/kafka/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
Surveillance et dépannage
Pour une référence complète des commandes Kubernetes utilisées dans cette section, consultez le Kubernetes Cheatsheet.
Vérifier l’état des StatefulSets
# Afficher les détails du StatefulSet
kubectl get statefulset mysql
kubectl describe statefulset mysql
# Vérifier l'ordre de création et l'état des pods
kubectl get pods -l app=mysql -w
# Afficher l'état des PVC
kubectl get pvc
kubectl describe pvc data-mysql-0
Problèmes courants
Pod bloqué en état Pending : Vérifiez l’état du PVC et la disponibilité du stockage
kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'
Stockage plein : Étendez le PVC si le StorageClass le permet
kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
Pod ne se termine pas : Vérifiez les verrous au niveau de l’application ou les problèmes de démontage du volume
kubectl delete pod mysql-0 --grace-period=0 --force
Métriques à surveiller
- Utilisation du stockage : Surveillez la capacité et le pourcentage d’utilisation du PVC
- Performance I/O : Suivez les IOPS, le débit et la latence
- Redémarrages de pod : Les redémarrages fréquents peuvent indiquer des problèmes de stockage
- Temps de liaison du PVC : Un temps de liaison lent indique des problèmes de provisionnement
Stratégies de migration
Lors de la migration vers des StatefulSets, assurez-vous que votre cluster Kubernetes est correctement configuré. Pour les configurations de laboratoire ou les petits clusters, consultez notre comparaison approfondie des distributions Kubernetes pour choisir la plateforme adaptée à vos besoins de charge.
De Deployment à StatefulSet
- Créer un StatefulSet avec des volumeClaimTemplates
- Échelonner le Deployment de manière gracieuse
- Restaurer les données depuis les sauvegardes vers les pods du StatefulSet
- Mettre à jour les références DNS/Service
- Supprimer l’ancien Deployment et les PVCs
Sauvegarde avant la migration
# Créer des snapshots des PVC existants
kubectl get pvc -o yaml > pvc-backup.yaml
# Créer des snapshots de volume
kubectl apply -f volume-snapshot.yaml
Considérations de sécurité
Chiffrement du stockage
Activez le chiffrement au repos en utilisant les paramètres de StorageClass :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: encrypted-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd-1234
Contrôle d’accès
Utilisez RBAC pour limiter qui peut créer/modifier des StatefulSets et des PVCs :
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: statefulset-manager
rules:
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete"]
Politiques réseau
Restreignez la communication pod à pod :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-network-policy
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3306
Optimisation des performances
Niveaux de performance du stockage
Choisissez les bonnes StorageClasses en fonction de la charge :
- Haute IOPS : Bases de données avec des lectures/écritures aléatoires lourdes (gp3, io2)
- Haute bande passante : Agrégation de journaux, analyse (st1, sc1)
- Équilibré : Applications générales (gp3)
Répartition des pods
Utilisez des anti-affinités de pod pour répartir les pods des StatefulSets entre les zones de disponibilité :
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: topology.kubernetes.io/zone
Demandes et limites de ressources
Définissez des ressources appropriées pour une performance cohérente :
resources:
requests:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
limits:
cpu: "4"
memory: "8Gi"
ephemeral-storage: "20Gi"
Schémas avancés
Application stateful avec des conteneurs initiaux
Utilisez des conteneurs initiaux pour l’initialisation de la base de données :
spec:
template:
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
if [[ ! -d /var/lib/mysql/mysql ]]; then
mysqld --initialize-insecure --datadir=/var/lib/mysql
fi
volumeMounts:
- name: data
mountPath: /var/lib/mysql
Pods à plusieurs conteneurs pour les sidecars
Ajoutez des sidecars de sauvegarde ou des agents de surveillance :
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
# ... configuration mysql ...
- name: backup-sidecar
image: mysql-backup:latest
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readOnly: true
ConfigMaps pour la configuration dynamique
Séparez la configuration de la définition du StatefulSet :
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
max_connections=200
innodb_buffer_pool_size=2G
---
spec:
template:
spec:
containers:
- name: mysql
volumeMounts:
- name: config
mountPath: /etc/mysql/conf.d
volumes:
- name: config
configMap:
name: mysql-config
Liens utiles
- Documentation Kubernetes StatefulSets
- Volumes persistants
- Classes de stockage
- Snapshots de volume
- Bonnes pratiques pour les StatefulSets
- Drivers CSI
- Solutions de sauvegarde Velero
- Stockage Rook Ceph
- Kubernetes Cheatsheet
- Installer Kubernetes avec Kubespray
- Comparaison des distributions Kubernetes pour un laboratoire à 3 nœuds
- Distributions Kubernetes - aperçu rapide de kubeadm, k3s, MicroK8s, Minikube, Talos Linux et RKE2
- Maîtrisez les Helm Charts : Guide de gestion des paquets Kubernetes