Du hast die Anforderung, dass deine Anwendung für eine Lastverteilung über mehrere Nodes skalieren muss, aber Zugriff auf ein gemeines PVC benötigt? Zu diesem Zweck benötigst du ein PVC welches ReadWriteMany (RWX)-fähig ist. Im Rahmen unserer Managed Kubernetes Cluster ist es möglich, einen CSI Cinder Blockstorage zu erstellen. Dieser ist jedoch wegen technischer Limitierungen nur ReadWriteOnce (RWO) fähig. Wie wir mittels eines Workarounds dennoch ein RWX-fähiges PVC auf die Beine stellen, zeigen wir dir hier an einen Beispiel aus der Praxis! Was PVCs sind, wird im Tutorial Persistente Volumes in Kubernetes erstellen erklärt, auf welchem das vorliegende Tutorial aufbaut.
Nehmen wir beispielsweise einen Webserver, welcher auf seinen PVC HTML-Seiten, Bilder, JS etc. gespeichert hat. Diese Dateien sollen einmalig hochgeladen und allen Instanzen des Webservers gleichzeitig zur Verfügung gestellt werden. Um die Last besser zu verteilen, wird das Deployment entsprechend skaliert. Somit laufen mehrere Pods des gleichen Types auf unterschiedlichen Servern. Damit alle Pods – über Hostgrenzen hinweg – Zugriff auf die Dateien haben, erstellen wir eine RWX Storageclass mit den nfs-ganesha-server-and-external-provisioner. Als Grundlage hierfür dient ein NWS Managed Kubernetes Cluster.
Nach Abschluss des Workarounds haben wir die Möglichkeit, PVCs zu erstellen, welche durch mehrere Pods gleichzeitig gelesen und beschrieben werden können. Ein Hinweis vorweg: das beschriebene Setup ist nicht HA fähig!
Voraussetzungen
Das Tutorial setzt Folgendes voraus:
- NWS Managed Kubernetes Cluster
- kubectl
- helm
Weitere Informationen zu diesen Themen findest Du bei uns in den NWS Docs.
Einrichtung
Mit folgendem Befehl fügen wir das Helm Repository hinzu, welches uns den nfs-ganesha-server-and-external-provisioner bereitstellt, nachfolgend NFS Provisioner genannt.
$ helm repo add nfs-ganesha-server-and-external-provisioner \
https://kubernetes-sigs.github.io/nfs-ganesha-server-and-external-provisioner/Danach können wir sofort mit der Installation loslegen. Zu beachten sind die Einstellungen, die dem –set Parameter folgen. Diese bewirken:
persistence.enabled=trueDieser Parameter stellt sicher, dass unsere Daten auf einem persistenten Volume gespeichert werden und auch nach Neustart des Pods noch vorhanden sind.
persistence.storageClass=standardHier wird angegeben, dass für die persistenten Daten die Storageclass „standard“ genutzt werden soll.
persistence.size=200GiDieser Parameter gibt an, wie groß das PVC sein soll, welches der NFS Provisioner für die Dateien holt.
Zu beachten ist, dass die PVC Größe, welche mit persistence.size angegeben wird, für alle NFS PVCs geteilt wird, welche über den NFS Provisioner bezogen werden. Es gibt noch viele weitere Konfigurationsoptionen mit denen der NFS Provisioner an die eigenen Bedürfnisse angepasst werden kann. Diese findest du hier.
$ helm install nfs-server \
nfs-ganesha-server-and-external-provisioner/nfs-server-provisioner \
--set persistence.enabled=true \
--set persistence.storageClass=standard,persistence \
--set size=200GiWenn der NFS Provisioner erfolgreich installiert werden konnte, erscheint eine Ausgabe wie folgt:
NAME: nfs-server
LAST DEPLOYED: Mon May 22 14:41:58 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NFS Provisioner service has now been installed.
A storage class named 'nfs' has now been created and is available to provision dynamic volumes.
You can use this storageclass by creating a `PersistentVolumeClaim` with the correct storageClassName attribute. For example:
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-dynamic-volume-claim
spec:
storageClassName: "nfs"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100MiNun sehen wir uns den eben erstellten NFS Provisioner Pod an und prüfen ob dieser läuft:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-server-nfs-server-provisioner-0 1/1 Running 0 36mUnd natürlich auch die dazugehörige Storageclass für die automatische Bereitstellung von NFS RWX PVCs. Diesen können wir nun verwenden, um dynamisch PVCs vom Typ RWX zu erstellen und zu nutzen.
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
encrypted cinder.csi.openstack.org Delete Immediate true 3h22m
encrypted-high-iops cinder.csi.openstack.org Delete Immediate true 3h22m
high-iops cinder.csi.openstack.org Delete Immediate true 3h22m
nfs cluster.local/nfs-server-nfs-server-provisioner Delete Immediate true 39m
nws-storage cinder.csi.openstack.org Delete Immediate true 3h22m
standard (default) cinder.csi.openstack.org Delete Immediate true 3h22mWebserver Beispiel
Wir haben jetzt ein PVC mit der Eigenschaft RWX welches wir dynamisch provisionieren können. Nun sehen wir uns konkret an, wie dieser in ein Deployment eingebunden wird. Wir legen zwei Dateien an; eine für unser Deployment des Webservers und eine für das RWX PVC:
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webserver
name: webserver
spec:
replicas: 1
selector:
matchLabels:
app: webserver
template:
metadata:
creationTimestamp: null
labels:
app: webserver
spec:
containers:
- image: nginx:latest
name: nginx
volumeMounts:
- mountPath: /files
name: files
volumes:
- name: files
persistentVolumeClaim:
claimName: nfs-pvc# nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: nfsUnser Deployment gibt an, dass wir vom NGiNX Container genau eine Replika laufen lassen wollen. Dieses bindet das dynamisch erstellte NFS PVC unter /files ein. Jetzt müssen wir die Definitionen noch mittels kubectl an Kubernetes verfüttern:
$ kubectl apply -f nfs-pvc.yaml nginx-deployment.yaml
persistentvolumeclaim/nfs-pvc created
deployment.apps/webserver createdWie wir sehen, läuft ein Webserver Pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-server-nfs-server-provisioner-0 1/1 Running 0 54m
webserver-5486dd9cf5-hfhnd 1/1 Running 0 114sNachdem dieser aber bei weitem nicht für unsere Last ausreicht, erweitern wir das Deployment auf 4 Replikationen:
$ kubectl scale deployment webserver --replicas=4
deployment.apps/webserver scaledEs empfiehlt sich ein kleiner Check und siehe da, alle 4 Pods laufen fröhlich vor sich hin:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-server-nfs-server-provisioner-0 1/1 Running 0 55m
webserver-5486dd9cf5-hfhnd 1/1 Running 0 3m9s
webserver-5486dd9cf5-nh9fl 1/1 Running 0 18s
webserver-5486dd9cf5-ss27f 1/1 Running 0 18s
webserver-5486dd9cf5-xl2lj 1/1 Running 0 18sJetzt prüfen wir noch, ob das NFS auch zwischen den Pods richtig funktioniert. Ein erster Befehl zeigt uns, dass /files leer ist. Mithilfe eines zweiten Befehls erstellen wir die Datei nfs-is-rwx. An der dritten Ausgabe können wir erkennen, dass wir erfolgreich waren. Wir haben in einem Pod erstellt und die Datei war sofort in einem anderen Pod vorhanden.
$ kubectl exec webserver-5486dd9cf5-hfhnd -- ls /files
$ kubectl exec webserver-5486dd9cf5-nh9fl -- touch /files/nfs-is-rwx
$ kubectl exec webserver-5486dd9cf5-xl2lj -- ls /files
nfs-is-rwxZusammenfassung
Du hast nun einen NFS Server eingerichtet, welcher im Hintergrund ein CSI Cinder Blockstorage nutzt. Der NFS Server nutzt das Block Storage im Hintergrund um via NFS ein RWX PVC für deine Pods bereitzustellen. Dadurch haben wir die technische Limitierung eines CSI Cinder Blockdevices umgangen und du kannst deinen NWS Managed Kubernetes Cluster fortan für mehr Anwendungsfälle nutzen.





0 Kommentare