Storage in Kubernetes
Container Storage Interface (CSI)
In the past, Kubernetes used Docker alone as the container runtime engine, and all the code to work with Docker was embedded within the Kubernetes source code.
With other container runtimes coming in, such as rkt, cri-o, it was important to open up and extend support to work with different container runtimes, and not be dependent on Kubernetes source code.
And that's how Container Runtime Interface (CRI) came in.
The CRI is a standard that defines how an orchestration solution like Kubernetes would communicate with a container runtime like Docker.
So, in the future, if any new container runtime interface is developed, they can simply follow the CRI standards and that new container runtime would work with Kubernetes without really having to work with Kubernetes team of developers or touch the Kubernetes source code.
Similarly, to extend support for different networking solutions, the Container Networking Interface (CNI) was developed.
Now any new network vendors could simply develop their plugin based on the CNI standards and make their solution work with Kubernetes.
Similarly, the Container Storage Interface (CSI) was developed to support multiple storage solutions.
With CSI, we can now write our drivers for our storage to work with Kubernetes.
Ex of CSI: Amazon EBS, Azure Disk, NetApp, Nutanix, HPE, Hitachi, Pure Storage, etc.
Working of CSI
CSI defines a set of Remote Procedure Calls (RPCs), that will be called by the container orchestrator, and these must be implemented by the storage drivers.
Ex.
When a pod is created and requires a volume, Kubernetes calls the create volume RPC and passes a set of details such as the volume name.
The storage driver should implement this RPC, handle that request to provision a new volume on the storage array, and return the results of the operation.
Note
CSI is not a Kubernetes-specific standard.
It is a universal standard, and if implemented allows any container orchestration tool to work with any storage vendor with a supported plugin.
Volumes in Kubernetes
Just as in Docker, the pods created in Kubernetes are transient in nature.
When a pod is created to process data, and then deleted, the data processed by it, gets deleted as well.
For this, we attach a volume to the pod.
The data generated by the volume is now stored in the volume, and even after the pod is deleted, the data remains.
When we create volumes, we configure volumes within the pod definition file, so every configuration information required to configure storage for the volume goes within the pod definition file.
Kubernetes supports various types of storage solutions, such as Amazon EBS, Azure Disk, NFS, Flocker, CephFS, ScaleIO, and Google's Persistent Disk.
Persistent Volumes (PV)
When we have a large environment which consists of a large number of pods, we have to configure storage for each pod as we have done above.
Every time there are changes to be made, we have to make them on all the pods.
Instead, we would like to manage storage more centrally. That is where Persistent Volume helps.
A Persistent Volume is a cluster-wide pool of storage volumes configured by an administrator to be used by users deploying applications on the cluster. The users can now select storage from this pool using Persistent Volume Claims.
Access Modes
It defines how a volume should be mounted on the hosts, whether in a read-only mode, read/write mode, etc.
The supported values:
ReadOnlyMany
ReadWriteOnce
ReadWriteMany
Capacity
- Specified the amount of storage to be reserved for the Persistent Volume.
Commands
kubectl create -f pv-definition.yaml
- To create the volume.
kubectl get persistentvolume
- To get the created persistent volumes.
Persistent Volume Claims (PVC)
Persistent Volume and Persistent Volume Claims are two separate objects in the Kubernetes namespace.
An administrator creates a set of persistent volumes, and a user creates persistent volume claims to use the storage.
Once the PVCs are created, Kubernetes bind the PV to the claims based on the request and properties set on the volume.
Every PVC is bound to a single PV. i.e. There is a one-to-one relationship between PVC and PV.
During the binding process, Kubernetes tries to find a PV that has sufficient capacity, as requested by the claim.
However, if there are multiple possible matches for a single claim, and we would like to specifically use a particular volume, we can use labels and selectors to bind to the right volumes.
If there are no volumes available, the PVC will remain in a pending state until newer volumes are made available to the cluster. Once newer volumes are available, the claim will automatically be bound to the newly available volume.
Commands
kubectl get persistentvolumeclaim
- To get the created persistent volume claims.
kubectl delete persistentvolumeclaim <claim-name>
- To delete the persistent volume claims.
Note
When the claim is deleted, we can choose what will happen to the persistent volume, by setting persistentVolumeReclaimPolicy.
By default, it is set to Retain, meaning the persistent volume will remain until it is manually deleted by the administrator. It is not available for reuse by any other claims.
The second option is Delete, it deletes the PV as soon as PVC is deleted.
The third option is Recycle, the data in the data volume will be scrubbed before making PV available to other claims.
Storage Classes
We know that we can create PVs and claim those volumes using PVCs and then use the PVCs in the pod definition file as volumes.
The problem with this approach is, that before the PV is created you must have created the storage on the cloud let's say AWS EBS to use it in PV.
Every time an application requires storage, we have to first manually provision the disk on the cloud, and then manually create a persistent volume definition file using the same name as that of the disk we created. This is called Static Provisioning Volumes.
With Storage Classes, we can define a provisioner, such as AWS EBC or Google Storage, that can automatically provision storage on that cloud and attach that to pods when a claim is made. This is called Dynamic Provisioning Volumes.
We can create a Storage Class as a Kubernetes object, and if we use storage classes then we no longer need the PV definition, because the PV and any associated storage is going to be created automatically when the storage class is created.
Storage class creates a PV internally, it's just that we don't have to manually create PV anymore.
In storage classes, along with the provisioner, we can pass additional parameters in parameter block such as the type of disk to provision, the replication type, etc. These parameters are very specific to the provisioner we use.
Subscribe to my newsletter
Read articles from Rohit Pagote directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Rohit Pagote
Rohit Pagote
I am an aspiring DevOps Engineer proficient with containers and container orchestration tools like Docker, Kubernetes along with experienced in Infrastructure as code tools and Configuration as code tools, Terraform, Ansible. Well-versed in CICD tool - Jenkins. Have hands-on experience with various AWS and Azure services. I really enjoy learning new things and connecting with people across a range of industries, so don't hesitate to reach out if you'd like to get in touch.