In kleinen und mittleren Kubernetes-Setups ist die Wahl des passenden Container-Storage-Infrastructure-Treibers entscheidend für die Funktionen und Leistungsfähigkeit der Umgebung. Für den Betrieb der Container Storage Infrastructure von Kubernetes steht eine reiche Auswahl unterschiedlicher Treiber zur Verfügung, wir stellen die wichtigsten und deren Features vor.
Ursprünglich liefen Container-Plattformen ohne persistenten Speicher. Da Anwender jedoch zunehmend Stateful-Applikationen auf die Stateless-Container-Cluster portierten, musste eine Lösung für dauerhafte Speicher her, sodass ein abstürzender oder stoppender Container Daten in einem Verzeichnis sichern kann. Die Container Storage Infrastructure (CSI) normiert seit 2019 daher eine Reihe von APIs, über die Kubernetes Speicher anfordern, modifizieren, löschen und an einen oder mehrere Pods anbinden kann.
Demgegenüber stehen die CSI-Treiber, die die Storage-Hersteller zur Verfügung stellen. Diese Treiber setzen die Kubernetes-CSI-API-Calls auf den zugehörigen Speicher um. CSI-Treiber steuern dabei zum einen klassische Hardware-Speichersysteme als auch Software-defined-Storage (SDS).
Was CSI und seine Treiber leisten
Ein CSI-Treiber, der dynamisch Speicher passend zu ankommenden Persistent Volumes (PV) erstellt, muss Grundfunktionen wie Volumes erzeugen oder löschen beherrschen. Darüber hinaus gibt es einen ganze Reihe weiterer optionaler Funktionen wie Snapshots, Clones oder Expansions. Zudem gibt es unterschiedliche Zugriffsformen wie "Read/Write Single" und "Read/ Write Multiple". Letztere gewährt mehreren PODs den gleichzeitigen Schreib/Lese-Zugang zu einem persistenten Volume. CSI-Treiber arbeiten entweder mit Block- oder File-Backends. Im Container selbst erscheinen beide einfach nur als angebundenes Unterverzeichnis.
Ursprünglich liefen Container-Plattformen ohne persistenten Speicher. Da Anwender jedoch zunehmend Stateful-Applikationen auf die Stateless-Container-Cluster portierten, musste eine Lösung für dauerhafte Speicher her, sodass ein abstürzender oder stoppender Container Daten in einem Verzeichnis sichern kann. Die Container Storage Infrastructure (CSI) normiert seit 2019 daher eine Reihe von APIs, über die Kubernetes Speicher anfordern, modifizieren, löschen und an einen oder mehrere Pods anbinden kann.
Demgegenüber stehen die CSI-Treiber, die die Storage-Hersteller zur Verfügung stellen. Diese Treiber setzen die Kubernetes-CSI-API-Calls auf den zugehörigen Speicher um. CSI-Treiber steuern dabei zum einen klassische Hardware-Speichersysteme als auch Software-defined-Storage (SDS).
Was CSI und seine Treiber leisten
Ein CSI-Treiber, der dynamisch Speicher passend zu ankommenden Persistent Volumes (PV) erstellt, muss Grundfunktionen wie Volumes erzeugen oder löschen beherrschen. Darüber hinaus gibt es einen ganze Reihe weiterer optionaler Funktionen wie Snapshots, Clones oder Expansions. Zudem gibt es unterschiedliche Zugriffsformen wie "Read/Write Single" und "Read/ Write Multiple". Letztere gewährt mehreren PODs den gleichzeitigen Schreib/Lese-Zugang zu einem persistenten Volume. CSI-Treiber arbeiten entweder mit Block- oder File-Backends. Im Container selbst erscheinen beide einfach nur als angebundenes Unterverzeichnis.
Block-Storage-Volumes benutzen ein lokales Dateisystem wie etwa XFS oder ext4, das der Kubernetes-Knoten selbst verwaltet und per Loop-Mount in den Container einblendet. Block-Storage-PVs sind vor allem bei Schreibzugriffen schneller, da sie den Dateisystemcache des lokalen Knotens verwenden, im Gegenzug aber keinen Read/Write-Many-Zugriff erlauben. Bei dateisystembasierten Speicherklassen (NFS, SMB oder CephFS) ist es exakt anders herum. Hier funktioniert der RWM-Modus, dafür fehlt der Schreibcache, den es bei einem netzwerkbasierten Filesystem nicht geben darf.
Ein Kubernetes-Cluster ist nicht auf einen einzelnen CSI-Treiber beschränkt. IT-Verantwortliche haben die Option, mehrere Storage-Anbindungen gleichzeitig zu verwenden, und jeder Treiber erhält dabei eine Storage-Klasse. Erstellt eine Anwendung einen "Persistent Volume Claim" (PVC), kann dieser den Namen der Storage-Klasse erhalten. In der Regel verfügt jeder Kubernetes-Cluster über eine Storage-Klasse, die über die Annotation "storageclass.kubernetes.io/is-default-class= true" verfügt. Diese fungiert dann als De-fault-Klasse und wird automatisch für alle PVCs herangezogen, die nicht explizit eine bestimmte Storage-Klasse anfordern.
Im Übrigen funktioniert ein Kubernetes-Setup auch ganz ohne CSI-Treiber. In einem solchen Szenario muss der Administrator entweder manuell auf den PVC einer Anwendung reagieren und ein zum Claim passendes PV erstellen. Alternativ definiert der Anwender eine Reihe leerer PVs, die sich dann von einem Claim nutzen lassen.
Wir stellen im Folgenden die wichtigsten Open-Source-CSI-Treiber vor, die sich für kleinere und mittelgroße Kubernetes-Installationen eignen, und spezifizieren deren Einsatzgebiete. Eine komplette Übersicht über alle aktuell verfügbaren Kubernetes-CSI-Treiber finden Sie unter [1].
Klassisch versus hyperkonvergent
Für viele IT-Verantwortliche stellt sich die Frage, wie sie ihren Kubernetes-Cluster samt Speicher am effizientesten betreiben. Sollen die Kubernetes-Knoten in virtuellen Maschinen oder direkt auf der Hardware laufen, kommen klassische Speichersysteme oder Software-defined Storage zum Einsatz? Liefern dezidierte Knoten den Storage oder kommen Speicherressourcen von den Knoten selbst in einer hyperkonvergenten Umgebung? Viele Fragen, mit leider vielen möglichen Antworten. Wer einen kompletten Neuaufbau eines Kubernetes-Clusters mit eigener Hardware erwägt, fährt natürlich mit einer Bare-Metal-Installation und hyperkonvergentem Storage aus den Knoten am effizientesten [2]. Hier braucht es keine externen, proprietären Speichersysteme mit aufwändiger SAN-Anbindung mehr. Moderner, hyperkonvergenter SDS liefert eine bessere Performance und Verfügbarkeit bei deutlich geringeren Kosten als klassische SAN-Speicher. Solche Installationen können dann bei Bedarf auch virtuelle Maschinen betreiben. Wer Kubernetes hingegen auf einer bereits bestehenden Virtualisierungsplattform mit angebundenem SAN aufsetzt, braucht eine andere Infrastruktur. Ein SDS mit eigener Redundanz in den virtuellen Maschinen ergibt hier keinen Sinn. Mit dem darunterliegenden SAN, das selbst bereits für Hochverfügbarkeit sorgt, verschwendet ein virtuelles SDS Kapazität und Performance. Hier genügt entweder ein sehr simpler CSI-Speicher oder der Cluster spricht den bestehenden SAN-Speicher direkt an.
Hostpath
Der simpelste CSI-Treiber für Kubernetes überhaupt ist Hostpath [3]. Wie der Name bereits sagt, legt diese Storage-Klasse bei Bedarf Unterverzeichnisse in einem Pfad des Hosts selbst an und blendet diese Unterverzeichnisse dann per Loop Mount in den Pod ein, der das PV angefordert hat. Hostpath bietet keine Redundanz für die damit erstellten PVs und funktioniert auch nur auf einem einzelnen Knoten. Das Github-Repository warnt Anwender jedoch eindringlich: "This driver is just a demo implementation and is used for CI testing". Dennoch kommt Hostpath gerne bei kleinen Single-Node-Setups zum Einsatz. Sowohl MicroK8s als auch K3s setzen in der Grundinstallation Hostpath ein.
Hostpath kann nicht viel mehr, als File-based PVs zu erstellen und zu löschen. Auch ist der Treiber nicht in der Lage, die Speicher-Quota zu kontrollieren oder zu begrenzen. Ein Pod könnte also ungehindert den kompletten freien Plattenplatz des Hosts auffüllen, indem er das angebundene PV vollschreibt. Aber dafür ist der CSI-Treiber sehr kompakt und kommt mit einem einzelnen Container ohne großen Ressourcenbedarf aus. Damit eignet sich Hostpath für kleine Edge-Installationen, speziell auf Hosts mit geringen Hardwareressourcen. Wie in [4] beschrieben, laufen Edge-Setups mit MicroK8s (Canonical) oder K3s (Suse) auf Systemen mit nur 1 GByte RAM und einem CPU-Kern und das, obwohl sie mit Hostpath einen dynamischen CSI-Treiber beherbergen.
CSI-NFS
Der CSI-NFS-Treiber [5] hebt das einfache Konzept des Hostpath-Treibers auf das nächste Level. Prinzipiell funktioniert dieser Treiber erst einmal ähnlich, denn auch er legt eigentlich nur Unterverzeichnisse für PVs an. Diese sitzen dann aber nicht in irgendeinem Host-Pfad, sondern auf einem NFS-Server. Den jeweiligen NFS-Mount bindet wiederum der Host in den anfordernden Pod ein. Der Treiber besteht aus zwei Komponenten: Der NFS-Controller wickelt die CSI-Aufgaben wie PVC verarbeiten, Volume erstellen und löschen ab. Zudem beherbergt er einen Snapshot-Controller, der – wie der Name vermuten lässt – Snapshots von PVs erstellt und damit auch bestehende PVs klonen kann. Pro Node gibt es zudem einen csi-nfs-node-Pod, der die NFS-Mounts in die jeweiligen Pods auf dem Knoten einblendet.
Als Dateisystem-Treiber erlaubt CSI-NFS natürlich den ReadWriteMany-Zugriff und kann dabei parallel mit dynamisch als auch mit statisch provisio- nierten Volumes umgehen. Letztere sind vor allem interessant, falls Systeme und Anwendungen außerhalb des Kubernetes-Clusters Zugriff auf NFS-Ressourcen ermöglichen. Hier gibt es diverse praktische Szenarien: Betreiben Sie beispielsweise auf Kubernetes einen Scale-out-Webserver-Cluster, können Sie die statischen Daten wie HTML-Seiten, CSS-Templates oder Multimediaressourcen auf einem NFS-Volume sammeln und damit allen Webserver-Pods zur Verfügung stellen. Die Verwaltung der statischen HTML-Daten kann dann wiederum von einem externen System erfolgen, das einen passenden Zugriff zur NFS-Freigabe erhält.
CSI-NFS-Volumes eignen sich auch gut als Backuplaufwerke. Pods und Sidecar-Container können dabei Daten aus laufenden Applikationen, beispielsweise via SQL-Dump, auf NFS-Freigaben sichern. Ein externes Backup- oder Data-Warehouse-System kann die Backupdaten dann weiterverarbeiten.
CSI-NFS läuft unabhängig vom verwendeten NFS-Server und unterstützt Protokollversionen bis 4.1. Der Treiber erlaubt mehrere Storage-Klassen mit unterschiedlichen Konfigurationen, wie NFS-Server, Basisverzeichnis oder Protokollversion. Damit eignet er sich auch gut in virtualisierten Setups (siehe Kasten "Klassisch versus hyperkonvergent") da er mit bestehenden NFS-Umgebungen ohne zusätzlichen SDS-Overheap auskommt.
Alternativ zu CSI-NFS gibt es den Treiber CSI-SMB [6], der einen vergleichbaren Funktionsumfang wie sein NFS-Pendant offeriert, dabei aber das SMB-Protokoll statt NFS verwendet. Dieser ist in Umgebungen mit bestehenden Windows-Fileserven interessant. Auch für das verteilte Cluster-Dateisystem GlusterFS gibt es einen passenden CSI-Treiber [7]. Leider ist es in den letzten Monaten ziemlich still um das Projekt und die damit verbundenen Add-ons geworden. Red Hat hat sich als kommerzieller Anbieter und langjährig treibende Kraft weitgehend aus dem GlusterFS-Projekt verabschiedet und stellt den Support für GlusterFS Ende 2024 ein.
TopoLVM
Ähnlich wie Hostpath arbeitet TopoLVM [8] als Single-Node-Storage – wobei das nicht die ganze Wahrheit ist. Dieser Treiber nutzt den Logical Volume Manager (LVM) des zugrunde liegenden Hosts und verwaltet PVs als Logical Volumes, auf Wunsch auch mit einem Thin-Provisioned-Pool. TopoLVM besteht aus mehreren Komponenten, wobei der Controller die Storage-Klassen verwaltet und die damit eintreffenden Persistent Volume Claims.
Der "Node" leitet die Anforderungen des Controllers an den eigentlichen Treiber "lvmd" weiter und verwaltet die Mountpoints und Dateisysteme der PVs. Der lvmd, der den Logical Volume Manager des Node-OS bedient, könnte als Systemdienst auf dem OS des Kubernetes-Knoten arbeiten. In der Regel startet TopoLVM jedoch lvmd als privilegierten Container innerhalb des Node-Pods.
Damit der lvmd korrekt funktioniert, braucht er eine passende Konfiguration, denn die gibt beispielsweise die Namen der zu verwendenden Volume Groups vor. Beim initialen Start kann lvmd diese Informationen aus einer Konfigurationsdatei importieren und sichert sie im Anschluss in einer Kubernetes Configuration Map.
Anders als Hostpath kontrolliert TopoLVM die Quota. Fordert ein PVC ein Volume mit 5 GByte Speicher an, liefert TopoLVM auch nur ein 5 GByte großes logisches Volume, das der angebundene Pod auch nicht überfüllen kann, wie das bei Hostpath der Fall ist. Darüber hinaus kann TopoLVM Snapshots erstellen, um damit PVs zu sichern – auch via LAN auf einen externen Backupspeicher.
Wie eingangs erwähnt, ist TopoLVM nur zum Teil ein Single-Node-Storage. Der TopoLVM-Controller kann mehrere Nodes nebst lvmds steuern und verteilt auf mehreren Knoten PVs verwalten. Allerdings stehen diese PVs dann jeweils nur für Pods zur Verfügung, die auf demselben Node wie lvmd arbeiten. Ein LAN-Zugriff auf PVs ist ebenso wenig möglich wie die Replikation zwischen PVs auf mehreren Knoten.
Damit eignet sich TopoLVM ähnlich wie Hostpath in erster Linie für kleine Single-Node-Setups auf Edge Devices. Allerdings ist der Overheap von TopoLVM und der Ressourcenbedarf des Treibers höher als bei Hostpath. Dafür liefert der Treiber im Gegenzug eine Quota-Kontrolle und Funktionen wie Volume-Snapshot und -Resize. TopoLVM ist der Standard-CSI-Treiber der Single-Node-Kubernetes-Distribution Microshift.
OpenEBS
OpenEBS [9] von DataCore (genauer gesagt von deren Tochter Perifery) ist bei näherem Hinsehen kein einzelner CSI-Treiber, sondern drei: Jiva, cStor und Mayastor. Allen gemein ist, dass sie lokale Speicherressourcen der Nodes mithilfe von Replikations-Controllern über das LAN spiegeln. Die lokalen Ressourcen stellen dann OpenEBS-Treiber zur Verfügung, die ähnlich wie Hostpath oder TopoLVM funktionieren.
Die drei Replikations-Engines zielen auf verschiedene Einsatzgebiete: Jiva beispielsweise nutzt Hostpath als Basis und operiert als File-based Storage, liefert damit also Read-Write-Many-Zugriffe, allerdings ohne Snapshots und Write-Cache. Flotter geht cStor ans Werk, das auf einen Basis-treiber ähnlich wie TopoLVM setzt und damit Snapshot-fähige, replizierte Block-Devices liefert. Eine Sonderrolle nimmt Mayastor ein, das auf Low-Latency-Applikationen mit NVME-Ressourcen in den Nodes abzielt.
Die Low-Level-CSI-Treiber von OpenEBS lassen sich dabei auch gänzlich ohne Replikations-Controller nutzen. So könnten Anwender beispielsweise den OpenEBS-Hostpath statt den zuvor genannten Hostpath-Treiber auf einem Single-Node-Set-up nutzen. Eine kuriose Sonderrolle für mutige Admins nimmt dabei sicher der OpenEBS-ZFS-CSI-Treiber ein. Dieser erstellt Block-PVs in ZFS-Pools.
Longhorn
Der Standard-Storage der Rancher Kubernetes-Distribution ist Longhorn [10]. Ähnlich wie OpenEBS setzt Longhorn auf einen vergleichsweise simplen Unterbau und fügt diesem LAN-Replikation hinzu. Auf ausgewählten Storage-Nodes erstellt Longhorn Block-Devices, allerdings nicht via LVM, sondern als Image-Files im regulären Dateisystem des Kubernetes-Knotens. Diese Images stellt der Node-CSI-Treiber dann via iSCSI im LAN bereit, wo sie der Longhorn-Controller über mehrere Nodes hinweg spiegeln kann.
Den eigentlichen Volume-Mount übernimmt der Longhorn-Client, der auf allen Nodes arbeitet und die Verbindung zum Storage via iSCSI herstellt. Longhorn erlaubt somit, dass entweder dezidierte Knoten Speicherressourcen zur Verfügung stellen oder der Speicher hyperkonvergent auf allen Knoten läuft. Der iSCSI-Transport für PV-Zugriff und Replikation belastet natürlich das Netzwerk, wie das auch bei anderen Storage-Replikationstechnologien üblich ist. Longhorn muss den Speicherzugriff aber nicht zwingend über das primäre LAN des Kubernetes-Clusters abwickeln, auf dem die Applikationen selbst laufen. Mithilfe der Kubernetes-Network-Plug-ins lässt sich der Storage-Traffic auch auf ein anderes LAN verlegen.
Ein Longhorn-Setup steuert auf Wunsch mehrere Storage-Klassen. So können Anwender für kritische Applikationen die Drei-Replica-Einstellung verwenden, aber parallel für Testsetups ohne gesteigerte Anforderungen an die Redundanz auch unreplizierte Volumes verwenden. Letzteres ist ebenfalls praktisch, wenn Administratoren Single-Node-Kubernetes-Setups benutzen. Wird der Cluster ausgebaut, wächst auch das Storage-System mit und fügt weitere Knoten und Redundanz hinzu.
Zudem lassen sich Knoten mit Tags versehen und dann verschiedenen Speicherklassen zuweisen. So sind Administratoren in der Lage, Knoten mit NVME- oder SSD-Speicher in einen schnellen Pool zu packen, während Knoten mit traditionellen Disks zu einer langsameren Storage-Klasse mit mehr Kapazität gehören. Per Vorgabe formatiert Longhorn die Block-Storages mit ext4. Auf Wunsch lässt sich dies in der Definition der Speicherklasse aber auch zu xfs ändern.
Longhorn liefert ein schlicht gehaltenes Web-UI mit, das in erster Linie eine Übersicht über die verwendeten Speicherressourcen und deren Replikationsstatus bietet. Die Konfiguration der Speicherklassen erfolgt jedoch über eine YAML-Deklaration. In der GUI lassen sich Snapshots von PVs erstellen oder ganze Laufwerke sichern. Dazu genügt Longhorn lediglich eine NFS-Freigabe.
Rook Ceph
Der Vollständigkeit halber erwähnen wir an dieser Stelle noch den Rook-Operator, der hyperkonvergente Ceph-Cluster auf Kubernetes aufsetzt. Wir gehen hier jedoch nicht auf die weiteren Details des Operators und den daraus resultierenden Kubernetes-Storage ein, weil sich Ceph nicht für mittelgroße oder gar kleinere Kubernetes-Setups eignet. Der Ressourcenbedarf des verteilten Objektspeichers ist hoch, sowohl was CPU- als auch Speicher- und vor allem die Netzwerkbelastung angeht. Eine detaillierte Beschreibung des Operators und der Konfiguration eines hyperkonvergenten Ceph-Clusters würde den Rahmen des Artikels sprengen.
Fazit
Auch wenn das ernüchternd simpel und altmodisch klingt: Jeder Kubernetes- Cluster sollte als zweiten CSI-Treiber im System über einen NFS-Storage verfügen. Hierüber lassen sich Backups mit oder ohne Sidecar-Container erledigen, Logs aggregieren oder RWM-Speicher für geclusterte Anwendungen aufsetzen. Die NFS-Anbindung sollte dabei jedoch nicht als Primärspeicher fungieren, da die Schreibperformance für I/O-intensive Jobs in Containern nicht ausreicht. Treiber wie TopoLVM und Hostpath eignen sich nur für Single-Node-Edge-Setups, für die der Administrator auch kein Scale-up plant. Doch gerade im Zusammenspiel mit einer NFS-Anbindung für Backups reicht ein solches Setup am Network Edge aus.
Wer hingegen das Single-Node-Setup als Beginn für einen wachsenden Cluster konzipiert oder von Haus aus mit einem Drei-Knoten-Setup ans Werk geht, der sollte Longhorn oder einen der Open-EBS-Speicher verwenden, um damit redundante und performante PVs aufzusetzen. Longhorn ist dabei zu präferieren, weil Setup und Betrieb des hyperkonvergenten Speichers deutlich simpler als bei OpenEBS ausfallen. Zudem gibt es die simple UI und das integrierte NFS-Backup-Feature.