Lab_03 - Persisting Data

Lab 03: Persisting Data

In this lab, we will see how we can keep our data, even though our containers are (and should be!) ephemeral.

emptyDir

We will start with a very simple example - the emptyDir volume type. This simply uses a directory on the node the pod is running on.

Connect to the master node with your user credentials. Make sure that there are no pods already running on the system (in the default namespace).

Copy the 02-alpine.yaml file to a new file: 09-emptydir.yaml

Edit the new file to add an emptyDir volume. Name the volume empty-volume, and mount it in /scratch

Create a pod based on this specification.

Solution

kubectl get pod

09-emptydir.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: alpine01
spec:
  containers:
    - image: alpine
      name: alpine
      stdin: true
      tty: true
      volumeMounts:
        - mountPath: /scratch
          name: empty-volume
  volumes:
    - name: empty-volume
      emptyDir: {}
kubectl apply -f 09-emptydir.yaml

Get a shell inside the pod. Create a file inside the /scratch directory, with a text contents of your choosing.

Hint

you can connect to the existing shell (kubectl attach), or you can open a new one (kubectl exec). Which one you choose is very relevant when you exit the pod!

Solution

kubectl exec -it alpine01 -- sh

Inside the container:

echo "test file" > testfile
ls

Exit the pod and delete it.

Solution

Inside the container:

exit
kubectl delete pod alpine01

Create a new pod based on the same yaml file.

Solution

kubectl apply -f 09-emptydir.yaml

Get a shell inside the new pod. Is your file still inside?

Solution

kubectl exec -it alpine01 -- sh

Note

While emptyDir volumes wil survive a pod crash/restart, they will be removed when the pod itself is removed.

Exit the pod and delete it.

hostPath

A more relevant (more… persistent) example is the hostPath volume type. This will actually persist across pod deletions, but has another disadvantage - it makes the pod dependent on a specific host (a specific host directory structure). So in order to make sure that the pods will find that directory structure, we will force them to always run on a specific host.

Needless to say, this is not something you would generally want to do in production. However, it is a useful exercise for dealing with labels and selectors.

In order to make testing easier, we want to force our pod to run on one specific node (it is easier this way than chasing the pods across multiple nodes). In order to do this, we will use a label. Label your first worker node (k8s-ID-02) with pathConfiguredID=true (replace ID with your user ID).

Solution

kubectl get nodes --show-labels
kubectl label node k8s-ID-02 pathConfiguredID=true

Make sure that the label has been correctly applied.

Solution

kubectl get nodes --show-labels

Copy the 09-emptydir.yaml file to 10-hostpath.yaml.

Edit the 10-hostpath.yaml file to add the following:

  • A node selector that forces the pod to run only on nodes labeled with pathConfiguredID=true. (under .spec.nodeSelector).

  • A volume named host-volume mapped to the host directory /tmp/hostpath

  • A volumeMount for the container that tells it to mount the volume under /hostvol

Create a pod based on this new specification file.

Solution

10-hostpath.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: alpine02
spec:
  nodeSelector:
    pathConfigured<your_user_id>: 'true'
  containers:
    - image: alpine
      name: alpine
      stdin: true
      tty: true
      volumeMounts:
        - mountPath: /hostvol
          name: host-volume
  volumes:
    - name: host-volume
      hostPath:
        path: /tmp/hostpath
        type: DirectoryOrCreate
kubectl apply -f 10-hostpath.yaml

Get a shell into the pod, and create a test file in /hostvol. Use a content of your choosing.

Solution

kubectl exec -it alpine02 -- sh

Inside the pod:

cd /hostvol
echo "hello" > testfile
exit

Exit the pod. Check the /tmp/hostpath directory. You should see your newly-created file.

Solution

ls /tmp/hostpath
cat /tmp/hostpath/testfile

Delete the pod, and create a new one based on the same YAML file. Get a shell into the pod. The file should still be in /hostvol.

Solution

kubectl delete pod alpine02
kubectl apply -f 10-hostpath.yaml
kubectl get pod
kubectl exec -it alpine02 -- sh

Inside the pod:

cd /hostvol/
cat testfile

Delete the pod.

Optional: NFS Volume

Getting closer to a real-life deployment - we will be using an NFS mount that we connect to over the network. The task is marked as optional because it has a prerequisite that cannot always be satisfied - a server that has been preconfigured to export an NFS mount over the network. The NFS mount has also been prepopulated with an index.html file, so that you can test whether your configuration is correct.

Note

While closer to a real-life example, we are not there yet! We are still using volumes defined at the pod level, which are closely coupled to the pods themselves. A real life scenario would actually be using PVs/PVCs (see the next section).

Copy 10-hostpath.yaml to 11-nfs.yaml.

