Ce module explore la gestion du stockage persistant dans Kubernetes, essentiel pour les applications stateful qui doivent préserver leurs données au-delà du cycle de vie des conteneurs.
Conteneurs = Éphémères par design :
Applications nécessitant une persistance :
Usage : Partage de données entre conteneurs d'un même Pod
apiVersion: v1
kind: Pod
metadata:
name: shared-storage-pod
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "while true; do echo $(date) > /shared/timestamp; sleep 10; done"]
volumeMounts:
- name: shared-data
mountPath: /shared
- name: reader
image: busybox:1.36
command: ["sh", "-c", "while true; do cat /shared/timestamp 2>/dev/null || echo 'No data'; sleep 5; done"]
volumeMounts:
- name: shared-data
mountPath: /shared
volumes:
- name: shared-data
emptyDir:
sizeLimit: 1Gi # Limite de taille (optionnel)
medium: Memory # Stockage en RAM (optionnel)
Caractéristiques :
Créer le fichier shared-demo.yaml :
apiVersion: v1
kind: Pod
metadata:
name: shared-demo
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "echo 'Hello from writer' > /shared/data.txt; sleep 3600"]
volumeMounts:
- name: shared-storage
mountPath: /shared
- name: reader
image: busybox:1.36
command: ["sh", "-c", "sleep 10; cat /shared/data.txt; sleep 3600"]
volumeMounts:
- name: shared-storage
mountPath: /shared
volumes:
- name: shared-storage
emptyDir: {}
# Appliquer le Pod
kubectl apply -f shared-demo.yaml
# Vérifier le partage
kubectl logs shared-demo -c reader # Affiche "Hello from writer"
# Nettoyer
kubectl delete pod shared-demo
Usage : Accéder aux fichiers/répertoires du Node (à éviter en production)
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: app
image: nginx:1.29.5
volumeMounts:
- name: host-logs
mountPath: /host/var/log
readOnly: true
volumes:
- name: host-logs
hostPath:
path: /var/log # Chemin sur le Node
type: Directory # Type requis
Types hostPath :
Directory : Doit être un répertoire existantFile : Doit être un fichier existantDirectoryOrCreate : Crée le répertoire s'il n'existe pasFileOrCreate : Crée le fichier s'il n'existe pas# Déjà vu dans les modules précédents
volumes:
- name: config-volume
configMap:
name: app-config
- name: secret-volume
secret:
secretName: app-secrets
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-example
labels:
type: local
performance: standard
spec:
capacity:
storage: 10Gi # Taille disponible
accessModes:
- ReadWriteOnce # Mode d'accès
persistentVolumeReclaimPolicy: Retain # Politique après suppression
storageClassName: manual # Classe de stockage
hostPath: # Type de stockage (exemple local)
path: /tmp/k8s-pv-data
type: DirectoryOrCreate
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-example
spec:
accessModes:
- ReadWriteOnce # Doit correspondre au PV
storageClassName: manual # Classe ciblée
resources:
requests:
storage: 5Gi # Taille demandée (≤ PV)
selector: # Sélecteur optionnel
matchLabels:
type: local
apiVersion: v1
kind: Pod
metadata:
name: pv-pod
spec:
containers:
- name: app
image: nginx:1.29.5
volumeMounts:
- name: persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: pvc-example
# 1. Créer un répertoire pour le stockage (sur le Node)
sudo mkdir -p /tmp/k8s-pv-data
sudo chmod 777 /tmp/k8s-pv-data
Créer le fichier demo-pv.yaml :
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv
labels:
type: local
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /tmp/k8s-pv-data
type: DirectoryOrCreate
# Appliquer le PV
kubectl apply -f demo-pv.yaml
# Vérifier le PV
kubectl get pv
kubectl describe pv demo-pv
Créer le fichier demo-pvc.yaml :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: manual
resources:
requests:
storage: 500Mi
selector:
matchLabels:
type: local
# Appliquer le PVC
kubectl apply -f demo-pvc.yaml
# Vérifier le binding
kubectl get pvc
kubectl describe pvc demo-pvc
kubectl get pv # Status devrait être "Bound"
Créer le fichier pv-test-pod.yaml :
apiVersion: v1
kind: Pod
metadata:
name: pv-test-pod
spec:
containers:
- name: web
image: nginx:1.29.5
volumeMounts:
- name: web-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-storage
persistentVolumeClaim:
claimName: demo-pvc
# Appliquer et tester le Pod
kubectl apply -f pv-test-pod.yaml
# Écrire des données dans le volume
kubectl exec pv-test-pod -- sh -c 'echo "<h1>Persistent Data</h1>" > /usr/share/nginx/html/index.html'
# Vérifier la persistance
curl $(kubectl get pod pv-test-pod -o jsonpath='{.status.podIP}')
# Supprimer le Pod et le recréer
kubectl delete pod pv-test-pod
Créer le fichier pv-test-pod-2.yaml :
apiVersion: v1
kind: Pod
metadata:
name: pv-test-pod-2
spec:
containers:
- name: web
image: nginx:1.29.5
volumeMounts:
- name: web-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-storage
persistentVolumeClaim:
claimName: demo-pvc
# Appliquer le nouveau Pod
kubectl apply -f pv-test-pod-2.yaml
# Vérifier que les données persistent
kubectl exec pv-test-pod-2 -- cat /usr/share/nginx/html/index.html
# Nettoyer
kubectl delete pod pv-test-pod-2
kubectl delete pvc demo-pvc
kubectl delete pv demo-pv
sudo rm -rf /tmp/k8s-pv-data
| Mode | Abréviation | Description | Cas d'Usage |
|---|---|---|---|
| ReadWriteOnce | RWO | Montage RW par un seul Node | Bases de données, applications single-instance |
| ReadOnlyMany | ROX | Montage RO par plusieurs Nodes | Assets statiques, configuration partagée |
| ReadWriteMany | RWX | Montage RW par plusieurs Nodes | Systèmes de fichiers partagés, logs centralisés |
| ReadWriteOncePod | RWOP | Montage RW par un seul Pod (K8s 1.22+) | Applications très sensibles au verrouillage |
spec:
persistentVolumeReclaimPolicy: Retain # ou Delete, Recycle (déprécié)
Une StorageClass définit les "classes" de stockage disponible avec :
# Voir les StorageClasses disponibles
kubectl get storageclass
# Marquer comme default
kubectl annotate storageclass standard storageclass.kubernetes.io/is-default-class="true"
# Voir la default
kubectl get sc -o yaml | grep default
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-ebs-gp3
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3 # Type de volume EBS
iops: "3000" # IOPS provisionnées
throughput: "125" # Throughput en MiB/s
encrypted: "true" # Chiffrement
allowVolumeExpansion: true # Permet l'agrandissement
reclaimPolicy: Delete
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-ssd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd # pd-standard ou pd-ssd
zones: us-central1-a,us-central1-b
replication-type: regional-pd
allowVolumeExpansion: true
reclaimPolicy: Delete
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium
provisioner: kubernetes.io/azure-disk
parameters:
storageaccounttype: Premium_LRS # Standard_LRS, Premium_LRS
kind: Managed # Shared, Dedicated, Managed
location: eastus
allowVolumeExpansion: true
reclaimPolicy: Delete
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
Créer le fichier fast-storage.yaml :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: kubernetes.io/no-provisioner # Pour demo locale
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: Delete
parameters:
type: "ssd"
# Appliquer la StorageClass
kubectl apply -f fast-storage.yaml
Créer le fichier dynamic-pvc.yaml :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-storage
resources:
requests:
storage: 1Gi
# Appliquer le PVC
kubectl apply -f dynamic-pvc.yaml
# Vérifier le statut (sera Pending sans provisioner réel)
kubectl get pvc dynamic-pvc
kubectl describe pvc dynamic-pvc
Créer le fichier manual-pv.yaml :
apiVersion: v1
kind: PersistentVolume
metadata:
name: manual-pv-for-dynamic
labels:
type: ssd
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: fast-storage
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp/fast-storage
type: DirectoryOrCreate
# Appliquer le PV
kubectl apply -f manual-pv.yaml
# Vérifier le binding
kubectl get pvc,pv
Créer le fichier dynamic-storage-pod.yaml :
apiVersion: v1
kind: Pod
metadata:
name: dynamic-storage-pod
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: dynamic-pvc
# Appliquer et tester le Pod
kubectl apply -f dynamic-storage-pod.yaml
# Tester l'utilisation
kubectl exec dynamic-storage-pod -- sh -c 'echo "Dynamic Storage Test" > /data/test.txt'
kubectl exec dynamic-storage-pod -- cat /data/test.txt
# Nettoyer
kubectl delete pod dynamic-storage-pod
kubectl delete pvc dynamic-pvc
kubectl delete pv manual-pv-for-dynamic
kubectl delete storageclass fast-storage
# Immediate : Binding dès la création du PVC
volumeBindingMode: Immediate
# WaitForFirstConsumer : Binding quand le premier Pod utilise le PVC
volumeBindingMode: WaitForFirstConsumer # Recommandé
# Voir l'état des volumes
kubectl get pv,pvc --all-namespaces
# Détails d'un volume
kubectl describe pv mon-volume
kubectl describe pvc ma-demande
# Surveiller l'utilisation
kubectl top nodes # Voir l'utilisation disque des Nodes
Points à retenir :
| Aspect | Deployment | StatefulSet |
|---|---|---|
| Identité | Pods interchangeables | Identité stable (pod-0, pod-1) |
| Stockage | Partagé (optionnel) | Persistant individuel |
| Réseau | Service load-balancé | Service headless + individuel |
| Déploiement | Parallèle | Séquentiel ordonné |
| Scaling | Immédiat | Ordonné (0→1→2→3) |
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web-stateful
spec:
serviceName: "web-service" # Service Headless associé
replicas: 3
selector:
matchLabels:
app: nginx-stateful
template:
metadata:
labels:
app: nginx-stateful
spec:
containers:
- name: nginx
image: nginx:1.29.5
ports:
- containerPort: 80
volumeMounts:
- name: web-storage
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # Template PVC pour chaque Pod
- metadata:
name: web-storage
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "fast-storage"
resources:
requests:
storage: 1Gi
---
# Service Headless requis
apiVersion: v1
kind: Service
metadata:
name: web-service
labels:
app: nginx-stateful
spec:
ports:
- port: 80
clusterIP: None # Headless Service
selector:
app: nginx-stateful
Créer le fichier stateful-storage.yaml :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: stateful-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
# Appliquer la StorageClass
kubectl apply -f stateful-storage.yaml
# Créer les répertoires pour chaque réplique
sudo mkdir -p /tmp/stateful-pv-0 /tmp/stateful-pv-1 /tmp/stateful-pv-2
Créer le fichier pv-stateful-0.yaml :
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-stateful-0
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: stateful-storage
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp/stateful-pv-0
type: DirectoryOrCreate
# Appliquer le premier PV
kubectl apply -f pv-stateful-0.yaml
Créer le fichier pv-stateful-1.yaml :
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-stateful-1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: stateful-storage
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp/stateful-pv-1
type: DirectoryOrCreate
# Appliquer le deuxième PV
kubectl apply -f pv-stateful-1.yaml
Créer le fichier pv-stateful-2.yaml :
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-stateful-2
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: stateful-storage
persistentVolumeReclaimPolicy: Delete
hostPath:
path: /tmp/stateful-pv-2
type: DirectoryOrCreate
# Appliquer le troisième PV
kubectl apply -f pv-stateful-2.yaml
Créer le fichier web-statefulset.yaml :
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
labels:
app: nginx-sts
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx-sts
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web-sts
spec:
serviceName: "nginx-headless"
replicas: 3
selector:
matchLabels:
app: nginx-sts
template:
metadata:
labels:
app: nginx-sts
spec:
containers:
- name: nginx
image: nginx:1.29.5
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "stateful-storage"
resources:
requests:
storage: 1Gi
# Appliquer le StatefulSet
kubectl apply -f web-statefulset.yaml
# Observer le déploiement ordonné
kubectl get statefulset -w # Observer le scaling séquentiel
kubectl get pods -l app=nginx-sts -w
# Vérifier les PVCs individuels
kubectl get pvc
kubectl get pv
# Personnaliser chaque Pod
kubectl exec web-sts-0 -- sh -c 'echo "<h1>Pod 0 Data</h1>" > /usr/share/nginx/html/index.html'
kubectl exec web-sts-1 -- sh -c 'echo "<h1>Pod 1 Data</h1>" > /usr/share/nginx/html/index.html'
kubectl exec web-sts-2 -- sh -c 'echo "<h1>Pod 2 Data</h1>" > /usr/share/nginx/html/index.html'
# Tester les identités réseau
kubectl run test-pod --image=busybox:1.36 -it --rm -- sh
# Dans le Pod de test :
nslookup web-sts-0.nginx-headless.default.svc.cluster.local
nslookup web-sts-1.nginx-headless.default.svc.cluster.local
# Chaque Pod a son propre DNS !
# Tester la persistance - supprimer un Pod
kubectl delete pod web-sts-1
kubectl get pods -w # Observer la recréation
# Vérifier que les données persistent
kubectl exec web-sts-1 -- cat /usr/share/nginx/html/index.html
# Scale down ordonné
kubectl scale statefulset web-sts --replicas=1
kubectl get pods -w # Observe : suppression dans l'ordre inverse (2→1→0)
# Scale up ordonné
kubectl scale statefulset web-sts --replicas=3
kubectl get pods -w # Observe : création dans l'ordre (0→1→2)
# Nettoyer
kubectl delete statefulset web-sts
kubectl delete service nginx-headless
kubectl delete pvc www-web-sts-0 www-web-sts-1 www-web-sts-2
kubectl delete pv pv-stateful-0 pv-stateful-1 pv-stateful-2
kubectl delete storageclass stateful-storage
sudo rm -rf /tmp/stateful-pv-0 /tmp/stateful-pv-1 /tmp/stateful-pv-2
Parfait pour :
# Surveiller l'utilisation
kubectl get pv,pvc --all-namespaces
kubectl describe pv <pv-name>
# Nettoyage périodique
kubectl get pv | grep Released # PVs à nettoyer
Dans le module suivant, nous explorerons la gestion des configurations et des secrets pour sécuriser et configurer vos applications.
TP associé : TP 5 : Stockage Persistant
Ce que vous avez appris :
Ce qui vous attend :