Implement NFS Persistent Volume on Kubernetes

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:

  1. Prepare a location for the Persistent Volume
  2. Create an NFS Persistent Volume
  3. Bind a Persistent Volume Claim to the Persistent Volume
  4. Create a Pod that mounts the Persistent Volume Claim
  5. 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

View this file on GitHub Gist

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

View this file on GitHub Gist

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

View this file on GitHub Gist

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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.