Déployer un StatefulSet sur Kubernetes — Guide pas-à-pas
Manifestes, déploiement, vérifications et tests (persistance, identité réseau stable, redémarrage, scale, update, debug).
0) Prérequis
kubectlconfiguré et pointant sur ton cluster :kubectl version --client && kubectl get nodes.- Optionnel : créer un namespace de test pour éviter de polluer
default:kubectl create namespace demo-ss || true kubectl config set-context --current --namespace=demo-ss
- Connaître ton
StorageClass(si tu veux PV dynamiques) :kubectl get storageclass.
1) Manifests (choisis l’option adaptée)
Deux options : A pour clusters avec provisioner dynamique (ex. GKE, EKS, Minikube avec standard), B pour cluster local sans provisioner (hostPath / PV statiques).
Option A — StatefulSet + PVC dynamique (StorageClass standard)
Enregistrer dans statefulset-nginx.yaml
# statefulset-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
labels:
app: nginx
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
name: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
serviceName: "nginx-headless"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
name: http
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 1Gi
Option B — Variante locale / Minikube / hostPath (si pas de provisioner)
Enregistrer dans statefulset-nginx-hostpath.yaml. Note : pour hostPath pur, un administrateur doit créer des PV statiques pointant vers des chemins sur chaque nœud.
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
name: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
serviceName: "nginx-headless"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
name: http
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ""
resources:
requests:
storage: 1Gi
PVC reste en Pending, vérifie le StorageClass ou crée des PV statiques (hostPath) correspondant aux réclamations.2) Déployer
Appliquer le manifest choisi :
kubectl apply -f statefulset-nginx.yaml # ou kubectl apply -f statefulset-nginx-hostpath.yaml
3) Vérifications initiales
Surveiller la création et vérifier que les PVC sont Bound :
kubectl get statefulset kubectl get sts -o wide kubectl get pods -l app=nginx -o wide kubectl get pvc kubectl get pv kubectl describe sts nginx kubectl describe pod nginx-0 kubectl describe pvc www-nginx-0
Résultat attendu : pods nginx-0, nginx-1, nginx-2 en Running; 3 PVCs www-nginx-0/1/2 en Bound.
4) Tests pratiques — identité réseau & persistance
Test A — identité réseau stable (hostname + FQDN)
Écrire un fichier depuis nginx-0 et lire depuis nginx-1 via le FQDN :
kubectl exec -it nginx-0 -- /bin/sh -c "hostname; echo 'hello-from-nginx-0' > /usr/share/nginx/html/index.html; cat /usr/share/nginx/html/index.html" kubectl exec -it nginx-1 -- /bin/sh -c "wget -qO- http://nginx-0.nginx-headless:80 || curl -s http://nginx-0.nginx-headless:80"
Test B — persistance après suppression d’un Pod
Écrire un fichier unique sur nginx-1, supprimer le pod, attendre la recréation puis vérifier :
kubectl exec -it nginx-1 -- /bin/sh -c "echo 'persist-$(date +%s)' > /usr/share/nginx/html/unique.txt; cat /usr/share/nginx/html/unique.txt" kubectl delete pod nginx-1 # attendre que nginx-1 soit recréé (kubectl get pods -w) kubectl exec -it nginx-1 -- cat /usr/share/nginx/html/unique.txt
Si le contenu est toujours présent → la PVC/PV a correctement persisté les données.
Test C — vérifier que chaque réplique a son PVC/PV
kubectl get pvc -o wide kubectl describe pvc www-nginx-0 kubectl describe pvc www-nginx-1 kubectl describe pvc www-nginx-2
5) Exposer un pod/service pour tests externes
Option rapide — port-forward
kubectl port-forward pod/nginx-0 8080:80 # puis depuis ta machine curl http://127.0.0.1:8080
Option NodePort (exposer au niveau nœud)
# nginx-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30080
# appliquer
kubectl apply -f nginx-nodeport.yaml
# accéder via NodeIP:30080
6) Scale & Update
Scale
kubectl scale sts nginx --replicas=5 kubectl get pods -l app=nginx
Mise à jour d’image (rolling update ordonné)
# patcher l'image (exemple)
kubectl patch sts nginx -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.26-alpine"}]}}}}'
# ou modifier YAML et appliquer : kubectl apply -f statefulset-nginx.yaml
Par défaut, StatefulSet met à jour pod par pod en respectant l’ordre (ordinal) ; pour contrôler la partition, utilisez spec.updateStrategy.rollingUpdate.partition.
7) Nettoyage
kubectl delete -f statefulset-nginx.yaml # supprime StatefulSet et Service kubectl delete pvc --all # supprime PVCs (optionnel, attention aux PV statiques) kubectl delete pv --all # si PV statiques, supprimer manuellement si nécessaire kubectl delete service nginx-nodeport # si créé
8) Résolution des problèmes courants & debugging
PVC en Pending
kubectl get storageclass kubectl describe pvc # si pas de StorageClass compatible -> créer StorageClass ou PV statiques
Pod stuck in ContainerCreating
kubectl describe pod kubectl get events --sort-by='.lastTimestamp' # vérifier logs du provisioner/csi-driver sur le namespace kube-system si volume attach échoue kubectl -n kube-system get pods | grep csi kubectl -n kube-system logs # si présent
Reverse Path Filter (rp_filter)
Sur l’hôte, si paquets asymétriques sont rejetés :
sysctl net.ipv4.conf.all.rp_filter # pour tester (désactiver temporairement) sudo sysctl -w net.ipv4.conf.all.rp_filter=0 sudo sysctl -w net.ipv4.conf.default.rp_filter=0 # persister via /etc/sysctl.d/99-rpfilter.conf si nécessaire
PV hostPath monté sur un autre nœud
Les PV de type hostPath existent physiquement sur un nœud. Si le scheduler place un pod sur un autre nœud, le volume ne pourra pas être monté. Solution : utiliser nodeAffinity sur le pod ou provisionner PV dynamiques.
9) Session exemple complète (copier-coller)
# 1. namespace (optionnel) kubectl create ns demo-ss kubectl config set-context --current --namespace=demo-ss # 2. apply manifest kubectl apply -f statefulset-nginx.yaml # 3. watch status kubectl get sts -w kubectl get pods -l app=nginx -o wide # 4. check PVCs kubectl get pvc kubectl describe pvc www-nginx-0 # 5. test persistence kubectl exec -it nginx-1 -- /bin/sh -c "echo 'hello-$(date +%s)' > /usr/share/nginx/html/test.txt; cat /usr/share/nginx/html/test.txt" kubectl delete pod nginx-1 # attendre que nginx-1 se recrée : kubectl get pods -w # vérifier le fichier apres recréation : kubectl exec -it nginx-1 -- cat /usr/share/nginx/html/test.txt # 6. port-forward for quick access kubectl port-forward pod/nginx-0 8080:80 curl http://127.0.0.1:8080