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