Kubernetes-Cluster sind in der Lage, die persistenten Applikationsdaten auf klassischen SAN-Speichern abzulegen. Einfacher und günstiger geht es jedoch mit hyperkonvergenten Storage-Werkzeugen, die die Daten auf den Disks der Kubernetes-Knoten speichern und ausfallsicher replizieren. Wie dies mit den Open-Source-Anwendungen Rook und Longhorn funktioniert, zeigt dieser Workshop.
Streng genommen sollten weder dieser Artikel noch die darin erwähnten Tools und Technologien existieren, denn das Konzept einer Scale-Out-Architektur, wie sie ein Kubernetes-Cluster bereitstellt, benötigt eigentlich überhaupt keinen persistenten Speicher. Und in den Anfängen früher Container-Implementierungen gab es keine "Persistent Volumes".
There and back again
Die Idee hinter Scale-Out ist ja eigentlich, dass Applikationen – auch diejenigen, die wichtige Unternehmensdaten beherbergen – verteilt auf einer stets redundanten Anzahl an Containern laufen. Fällt einer aus, erstellt Kubernetes sofort einen neuen und die Applikation kümmert sich selbst umgehend darum, die Datenbestände mit der gewünschten Redundanz auf die laufenden Container zu verteilen. Um die Datensicherung sorgt sich die Applikation ebenfalls eigenständig, indem sie in gegebenen Intervallen ein Backup ihres Datenbestands mit Hausmitteln wie einem Datenbank-Dump ablegt – und dazu reicht eigentlich eine NFS-Freigabe. Soweit die Theorie zu redundanten, nie ausfallenden Scale-Out-Applikationen.
Die Realität sieht leider anders aus. Entgegen allen Warnungen, auf Scale-Out-Umgebungen nur die dazu passenden Applikationen zu betreiben, haben die Anwender einfach ihre bestehenden Tools und Programme in Container gestopft, auch wenn sie dort nicht hingehören. Vielen ist es verständlicherweise zu aufwendig, gewachsene Datenbankschemata und Jahrzehnte an SQL-Erfahrung über Bord zu werfen, um die Datenhaltung einer Applikation auf neue Scale-Out-Datenbanken umzustellen.
Streng genommen sollten weder dieser Artikel noch die darin erwähnten Tools und Technologien existieren, denn das Konzept einer Scale-Out-Architektur, wie sie ein Kubernetes-Cluster bereitstellt, benötigt eigentlich überhaupt keinen persistenten Speicher. Und in den Anfängen früher Container-Implementierungen gab es keine "Persistent Volumes".
There and back again
Die Idee hinter Scale-Out ist ja eigentlich, dass Applikationen – auch diejenigen, die wichtige Unternehmensdaten beherbergen – verteilt auf einer stets redundanten Anzahl an Containern laufen. Fällt einer aus, erstellt Kubernetes sofort einen neuen und die Applikation kümmert sich selbst umgehend darum, die Datenbestände mit der gewünschten Redundanz auf die laufenden Container zu verteilen. Um die Datensicherung sorgt sich die Applikation ebenfalls eigenständig, indem sie in gegebenen Intervallen ein Backup ihres Datenbestands mit Hausmitteln wie einem Datenbank-Dump ablegt – und dazu reicht eigentlich eine NFS-Freigabe. Soweit die Theorie zu redundanten, nie ausfallenden Scale-Out-Applikationen.
Die Realität sieht leider anders aus. Entgegen allen Warnungen, auf Scale-Out-Umgebungen nur die dazu passenden Applikationen zu betreiben, haben die Anwender einfach ihre bestehenden Tools und Programme in Container gestopft, auch wenn sie dort nicht hingehören. Vielen ist es verständlicherweise zu aufwendig, gewachsene Datenbankschemata und Jahrzehnte an SQL-Erfahrung über Bord zu werfen, um die Datenhaltung einer Applikation auf neue Scale-Out-Datenbanken umzustellen.
Mit dem klassischen SQL-Server und anderen Legacy-Technologien halten die damit verbundenen Storage- und Backupkonzepte Einzug in die Kubernetes-Welt. Auf einer Plattform, die ursprünglich antrat, diese Techniken überflüssig zu machen, tauchen wieder Volume-Snapshots, synchrone Spiegel oder gar Host-based Mirroring auf. Eigentlich fehlen nur noch Großvater-Vater-Sohn-Tape-Backups – aber so etwas hat der ein oder andere Anwender mit Sicherheit auch schon versucht.
Von SAN zu Hyperconverged
Schon vor dem Siegeszug der Cloudtechnologien begann ein Wandel in den bestehenden Storage-Architekturen. Tools wie VMwares vSAN, Nutanix, DRDB, Ceph oder GlusterFS lösten dezidierte und teure SANs ab, indem sie die Plattenressourcen in den Servern redundant vernetzten. Diese Ansätze sind als "converged" oder "hyperconverged" Storage bekannt. Der feine Unterschied ist dabei, dass Converged-Storage im Backend meistens direkt die Platten oder SSDs auf Blockebene steuert und auf der anderen Seite seine Dienste über SAN-Protokolle wie rados, iSCSI oder NFS zur Verfügung stellt.
Hyperkonvergenter Speicher hingegen arbeitet ausschließlich mit seiner Zielplattform, in unserem Fall also Kubernetes, und stellt somit nur Kubernetes-Storage-Ressourcen bereit. Zudem benötigen viele dieser Werkzeuge im Backend keinen direkten Zugriff auf Block-Devices und können mit bestehenden Dateisystemen als Quelle umgehen. Im Folgenden stellen wir ein Converged- und Hyperconverged-Storage-Produkt für Kubernetes-Cluster vor, das wir in einem Testsetup installieren.
Kubernetes und die Volumes
Um die Funktionsweise der Hyperkonvergenz zu verstehen, müssen wir uns vor Augen führen, wie Kubernetes mit Storage umgeht. Jede Applikation in einem Kubernetes-Cluster verfügt über ein bestimmtes Kontingent an Plattenspeicher. Allerdings geht dieser "flüchtige" Speicher verloren, wenn der Container abstürzt oder gelöscht wird. Der Container-interne Speicher eignet sich daher nicht für Applikations- oder Konfigurationsdaten. Die Konfiguration einer Applikation steht in einer Config-Map, die Kubernetes verwaltet, und erfordert daher keinen nicht-flüchtigen Speicher im Container.
Um die Applikationsdaten zu sichern, kann ein "Deployment" oder ein "Stateful Set" von Kubernetes persistenten Speicher anfordern. Diesen Request formuliert die Applikation in einem sogenannten "Persistent Volume Claim" (PVC). Dieser beschreibt, wie viel Speicher die Applikation anfragt und ob mehrere Container parallel darauf müssen oder nicht. Der PVC gibt auch an, ob die Applikation ein Dateisystem als Speicher oder ein Block-Device wünscht. Aus Sicht der Applikation macht das übrigens keinen Unterschied. Ein Block-Device wird dem Kubernetes-Host zugewiesen, auf dem der Container läuft. Dieser Host formatiert dann das Block-Device mit einem Dateisystem und mountet es an die vorgegebene Stelle in den Container, genau so, wie es bei einem Dateisystem-PV der Fall ist. Das Applikationsmanagement kann bei einem Block-Device allerdings auf Storage-Managementfunktionen wie Snapshots zugreifen, sofern der darunter liegende Storage-Provider das unterstützt.
Sobald eine Applikation einen PVC anfordert, wartet das Deployment, bis der Cluster darauf reagiert. Um den Claim zu bearbeiten, braucht Kubernetes eine passende Storage-Class via CSI (Container Storage Interface). Im modularen Konzept von Kubernetes gibt es keinen Default-Storage-Treiber. Der IT-Verantwortliche muss ein CSI seiner Wahl installieren oder PVs von Hand erstellen. Je nach Kubernetes-Distribution liefern die jeweiligen Hersteller unterschiedliche CSIs mit.
Für eine einfache Testinstallation mit nur einer Kubernetes-Node ohne Redundanz gibt es simple Optionen: Den Host- oder Local-Path-Provisioner sowie TopoLVM. Wie der Name bereits verrät, erstellt der Host/Local-Path-Provisioner einfach Unterverzeichnisse in einem bestehenden Dateisystem auf dem Kubernetes-Host. Die Unterverzeichnisse mountet Kubernetes an der definierten Stelle in den Container. Dieser Provisioner hat allerdings den Nachteil, dass er nur Files-Storage bereitstellen kann und die tatsächlich vom Container verwendete Kapazität (Quota) nicht richtig kontrolliert beziehungsweise limitiert. Daher setzen auch kleinere Kubernetes-Distributionen lieber TopoLVM ein. Dieses erstellt logische Volumes auf einem oder mehreren physischen Datenträgern des Hosts. TopoLVM kann somit die Quoten einhalten, trennt den Storage der Container strikt voneinander und liefert Funktionen wie Snapshots.
Bild 1: Rancher bringt einen Softwarekatalog mit Helm-Charts mit. Darin findet sich auch die Installation, die mit einem Klick Longhorn-Storage einrichtet.
Testumgebung einrichten
Aufgrund der Komplexität können wir für diesen Artikel nicht alle verfügbaren Storage-Produkte installieren und testen. Wir beschränken uns auf Rook [1] und Longhorn [2]. Beide bringen von Haus aus eine Web-UI für das Management mit und sind erstaunlich einfach aufzusetzen.
Für diesen Artikel setzen wir ein Drei-Knoten-Rancher-Setup mit RK2 auf. Dabei kommen drei virtuelle Maschinen mit jeweils 8 GByte RAM und vier vCPUs und RHEL 8.7 zum Einsatz. Das Testszenario funktioniert auch mit einem EL8-Klon oder Centos-8-Streams. Je nachdem, welche Storage-Setups Sie testen möchten, benötigen Sie bei Longhorn nur eine Disk pro VM. Diese Software platziert das Storage-Backend im Dateisystem des Knotens. Für Rook fügen Sie jeder VM eine bis drei zusätzliche Disks hinzu, die Ceph für OSDs verwenden kann. Wir verpassen den Testmaschinen sowohl eine 80 GByte große Root-Disk als auch drei vDisks zu je 20 GByte für Ceph und installieren beide Storage-Werkzeuge im Rancher-Cluster simultan.
Bild 2: Die übersichtliche Web-UI von Longhorn zeigt im Status eines Volumes unter anderem, auf welchen Knoten die Replikas liegen und ermöglicht Backups und Snapshots.
Das Setup untergliedert sich in zwei Teile: Zuerst richten Sie die Kubernetes-Distribution RKE2 ein und im Anschluss fügen Sie den Management-Layer "Rancher" hinzu. Um das zu vereinfachen, schalten Sie auf den Testknoten die Firewall ab, die sonst die Kommunikation zwischen den vernetzten Storage-Setups behindert. Installieren Sie die Dependencies "nfs-utils", "cryptsetup", "iscsi-initiator-utils" und aktivieren Sie den iscsid-Service. Longhorn setzt iSCSI für die Datenreplikation zwischen den Knoten ein, Ceph nutzt sein eigenes rados-Protokoll.
Auf dem ersten Knoten starten Sie das "Master"-Setup-Skript direkt von der RKE-Website:
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE=server sh -
Dieses erkennt Ihren OS-Typ, fügt die nötigen RPM-Repositories ein und installiert die Basisdienste. Im Anschluss starten Sie den Service "rke2-server.service", um die Control-Plane hochzufahren und alle nötigen Container-Images einzurichten. Sobald die Control-Plane läuft, kopieren Sie das erstellte Auth-Token ("/var/lib/rancher/rke2/server/node-token") des Master-Nodes und die Information zum Master-Knoten selbst nach "/etc/rancher/rke2/ config.yaml" auf den beiden anderen Knoten. Die Datei enthält dann auf den Knoten 2 und 3 folgende Zeilen:
server: https://<IP-Adresse der Master-Node>:9345
token: <Inhalt der Node-Token-Datei>
Dann führen Sie auf beiden Knoten das Install-Skript für den RKE2-Agenten aus:
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE=agent sh -
Nun starten Sie den Agenten-Service "rke2-agent.service". Damit haben Sie das Wesentliche bereits erledigt. Im Anschluss installieren Sie den Kubernetes-Paketmanager "helm" auf dem Master-Knoten. Mit Helm richten Sie das Rancher Chart ein. Zuvor brauchen Sie aber noch den Cert-Manager:
Bitte prüfen Sie auf der Github-Seite des Cert-Managers [3], welche Version aktuell ist. Wir nutzen 1.11.0, bis dieser Artikel erscheint, dürfte es jedoch neuere Versionen geben.
Mit dem Helm-Chart von Rancher übermitteln Sie die Basiskonfiguration:
helm upgrade -i rancher rancher-latest/rancher \
--create-namespace --namespace cattle-system \
--set hostname=<FQDN des Master-Knotens> \
--set bootstrapPassword=Master-Passwort \
--set replicas=1
Mit "replica=1" beschränken Sie die Master-Funktionen und etcd auf den ersten Knoten. Das reicht für einen Test. In produktiven Umgebungen nutzen Sie hingegen "replicas=3".
Nach ein paar Minuten hat Rancher alle Container gestartet und Sie können sich via "https://<FQDN des Masters>" mit dem zuvor vergebenen Passwort am Rancher-Dashboard anmelden. Alles Weitere installieren Sie via Helm-Charts aus dem Menü "Apps / Charts" oder via kubectl-Befehl auf der Kommandozeile.
OpenEBS und Ondat
Der Vollständigkeit halber stellen wir zwei weitere hyperkonvergente Ansätze für Kubernetes vor, die wir jedoch nicht dem praktischen Test unterzogen haben.OpenEBS [5] stammt ursprünglich von MayaData, das 2021 von DataCore übernommen wurde. OpenEBS besteht aus zwei Komponenten: Die Control-Plane stellt via API das Storagemanagement-Interface bereit und verwaltet Plug-ins. Als Backend setzt die Software auf relativ simple CSI-Treiber, wie TopoLVM, Hostpath oder bereits bestehende Kubernetes-PVs. Mit den Plug-ins (beispielsweise für Velero) kann das Tool die Daten einer Container-Applikation direkt über den Speicher-Layer sichern, ohne dass die Applikation selbst ein Backuptool benötigt. Die Control-Plane alleine liefert nur Storage-Managementfunktionen und Backup, aber noch keine Redundanz. Dafür ist die zweite Komponente zuständig, eine so genannte "Data Engine" oder "Storage-Engine". Diese spiegelt Back-end-PVs mehrerer Kubernetes-Hosts. OpenEBS läßt sich über ein Helm-Chart einrichten, bringt aber leider keine hübsche Managementoberfläche mit.Ondat [6] gehört zu den Pionieren des Hyperconverged Storage für Kubernetes und nutzt den freien Plattenspeicher der Kubernetes-Nodes, um daraus redundante PVs zu generieren und auf mehrere Knoten zu replizieren. Ondat ist in erster Linie ein kommerzielles Storage-Produkt. Sie können ohne Lizenz eine Community-Edition nutzen, die jedoch auf 1 TByte Speicher pro Cluster limitiert ist.
Rook aufsetzen und nutzen
Ursprünglich verfolgte Rook einen offenen Managementansatz und fügte Storage-Verwaltungsfunktionen zu bestehenden CSIs hinzu. Rook selbst kümmert sich dabei aber nicht um die Redundanz – die bringen die CSIs selbst mit. Allerdings entwickelt Rook den modularen Support für verschiedene Storage-Backends nicht mehr weiter und konzentriert sich stattdessen ausschließlich auf Ceph.
Rook ist somit der "Ceph-Operator" für Kubernetes mit den passenden Managementfunktionen. Das Tool durchsucht die angebundenen Kubernetes-Konten nach freien Block-Devices, die es dann zu einem Ceph-Cluster zusammenfasst. Pro Device startet Rook einen OSD-Container. Alle Ceph-Verwaltungsaufgaben wie Monitors oder Gateways startet die Software ebenfalls als Kubernetes-Container. Die so zusammengefassten Speicherressourcen stellt das Tool dann dem Kubernetes-Cluster als Block-PV (rbd) oder File-PV (CephFS) zur Verfügung.
Zudem können Sie über das S3-Gateway auch S3-Freigaben anlegen. Rook-Ceph tritt dann eher als Converged-Storage auf, denn die Ressourcen stehen über Speicherprotokolle wie S3 (HTTPS) auch externen Applikationen zur Verfügung. Dabei arbeitet Rook mit allen Kubernetes-Distributionen zusammen. Eine kommerzielle Version integriert sich in Red Hats "OpenShift Data Foundation" zusammen mit dem Storage-Gateway NooBaa.
Mit Rook wird das Ceph-Setup unter Kubernetes ziemlich einfach. Notieren Sie sich die aktuelle Versionsnummer von "https:// github.com/rook/rook/releases" und klonen dann das passende Repository:
Sobald der Operator läuft, starten Sie den Ceph-Cluster über den Befehl kubectl create -f cluster.yaml. Sie passen die Konfiguration des Operators und des Clusters im YAML-Code an, die Vorgaben aus dem examples-Directory genügen jedoch für unsere Tests.
Das Ceph-Setup richtet auch gleich die Web-UI ein. Das Admin-Passwort dazu finden Sie in der Rancher-UI unter "Secrets / Namespace rook-ceph / rook-ceph-dashboard-password". Den Web-UI-Link zum Dashboard finden Sie in Rancher bei "Services / Namespace: rook-ceph / rook-ceph-mgr-dashboard". Klicken Sie auf den Link "https-dashboard" in der zweiten Spalte und melden Sie sich bei Ceph als "admin" mit dem zuvor extrahierten Passwort an.
Im Dashboard können Sie verfolgen, wie Ceph die freien Disks der Rancher-Knoten automatisch findet, formatiert und dem Ceph-Cluster hinzufügt. Um Ceph nun mit Rancher zu nutzen, erstellen Sie eine Storage-Class mit File- (CephFS) oder Block-Backend (rbd). Das passende YAML-File für beide Storage-Klassen finden Sie in der Rook-Dokumentation im Quickstart-Dokument [4]. Unter der Überschrift "Storage" finden Sie Links zu den YAML-Dateien für CephFS und rbd.
Haben Sie den Operator mit den Basisparametern installiert, funktionieren auch die Storage-Klassen mit den unveränderten Dateien aus der Dokumentation. Sobald Sie die Storage-Klasse anlegen, bereitet Ceph im Hintergrund die passenden Pools vor und Ihre Applikationen haben im Anschluss die Möglichkeit, PVCs anzufordern.
Ceph ist natürlich ein aufwendigeres Storage-Backend als Longhorn, das auch mehr Ressourcen in Anspruch nimmt. Es eignet sich weniger für kleinere Setups, kann im Gegenzug aber bei großen Installationen mit vielen Knoten punkten. Ein weiterer Vorteil ist, dass es via Gateway S3-Storage zur Verfügung stellen kann.
Bild 3: Der Rook-Operator richtet auch gleich das Ceph-Dashboard ein, über das der Administrator den Ceph-Cluster überwacht und konfiguriert.
Longhorn in Kubernetes betreiben
Ähnlich wie Rook ist auch Longhorn ein Open-Source-Projekt der CNCF (Cloud Native Computing Foundation) und stammt ursprünglich vom amerikanischen Softwareunternehmen Rancher Labs, das 2020 von Suse übernommen wurde. Das Prinzip von Longhorn ist recht simpel und daher auch effektiv: Das Block-Storage-System erstellt Diskimages im Dateisystem der angebundenen Knoten unter "/var/lib/longhorn".
Je nach Konfiguration legt Longhorn pro virtueller Disk eine oder zwei Kopien (Replica 2 oder 3) via LAN auf anderen Kubernetes-Knoten des Clusters an. Für die blockbasierte Replikation nutzt Longhorn das iSCSI-Protokoll. Neben der Replikation unterstützt Longhorn auch Funktionen wie Snapshots und differenzielle Diskbackups.
Die Installation von Longhorn ist denkbar einfach: Rancher bringt einen eigenen Applikationskatalog mit Helm-Charts mit. Klicken Sie in der Rancher-UI unter "Apps / Charts" auf das Longhorn-Icon und rollen damit den Operator aus. Nach wenigen Minuten erscheinen sowohl der Menüpunkt "Longhorn" für die Web-UI des Storage-Managers in der Rancher-UI als auch die passende Storage-Klasse.
Longhorn ermittelt nach dem Start den verfügbaren freien Speicher auf den angebundenen Knoten. Einen Teil davon reserviert das System für Snapshots, der Rest steht für Volumes zur Verfügung. Die Oberfläche zeigt übersichtlich, welcher Knoten wie viel Speicher beisteuert und wo die erstellten Volumes liegen. Hier können Sie auch Snapshots von Volumes oder Backups erstellen. Alles, was Longhorn für seine Backupfunktion benötigt, ist eine NFS-Freigabe. Dorthin sichert es dann komplette Volumes, Differenzbackups oder die Systemkonfiguration.
Auch Longhorn steht als Open Source für alle Kubernetes-Distributionen bereit. Eine kommerzielle Implementierung von SUSE betreibt Longhorn integriert mit RKE2 und Rancher. Und wesentlich einfacher kann die Verwaltung von fehlertolerantem Speicher kaum sein. Es gibt sogar Single-Node CSIs (Topo-LVM), die komplizierter zu konfigurieren sind. Im Vergleich dazu ist es einfacher, Longhorn als nicht-redundantes "Replica 1"-Setup einzurichten. Die Web-UI vereinfacht den Umgang mit Storage und den Funktionen wie Snapshots.
Fazit
Für kleinere Kubernetes-Installationen mit weniger als zehn Knoten und moderaten Anforderungen an die Kapazität eignet sich Longhorn sehr gut als hyperkonvergenter Storage. Das Setup kommt mit wenigen Ressourcen aus, liefert eine übersichtliche Web-UI und stellt keine großen Herausforderungen an die Diskkonfiguration der Knoten.
Rook mit Ceph ist hingegen ein komplexeres Setup, das im Gegenzug allerdings in großen Umgebungen mit vielen Knoten und unterschiedlichen Disk-Backends (HDD vs NVME) besser zurechtkommt. Auch die Option des S3-Zugangs und die hilfreiche GUI dürften für viele Admins von Interesse sein.