Gestion du Stockage Persistant avec Kubernetes
Bienvenue dans cette leçon dédiée à la gestion du stockage persistant avec Kubernetes, un pilier fondamental pour le déploiement et la scalabilité d'applications modernes. Dans le cadre de notre cours "Maîtriser Docker et Kubernetes : Déploiement et Scalabilité d'Applications Modernes", il est crucial de comprendre comment vos applications peuvent conserver leurs données au-delà du cycle de vie d'un Pod.
Introduction : Pourquoi le Stockage Persistant est Crucial dans Kubernetes ?
Kubernetes est conçu pour gérer des applications stateless (sans état) par nature. Cela signifie que les Pods peuvent être créés, détruits, déplacés ou redémarrés à tout moment sans que l'application ne perde de données importantes, car toutes les informations d'état sont supposées être stockées ailleurs (par exemple, dans une base de données externe).
Cependant, la réalité des applications modernes est bien différente. La plupart des applications ont besoin de stocker des données : fichiers de configuration, bases de données, journaux, caches, etc. Ces applications sont dites stateful (avec état). Si un Pod hébergeant une base de données MySQL ou une application comme WordPress est supprimé et recréé, toutes ses données seraient perdues sans un mécanisme de stockage persistant.
C'est là qu'intervient la gestion du stockage persistant dans Kubernetes. Elle offre un moyen robuste et flexible pour les applications de conserver leurs données, même si les Pods qui les hébergent sont supprimés ou déplacés. Kubernetes découple la logique de stockage de la logique applicative, permettant aux développeurs de se concentrer sur l'application, tandis que l'infrastructure de stockage est gérée par le cluster.
Concepts Fondamentaux du Stockage Persistant
Kubernetes introduit plusieurs abstractions clés pour gérer le stockage persistant : les Volumes, les PersistentVolumes (PV), les PersistentVolumeClaims (PVC) et les StorageClasses.
1. Volumes
Un Volume est un répertoire accessible aux conteneurs d'un Pod. C'est l'unité de stockage la plus élémentaire dans Kubernetes. Un Volume a la même durée de vie que le Pod qui l'héberge. Si le Pod est supprimé, le Volume l'est aussi.
Il existe deux catégories principales de Volumes :
- Volumes Éphémères : Leur contenu est perdu lorsque le Pod est détruit.
emptyDir: Un volume vide créé au démarrage du Pod et supprimé avec lui. Utile pour le stockage temporaire, le caching ou le partage de fichiers entre conteneurs au sein du même Pod.hostPath: Monte un fichier ou un répertoire du nœud hôte dans le Pod. Attention : Son utilisation est généralement déconseillée en production car elle lie le Pod à un nœud spécifique et compromet la portabilité et la haute disponibilité.
- Volumes Persistants : Leur contenu survit au cycle de vie du Pod. C'est ici qu'interviennent les PV et les PVC.
2. PersistentVolume (PV)
Un PersistentVolume (PV) est une ressource dans le cluster qui représente une pièce de stockage physique ou logique, provisionnée par l'administrateur du cluster ou dynamiquement par un provisionneur de stockage. C'est une ressource de cluster, indépendante de la vie d'un Pod.
Un PV est un "morceau" de stockage abstrait, comme un disque dur sur un serveur, un volume NFS partagé, un volume sur un cloud public (EBS, GCE Persistent Disk, Azure Disk, etc.).
Ses attributs clés incluent :
- Capacité (
capacity): La taille du volume (ex:10Gi). - Modes d'Accès (
accessModes): Décrit comment le volume peut être monté :ReadWriteOnce(RWO): Le volume peut être monté en lecture-écriture par un seul nœud.ReadOnlyMany(ROX): Le volume peut être monté en lecture seule par plusieurs nœuds.ReadWriteMany(RWX): Le volume peut être monté en lecture-écriture par plusieurs nœuds.ReadWriteOncePod(RWOP): Le volume peut être monté en lecture-écriture par un seul Pod. (Introduit plus récemment, spécifique aux Pods et non aux nœuds).
- Politique de Récupération (
persistentVolumeReclaimPolicy): Ce qui arrive au volume une fois qu'il n'est plus utilisé par un PVC.Retain: Le volume n'est pas supprimé et reste disponible pour une inspection manuelle (et réutilisation).Delete: Le volume et son contenu sont supprimés.Recycle: (Déprécié) Le volume est nettoyé et rendu disponible.
3. PersistentVolumeClaim (PVC)
Un PersistentVolumeClaim (PVC) est une requête de stockage faite par un utilisateur ou une application. C'est l'équivalent utilisateur d'un Pod qui demande des ressources CPU et mémoire. Un PVC demande une quantité spécifique de stockage avec certains modes d'accès.
Lorsqu'un PVC est créé, Kubernetes essaie de trouver un PV existant qui correspond aux critères du PVC (capacité, modes d'accès). Si un PV est trouvé, il est "lié" (bound) au PVC. Si aucun PV correspondant n'est trouvé et qu'une StorageClass est spécifiée (ou qu'une par défaut existe), un PV peut être dynamiquement provisionné.
Le PVC agit comme un "contrat" entre l'application et le stockage, découplant les détails de l'infrastructure de stockage de l'application elle-même.
4. StorageClass
Une StorageClass est une ressource qui définit une "classe" de stockage. Elle encapsule la configuration du provisionnement dynamique de PV. Au lieu de créer manuellement des PV pour chaque demande, une StorageClass permet à Kubernetes de provisionner automatiquement un PV en fonction des besoins d'un PVC.
Chaque StorageClass a un provisioner (par exemple, kubernetes.io/aws-ebs, kubernetes.io/gce-pd, csi.k8s.io/hostpath, ou des provisionneurs tiers comme nfs.csi.k8s.io), des parameters spécifiques à ce provisioner (type de disque, performance, etc.) et une reclaimPolicy.
L'utilisation des StorageClasses est la méthode préférée et la plus moderne pour gérer le stockage persistant dans Kubernetes car elle automatise et standardise le processus de provisionnement.
Workflow de Stockage Persistant
Voici comment ces composants interagissent pour fournir du stockage persistant à une application :
- Administrateur/Opérateur (ou un provisionneur dynamique) configure les infrastructures de stockage sous-jacentes (disques, partages NFS, etc.) et crée des StorageClasses.
- Un développeur d'application a besoin de stockage. Au lieu de se soucier de l'infrastructure physique, il crée un PersistentVolumeClaim (PVC) spécifiant la taille et les modes d'accès requis, et éventuellement une
StorageClasspréférée. - Kubernetes intercepte le PVC :
- Si le PVC spécifie une
StorageClasset que celle-ci est configurée pour le provisionnement dynamique, le provisionneur associé à cetteStorageClasscrée un nouveau PersistentVolume (PV) sur l'infrastructure de stockage et le lie au PVC. - Si aucun
StorageClassn'est spécifié, ou si laStorageClassest pour le provisionnement statique, Kubernetes essaie de trouver un PV existant (préalablement créé manuellement par l'administrateur) qui correspond aux exigences du PVC et le lie.
- Si le PVC spécifie une
- Une fois le PVC lié à un PV, il est dans l'état
Bound. - Le Pod de l'application peut maintenant référencer le PVC pour monter le stockage. Le Pod ne voit que le PVC, pas le PV sous-jacent, ce qui maintient l'abstraction.
- Lorsque le Pod utilise le volume, les données sont stockées sur le PV physique.
- Si le Pod est supprimé, le PVC et le PV persistent. Un nouveau Pod peut être créé et réutiliser le même PVC pour accéder aux mêmes données.
- Lorsque le PVC n'est plus nécessaire, il peut être supprimé. Selon la
reclaimPolicydu PV, le volume physique sera retenu ou supprimé.
Exemples Pratiques de Gestion du Stockage
Nous allons démontrer comment configurer et utiliser le stockage persistant avec une StorageClass pour le provisionnement dynamique, ce qui est la méthode la plus courante et la plus flexible. Pour cet exemple, nous allons simuler un provisionnement de stockage local (souvent utilisé en développement avec Minikube ou Kind, ou avec des CSI drivers comme local-path-provisioner).
1. Définition d'une StorageClass (pour le provisionnement dynamique local)
Pour simuler un environnement de provisionnement dynamique sans dépendre d'un cloud provider spécifique, nous utiliserons la StorageClass standard qui est souvent pré-configurée ou qui peut être remplacée par un provisionneur comme local-path-provisioner (nécessite son installation préalable). Ici, nous allons simplement référencer une StorageClass qui est supposée exister (comme la standard par défaut sur Minikube ou GKE/EKS/AKS). Si vous utilisez un cluster sans StorageClass par défaut, vous devriez en créer une, par exemple pour hostPath (pour des tests locaux uniquement) ou un CSI driver.
Pour la démonstration, nous allons assumer qu'une StorageClass nommée local-storage est disponible. Si vous exécutez sur Minikube, la StorageClass par défaut est souvent standard. Vous pouvez la lister avec kubectl get sc. Si local-path-provisioner est installé, sa StorageClass par défaut peut aussi être standard ou local-path.
Voici un exemple d'une StorageClass simple si vous deviez la définir (nécessite un provisioner comme k8s.io/minikube-hostpath pour Minikube ou csi.storage.k8s.io/hostpath pour certains environnements de test) :
# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mon-stockage-local
provisioner: k8s.io/minikube-hostpath # Ou un provisioner de votre choix (ex: nfs.csi.k8s.io, csi.k8s.io/hostpath)
reclaimPolicy: Delete
volumeBindingMode: Immediate # Crée le PV dès la création du PVC
parameters:
type: standard # Paramètre spécifique au provisioner, peut varier
Pour appliquer (si vous avez un provisioner adapté) : kubectl apply -f storageclass.yaml
2. Création d'un PersistentVolumeClaim (PVC)
Ce PVC demandera 1 GiB de stockage et sera accessible en lecture-écriture par un seul nœud. Il utilisera la StorageClass par défaut de votre cluster (si vous n'en spécifiez pas, ou mon-stockage-local si vous l'avez créé ci-dessus).
# my-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mon-pvc-wordpress
spec:
accessModes:
- ReadWriteOnce # Peut être monté en lecture-écriture par un seul nœud
resources:
requests:
storage: 1Gi # Demande 1 Gigabyte de stockage
# storageClassName: mon-stockage-local # Décommentez si vous avez défini une StorageClass spécifique
Explication du code my-pvc.yaml :
apiVersion: v1,kind: PersistentVolumeClaim: Indique que nous définissons une ressource de type PVC.metadata.name: mon-pvc-wordpress: Le nom unique de notre PersistentVolumeClaim.spec.accessModes: [ReadWriteOnce]: Spécifie que ce volume ne peut être monté en lecture-écriture que par un seul nœud à la fois.spec.resources.requests.storage: 1Gi: Demande une capacité de stockage de 1 Gigabyte.spec.storageClassName: mon-stockage-local: (Optionnel) Si spécifié, le PVC tentera d'utiliser laStorageClassnomméemon-stockage-localpour provisionner le PV. Si non spécifié, il utilisera laStorageClasspar défaut du cluster.
Pour appliquer : kubectl apply -f my-pvc.yaml
Vous pouvez vérifier l'état du PVC : kubectl get pvc mon-pvc-wordpress
Il devrait passer à l'état Bound après un court instant, indiquant qu'un PV lui a été alloué (ou dynamiquement provisionné).
3. Utilisation du PVC dans un Pod
Maintenant, nous allons créer un Pod qui utilise ce PVC pour stocker des données. Nous allons simuler une application très simple (un conteneur Nginx) qui monte ce volume. Dans un scénario réel, ce serait une base de données ou une application web qui écrit des données.
# pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: mon-pod-nginx-avec-stockage
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: mon-stockage-persistant
mountPath: /usr/share/nginx/html # Chemin où le volume sera monté dans le conteneur
volumes:
- name: mon-stockage-persistant
persistentVolumeClaim:
claimName: mon-pvc-wordpress # Référence au nom de notre PVC
Explication du code pod-with-pvc.yaml :
apiVersion: v1,kind: Pod: Indique que nous définissons une ressource de type Pod.metadata.name: mon-pod-nginx-avec-stockage: Le nom de notre Pod.spec.containers: La définition du ou des conteneurs dans le Pod.name: nginx-container: Nom du conteneur.image: nginx:latest: Image Docker utilisée.volumeMounts: Section cruciale qui définit où les volumes doivent être montés à l'intérieur du conteneur.name: mon-stockage-persistant: Fait référence au nom du volume défini dans la sectionspec.volumesdu Pod.mountPath: /usr/share/nginx/html: Le chemin à l'intérieur du conteneur où le volume sera monté. Pour Nginx, c'est l'emplacement par défaut des fichiers HTML.
spec.volumes: Définit les volumes disponibles pour le Pod.name: mon-stockage-persistant: Nom interne du volume, utilisé parvolumeMounts.persistentVolumeClaim.claimName: mon-pvc-wordpress: Référence notre PVC précédemment créé,mon-pvc-wordpress.
Pour appliquer : kubectl apply -f pod-with-pvc.yaml
Vous pouvez vérifier que le Pod est en cours d'exécution : kubectl get pod mon-pod-nginx-avec-stockage
Pour vérifier que le volume est bien monté et que des données peuvent y être écrites :
- Accédez au shell du Pod :
kubectl exec -it mon-pod-nginx-avec-stockage -- bash - Dans le shell du Pod, créez un fichier dans le chemin monté :
echo "Hello from Persistent Storage!" > /usr/share/nginx/html/index.html - Quittez le shell :
exit
Maintenant, si vous supprimez le Pod : kubectl delete pod mon-pod-nginx-avec-stockage
Puis recréez-le avec le même YAML : kubectl apply -f pod-with-pvc.yaml
Accédez de nouveau au shell du nouveau Pod : kubectl exec -it mon-pod-nginx-avec-stockage -- bash
Vérifiez le contenu du fichier : cat /usr/share/nginx/html/index.html
Vous devriez voir "Hello from Persistent Storage!". Cela démontre que les données ont persisté malgré la suppression et la recréation du Pod, car elles étaient stockées sur le PersistentVolume sous-jacent, accessible via le PersistentVolumeClaim.
Considérations Avancées et Bonnes Pratiques
- StatefulSets: Pour les applications stateful complexes comme les bases de données, Kubernetes offre le contrôleur
StatefulSet. Un StatefulSet garantit un ordre de déploiement et de suppression stable, des noms réseau stables pour les Pods, et des PersistentVolumeClaims uniques pour chaque réplique, assurant ainsi une identité stable pour chaque instance. - CSI Drivers (Container Storage Interface): C'est l'interface standard pour exposer des systèmes de stockage arbitraires à Kubernetes. Les fournisseurs de stockage (cloud providers, NFS, Ceph, etc.) implémentent des pilotes CSI qui permettent à leurs solutions de s'intégrer nativement avec Kubernetes, offrant des fonctionnalités avancées comme les snapshots, le clonage de volumes ou le redimensionnement.
- Snapshots et Clonage de Volumes: Les API de Volume Snapshot et Volume Clone permettent de créer des instantanés de vos PersistentVolumes ou de cloner un volume existant, ce qui est crucial pour les sauvegardes, les restaurations ou la création d'environnements de développement à partir de données existantes.
- Expansion de Volumes: Certains
StorageClassesetprovisionersupportent l'expansion de volume en ligne, permettant d'augmenter la taille d'un PV existant sans interruption de service. - Performance et Redondance: Le choix du
StorageClasset du provisionneur est critique pour les performances. Un stockage SSD sera plus rapide qu'un HDD. Pour les applications critiques, il est essentiel de considérer la redondance et la tolérance aux pannes du système de stockage sous-jacent. - Sauvegarde et Restauration: La gestion du stockage persistant ne remplace pas une stratégie de sauvegarde et de restauration robuste. Des outils comme Velero peuvent être utilisés pour sauvegarder et restaurer les ressources Kubernetes et les volumes persistants.
Conclusion
La gestion du stockage persistant est une compétence indispensable pour quiconque déploie des applications stateful sur Kubernetes. En comprenant les concepts de PersistentVolume, PersistentVolumeClaim et StorageClass, vous pouvez concevoir des architectures résilientes où les données de vos applications survivent aux vicissitudes des Pods.
Le découplage entre la requête de stockage (PVC) et l'implémentation physique (PV, gérée par une StorageClass ou un administrateur) rend Kubernetes extrêmement flexible et puissant. Que vous utilisiez des solutions de stockage locales, sur le cloud, ou des systèmes de fichiers distribués, Kubernetes fournit une interface uniforme pour toutes vos exigences de persistance. C'est un pilier fondamental pour la construction d'applications modernes, fiables et scalables.