TT Lab
Get started
Learn Learning paths Courses

KCNA — Kubernetes and Cloud Native Associate

A Matching Disk Exists, Yet the PVC Won't Bind

Continue in TT Lab

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

  1. Create the namespace kcna-storage.
  2. Create the StorageClass local-fast (no-provisioner, WaitForFirstConsumer, Retain).
  3. Create the PV pv-fast-1 (1Gi, ReadWriteOnce, Retain, hostPath).
  4. Create the PVC data and confirm that it stays in Pending because there is no consumer.
  5. Confirm that when the Pod writer mounts data, the PVC gets bound to pv-fast-1.
  6. Confirm that the PVC wide, which demands ReadWriteMany, and the Pod wider never get bound.
  7. Delete writer and data and confirm that pv-fast-1 remains as Released.
  8. Record the observed results in /root/kcna-storage/report.txt as a ledger.

Notes

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.