KCNA — Kubernetes and Cloud Native Associate
A Matching Disk Exists, Yet the PVC Won't Bind
Goal
With a no-provisioner StorageClass and a hand-made PV, you observe for yourself when a PVC gets bound (WaitForFirstConsumer), what kind of request never gets bound (accessMode mismatch), and how the PV remains after the PVC is deleted (Retain → Released).
Why it matters
Data must survive even when Pods disappear. For this, Kubernetes separates "the disk itself (PV)" from "the request for a disk (PVC)" and leaves the job of pairing the two to a controller. So behind the single symptom that a PVC is Pending hide two entirely different causes: "no Pod is using it yet, so it is waiting on purpose" and "there is no volume that matches the conditions at all."
The reclaim policy is also a place where incidents often happen in operations. With the Delete policy, deleting one PVC makes the data disappear, and with Retain the data is protected but the PV remains as Released and is not reused automatically. If you delete a PVC without knowing the difference, something unrecoverable can happen.
Steps
- Create the namespace
kcna-storage. - Create the StorageClass
local-fast(no-provisioner, WaitForFirstConsumer, Retain). - Create the PV
pv-fast-1(1Gi, ReadWriteOnce, Retain, hostPath). - Create the PVC
dataand confirm that it stays in Pending because there is no consumer. - Confirm that when the Pod
writermountsdata, the PVC gets bound topv-fast-1. - Confirm that the PVC
wide, which demands ReadWriteMany, and the Podwidernever get bound. - Delete
writeranddataand confirm thatpv-fast-1remains as Released. - Record the observed results in
/root/kcna-storage/report.txtas a ledger.
Notes
- The Events of
kubectl describe pvc <이름> -n kcna-storagetell you "why it has not been bound yet." - The CLAIM column of
kubectl get pvremembers the old PVC name even after Released, which is why it is not automatically bound to another PVC. - Official docs: Persistent Volumes · Storage Classes.
Open a workshop that will hand out disks
Create the namespace kcna-storage.
A PVC and a Pod belong to a namespace, but a PV and a StorageClass are cluster-scoped. First prepare the namespace the PVC will go into. If you generate create with --dry-run=client -o yaml and apply it, it is safe to run repeatedly.
A storage class that waits until someone shows up to use it
Create the StorageClass local-fast. The provisioner is kubernetes.io/no-provisioner, the volumeBindingMode is WaitForFirstConsumer, and the reclaimPolicy is Retain.
no-provisioner means volumes are not created automatically, so the administrator prepares the PV by hand. The default volumeBindingMode, Immediate, pairs a PV as soon as a PVC is created, while WaitForFirstConsumer postpones the pairing until a Pod that uses that PVC is scheduled. A StorageClass has no kubectl create subcommand, so write it as YAML (apiVersion storage.k8s.io/v1) and apply it.
A 1Gi disk the administrator laid out by hand
Create the PersistentVolume pv-fast-1: capacity 1Gi, accessModes [ReadWriteOnce], persistentVolumeReclaimPolicy Retain, storageClassName local-fast, hostPath path /tmp/pv-fast-1.
A PV is a cluster-scoped object, so there is no namespace in its metadata. To be paired with a PVC, the storageClassName, accessModes, and capacity must satisfy the request. The StorageClass's reclaimPolicy applies only to dynamically provisioned PVs, so for a PV you make by hand you have to write the reclaim policy directly in the spec. Look at STATUS right after creating it.
Even though a matching disk exists, the PVC is Pending
In the namespace kcna-storage, create a PVC data: accessModes [ReadWriteOnce], storageClassName local-fast, requested capacity 1Gi. No Pod uses this PVC yet, so it stays in Pending. Record that moment in /root/kcna-storage/pending.json, with the three keys of the PVC: uid, phase, and volumeName. Even if the PVC is bound and deleted in later steps, this step is judged by this record and the cluster's events.
Even if a PV that matches the conditions exists, with a WaitForFirstConsumer class binding does not happen until a consumer (a Pod) appears. This is not a fault but the design: it is so that after it is decided which node the Pod goes to, a volume usable on that node can be chosen. After reading the reason from the Events of kubectl describe pvc data, pick the three needed values from kubectl get pvc data -o json with jq and save them to the file. If you make up the values by hand, they will not match the UID in the events and it fails.
When the Pod appears, the PVC gets bound
In the namespace kcna-storage, create a Pod writer (image nginx:1.27-alpine) that mounts the PVC data at /data. If the PVC data becomes Bound and the paired volume is pv-fast-1, record that moment's PVC uid, phase, and volumeName in /root/kcna-storage/bound.json.
Declare a persistentVolumeClaim (claimName) volume in the Pod spec's volumes, and connect that volume name to a mountPath in the container's volumeMounts. The moment the scheduler places the Pod on a node, the delayed binding proceeds. Instead of a fixed sleep, re-read the PVC's status.phase every few seconds and wait until it becomes Bound.
A demand to be shared by many is accepted by no one
In the namespace kcna-storage, create a PVC wide: accessModes [ReadWriteMany], storageClassName local-fast, requested capacity 1Gi. Then have a Pod wider (image nginx:1.27-alpine) mount this PVC. No PV provides ReadWriteMany, so the PVC wide must not become Bound.
A PVC's accessModes is the demand "give me a volume that can do it this way." The only PV in the cluster is ReadWriteOnce, and even that is already bound to another PVC. Even with a consumer Pod, if no PV satisfies the conditions, the PVC stays Pending and the Pod cannot be scheduled. Create the PVC and Pod in the same shape as step 05, only with different accessModes.
The PVC was deleted but the disk remains
Delete the Pod writer, then delete the PVC data. The PV pv-fast-1, whose reclaim policy is Retain, must not be deleted and must remain in the Released state.
A PVC in use is not deleted until the Pod is gone because of the protection finalizer, so delete the Pod first. Once the PVC is gone, the PV follows the reclaim policy: with Delete the PV is deleted too, and with Retain it remains as Released to protect the data and is not bound again to another PVC. Poll until the PV's STATUS changes.
Leave a ledger of how the disk flowed
Write the observed results to /root/kcna-storage/report.txt in exactly four lines: sc-binding-mode=WaitForFirstConsumer, pv-reclaim=Retain, pv-phase-after-release=Released, and wide-pvc=Pending. The values must match the actual cluster state.
The grader checks each line against the actual StorageClass's volumeBindingMode, the PV's reclaim policy and phase, and the phase of the PVC wide. Copy the values you confirmed with kubectl get sc,pv and kubectl get pvc -n kcna-storage as they are.