Dynamic vs Static Volume Provisioning in Kubernetes
In Kubernetes, if you store some data (create a file, write to it, modify a file, etc.) in a container running inside your pod, the changes won’t persist once the container stops running (crashes, stops normally or restarts). For better data persistence, Kubernetes provides the following options:
- Ephemeral Volumes – Specified inline in the pod’s
spec
, these follow the pod’s lifecycle, i.e., get created and deleted along with the pod. Hence, they survive across pod restarts (or stop and start) but not when the pod is deleted. The volume itself is backed by the node’s storage or separate disks. - Persistent Volumes – Independent of the pod lifecycle, these are separately created via the PersistentVolume object and used with the PersistentVolumeClaim object. The volume itself is backed by separate disks that you create in your cloud provider or on-prem.
Now when specifically working with Persistent Volumes, they can be provisioned statically/manually or dynamically. Ephemeral volumes are sort of always provisioned dynamically. The purpose of this article is to understand how static and dynamic provisioning differ through some examples that work with Persistent Volumes.
Static Volume Provisioning
When provisioning a volume manually, we first create a storage backend (or a disk) in our cloud provider (GCP, AWS, Azure, etc.) or on-prem. Of course, we could also use a pre-existing disk. Then we create a PersistentVolume
resource in our K8S cluster that points to that disk.
For example in the case of GCP/GKE, say we created a disk (Compute Engine) called my-disk
, then a PV with the following manifest would have to be created:
apiVersion: v1
kind: PersistentVolume
metadata:
name: PV_NAME
spec:
storageClassName: "STORAGE_CLASS_NAME"
capacity:
storage: DISK_SIZE
accessModes:
- ReadWriteOnce
csi:
driver: pd.csi.storage.gke.io
volumeHandle: projects/PROJECT_NAME/zones/ZONE/disks/my-disk
fsType: ext4
For other CSI drivers, to figure out the right values for spec.csi
, follow the relevant driver link from here where you will all the information. For instance, here’s how you do it for AWS:
spec:
...
csi:
driver: ebs.csi.aws.com
fsType: ext4
volumeHandle: {EBS volume ID}
For In-tree volume plugins, the right configuration manifests can be found in the K8S documentation.
Now, in order to use the PV above, a PVC pointing to it must be created. This PVC will be used by a pod later:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: PVC_NAME
spec:
storageClassName: "STORAGE_CLASS_NAME"
volumeName: PV_NAME
accessModes:
- ReadWriteOnce
resources:
requests:
storage: DISK_SIZE
Feel free to use the PVC in a pod then:
apiVersion: v1
kind: Pod
metadata:
name: POD_NAME
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: PVC_NAME
containers:
- name: web
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
Dynamic Volume Provisioning
Compared to statically provisioning volumes, where the disk on the cloud provider (or on-prem) and PersistentVolume
object in the K8S cluster has to be created manually, with dynamic volume provisioning we just have to create our PVC using the right storage class and both the disk in the cloud provider (or on-prem) and the PV object are automatically created by the volume plugin (if supported).
With dynamic volume provisioning, you will most likely be using one of the CSI (out-of-tree) volume plugins. Refer to this list to find out whether your plugin supports dynamic provisioning or not.
Assuming we have a StorageClass
that looks like this:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
parameters:
type: pd-standard
provisioner: pd.csi.storage.gke.io
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
Let’s discuss a few fields first:
provisioner
specifies which volume plugin to use, in this case, it is the GCP persistent disk CSI volume plugin that does support dynamic volume provisioning.parameters
are passed to the plugin for configuration. Follow your driver documentation to learn more about supported parameters.
Now let’s create a PVC with the storage class above:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 10Gi
Let’s look at all the resources now:
# Create a PVC from the manifest above
$ k apply -f pvc.yaml
persistentvolumeclaim/myclaim created
$ k get pvc
NAME STATUS VOLUME CAPACITY STORAGECLASS
myclaim Bound pvc-5fbffcf5-e417-4591-96b3-1cd6bd099709 10Gi standard
# Automatically created PersistentVolume via the standard StorageClass
$ k get pv
NAME CAPACITY RECLAIM POLICY STATUS CLAIM STORAGECLASS
pvc-5fbffcf5-e417-4591-96b3-1cd6bd099709 10Gi Delete Bound test/myclaim standard
# Automatically created disk in GCP Compute Engine
$ gcloud compute disks list
NAME LOCATION LOCATION_SCOPE SIZE_GB TYPE STATUS
pvc-5fbffcf5-e417-4591-96b3-1cd6bd099709 us-west1-b zone 10 pd-standard READY
As you can see, we created the PVC resource that automatically created:
- A disk in GCP Compute Engine.
- A PV resource pointing to the disk.
This was done by the CSI plugin specified in the SC’s provisioner
field. The PVC also bound itself to the PV. Now any pod can claim the PVC just like we saw in the static provisioning section (pod manifest) above.