TT Lab
Get started
Learn Learning paths Courses

CKA — Kubernetes Administrator

Tying the Three Storage Layers Together

Continue in TT Lab

Goal

You create the three layers, StorageClass, PV, and PVC, yourself to get a feel for the binding conditions, and you check how a StatefulSet handles volumes.

Why it matters

The reason a PVC is Pending is always one of three. The class name does not match, the access mode does not match, or the capacity is insufficient. To that you can add the case where there is no provisioner so dynamic provisioning does not happen. This lab environment has no real CSI driver, so you check these conditions one by one with static binding. You cannot write actual data, but what the exam asks is also mostly the object spec and the binding state.

It is also important to know in advance the fields that are useless to change later, such as allowVolumeExpansion. A PVC that came from a class created with false cannot be expanded even if you fix the class later.

Steps

  1. Create the namespace cka-storage and the StorageClass cka-fast. provisioner nfs.csi.k8s.io, reclaimPolicy Delete, volumeBindingMode WaitForFirstConsumer, allowVolumeExpansion true.
  2. Create the StorageClass cka-archive. provisioner nfs.csi.k8s.io, reclaimPolicy Retain, volumeBindingMode Immediate. Then, on cka-fast, add the annotation storageclass.kubernetes.io/is-default-class: "true" to make it the default class. There must be exactly one default class.
  3. Create the PersistentVolume cka-pv-1. capacity 2Gi, accessModes [ReadWriteOnce], persistentVolumeReclaimPolicy Retain, storageClassName cka-manual, volume source hostPath /mnt/cka-data.
  4. In cka-storage, create the PVC cka-pvc-1. Request 1Gi, accessModes [ReadWriteOnce], storageClassName cka-manual. It must be Bound to cka-pv-1.
  5. Create the PersistentVolume cka-pv-rwx. capacity 5Gi, accessModes [ReadWriteMany], storageClassName cka-shared, volume source NFS (server 10.0.0.109, path /volume1/k8s). Then create the PVC cka-pvc-rwx (5Gi, RWX, cka-shared) and bind it.
  6. Create the headless Service db-headless (clusterIP None, port 5432, selector app=db) and the StatefulSet db. serviceName db-headless, 2 replicas, image nginx:1.27, podManagementPolicy Parallel, volumeClaimTemplates with name data (accessModes [ReadWriteOnce], storageClassName cka-fast, 1Gi), persistentVolumeClaimRetentionPolicy with whenDeleted Delete / whenScaled Retain. The PVCs data-db-0 and data-db-1 must be created automatically.
  7. In cka-storage, create the Pod vol-demo (image nginx:1.27). The volume cache is an emptyDir with medium Memory and sizeLimit 128Mi, mounted at /cache. The volume hostlogs is a hostPath with path /var/log and type Directory, mounted read-only at /hostlogs.
  8. Create the PersistentVolume cka-pv-static (capacity 1Gi, accessModes [ReadWriteOnce], no storageClassName at all, hostPath /mnt/cka-static), and create the PVC cka-pvc-static (1Gi, RWO) with storageClassName explicitly set to an empty string, and bind it.

Reference

Create a StorageClass

Create the namespace cka-storage and the StorageClass cka-fast. provisioner nfs.csi.k8s.io, reclaimPolicy Delete, volumeBindingMode WaitForFirstConsumer, allowVolumeExpansion true.

provisioner is a string, and the object is created even if the driver does not actually exist. reclaimPolicy, volumeBindingMode, and allowVolumeExpansion are top-level fields of the StorageClass, not inside parameters.

A second class and designating the default class

Create the StorageClass cka-archive. provisioner nfs.csi.k8s.io, reclaimPolicy Retain, volumeBindingMode Immediate. Then, on cka-fast, add the annotation storageclass.kubernetes.io/is-default-class: "true" to make it the default class. There must be exactly one default class.

The default class is set with an annotation. If there is more than one default, you cannot predict which one will be used, so there must be exactly one.

Create a PersistentVolume yourself

Create the PersistentVolume cka-pv-1. capacity 2Gi, accessModes [ReadWriteOnce], persistentVolumeReclaimPolicy Retain, storageClassName cka-manual, volume source hostPath /mnt/cka-data.

A PV is cluster-scoped. Note that the name of the reclaim policy field is different from the one in a StorageClass.

Check static binding

In cka-storage, create the PVC cka-pvc-1. Request 1Gi, accessModes [ReadWriteOnce], storageClassName cka-manual. It must be Bound to cka-pv-1.

The binding conditions are three: the class name matches, the access modes are included, and the capacity is sufficient. If even one is off, it quietly stays Pending.

Create an RWX volume

Create the PersistentVolume cka-pv-rwx. capacity 5Gi, accessModes [ReadWriteMany], storageClassName cka-shared, volume source NFS (server 10.0.0.109, path /volume1/k8s). Then create the PVC cka-pvc-rwx (5Gi, RWX, cka-shared) and bind it.

The accessModes of the PV and the PVC must match for them to bind. The NFS volume source requires two fields, server and path.

Create PVCs automatically with volumeClaimTemplates

Create the headless Service db-headless (clusterIP None, port 5432, selector app=db) and the StatefulSet db. serviceName db-headless, 2 replicas, image nginx:1.27, podManagementPolicy Parallel, volumeClaimTemplates with name data (accessModes [ReadWriteOnce], storageClassName cka-fast, 1Gi), persistentVolumeClaimRetentionPolicy with whenDeleted Delete / whenScaled Retain. The PVCs data-db-0 and data-db-1 must be created automatically.

The PVC name is formed by combining the template name and the Pod name. You need to change the default behavior in which the next Pod is not created until the first Pod becomes Ready.

emptyDir and hostPath mounts

In cka-storage, create the Pod vol-demo (image nginx:1.27). The volume cache is an emptyDir with medium Memory and sizeLimit 128Mi, mounted at /cache. The volume hostlogs is a hostPath with path /var/log and type Directory, mounted read-only at /hostlogs.

Volume definitions go in spec.volumes, and mounts go in the container's volumeMounts. A memory-backed emptyDir is specified with the medium field.

Putting it together: reject the default StorageClass

Create the PersistentVolume cka-pv-static (capacity 1Gi, accessModes [ReadWriteOnce], no storageClassName at all, hostPath /mnt/cka-static), and create the PVC cka-pvc-static (1Gi, RWO) with storageClassName explicitly set to an empty string, and bind it.

Omitting a field is different from specifying it explicitly as an empty string. The former gets the default injected, and the latter is a refusal.