USB disk als NFS share voor K8s

Data die op een lokale drive geplaatst wordt in een pod zal verloren gaan als de pod verwijderd wordt. Vaak willen we dat de data bewaard blijft zodat een nieuwe replica van de pod of een andere pod (op dezelfde node) er ook bij kan. Hiervoor maken we dan een volume (hostPath) op de node maar wat als er multi-node gebruikt wordt of als er iets met de node fout gaat? Om de data beschikbaar te stellen zullen we dus Persistant Volumes moeten gaan gebruiken en NFS is daar een goede kandidaat voor.

Uiteindelijk is het de bedoeling dat een K8s-Administrator een Persistent Volume (PV) kan aanmaken voor een shared disk op een NFS-server en zodra deze gereed is voor gebruik dat een Developer vervolgens een Persistent Volume Claim (PVC) maakt om (een gedeelte van) de PV te kunnen gebruiken in zijn/haar applicatie.

Voorbereiden disk

Allereerst gaan we een USB Externe disk van 500Gb aan de control-Raspberry PI van onze cluster koppelen en deze formateren als ext4 partitie zodat de NFS-server deze kan gebruiken. De USB disk voorzien we van spanning middels een USB-Bridge want de RPi zelf heeft daar niet genoeg stroom voor. Met het volgende commando zien we de disk op één van de USB poorten:

$ lsusb 
Bus 001 Device 005: ID 0480:a00d Toshiba America Inc STOR.E BASICS 500GB

Eenmaal deze gekoppeld is, gaan we een partitie maken. Mocht dit een tweede, derde of vierde disk zijn dan wordt het device /dev/sdb, /dev/sdc of /dev/sdd. In dit geval is dit de enige externe disk op de RPi dus wordt het device /dev/sda

$ fdisk /dev/sda

Met ‘n’ kan een nieuwe partitie gemaakt worden. Gebruik ‘d’ om eventuele oude partities te verwijderen.

Command (m for help): n
 Partition type
    p   primary (0 primary, 0 extended, 4 free)
    e   extended (container for logical partitions)
 Select (default p): p
 Partition number (1-4, default 1): 
 First sector (2048-976771082, default 2048): 
 Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976771082, default 976771082): 
 Created a new partition 1 of type 'Linux' and of size 465.8 GiB.

Vergeet niet om de nieuwe partitie weg te schrijven met ‘w’!

Formateren van de nieuwe partitie gaat met:

$ sudo mkfs -t ext4 /dev/sda1

Mount point

We maken nu een mount-point en koppelen de USB disk hieraan:

$ sudo mkdir -p /mnt/storage
$ sudo chown -R pi:pi /mnt/storage
$ sudo mount /dev/sda1 /mnt/storage -o uid=pi,gid=pi

Om het mounten van de disk permanent te maken, ook na een herstart, voegen we de volgende regel toe aan /etc/fstab

/dev/sda1 /mnt/storage ext4 noatime,nodiratime,data=writeback,barrier=0,nobh,errors=remount-ro 0 1

NFS Server

Om nu de disk te delen over het netwerk, kan NFS hiervoor gebruikt worden. Installatie van de NFS Server op de RPi:

$ sudo apt install -y nfs-kernel-server nfs-common
$ sudo systemctl enable nfs-kernel-server

Hierna gaan we het bestand /etc/exports aanpassen om de gedeelde schijf via NFS bekend te maken op het netwerk. Voeg de volgende regel toe aan /etc/exports: (pas het ip-adres van het netwerk aan)

/mnt/storage 192.168.1.0/24(rw,all_squash,insecure,no_subtree_check)

Vervolgens dienen de volgende commando’s uitgevoerd te worden:

$ sudo exportfs -ra
$ sudo update-rc.d rpcbind enable
$ sudo update-rc.d nfs-common enable
$ sudo service rpcbind restart
$ sudo systemctl restart nfs-kernel-server

Om nu de NFS share te mounten op een ander systeem in het netwerk, kunnen de volgende commando’s uitgevoerd worden:

$ sudo apt install -y nfs-common
$ sudo mkdir -p /mnt/nfs
$ sudo chown -R pi:pi /mnt/nfs
$ sudo mount -t nfs 192.168.1.123:/mnt/storage/ /mnt/nfs

K8s Persistent Volume

Als administrator van een Kubernetes Cluster kun je een Persistent Volume (PV) aanmaken die later gebruikt kunnen worden door developers. Maak een bestand genaamd pv-nfs.yaml met de volgende inhoud:

