Zurück

Exten: die eigene NVMe Block Storage Engine von OVHcloud

Tristan Groléat06.08.20266 Minuten Lesezeit

Exten: die eigene NVMe Block Storage Engine von OVHcloud

Aufgrund seiner Zuverlässigkeit und guten Integration in OpenStack war Ceph jahrelang unsere Blockspeicherlösung. Um jedoch mehr Leistung zu einem geringeren Preis anbieten zu können, haben wir Exten übernommen und können nun unsere eigene NVMe-Block-Storage-Lösung in C++ und Go erstellen. Unsere Ziele dabei sind:

  • Nutzung von NVMe-Laufwerken, um leistungsstarke Volumes anzubieten
  • Verwendung intelligenter Kodierungsalgorithmen, um weniger Platz auf Laufwerken zu benötigen (Reed-Solomon-Kodierung, Komprimierung)
  • Kleine Cluster, die einfach zu pflegen sind

Exten läuft nun seit über einem Jahr in der Produktion. In diesem Blog beschreiben wir im Detail, wie es funktioniert und welche Möglichkeiten die Architektur künftig bieten wird.

 Klicken Sie auf den Link am Ende, um Ihr Feedback zu geben und die Zukunft von Exten mitzugestalten.

GRPC RAFT CONSENSUS

Anatomie eines Clusters

Ein typischer Exten-Cluster besteht aus sechs Servern. Diese Server sind nah beieinander, sodass die Latenz zwischen ihnen niedrig ist, aber nicht zu nah, um die Wahrscheinlichkeit eines Ausfalls mehrerer Server gleichzeitig zu begrenzen. Jeder Server verfügt über Dutzende von NVMe-Laufwerken, um Benutzerdaten zu speichern.

Exten basiert auf dem NVMe-Protokoll. Es verwendet NVMe-Laufwerke, nutzt aber auch das NVMe over Fabric-Protokoll, um Nutzer-Volumes verfügbar zu machen und zwischen Hosts zu kommunizieren. Volumes sind virtuelle Laufwerke, die Kund:innen an ihre VMs anhängen können. Dieses Protokoll verwendet dieselben Befehle wie NVMe-Laufwerke, jedoch über eine Netzwerkverbindung (TCP oder RDMA), und ist für hohe Leistung und niedrige Latenz konzipiert.

Ein Vorteil von NVMe over Fabric ist, dass der Linux-Kernel es nativ unterstützt. Das Verbinden eines Exten-Volumes mit einem Linux-Host ist so einfach wie das Ausführen eines nvme connect-Befehls mit den richtigen Parametern: Das Volume erscheint dann genau wie jedes andere lokale NVMe-Laufwerk. Als Public-Cloud-Kund:in sehen Sie nichts davon – das Volume wird einfach wie bei Ceph an Ihre Instanz angehängt.

Exten setzt sich aus einer in C++ geschriebenen Data Plane und einer in Go geschriebenen Controle Plane zusammen. Beide kommunizieren über gRPC. Die Data Plane empfängt IO über NVMe over Fabric und leitet es an die NVMe-Laufwerke weiter. Die Control Plane stellt eine API zur Verfügung, mit der Volumes erstellt und gelöscht werden können und der Cluster verwaltet werden kann.

Zuverlässige Datensicherheit

Der Schutz von Kundendaten ist die größte Verantwortung eines Exten-Clusters. Wir gehen dies auf mehreren Ebenen an.

Control-Plane-seitig verwenden wir den bekannten Raft Consensus-Algorithmus, um die Integrität des Clusters auf fehlertolerante Weise zu gewährleisten. Das funktioniert, indem ein Leader gewählt wird und alle neuen Informationen – ein neu erstelltes Volume, eine aktivierte Festplatte – in einem Log protokolliert werden, das auf jedem Host repliziert wird. Wenn ein Host ausfällt, arbeiten die anderen weiter. Die einzige Einschränkung ist, dass eine Mehrheit der Hosts verfügbar bleiben muss, um „Split-Brain“-Probleme zu vermeiden. Ein Cluster mit sechs Nodes kann also zwei Hosts verlieren und trotzdem weiterarbeiten.

Die Benutzerdaten selbst oder die eigentlichen IOs auf den Volumes gehen nicht über Raft; das wäre viel zu langsam. Stattdessen verwenden wir einen anderen bekannten Algorithmus: einen Reed-Solomon-Code.

