Apache Kafka hat sich in den letzten Jahren als führendes System für verteilte Nachrichtenübermittlung etabliert. Als Open-Source-Projekt begann Kafka seine Reise bei LinkedIn und wuchs schnell zu einer weit verbreiteten Plattform zur Verarbeitung von Logs, Streaming-Daten und zur Ereignisverarbeitung heran. Allerdings wurde Kafka ursprünglich entworfen, um in traditionellen lokalen Rechenzentren genutzt zu werden. Mit dem Übergang vieler Unternehmen in die Cloud ergeben sich neue Herausforderungen, vor allem wenn Kafka auf Cloud-Infrastrukturen betrieben wird. Besonders der Versuch, Kafka-Daten vollständig in objektbasierten Speichern wie Amazon S3 abzulegen, stellt Entwickler und Betreiber vor komplexe technische Probleme.
Die Kostenstruktur bei Kafka ist ein zentraler Faktor für den Wunsch, Daten in kostengünstigere und flexiblere Speichersysteme auszulagern. Kafka bindet Compute- und Speicherkapazitäten eng zusammen, was bedeutet, dass eine Skalierung des Speichers immer auch eine Erweiterung der Rechenressourcen erfordert. Das führt oft zu ineffizientem Ressourceneinsatz und höheren Gesamtkosten. Zudem sorgt die native Replikations-Architektur von Kafka für eine leistungsfähige Datenhaltbarkeit, führt aber im Cloud-Umfeld zu zusätzlichen Gebühren durch Datenverkehr zwischen Availability Zones (AZs). Diese Cross-AZ-Transferkosten entstehen, weil Produzenten Nachrichten an den Leader einer Partition schreiben müssen, welcher sich häufig in einer anderen Zone befindet, wodurch Daten mehrfach über Zonen hinweg übertragen werden.
Durch den Einsatz von S3, einem hochskalierbaren und kostengünstigen objektbasierten Speicher, kann Kafka von den drastischen Kostenvorteilen profitieren. S3 übernimmt selbst die Datenreplikation und stellt hohe Verfügbarkeit sowie Haltbarkeit sicher, was das aufwendige Replizieren innerhalb der Kafka-Cluster überflüssig macht. Gleichzeitig erlaubt die Entkopplung von Compute und Storage eine unabhängige Skalierung der beiden Komponenten. Dennoch bringt die Nutzung von S3 als Speicherebene erhebliche technische Hürden mit sich, die innovative Lösungsansätze erfordern. Eine der gravierendsten Herausforderungen ist die erhöhte Latenz.
Während lokale NVMe-SSDs Latenzen im Bereich von Mikrosekunden bieten, liegen S3-Anfragen bei Medianwerten von etwa 15 Millisekunden und Spitzenwerten von 60 Millisekunden, also einem Faktor von rund eintausendmal langsamer. Diese Diskrepanz kann die typische Streaming-Latenz von Kafka deutlich beeinträchtigen. Einige Projekte, die Kafka-kompatible Systeme auf S3 bauen, akzeptieren diese Latenzerhöhung als Kompromiss zugunsten von Kosteneinsparungen. Sie bestätigen Nachrichten erst nach dauerhafter Speicherung im Objekt-Speicher, was zu Verzögerungen führt. Eine innovative Lösung ist die Integration eines Write Ahead Logs (WAL) auf schnellen Speichermedien, beispielsweise AWS Elastic Block Store (EBS), das als Zwischenspeicher fungiert.
Dabei werden eingehende Nachrichten zunächst in diesem WAL persistiert und erst später asynchron auf S3 ausgelagert. Diese Architektur ermöglicht eine deutlich niedrigere Schreiblatenz, da Produzenten eine Bestätigung erhalten, sobald die Nachricht im WAL steht, ohne auf die teurere S3-Speicherung warten zu müssen. Auf diese Weise wird die Latenz unter Kontrolle gehalten, wobei aber die Komplexität der Datensynchronisation zwischen WAL und S3 steigt. Ein weiteres erhebliches Problem sind die hohen Kosten für häufige Schreiboperationen auf S3. Die Anzahl der PUT-Anfragen korreliert direkt mit den Betriebskosten, was bei Kafka-typisch sehr hohen Nachrichtenraten erhebliche Summen verursachen kann.
Um dem entgegenzuwirken, nutzen fast alle Kafka-S3-Implementierungen einen Batch-Ansatz. Dabei werden viele Nachrichten gesammelt und gemeinsam in größeren Objekten abgelegt, sodass die Anzahl der PUT-Operationen reduziert wird. Durch intelligente Zusammenstellung dieser Batch-Objekte, die sowohl partitioniert als auch partitionübergreifend sein können, lässt sich ein Kompromiss zwischen Kosten, Latenz und Lesefähigkeit herstellen. Allerdings führt das Batching zu einer Fragmentierung der Daten, welche die Lesegeschwindigkeit beeinträchtigen kann. Um dem entgegenzuwirken, implementieren einige Systeme Hintergrundprozesse, die sogenannte Kompaktierungen durchführen – also das Zusammenführen mehrerer kleiner Objekte zu größeren, geordneten Einheiten.
So wird gewährleistet, dass Lese-Anfragen möglichst effizient und sequentiell ausgeführt werden können, auch wenn sich die Daten physisch über mehrere Objekte hinweg erstrecken. Caching spielt eine Schlüsselrolle, um sowohl die Latenz beim Lesen als auch die Kosten für GET-Anfragen auf S3 zu senken. Durch geschickten Einsatz von unterschiedlichen Cache-Typen lassen sich Hot-Data (aktuelle, häufig gelesene Nachrichten) und Cold-Data (historische Nachrichten) getrennt verwalten. Ein Log-Cache im Speicher dient für sehr schnelle Zugriffe auf kürzlich geschriebene oder gelesene Daten, während ein Block-Cache große Datenblöcke speichern kann, die bei historischen Abfragen benötigt werden. Zusätzlich können Techniken wie das Vorabladen von Daten und das Batch-Lesen die Effektivität steigern.
Die Kunst liegt darin, den Cache so zu verwalten, dass ein hoher Cache-Hit-Rate erreicht wird und gleichzeitig Inkonsistenzen sinnvoll gemanagt werden. Ein gerade im objektbasierten Speichern schwieriges Thema ist das effiziente Management von Metadaten. Während bei Kafka die Metadatenstruktur auf der Verzeichnis- und Dateiorganisation in einem Dateisystem basiert, funktioniert S3 mit einer flachen Objektstruktur, die nur durch das Namensschema hierarchisch wirkt. Das Listing von Objekten mittels LIST-Anfragen ist zudem relativ teuer und langsam, was häufige Metadaten-Abfragen verteuert und verlangsamt. Lösungen wie AutoMQ optimieren dieses Problem durch die Nutzung von Kompaktierungsverfahren, um die Anzahl der gespeicherten Objekte und damit der Metadaten zu minimieren.
Außerdem verwenden sie spezielle Schlüssel-Wert-Streams, um Metadaten anstelle von LIST-Anfragen vorab zu verwalten. Dadurch können Informationen über Segmente und Partitionen effizient bereitgestellt werden, ohne übermäßig auf teure LIST-Operationen zurückgreifen zu müssen. Neben diesen technischen Herausforderungen ist die Gewährleistung der vollständigen Kafka-Kompatibilität ein zentrales Anliegen. Kafka verfolgt ein Protokolldesign, das stark auf die Annahmen der lokalen Dateisysteme mit append-only Logdateien und Segmenten abgestimmt ist. Das direkte Abbilden dieser Strukturen auf ein objektbasiertes Speichersystem, das keine Append-Operationen unterstützt, erfordert tiefgreifende Änderungen beim Speicher-Subsystem.
Ein Ansatz besteht darin, das Kafka-Protokoll komplett neu zu entwickeln, um besser auf die Spezifika von Objektspeichern einzugehen. Beispiele hierfür sind WarpStream und Bufstream. Diese Lösungen tendieren dazu, Datenlokalität und Partition-Leader-Konzepte zu ignorieren, um Flexibilität zu gewinnen. AutoMQ verfolgt einen anderen Weg: Es möchte die Kafka-Protokoll-Logik möglichst unverändert lassen und lediglich das Speicher-Subsystem austauschen. Das heißt, Funktionen wie Partitionen, Replikationsmanagement und Consumer-Coordinierung bleiben erhalten, während die Speicherverwaltung über Streams und Write Ahead Logs abstrahiert wird.
AutoMQ verwendet weiterhin eine Segmentierung der Daten, jedoch mit einer zusätzlichen Schicht namens Stream, die das Batching und die Objekt-Organisation für S3 ermöglicht. Ein zentrales Designprinzip bei Kafka ist die Trennung zwischen Shared-Nothing und Shared-Disk-Architekturen. Kafka nutzt eine Shared-Nothing-Strategie, bei der jeder Broker spezifische Partitionen besitzt, um Datenlokalität sicherzustellen. Zugleich bieten objektbasierte Speichersysteme einen Shared-Disk-Charakter, da alle Knoten auf denselben Datenpool zugreifen können. Systeme wie AutoMQ versuchen, diese beiden Welten zu vereinen, indem sie weiterhin Partition-Broker-Zuordnungen beibehalten und so die Vorteile der Datenlokalität erhalten, während die Daten selbst in einem für alle zugänglichen Objektstore liegen.
Der Betrieb eines Kafka-kompatiblen Systems auf S3 führt auch zu erhöhter Netzwerkkomplexität und durch zusätzliche Funktionen wie WAL, Caching und Kompaktierung zu höherer Belastung der Broker. Eine fein abgestimmte Steuerung dieser Workloads ist essenziell, damit sich kritische Operationen wie das Schreiben von Nachrichten nicht mit Hintergrundprozessen wie Kompaktierungen in die Quere kommen. AutoMQ implementiert ein priorisiertes Traffic-Management, das verschiedene Arten von Netzwerkverkehr nach ihrer Wichtigkeit differenziert und so Servicequalität und Systemstabilität garantiert. Ein weiterer wichtiger Aspekt, gerade im Cloud-Umfeld, sind die Cross-Availability Zone (AZ) Kosten. Kafka verursacht solche Kosten durch die zwingende Kommunikation mit Partition-Leadern, die sich oft in unterschiedlichen AZs befinden, sowie durch Replikationsaufwand.
Während das Verlegen von Replikationsverantwortung an S3 diese Kosten reduzieren kann, bleibt die Herausforderung der producerseitigen Kommunikation zum Leader bestehen. AutoMQ adressiert dies durch einen intelligenten Mechanismus im Metadaten-Management, der es erlaubt, die Broker-Informationsantwort so zu gestalten, dass Produzenten stets den nächstgelegenen Broker in ihrer AZ zugewiesen bekommen. Die Nachrichten werden dann zunächst lokal in der jeweiligen AZ abgespeichert und später asynchron zum eigentlichen Partition-Leader übertragen. Dieses Verfahren reduziert die Cross-AZ-Kommunikation erheblich und damit die Kosten, während es gleichzeitig die Vorteile der datenlokalen Verarbeitung bewahrt. Abschließend lässt sich sagen, dass die Integration von Kafka mit Amazon S3 als Speichermedium viele vielschichtige Herausforderungen mit sich bringt, die von Systemlatenz über Kostenoptimierung und Caching bis hin zu Kompatibilität und Netzwerkmanagement reichen.