Edit the 11-nfs.yaml file to change the following:

  • the pod should be running nginx (which also means that stdin and tty are no longer necessary)

  • the nodeSelector is no longer necessary (all nodes have equal access to the NFS datastore)

  • the pod name should be nginx-01

  • the volume type is now nfs, on server 10.10.17.34, path /NFS_STUDENTS (remember that file and directory names are case sensitive!)

  • the volume name is nfs-vol-xx (with xx being your user ID)

  • the volume is mounted inside the container as /usr/share/nginx/html (the default nginx web server root)

Create a pod based on this specification.

Solution

10-hostpath.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-01
spec:
  containers:
    - image: nginx
      name: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: nfs-vol
  volumes:
    - name: nfs-vol
      nfs:
        server: 10.10.17.34
        path: /NFS_STUDENTS
kubectl apply -f 11-nfs.yaml

Make sure that the pod is running correctly.

If the pod seems to be stuck in „Pending” or „ContainerCreating”, it is likely that the NFS mount has failed. Use kubectl describe to get more details, and ask your instructor for assistance.

Solution

kubectl get pod

Get the pod IP address. Connect to the pod IP address using curl. You should get a page confirming that you have successfully completed this task.

Solution

kubectl get pod -o wide
curl IP_ADDR

Delete the pod.

Solution

kubectl delete pod nginx-01

Bonus: PVs

For the most complex scenario, we will use the NFS mount to provision multiple persistent volumes for our various pods. We will work with persistent volumes, persistent volume claims, and pods. To keep things simpler, we will define the components in separate files.

Create a YAML file (12-nfs-pv.yaml) for the NFS persistent volume. The details are below:

  • name: nfs-pv01-ID (replace ID with your assigned ID)

  • .spec.capacity.storage: 2Mi

  • .spec.accessModes: [ ReadWriteOnce ]

  • type: nfs

  • .spec.nfs.server: 10.10.17.34

  • .spec.nfs.path: /NFS_STUDENTS

Note

If you have not seen the „Mi” notation before, that is a „mebibyte”. The kibi/mebi/gibi notation has been introduced to differentiate between regular SI prefixes (powers of 10) and binary prefixes (powers of 2). More details here: https://en.wikipedia.org/wiki/Binary_prefix

Hint

apiVersion: v1
kind: PersistentVolume
metadata:
  name: PV_NAME
spec:
  capacity:
    storage: SIZE
  accessModes:
    - ReadWriteOnce
  nfs:
    server: NFS_SERVER
    path: SERVER_PATH

Solution

12-nfs-pv.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv01
spec:
  capacity:
    storage: 2Mi
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 10.10.17.34
    path: /NFS_STUDENTS

Apply the file to create a PersistentVolume.

Solution

kubectl apply -f 12-nfs-pv.yaml

Check the list of PersistentVolumes to verify that the new volume has been successfully created.

Solution

kubectl get pv

Create another YAML file ( 13-nfs-pvc.yaml ), that defines a PersistentVolumeClaim. The claim will be named nfs-pvc01, and will request a PV with a size of 2Mi.

Hint

apiVersion: v1
metadata:
  name: PVC_NAME
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: SIZE

Create a PVC based on this new file.

Solution

13-nfs-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc01
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Mi
kubectl apply -f 13-nfs-pvc.yaml

Verify that the PVC has been successfully created, and that Kubernetes has bound it to the available NFS PV ( nfs-pv01 ).

Solution

kubectl get pvc

Create a YAML file ( 14-nfs-pod.yaml ) for a pod that mounts the PVC as a volume:

  • The pod should run nginx

  • The name of the pod should be nginx-nfs

  • The pod should mount a volume under /usr/share/nginx/html

  • The volume should be the PVC we created earlier ( nfs-pvc01 )

Hint

apiVersion: v1
kind: Pod
metadata:
  name: ...
spec:
  containers:
    - image: ...
      name: ...
      volumeMounts:
        - mountPath: ...
          name: ...
  volumes:
    - name: ...
      persistentVolumeClaim:
        claimName: ...

Create a pod based on this file. Check that the pod has successfully started.

Solution

14-nfs-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-nfs
spec:
  containers:
    - image: nginx
      name: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: nginx-nfs-volume
  volumes:
    - name: nginx-nfs-volume
      persistentVolumeClaim:
        claimName: nfs-pvc01
kubectl apply -f 14-nfs-pod.yaml
kubectl get pod

Get the pod IP address and connect to it using curl. What do you see?

Solution

kubectl get pod -o wide
curl IP_ADDRESS

Note

You should get the exact same congratulatory file you have seen in the previous exercise. And it is normal - after all, we are mounting the exact same volume! The difference is that this time the allocation of the volume is done dynamically, based on the volume size (specified in the PV) and the size requested by the pod (via PVC).

Cleaning Up

Delete all PVs, PVCs, and pods.

Solution

kubectl delete pv,pvc,pod --all

Last updated