apiVersion: v1
 kind: PersistentVolume
 metadata:
   name: nfs-share
   namespace: default
 spec:
   capacity:
     storage: 20Gi 
   accessModes:
     - ReadWriteMany
   persistentVolumeReclaimPolicy: Retain
   nfs:
     server: 192.168.1.123
     path: /mnt/storage

Dit is een vorbeel om 20Gb van de schijf te gebruiken waarbij de naam van de PV ‘nfs-share’ is. De accessMode geeft aan dat eerdere containers deze share mogen gebruiken om te lezen en te schrijven. Voer het volgende commando uit om de PV aan te maken:

$ kubectl create -f pv-nfs.yaml
$ kubectl get pv

NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nfs-share   20Gi       RWX            Recycle          Available                                   2s

K8s Persistent Volume Claim

De PV die eerder aangemaak is door de administrator kan nu gebruikt worden middels een claim door een developer. Daartoe dient een Persistent Volume Claim en die maken we aan met het bestand pvc-nfs.yml:

apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
   name: nfs-app
   namespace: default
 spec:
   accessModes:
     - ReadWriteMany
   resources:
     requests:
       storage: 5Gi

Hiermee vragen we om een schijf van 5Gb grootte en er wordt automatisch gezocht naar een geschikte PV in dezelfde namespace waarbij gecontroleerd wordt op:

  • Voldoende capaciteit (resources)
  • AccessMode overeenkomst
  • Volume Mode
  • Storage Class
  • Selector

Indien een specifieke PV gewenst is voor de PVC, dan kan gebruik gemaakt wordt van ‘labels‘ en ‘selectors‘. Indien er een match plaatsvindt zal de PVC ge’bound‘ worden aan de PV. Indien er 5Gi gevraagd wordt en de PV heeft 20Gi beschikbaar, dan wordt dit als een match beschouwd en aangezien er een 1-op-1 relatie is tussen de PV en de PVC zal de overige ruimte niet door een andere PVC geclaimd kunnen worden!

Er wordt gezocht zodra het volgende commando wordt uitgevoerd:

$ kubectl create -f pvc-nfs.yaml

met als resultaat een match en dus ge-bound:

$ kubectl get pv
NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
nfs-share   20Gi       RWX            Recycle          Bound    default/nfs-app
                           4m3s
$ kubectl get pvc
NAME        STATUS   VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-app     Bound    nfs-share   20Gi       RWX                           85m

Een PVC kan verwijderd worden met het commando:

$ kubectl delete pvc nfs-app

en afhankelijk van de ReclaimPolicy in de PV, zal de PV bewaard blijven of verwijderd worden.

  • Retain = bewaren tot een ‘pv delete’ commando
  • Delete = verwijderen
  • Recycle = verwijder data en maak beschikbaar voor andere claims

De PV en PVC maken gebruik van de default StorageClass voor NFS en kan opgevgraagd worden met:

$ kubectl get storageclass
NAME       PROVISIONER                AGE
nfs-ssd1   nfs-provisioner/nfs-ssd1   4d20h

NFS in een K8s pod

We maken een manifest dat een pod aanmaakt en gebruik maakt van de NFS share. Maak een bestand genaamd nfs-in-a-pod.yaml met de volgende inhoud:

kind: Pod
apiVersion: v1
metadata:
   name: nfs-in-a-pod
spec:
   containers:
     - name: app
       image: alpine
       volumeMounts:
         - name: nfs-volume
           mountPath: /mnt/nfs 
       command: ["/bin/sh"]
       args: ["-c", "sleep 500000"]
   volumes:
     - name: nfs-volume
       nfs:
         server: 192.168.1.123
         path: /mnt/storage

De pod maken we aan met:

$ kubectl create -f nfs-in-a-pod.yaml

Nadat de pod status ‘Ready’ is kunnen we deze gebruiken om als test een bestand in de NFS share te maken:

$ kubectl exec -it nfs-in-a-pod sh
$ touch /mnt/nfs/fromApod

Controle

Via een ander systeem, bv. je laptop, kun je de NFS share ook mounten en het bestand gemaakt in de pod zien.

Nicos-MacBook-Pro:~ nico$ sudo mount -t nfs 192.168.1.123:/mnt/storage/ /mnt/nfs
Nicos-MacBook-Pro:~ nico$ ls -l /mnt/nfs
-rw-r--r--  1 65534  65534     0 Aug  3 13:44 fromApod

That’s all, folks!