# 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 <a href="#emptydir" id="emptydir"></a>

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 href="#hostpath" id="hostpath"></a>

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 <a href="#optional-nfs-volume" id="optional-nfs-volume"></a>

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 <a href="#bonus-pvs" id="bonus-pvs"></a>

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 <a href="#cleaning-up" id="cleaning-up"></a>

Delete all PVs, PVCs, and pods.

Solution

```
kubectl delete pv,pvc,pod --all
```

\\
