Un volume Block suit votre pod d'un nœud à l'autre, mais jamais d'une zone à l'autre. Cette leçon crée un volume persistant sur Kapsule, prouve que les données survivent au pod qui les a écrites, puis provoque le blocage qui compte : forcer le pod dans une autre zone. Vous y verrez un message d'erreur qui accuse la mauvaise cause, et vous saurez pourquoi un PersistentVolumeClaim peut rester Pending pendant des heures sans qu'aucune panne ne soit en cours.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Choisir une classe de stockage en sachant si elle laisse survivre le volume.
- Comprendre pourquoi un PVC reste
Pendingsans être en panne. - Prouver que les données survivent au pod, et mesurer la réattache.
- Reconnaître la contrainte zonale derrière un message trompeur.
Prérequis
Section intitulée « Prérequis »- Un cluster Kapsule : voir créer un cluster Kapsule.
kubectlconfiguré, la CLIscw2.62.0 etjq.- Pour la dernière partie, un second pool dans une autre zone, que la leçon vous fera créer puis supprimer.
Quelle classe de stockage choisir ?
Section intitulée « Quelle classe de stockage choisir ? »Huit classes sont disponibles, et elles vont par paires : chacune a une jumelle qui laisse survivre le volume. C'est la distinction la plus coûteuse à ignorer.
kubectl get storageclass -o 'custom-columns=NOM:.metadata.name,PROVISIONNEUR:.provisioner,DEFAUT:.metadata.annotations.storageclass\.kubernetes\.io/is-default-class,LIAISON:.volumeBindingMode'La sortie doit afficher huit lignes, toutes servies par csi.scaleway.com. Relevé le 2026-09-12 :
| Classe | Par défaut | Ce qu'elle change |
|---|---|---|
sbs-default | oui | le choix si vous ne précisez rien |
sbs-5k, sbs-15k | non | le débit en entrées-sorties par seconde |
scw-bssd | non | l'ancienne gamme Block |
toutes en -retain | non | le volume survit à la suppression du PVC |
La jumelle -retain est un piège budgétaire, pas une option de confort. Avec elle, supprimer le PersistentVolumeClaim ne supprime pas le volume : celui-ci passe en Released, reste provisionné chez Scaleway, et continue d'être facturé indéfiniment. C'est exactement le genre de reste discret que personne ne retrouve six mois plus tard, puisqu'il n'apparaît plus dans aucun objet Kubernetes. Employez -retain quand la perte de données serait pire que la facture, typiquement pour une base, et sachez alors que vous devrez supprimer le volume à la main. Partout ailleurs, la classe par défaut et sa politique Delete font le travail.
Pourquoi mon PVC reste-t-il Pending ?
Section intitulée « Pourquoi mon PVC reste-t-il Pending ? »Parce qu'aucun pod ne le consomme, et c'est le comportement normal. Les huit classes sont en WaitForFirstConsumer : rien n'est provisionné tant que le planificateur n'a pas choisi un nœud.
kubectl apply -f - <<'YAML'apiVersion: v1kind: PersistentVolumeClaimmetadata: name: donneesspec: accessModes: [ReadWriteOnce] resources: requests: storage: 5GYAMLRegardez maintenant ce qui existe, et surtout ce qui n'existe pas :
kubectl get pvc donnees -o 'custom-columns=ETAT:.status.phase,VOLUME:.spec.volumeName'kubectl get events --field-selector involvedObject.name=donnees \ -o 'custom-columns=RAISON:.reason,MESSAGE:.message' --no-headers | tail -1scw block volume list zone=fr-par-1 -o json \ | jq -r 'if length == 0 then "aucun volume provisionné" else .[].name end'Les trois sorties doivent afficher, dans l'ordre : Pending sans volume, le message du provisionneur, et aucun volume provisionné.
donnees Pending <none>WaitForFirstConsumer waiting for first consumer to be created before bindingaucun volume provisionnéLe pod déclenche la liaison, et le volume apparaît
Section intitulée « Le pod déclenche la liaison, et le volume apparaît »Créer le pod suffit : la liaison et le provisionnement se font dans la foulée.
kubectl apply -f - <<'YAML'apiVersion: v1kind: Podmetadata: name: ecrivainspec: containers: - name: c image: alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce command: ["sh","-c","echo \"écrit le $(date -Iseconds)\" >> /data/journal.txt && sleep 3600"] volumeMounts: - { name: v, mountPath: /data } volumes: - name: v persistentVolumeClaim: { claimName: donnees }YAMLkubectl wait --for=condition=ready pod/ecrivain --timeout=300sLa sortie doit finir par pod/ecrivain condition met. Vérifiez alors les deux côtés :
kubectl get pvc donnees -o 'custom-columns=ETAT:.status.phase,VOLUME:.spec.volumeName'scw block volume list zone=fr-par-1 -o json \ | jq -r '.[] | "\(.size / 1000000000) Go zone \(.zone) \(.status)"'La première sortie doit afficher Bound, la seconde un volume in_use dans la zone de votre nœud.
Les données survivent-elles au pod ?
Section intitulée « Les données survivent-elles au pod ? »Oui, et la réattache prend quatre secondes. C'est la raison d'être du volume persistant, et cela se prouve plutôt que se suppose.
kubectl delete pod ecrivain --wait=true
kubectl apply -f - <<'YAML'apiVersion: v1kind: Podmetadata: name: lecteurspec: containers: - name: c image: alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce command: ["sh","-c","cat /data/journal.txt && sleep 3600"] volumeMounts: - { name: v, mountPath: /data } volumes: - name: v persistentVolumeClaim: { claimName: donnees }YAMLkubectl wait --for=condition=ready pod/lecteur --timeout=300skubectl exec lecteur -- cat /data/journal.txtLa dernière sortie doit afficher la ligne écrite par le pod précédent. Mesuré le 2026-09-12 : 4 secondes entre la création du nouveau pod et sa disponibilité.
Le planificateur replace spontanément le pod sur le nœud où le volume est attaché. Ce n'est pas un hasard : le volume est une contrainte de placement au même titre que le processeur ou la mémoire. Forcer le pod sur un autre nœud de la même zone fonctionne aussi, le volume se détachant puis se rattachant.
Que se passe-t-il si le pod change de zone ?
Section intitulée « Que se passe-t-il si le pod change de zone ? »Il ne démarre jamais. C'est la contrainte la plus structurante du stockage Block, et celle qui décide de votre architecture bien avant le premier incident.
Créez un pool dans une seconde zone, puis forcez le pod à y aller :
NOEUD_AUTRE_ZONE=$(kubectl get nodes -l topology.kubernetes.io/zone=fr-par-2 \ --no-headers | awk '$2=="Ready"{print $1; exit}')
kubectl delete pod lecteur --wait=truekubectl apply -f - <<YAMLapiVersion: v1kind: Podmetadata: name: hors-zonespec: nodeName: ${NOEUD_AUTRE_ZONE} containers: - name: c image: alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce command: ["sh","-c","cat /data/journal.txt && sleep 3600"] volumeMounts: - { name: v, mountPath: /data } volumes: - name: v persistentVolumeClaim: { claimName: donnees }YAML
sleep 60kubectl get pod hors-zone -o 'custom-columns=ETAT:.status.phase,NOEUD:.spec.nodeName'kubectl get events --field-selector involvedObject.name=hors-zone \ -o 'custom-columns=RAISON:.reason,MESSAGE:.message' --no-headers | tail -1La sortie doit afficher Pending et le message d'échec d'attachement. Mesuré le 2026-09-12 :
FailedAttachVolume AttachVolume.Attach failed for volume "pvc-..." :rpc error: code = Internal desc = failed to attach volume to instance:failed to attach volume: scaleway-sdk-go: resource instance_serverwith ID 89e4504f-... is not foundLe contrôle qui rend la preuve valable
Section intitulée « Le contrôle qui rend la preuve valable »Un échec seul ne prouve rien : il pourrait venir du nœud, du pilote ou du volume. Refaites donc le même pod, avec le même volume, sur un nœud de la bonne zone :
NOEUD_BONNE_ZONE=$(kubectl get nodes -l topology.kubernetes.io/zone=fr-par-1 \ --no-headers | awk '$2=="Ready"{print $1; exit}')kubectl delete pod hors-zone --wait=trueRecréez le pod en remplaçant nodeName par ${NOEUD_BONNE_ZONE}. Il doit démarrer et relire la même donnée. Mesuré : Running, avec la ligne écrite au tout début du lab.
Seule la zone a changé entre les deux essais. C'est ce qui autorise à conclure.
Ce que cela impose à votre architecture
Section intitulée « Ce que cela impose à votre architecture »| Besoin | Ce que le volume Block permet | Ce qu'il faut faire sinon |
|---|---|---|
| Un pod avec état, une zone | oui, c'est le cas nominal | rien de plus |
| Bascule automatique vers une autre zone | non, le volume ne suit pas | réplication applicative, ou base managée |
| Plusieurs pods sur le même volume | non, ReadWriteOnce | File Storage, qui accepte ReadWriteMany |
| Survie à la perte d'une zone | non | sauvegardes vers Object Storage, ou réplication |
Le raccourci à éviter est de croire qu'un cluster multi-zone rend une application avec état multi-zone. Les nœuds se répartissent, le volume ne bouge pas, et une base de données sur volume Block reste attachée à sa zone quoi que fasse le planificateur. La conséquence pratique se mesure au moment de l'incident, pas avant : si la zone qui héberge le volume devient indisponible, votre pod ne redémarrera nulle part. Il restera Pending jusqu'au retour de la zone, et aucune répartition de nœuds n'y changera rien, puisque le problème n'est pas de trouver une machine mais de rattacher un disque qui n'existe que là-bas. Les trois réponses possibles sont toutes applicatives : répliquer les données entre plusieurs volumes zonaux depuis l'application elle-même, confier la persistance à un service managé qui gère sa propre redondance, ou accepter l'indisponibilité en s'appuyant sur des sauvegardes vers Object Storage. Aucune ne consiste à mieux configurer Kubernetes.
Nettoyer
Section intitulée « Nettoyer »La politique de récupération décide si le volume survit : lisez-la avant de supprimer quoi que ce soit.
kubectl get pv -o 'custom-columns=NOM:.metadata.name,POLITIQUE:.spec.persistentVolumeReclaimPolicy,ETAT:.status.phase'La sortie doit afficher Delete si vous avez employé la classe par défaut. Avec Retain, le volume vous survivra et il faudra le supprimer à la main.
kubectl delete pod --all --ignore-not-found=truekubectl delete pvc donnees --ignore-not-found=true --wait=true
scw block volume list zone=fr-par-1 -o json \ | jq -r 'if length == 0 then "aucun volume restant" else .[] | "RESTE \(.id)" end'La dernière sortie doit afficher aucun volume restant. Ne vous arrêtez pas au kubectl delete : c'est la liste côté Scaleway qui fait foi, puisque c'est elle qui est facturée.
N'oubliez pas de supprimer le pool de la seconde zone, créé pour la démonstration :
CLUSTER_ID=$(scw k8s cluster list region=fr-par -o json | jq -r '.[0].id')
scw k8s pool list cluster-id="${CLUSTER_ID}" region=fr-par -o json \ | jq -r '.[] | select(.zone == "fr-par-2") | .id' \ | xargs -r -I{} scw k8s pool delete {} region=fr-par -o jsonLimites, quotas et plafonds
Section intitulée « Limites, quotas et plafonds »| Plafond | Valeur | Source |
|---|---|---|
| Mode d'accès du Block Storage | ReadWriteOnce uniquement | pilote scaleway-csi |
| Portée d'un volume | zonale, jamais régionale | mesuré le 2026-09-12 |
| Taille minimale d'un volume | 5 Go | Block Storage |
Écart entre 5Gi demandé et facturé | 5,37 Go décimaux | mesuré le 2026-09-12 |
| Extension de volume | autorisée sur les 8 classes | allowVolumeExpansion: true |
| Réattache sur un nouveau pod | 4 s | mesuré le 2026-09-12 |
Dépannage
Section intitulée « Dépannage »Les trois premiers symptômes portent le message exact, tel que Kubernetes le renvoie.
| Symptôme | Cause | Solution |
|---|---|---|
waiting for first consumer to be created before binding | la classe est en WaitForFirstConsumer, aucun pod ne consomme | créer le pod ; ce n'est pas une panne |
resource instance_server with ID ... is not found | le nœud et le volume sont dans des zones différentes | ramener le pod dans la zone du volume |
Multi-Attach error for volume | un autre pod tient déjà le volume en ReadWriteOnce | attendre la fin du détachement, ou passer à File Storage |
| Un volume reste facturé après suppression du PVC | la classe employée était en -retain | supprimer le volume avec scw block volume delete |
Antipatterns à éviter
Section intitulée « Antipatterns à éviter »Ces quatre erreurs supposent que le stockage suit le calcul. Il ne le suit pas : il est ancré à une zone.
| Antipattern | Conséquence | Discipline |
|---|---|---|
| Répartir une application avec état sur plusieurs zones | le pod ne redémarre jamais hors de sa zone | réplication applicative, ou base managée |
Employer une classe -retain par prudence | des volumes facturés que plus aucun objet ne référence | -retain seulement là où la perte serait pire, et suppression manuelle assumée |
Écrire les tailles en Gi | 7 % de stockage facturé en plus, sur chaque volume | écrire en G, comme la facture |
Conclure d'un kubectl delete pvc que le volume est parti | il peut survivre selon la politique | vérifier avec scw block volume list |
Le stockage sous l'angle Well-Architected
Section intitulée « Le stockage sous l'angle Well-Architected »Fiabilité
Section intitulée « Fiabilité »Que devient votre application si la zone du volume tombe ? Elle ne redémarre pas ailleurs. Le volume est zonal, et aucun réglage Kubernetes ne le déplace.
Discipline : décider explicitement, pour chaque charge avec état, si elle tolère d'être liée à une zone. Si non, la réplication se fait au niveau applicatif ou par un service managé, jamais par le volume.
Optimisation des coûts
Section intitulée « Optimisation des coûts »Combien de volumes payez-vous qu'aucun objet Kubernetes ne référence ? La question ne se répond pas dans le cluster, puisque ces volumes n'y apparaissent plus.
Discipline : relister régulièrement scw block volume list et confronter à kubectl get pv, réserver -retain aux cas décidés, et écrire les tailles en gigaoctets décimaux.
Sécurité
Section intitulée « Sécurité »Qui peut lire ce volume s'il est détaché ? Le pilote scaleway-csi sait chiffrer un volume au repos, par LUKS, en posant encrypted: true sur la classe de stockage et une phrase secrète dans un Secret. Ce guide ne l'a pas mesuré ; la capacité est documentée dans le dépôt du pilote.
Discipline : chiffrer les volumes portant des données sensibles plutôt que de compter sur l'isolation du cluster, et garder la phrase secrète hors du dépôt.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Huit classes de stockage, toutes en
csi.scaleway.com,sbs-defaultétant celle par défaut. - Chaque classe a une jumelle
-retaindont le volume survit au PVC et continue d'être facturé. - Un PVC sans consommateur reste
Pendingindéfiniment : c'estWaitForFirstConsumer, pas une panne. - Rien n'est facturé avant qu'un pod ne consomme le PVC.
5Gifait provisionner 5,37 Go décimaux : écrivez5Gpour payer ce que vous demandez.- Les données survivent au pod, avec une réattache mesurée à 4 secondes.
- Le volume suit le pod d'un nœud à l'autre, dans la même zone.
- Il ne change jamais de zone : le pod reste
Pendingpour toujours. resource instance_server ... is not foundsignifie « mauvaise zone », pas « instance supprimée ».- Vérifiez la suppression avec
scw block volume list, jamais avec le seulkubectl delete.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Bases de données managées : la carte du volet : quand cesser de gérer soi-même la persistance d'une application, et ce qu'on ne pourra plus changer une fois la base créée.
- PostgreSQL en réseau privé : sauvegarder, perdre, restaurer : la même question qu'ici, ce qui survit à la destruction, posée cette fois sur une base managée.
- Cockpit : observer un incident sans rien provisionner : les métriques que ce cluster produit déjà, et pendant combien de temps elles restent lisibles après sa disparition.
Ressources externes
Section intitulée « Ressources externes »- Pilote scaleway-csi : le code du provisionneur, le chiffrement LUKS des volumes et les snapshots, avec leurs exemples.
- Block Storage Scaleway : les gammes, les débits en entrées-sorties et la grille tarifaire du stockage.
- File Storage avec Kubernetes : la réponse officielle quand plusieurs pods doivent écrire dans le même stockage.
- Volumes persistants, documentation Kubernetes : les modes d'accès, les politiques de récupération et le cycle de vie, communs à tous les fournisseurs.