Dieser Fehlerkorrekturcode ist konzeptionell einfach. Nehmen wir RS 4+2: Wir teilen die Daten in vier gleiche Teile, dann berechnen wir zwei neue Teile derselben Größe. Wenn Sie vier dieser sechs Teile lesen können, können Sie die ursprünglichen Daten wiederherstellen: Der Code ermöglicht es Ihnen, die beiden fehlenden zu berechnen. Das ergibt ein Verhältnis von 1,5 (gespeicherte Daten/empfangene Daten) und demnach können wir zwei Laufwerke verlieren, ohne aber Daten zu verlieren. Es ist eine hocheffiziente Art, Redundanz hinzuzufügen. Der einfachste Ansatz – der üblicherweise als Mirroring bezeichnet wird und bei dem man die gesamte Datenmenge mehrmals auf verschiedene Laufwerke kopiert – würde drei Kopien erfordern, um den Verlust von zwei Laufwerken zu überleben. Mit Reed-Solomon speichern wir bei gleicher Redundanz das 1,5-Fache der Daten.

Defekte Laufwerke können auf zwei Arten erkannt werden: Entweder schlägt ein Lesevorgang einfach fehl oder er liefert fehlerhafte Daten – was dank einer von uns in jedem Sektor hinzugefügten Prüfsumme erkannt wird. Wenn eine Festplatte ausfällt, wird der fehlende Teil automatisch neu berechnet und auf einer gesunden Festplatte gespeichert, wodurch das Risiko eines Datenverlusts verringert wird.

Schnelle I/O

Wenn die CPUs und der RAM schneller arbeiten, muss die Speicherung mitziehen. Das Ziel von Exten ist es, die zugrunde liegende Hardware optimal zu nutzen und den Kund:innen eine leistungsstarke IO zu liefern. Die Wahl des NVMe over Fabric-Protokolls ist ein erster Schritt in Richtung IO mit möglichst geringem Overhead, aber auch der gesamte Software-Stack ist auf Performance ausgelegt.

Das Verarbeitungsmodell ist datenzentrisch. Auf der Ebene der Netzwerkkarte (Network Interface Card, NIC) empfangen und senden wir Daten über eine Reihe von festen Puffern, die im RAM gespeichert sind. Die CPUs verarbeiten jeden Puffer mit zwei Zielen: das Vermeiden von Datenkopien und das Vermeiden von Kontextwechseln.

Um Datenkopien zu vermeiden, arbeitet der Code durchgehend mit Zeigern (Pointer) auf vorab zugewiesene Puffer – direkt von der Netzwerkkarte (NIC) bis zum Laufwerk. Daten werden nur dann modifiziert, wenn es unvermeidlich ist (Komprimierung, Reed-Solomon-Codierung …). Zur Vermeidung von Kontextwechseln erfolgt die IO-Verarbeitung über vollständig asynchrone Methoden, die auf kleinen Dateneinheiten operieren. Eine CPU verarbeitet einen Puffer so weit wie möglich, bevor sie zum nächsten wechselt. Das ist allerdings ein Balanceakt: Wir dürfen den nächsten Puffer nicht zu lange warten lassen und müssen dafür sorgen, dass die Verarbeitung fair bleibt. Wir haben ein ganzes C++-Framework speziell für diese Anforderungen entwickelt, um den Code übersichtlich und wartungsfreundlich zu halten.

Neben dem Code selbst tragen mehrere andere Faktoren zur Leistungsverbesserung bei:

  • Die Wahl des Komprimierungsalgorithmus (manchmal ist keine Komprimierung die beste Wahl, z. B. bei verschlüsseltem Datenverkehr)
  • Die Wahl des Netzwerkprotokolls: RDMA ist schneller als TCP, bringt aber mehr Einschränkungen beim Deployment mit sich
  • Die Art und Weise, wie wir Volumes CPU-Cores zuweisen

Bei unseren Public-Cloud-Angeboten wenden wir auch QoS-Regeln an, um Fairness über alle Volumes hinweg zu gewährleisten.

Die Zukunft von Exten

Exten ist jetzt in der Produktion und stellt bereits heute Volumes für Public-Cloud-Kund:innen bereit. Es ist für Kund:innen transparent und bedient derzeit dieselben Angebote wie Ceph – aber es ist ein Software-Stack, den wir vollständig kontrollieren, gestützt auf NVMe für starke Leistung.

Wir arbeiten kontinuierlich an Verbesserungen, um unseren Kund:innen zuverlässigere Volumes mit noch höherer Performance und mehr Flexibilität an die Hand geben zu können. Wir beleuchten beispielsweise die Verwendung von RDMA, um die Leistung zu verbessern (aktuell verwenden wir TCP).

Der erste Schritt der Exten-Reise war, ein zuverlässiges Angebot zur Ablösung von Ceph bereitzustellen. Da wir den Stack nun vollständig kontrollieren, sind wir bereit, neue Funktionen hinzuzufügen und das Angebot an die Anforderungen der Kund:innen anzupassen.

Haben Sie Use Cases rund um Block Storage, die wir kennen sollten, oder Funktionen, die Sie vermissen? Erzählen Sie uns davon auf Discord oder der Cloud-Mailing-Liste: Ihr Feedback prägt, was für Exten als Nächstes kommt.


Teilen auf: