After setting up NFS in a previous post as well as using k3s to set up a lightweight Kubernetes cluster, we need to configure a Persistent Volume on Kubernetes to allow us to use the NFS storage in our containers.
The process to do this is as follows:
- Prepare a location for the Persistent Volume
- Create an NFS Persistent Volume
- Bind a Persistent Volume Claim to the Persistent Volume
- Create a Pod that mounts the Persistent Volume Claim
- Test the volume on the Pod
Prepare a location for the Persistent Volume
A Persistent Volume represents storage that is available for use in the cluster. There is no reason why we cannot create a single Persistent Volume that represents the entire NFS folder we have mounted, however, since several Pods will be storing data in that folder we might get conflicts if more than one of them creates a file with the same name. Consequently, it makes more sense to create Persistent Volumes for specific purposes.
To test our storage we will be using an Nginx container and the storage will keep the HTML files associated with the container. With that approach in mind we will be creating the following folders:
cd /mnt/nfs
mkdir pv
cd pv
mkdir nginx-html
Create an NFS Persistent Volume
The Persistent Volume (PV) we will be creating will be tied to our underlying NFS server. Since this volume will only be used to keep a few test pages we will be limiting the size of the volume to 50MB.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nginx-html
spec:
capacity:
storage: 50Mi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: pv-nginx-html # storage class will be matched by the pvc
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /mnt/nfs/pv/nginx-html # path on the NFS server
server: 192.168.0.151 # the NFS server
We configure the NFS server name as well as the path on the server where the volume will be mounted. The storage class name will later be used by the Persistent Volume Claim to bind to the Persistent Volume.
Deploy the Persistent Volume:
$ kubectl apply -f pv-nginx-html.yml
persistentvolume/pv-nginx-html created
Bind a Persistent Volume Claim to the Persistent Volume
A Persistent Volume Claim (PVC) is a request to use storage and in our case, we will be requesting the use of the Persistent Volume we have just created. It must also be noted that a PVC can also be dynamically provisioned using Storage Classes with a provisioner. This post is about Persistent Volumes, however, and consequently, we will be binding to the PV we have created.
This is the definition of our Persistent Volume Claim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nginx-html
spec:
storageClassName: pv-nginx-html # k8s will look for a pv matching this storage class
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Mi # k8s will find a matching pv with at least this amount of storage
Kubernetes will match the Persistent Volume we created earlier using the storage class name and the storage request. It always attempts to find a Persistent Volume with at least the amount of storage requested.
Deploy the Persistent Volume Claim:
$ kubectl apply -f pvc-nginx-html.yml
persistentvolumeclaim/pvc-nginx-html created
Lets have a look at what has been created:
$ kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pv-nginx-html 50Mi RWX Recycle Bound default/pvc-nginx-html pv-nginx-html 23h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc-nginx-html Bound pv-nginx-html 50Mi RWX pv-nginx-html 23h
The Persistent Volume Claim has been bound to the Persistent Volume. It must be noted that a Persistent Volume can have only one Persistent Volume Claim bound to it.
Create a Pod that mounts the Persistent Volume Claim
We will be creating a Deployment with a volume. If you look at the YAML below you will notice that the spec includes a volume that specifies the PVC to use and the container then mounts that volume at the path specified.
Here is the deployment definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-with-pvc-html
labels:
app: nginx-with-pvc-html
spec:
replicas: 1
selector:
matchLabels:
app: nginx-with-pvc-html
template:
metadata:
labels:
app: nginx-with-pvc-html
spec:
volumes:
- name: nfs-html
persistentVolumeClaim:
claimName: pvc-nginx-html # name of the pvc being used as the volume
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nfs-html # name of the volume specified under 'volumes'
mountPath: /var/www/html # path where the volume will be accessible in the container
Let’s deploy the Pod:
$ kubectl apply -f nginx-with-pvc-html.yml
deployment.apps/nginx-with-pvc-html created
Test the volume on the Pod
To test the volume we will connect to the container in the pod and then create a file. Afterwards, we will confirm that the file was created in our Persistent Volume path.
We will list the pods so that we can get our exact pod name:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-with-pvc-html-6fdb4bd6bd-lzlwv 1/1 Running 0 23h
Then we exec into the pod, go to the mounted path and create a test file. Afterwards, we exit again.
$ kubectl exec nginx-with-pvc-html-6fdb4bd6bd-lzlwv -it -- bash
$ cd /var/www/html
$ echo "<h1>This is a test index.html</h1>" >> index.html
$ exit
Lastly, we go the NFS path that was specified in our Persistent Volume and confirm that the file was created.
$ cd /mnt/nfs/pv/nginx-html
$ ls
index.html