Kubernetes Storage By Example: Part 2
Exploring more Kubernetes storage topics through example.
This article is part of a series starting with Kubernetes Storage By Example: Part 1.
PersistentVolume and PersistentVolumeClaim
In the previous example, there were three distinct tasks:
- Create the data folder: had to be done using the root user of our development machine; this is an infrastructure task
- Create the Kubernetes Volume: requires knowledge of the development machine (folder location); this is an infrastructure task
- Create the Kubernetes Pod; this is a development task
The problem, however, with the previous example is that the creation of the Kubernetes Volume and Pod are tightly coupled (defined in the Pod configuration) and yet these are different types (infrastructure and development) of tasks that are often managed by different teams of people.
We can decouple this configuration using a Kubernetes PersistentVolume (infrastructure task) and PersistentVolumeClaim (development task).
A PersistentVolume (PV) is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes. It is a resource in the cluster just like a node is a cluster resource. PVs are volume plugins like Volumes, but have a lifecycle independent of any individual Pod that uses the PV. This API object captures the details of the implementation of the storage, be that NFS, iSCSI, or a cloud-provider-specific storage system.
A PersistentVolumeClaim (PVC) is a request for storage by a user. It is similar to a Pod. Pods consume node resources and PVCs consume PV resources. Pods can request specific levels of resources (CPU and Memory). Claims can request specific size and access modes (e.g., they can be mounted once read/write or many times read-only).
— Kubernetes — Persistent Volumes
note: While it is not demonstrated in this article, PersistentVolumeClaims (and their bound PersistentVolumes) exist separately from their connected Pods, i.e., one can disconnect them and reconnect them to other Pods.
In this configuration, the infrastructure team provisions a PersistentVolume with a storageClassName (development) identifier.
persistent-volume/pv.yaml
The development team then creates a PersistentVolumeClaim using the development storageClassName; thus binding it to the provided PersistentVolume. The development team required no knowledge of how the infrastructure team provisioned the volume; to the development team it is just a ReadWriteOnce 1Gi volume.
persistent-volume/pvc.yaml
Finally, the development team connects the PersistentVolumeClaim (bound to the PersistentVolume) to the Pod.
After applying this configuration, we can see that we can read the hello.txt file as before:
kubectl exec persistent-volume -- cat /data/hello.txt
StorageClass
Using the previous pattern, the infrastructure team is required to create a PersistentVolume for each PersistentVolumeClaim the development team requires; this is called static provisioning.
Another approach is the infrastructure team can create a Kubernetes StorageClass that dynamically provisions PersistentVolumes on demand.
A StorageClass provides a way for administrators to describe the “classes” of storage they offer. Different classes might map to quality-of-service levels, or to backup policies, or to arbitrary policies determined by the cluster administrators. Kubernetes itself is unopinionated about what classes represent. This concept is sometimes called “profiles” in other storage systems.
— Kubernetes — Storage Classes
Here the infrastructure team creates a StorageClass (identified by the name development-dynamic) that automatically provisions PersistentVolumes using the microk8s.io/hostpath provisioner. This provisioner, specific to microk8s, uses folders and files on the development workstation.
storage-class/sc.yaml
As before, the development team only needs to specify the storageClassName, e.g., development-dynamic, using a PersistentVolumeClaim to bind it to a dynamically provisioned PersistentVolume.
note: Observe that, here, the storageClassName resolves to a StorageClass; in the previous example it resolves to a PersistentVolume.
storage-class/pvc.yaml
Finally, the development team connects the PersistentVolumeClaim (bound to the PersistentVolume) to the Pod.
storage-class/pod.yaml
After applying this configuration, we can see that we can first write and then read the hello.txt file as expected:
kubectl exec storage-class -- sh -c "echo \"Hello World\" > /data/hello.txt"kubectl exec storage-class -- cat /data/hello.txt
Wrap Up
Hope you found this as useful as it was for me to write.