Introduction
Une Persistent Volume Claim (PVC) Kubernetes est le principal moyen pour les développeurs et les opérateurs de demander du stockage sans coupler une charge de travail à un backend de stockage spécifique. Lorsqu'une PVC reste en attente (Pending), qu'un pod ne peut pas monter son volume ou que les données sont inaccessibles, chaque minute d'incertitude ajoute un risque aux services stateful.
Ce guide de terrain s'adresse aux développeurs, consultants DevOps et équipes techniques de startups qui gèrent des clusters Kubernetes en production. Il couvre les étapes de dépannage des PVC Kubernetes, les erreurs courantes, l'inspection des journaux, les diagnostics en ligne de commande et les stratégies de récupération avec des exemples concrets. Chaque scénario inclut des commandes, les sorties attendues et les signaux d'échec afin que vous puissiez passer du symptôme à la résolution confirmée sans conjecture.
Le principe opérationnel est simple : observer avant de modifier. Vérifiez l'état actuel, limitez le rayon d'impact de toute modification, utilisez des espaces réservés au lieu de secrets et documentez le chemin de retour en arrière. Chaque section construit une procédure reproductible et adaptée à la version que vous pouvez exécuter sur votre propre cluster.
Inventaire des versions et de l'environnement
Avant de toucher à une PVC, établissez ce que vous exécutez. Cet inventaire évite les suppositions erronées sur les versions d'API, les classes de stockage par défaut et le comportement spécifique au fournisseur.
Identifier la version de Kubernetes et les pilotes de stockage
Exécutez :
kubectl version --short
La sortie attendue inclut les versions client et serveur, par exemple :
Client Version: v1.27.1
Server Version: v1.26.3
Si la version du serveur est antérieure à 1.20, l'API PersistentVolumeClaim peut encore être en v1beta1 ; pour 1.20 et versions ultérieures, elle est stable en v1. Cela est important car les anciens clusters peuvent ne pas disposer de fonctionnalités telles que l'expansion de volume ou la migration CSI.
Ensuite, listez les classes de stockage configurées :
kubectl get storageclass
Exemple de sortie :
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) kubernetes.io/gce-pd Delete Immediate false 21d
fast ebs.csi.aws.com Delete WaitForFirstConsumer true 10d
Notez la colonne PROVISIONER. Les provisionneurs intégrés comme kubernetes.io/gce-pd sont remplacés par des pilotes CSI tels que pd.csi.storage.gke.io ou ebs.csi.aws.com. Si une PVC référence une classe de stockage avec un provisionneur intégré qui a été migré ou supprimé dans votre version de cluster, le provisionnement échouera.
Vérifier les événements et le statut de la PVC
Effectuez une observation en lecture seule :
kubectl get pvc --all-namespaces
Exemple de sortie :
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
default data-x Bound pvc-9f3c2d1e-0a4b-4d8e-b1e2-123456789abc 10Gi RWO standard 5d
default cache-y Pending fast 2m
Une PVC en Pending est le point de départ le plus courant pour le dépannage. Décrivez la PVC pour faire apparaître les erreurs de provisionnement :
kubectl describe pvc cache-y
Recherchez la section Events. Messages d'échec typiques :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ProvisioningFailed 10s persistentvolume-controller storageclass.storage.k8s.io "fast" not found
Cette erreur spécifique signifie que la PVC référence une classe de stockage qui n'existe pas dans le cluster. Vérifiez avec kubectl get storageclass fast ; si cela renvoie NotFound, créez la classe de stockage ou modifiez la PVC pour utiliser une classe existante.
Capturer la référence de l'environnement
Enregistrez ce qui suit avant d'apporter des modifications :
kubectl get pvc -o yaml > pvc-before.yaml
kubectl get pv -o yaml > pv-before.yaml
kubectl get events --sort-by=.lastTimestamp > events-before.log
Cet instantané vous donne une référence de retour en arrière et une chronologie des événements récents.
Chemin de configuration sûr
Lorsqu'une PVC est mal configurée, corrigez d'abord le plus petit objet possible. Appliquer un large ensemble de modifications sans vérification obscurcit souvent la cause racine et élargit le rayon d'impact.
Valider un manifeste de PVC avant de l'appliquer
Utilisez --dry-run=server pour tester contre le serveur d'API sans persister l'objet :
kubectl apply -f pvc.yaml --dry-run=server
Si le manifeste contient une version d'API obsolète, le serveur le rejette avec un message comme :
error: unable to recognize "pvc.yaml": no matches for kind "PersistentVolumeClaim" in version "v1beta1"
Corrigez l'apiVersion en v1 et réessayez.
Exemple de manifeste de PVC
Voici une PVC minimale pour une classe de stockage basée sur CSI (AWS EBS dans cet exemple) :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data
namespace: production
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 20Gi
Appliquez-la :
kubectl apply -f pvc.yaml
Puis surveillez le statut :
kubectl get pvc mysql-data -w
Progression attendue :
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-data Pending fast 0s
mysql-data Bound pvc-1234abcd-5678-ef90-1234-567890abcdef 20Gi RWO fast 5s
Si elle reste en attente au-delà du temps de provisionnement normal (généralement 30 à 60 secondes pour les volumes cloud), passez à l'étape suivante.
Isoler les incohérences de classe de stockage
Une PVC peut être en attente car la classe de stockage exige le mode de liaison WaitForFirstConsumer. Dans ce cas, la PVC reste en attente jusqu'à ce qu'un pod qui l'utilise soit planifié. Vérifiez la classe de stockage :
kubectl get storageclass fast -o yaml | grep volumeBindingMode
volumeBindingMode: WaitForFirstConsumer
Si vous attendiez une liaison immédiate, modifiez la classe de stockage ou créez un pod qui référence la PVC. Par exemple :
apiVersion: v1
kind: Pod
metadata:
name: mysql-test
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-data
Après avoir appliqué le pod, revérifiez le statut de la PVC. Elle devrait se lier en quelques secondes si le backend de stockage est sain.
Vérification et diagnostics
Une fois la PVC liée (Bound), vérifiez que les applications peuvent réellement l'utiliser. Les échecs de montage, les erreurs de permission et les problèmes de capacité n'apparaissent souvent qu'à l'exécution.
Confirmer le montage du volume dans le pod
Listez les pods utilisant la PVC :
kubectl get pods -n production -l app=mysql -o wide
Décrivez le pod et recherchez les erreurs de volume :
kubectl describe pod mysql-0 -n production
Événements courants liés au volume :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedMount 2m kubelet Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[data default-token-xxxxx]: timed out waiting for the condition
Cela indique souvent que le pilote CSI ne peut pas attacher le volume au nœud, peut-être en raison de permissions IAM, d'une discordance de zone ou de la capacité du nœud. Vérifiez les pods du pilote :
kubectl get pods -n kube-system | grep csi
Si les pods du contrôleur CSI ou du plugin de nœud sont en boucle de crash, inspectez leurs journaux :
kubectl logs -n kube-system ebs-csi-controller-<pod-id> --previous
Recherchez les erreurs de permission refusée comme :
AccessDeniedException: User: arn:aws:sts::123456789012:assumed-role/eks-node-role/i-0abc123 is not authorized to perform: ec2:AttachVolume
Corrigez le rôle IAM ou le profil d'instance utilisé par le nœud.
Valider les permissions et la propriété des fichiers
Un volume est monté, mais l'application ne peut pas y écrire. Exécutez dans le pod :
kubectl exec -it mysql-0 -n production -- ls -ld /var/lib/mysql
La sortie peut montrer :
drwxr-xr-x 3 root root 4096 Jul 10 12:34 /var/lib/mysql
Si le conteneur s'exécute en tant que non-root (par exemple, utilisateur 999), il manque de permission d'écriture. Corrigez en utilisant un securityContext avec fsGroup dans la spécification du pod, ou en utilisant un conteneur d'initialisation pour ajuster la propriété. Par exemple, ajoutez à la spécification du pod :
securityContext:
fsGroup: 999
Appliquez la modification du pod et confirmez la propriété :
kubectl exec -it mysql-0 -- ls -ld /var/lib/mysql
Devrait maintenant montrer l'écriture en groupe et le bon ID de groupe.
Surveiller la capacité et l'expansion
Vérifiez la capacité actuelle de la PVC et son utilisation :
kubectl get pvc mysql-data -n production -o wide
Pour voir l'utilisation réelle du système de fichiers, exécutez dans le pod :
kubectl exec -it mysql-0 -n production -- df -h /var/lib/mysql
Exemple :
Filesystem Size Used Avail Use% Mounted on
/dev/nvme1n1 20G 14G 5.2G 73% /var/lib/mysql
Si la classe de stockage prend en charge l'expansion (allowVolumeExpansion: true), vous pouvez augmenter la taille de la PVC :
kubectl patch pvc mysql-data -n production -p '{"spec":{"resources":{"requests":{"storage":"30Gi"}}}}'
Surveillez la condition de la PVC :
kubectl get pvc mysql-data -n production -w
Événement attendu :
Normal Resizing 10s external-resizer External resizer is resizing volume pvc-...
Si l'expansion n'est pas activée, vous verrez une erreur comme :
Warning ExternalExpanding 5s volume_expand Ignoring the PVC: didn't find a plugin capable of expanding the volume
Dans ce cas, vous devez provisionner une nouvelle PVC plus grande et migrer les données.
Modes de défaillance et récupération
Plusieurs modes de défaillance distincts affectent les PVC. Connaître les symptômes et le chemin de récupération pour chacun réduit les temps d'arrêt.
Mode de défaillance 1 : PVC bloquée en attente - Aucune classe de stockage correspondante
Symptôme : kubectl get pvc affiche Pending indéfiniment. Describe ne montre aucun événement ou un ProvisioningFailed avec « storageclass not found ».
Récupération :
- Vérifiez que la classe de stockage existe :
kubectl get storageclass - Si elle est manquante, créez-la ou modifiez le champ
storageClassNamede la PVC.
- Pour modifier :
kubectl patch pvc <name> -p '{"spec":{"storageClassName":"standard"}}' - Remarque : vous ne pouvez changer la classe de stockage que si la PVC n'est pas encore liée et que la nouvelle classe existe.
- Si la classe de stockage existe mais qu'aucun provisionneur ne s'exécute, vérifiez les pods du pilote CSI dans
kube-system.
Mode de défaillance 2 : PVC liée mais le pod ne parvient pas à monter
Symptôme : Describe du pod montre FailedMount avec Unable to attach or mount volumes. Le statut de la PVC est Bound.
Récupération :
- Vérifiez si le pod est planifié sur un nœud dans la même zone de disponibilité que le volume (pour le stockage zonal comme EBS). Si ce n'est pas le cas, le pod peut devoir être replanifié ou le volume recréé dans la bonne zone.
- Vérifiez que le plugin de nœud du pilote CSI s'exécute sur le nœud :
kubectl get pods -n kube-system -o wide | grep csi-node - Inspectez les journaux du contrôleur CSI pour les erreurs d'attachement.
- Vérifiez les limites d'attachement de volume du nœud.
Exemple de vérification :
kubectl get volumeattachments
Si l'attachement de volume est bloqué en attached: false ou n'est pas présent, forcez la suppression de l'attachement après avoir confirmé que le pod ne l'utilise plus :
kubectl patch volumeattachment <name> -p '{"metadata":{"finalizers":[]}}' --type=merge
kubectl delete volumeattachment <name>
Cela ne doit être fait que lorsque le pod attaché est terminé ou que le cluster est dans un état connu.
Mode de défaillance 3 : Corruption de données ou système de fichiers perdu
Symptôme : les journaux d'application montrent des erreurs d'entrée/sortie, ou des fichiers sont manquants.
Récupération :
- Prenez un instantané du système de fichiers si le fournisseur de stockage le prend en charge. Pour AWS EBS, vous pouvez créer un instantané à partir du volume sous-jacent du PV. Identifiez le PV :
kubectl get pv pvc-<id> -o yaml
Notez le volumeID sous spec.csi.volumeHandle, par exemple vol-0abcd1234ef567890.
- Créez un instantané via la CLI cloud ou utilisez Kubernetes VolumeSnapshot si le pilote CSI le prend en charge.
- Restaurez à partir de l'instantané dans une nouvelle PVC et pointez le pod vers celle-ci.
- Pour une corruption mineure, tentez
fscksur un volume détaché (pas possible sur les volumes attachés).
Mode de défaillance 4 : PVC supprimée accidentellement
Symptôme : kubectl get pvc montre que la PVC est absente ; les pods échouent avec MountVolume.SetUp failed for volume "data" failed to get PVC.
Récupération :
- Si la politique de récupération sur le PV était
Retain, le volume sous-jacent existe toujours. Trouvez le PV :
kubectl get pv | grep Released
Il peut afficher le statut Released avec une référence de claim vers la PVC supprimée.
- Créez une nouvelle PVC avec le même
volumeNamepour la relier :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data # même nom qu'avant si possible
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
volumeName: pv-name # spécifier le PV existant
- Si la politique de récupération était
Delete, le volume peut avoir été déprovisionné. Restaurez à partir d'une sauvegarde ou d'un instantané.
Mode de défaillance 5 : PVC sur un nœud qui devient NotReady
Symptôme : les pods utilisant la PVC sont bloqués en terminaison ou ne peuvent pas démarrer car le nœud est NotReady.
Récupération :
- Vérifiez le statut du nœud :
kubectl get nodes - Si le nœud est hors service, cordonnez-le :
kubectl cordon <node>
- Supprimez le pod qui était sur ce nœud, ce qui permet sa replanification ailleurs. Pour les charges de travail stateful, assurez-vous que la PVC n'est pas attachée au nœud mort avant de supprimer.
- Si le volume était attaché au nœud mort, forcez le détachement en supprimant l'objet
VolumeAttachmentcomme décrit ci-dessus. - Une fois le nœud revenu ou remplacé, vérifiez que le pod démarre sur un nouveau nœud et que la PVC reste liée.
Liste de contrôle opérationnelle
Utilisez cette liste avant et après tout changement de PVC pour garantir la sécurité et la vérification.
Liste de contrôle avant changement
- [ ] Enregistrez l'état actuel des PVC, PV et pods avec
kubectl get pvc,pv,pod -n <namespace> -o wide. - [ ] Capturez la sortie describe pour les objets en échec :
kubectl describe pvc <name> > pvc-desc-before.txt. - [ ] Exportez des instantanés YAML pour le retour en arrière :
kubectl get pvc <name> -o yaml > pvc-before.yaml. - [ ] Identifiez la classe de stockage et son provisionneur :
kubectl get storageclass <name> -o yaml. - [ ] Confirmez que les pods du pilote CSI ou du provisionneur intégré sont sains :
kubectl get pods -n kube-system | grep csi. - [ ] Vérifiez la capacité disponible dans le backend de stockage si connue (par exemple, les limites de volume AWS EBS dans la région/AZ).
- [ ] Déterminez l'impact : quels pods utilisent cette PVC ?
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.spec.volumes[].persistentVolumeClaim.claimName=="<pvc-name>") | .metadata.name' - [ ] Préparez un plan de retour en arrière : si le changement échoue, quel objet devez-vous restaurer ? Enregistrez des copies de tout objet à modifier.
Vérification après changement
- [ ] Confirmez que le statut de la PVC est Bound et qu'aucun nouvel événement ne se déclenche :
kubectl get pvc <name> -wpendant quelques minutes. - [ ] Vérifiez les points de montage du pod :
kubectl exec <pod> -- df -h <mount-path> - [ ] Testez l'accès en écriture de l'application :
kubectl exec <pod> -- touch <mount-path>/testfile && kubectl exec <pod> -- rm <mount-path>/testfile - [ ] Validez l'intégrité des données si applicable (par exemple, exécutez une requête de base de données ou vérifiez un hachage de fichier).
- [ ] Surveillez les journaux pour les erreurs liées au volume au cours des 5 à 10 prochaines minutes :
kubectl logs <pod> --since=10m | grep -i volume - [ ] Documentez le changement et les résultats de vérification dans le ticket d'incident ou de changement.
Exemple de session de vérification
Supposons que vous avez augmenté la taille de la PVC de 20Gi à 30Gi. Après avoir appliqué le patch :
kubectl get pvc mysql-data -n production
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-data Bound pvc-1234abcd-5678-ef90-1234-567890abcdef 30Gi RWO fast 6d
Puis dans le pod :
kubectl exec -it mysql-0 -n production -- df -h /var/lib/mysql
Filesystem Size Used Avail Use% Mounted on
/dev/nvme1n1 30G 14G 15G 47% /var/lib/mysql
Cela confirme que l'expansion a réussi du point de vue de l'application.
Conclusion
Le dépannage des Persistent Volume Claims Kubernetes est un processus structuré : identifiez la défaillance exacte, observez l'état actuel, ne changez qu'une variable à la fois et vérifiez avant de continuer. Ce guide a parcouru l'inventaire de l'environnement, la validation de configuration, les commandes de diagnostic, les modes de défaillance courants avec les étapes de récupération et une liste de contrôle opérationnelle.
La prochaine fois que vous faites face à une PVC en attente ou à une erreur de montage, commencez par kubectl get pvc --all-namespaces et kubectl describe pvc <name>. Utilisez les événements comme signal principal. Rappelez-vous que le stockage est une dépendance stateful ; les changements peuvent avoir des conséquences durables. Ayez toujours un plan de retour en arrière et documentez votre vérification.
Pour approfondir les classes de stockage, l'expansion des PVC et les spécificités des pilotes CSI, explorez la documentation officielle du stockage Kubernetes. Mais les commandes et les modèles présentés ici devraient couvrir la majorité des incidents de production. Gardez ce guide à portée de main, et que vos volumes restent liés et sains.