Creating persistent storage

A key requirement of our database service is persistent storage, and for our single-node local Kubernetes development environment, the hostPath volume type represents the standard option for providing simple persistent storage requirements.

Although you can create a hostPath volume very easily by specifying a path directly in your volume definition (see the example pod definition at https://kubernetes.io/docs/concepts/storage/volumes/#hostpath), one problem with such an approach is that it creates a hard dependency on the underlying volume type, and also requires manual cleanup if you ever want to delete the pod and the data associated with the volumes.

A very useful feature of the Docker Desktop Kubernetes support is the inclusion of a dynamic volume provisioner called docker.io/hostpath that automatically creates volumes of type hostPath for you, which is available via the default storage class that you can view by running the kubectl get sc command:

> kubectl get sc
NAME PROVISIONER AGE
hostpath (default) docker.io/hostpath 2d

A storage class provides an abstraction over the underlying volume type, meaning your pods can request storage from a specific class. This includes generic requirements such as volume size, without needing to worry about the underlying volume type. In the case of Docker Desktop, a default storage class is included out of the box, which provisions storage requests using the hostPath volume type.

However, later on when we set up a Kubernetes cluster in AWS using EKS, we will configure a default storage class that uses the AWS Elastic Block Store (EBS) as the underlying volume type. Taking this approach means that we don't need to change our pod definitions, as we will be referring to the same storage class in each environment.

If you are using minikube, a dynamic provisioner called k8s.io/minikube-hostpath provides similar functionality to the Docker hostpath provisioner, but mounts volumes under /tmp/hostpath-provisioner.

To use storage classes rather than specify your volume types directly with your pod definitions, you need to create a persistent volume claim, which provides a logical definition of storage requirements such as volume size and access mode. Let's define a persistent volume claim, but before we do this we need to establish a new folder called k8s/db in the todobackend repository that will store our database service configuration:

todobackend> mkdir -p k8s/db
todobackend> touch k8s/db/storage.yaml

Within this folder, we will create a file called k8s/db/storage.yaml, in which we will define a persistent volume claim:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: todobackend-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi

We create the claim (called todobackend-data) in a dedicated file, as this will allow us to independently manage the life cycle of the claim. One property that is not included in the preceding example is the spec.storageClassName property if this is omitted, the default storage class is used, however bear in mind that you can create and reference your own storage classes. The spec.accessModes property specifies how the storage should be mounted in the case of both local storage and EBS storage in AWS, we only want a single container at a time to be able to read and write to the volume, which is encompassed by the ReadWriteOnce access mode.

The spec.resources.requests.storage property specifies the size of the persistent volume, which in this case we configure as 8 GB.

If you are using Docker for Windows, you will be prompted to share your C:\ with Docker the first time you attempt to use the Docker hostPath provisioner.

If you now deploy the persistent volume claim using kubectl, you can use the kubectl get pvc command to view your newly created claim:

> kubectl apply -f k8s/db/storage.yaml
persistentvolumeclaim "todobackend-data" created
> kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
todobackend-data Bound pvc-afba5984-9223-11e8-bc1c-025000000001 8Gi RWO hostpath 5s

You can see that when you create a persistent volume claim, a persistent volume is dynamically created. When using Docker Desktop, this is actually created in the path ~/.docker/Volumes/<persistent-volume-claim>/<volume>:

> ls -l ~/.docker/Volumes/todobackend-data
total 0
drwxr-xr-x 2 jmenga staff 64 28 Jul 17:04 pvc-afba5984-9223-11e8-bc1c-025000000001

If you are using Docker for Windows and you are using the Windows Subsystem for Linux, you can create a symbolic link to the .docker folder on your Windows host:

> ln -s /mnt/c/Users/<user-name>/.docker ~/.docker
> ls -l ~/.docker/Volumes/todobackend-data
total 0
drwxrwxrwx 1 jmenga jmenga 4096 Jul 29 17:04 pvc-c02a8614-932d-11e8-b8aa-00155d010401

Note that if you followed the instructions in Chapter 1, Container and Docker Fundamentals, for setting up the Windows Subsystem for Linux, you already configured /mnt/c/Users/<user-name>/ as your home directory so you don't need to perform the configuration above.