
Mit der Datenbank Apache Cassandra und der Notwendigkeit, sie innerhalb einer Infrastruktur auf Basis von Kubernetes zu betreiben, haben wir regelmäßig zu tun. In diesem Beitrag teilen wir unsere Sichtweise zu den notwendigen Schritten, Kriterien und bestehenden Lösungen (einschließlich einer Übersicht über Operatoren) für die Migration von Cassandra nach K8s.
„Wer eine Frau führen kann, wird auch mit einem Staat fertig.“
Wer ist Cassandra? Es handelt sich um ein verteiltes Speichersystem, das dafür entwickelt wurde, große Datenmengen zu verwalten und gleichzeitig hohe Verfügbarkeit ohne einen Einzelpunkt des Ausfalls zu gewährleisten. Das Projekt benötigt kaum eine lange Einleitung, daher nenne ich nur die wichtigsten Merkmale von Cassandra, die im Kontext dieses Artikels von Bedeutung sind:
- Cassandra ist in Java geschrieben.
- Die Topologie von Cassandra umfasst mehrere Ebenen:
- Node — eine einmalig bereitgestellte Instanz von Cassandra;
- Rack — eine Gruppe von Cassandra-Instanzen, die aus einem bestimmten Grund zusammengefasst sind und sich im selben Rechenzentrum befinden;
- Datacenter — die Gesamtheit aller Gruppen von Cassandra-Instanzen, die sich in einem Rechenzentrum befinden;
- Cluster — die Gesamtheit aller Rechenzentren.
- Zur Identifizierung eines Knotens verwendet Cassandra die IP-Adresse.
- Um die Geschwindigkeit von Lese- und Schreiboperationen zu erhöhen, speichert Cassandra einen Teil der Daten im Arbeitsspeicher.
Nun zum eigentlichen möglichen Umzug nach Kubernetes.
Checkliste für die Migration
Wenn es um die Migration von Cassandra nach Kubernetes geht, hoffen wir, dass das Management dadurch einfacher wird. Was wird dafür benötigt und was kann dabei helfen?
1. Datenspeicher
Wie bereits erwähnt, speichert Cassandra einen Teil der Daten im Arbeitsspeicher – in Memtable. Aber es gibt auch einen anderen Teil der Daten, der auf der Festplatte gespeichert wird – in Form von SSTable. Zu diesen Daten kommt eine Entität hinzu, Commit Log – Aufzeichnungen über alle Transaktionen, die ebenfalls auf der Festplatte gespeichert werden.

Schematik der Schreibtransaktionen in Cassandra
In Kubernetes können wir für die Datenspeicherung PersistentVolume verwenden. Dank ausgereifter Mechanismen wird die Arbeit mit Daten in Kubernetes von Jahr zu Jahr einfacher.

Jedem Pod mit Cassandra weisen wir ein eigenes PersistentVolume zu.
Es ist wichtig zu beachten, dass Cassandra von Natur aus die Replikation von Daten unterstützt und hierfür integrierte Mechanismen bietet. Daher ist es nicht notwendig, für die Datenspeicherung verteilte Systeme wie Ceph oder GlusterFS zu verwenden, wenn Sie ein Cassandra-Cluster aus einer großen Anzahl von Knoten aufbauen. In diesem Fall wäre es sinnvoll, die Daten auf der Festplatte des Knotens mit Hilfe von oder durch das Einbinden von hostPath.
Eine andere Frage ist, wenn Sie für jede Feature-Branch eine separate Umgebung für Entwickler erstellen möchten. In diesem Fall wäre der richtige Ansatz, einen Cassandra-Knoten zu betreiben und die Daten in einem verteilten Speicher zu speichern, also würden die erwähnten Ceph und GlusterFS Ihre Optionen sein. Dadurch kann der Entwickler sicher sein, dass er seine Testdaten nicht verliert, selbst wenn einer der Knoten des Kubernetes-Clusters ausfällt.
2. Überwachung
Die nahezu alternativlose Wahl für die Implementierung der Überwachung in Kubernetes ist Prometheus (detailliert haben wir dies in ). Wie steht es um Cassandras Metrikenexporter für Prometheus? Und, was vielleicht noch wichtiger ist, wie sieht es mit den entsprechenden Dashboards für Grafana aus?

Beispiel für das Erscheinungsbild von Grafiken in Grafana für Cassandra
Es gibt nur zwei Exporter: und .
Wir haben uns für den ersten entschieden, weil:
- JMX Exporter wächst und entwickelt sich weiter, während Cassandra Exporter nicht die nötige Unterstützung aus der Community erhalten hat. Cassandra Exporter unterstützt nach wie vor nicht die meisten Versionen von Cassandra.
- Man kann ihn als javaagent starten, indem man das Flag hinzufügt
-javaagent:/cassandra-exporter.jar=--listen=:9180. - Dafür gibt es ein , das mit Cassandra Exporter nicht kompatibel ist.
3. Auswahl der Kubernetes-Primitiven
Basierend auf der oben beschriebenen Clusterstruktur von Cassandra versuchen wir, alles, was dort beschrieben ist, in Kubernetes-Terminologie zu übersetzen:
- Cassandra Node → Pod
- Cassandra Rack → StatefulSet
- Cassandra Datacenter → Pool aus StatefulSets
- Cassandra Cluster → ???
Es scheint, dass eine zusätzliche Entität fehlt, um das gesamte Cassandra-Cluster gleichzeitig zu verwalten. Aber wenn etwas fehlt, können wir es erschaffen! In Kubernetes gibt es dafür den Mechanismus zur Definition eigener Ressourcen — .

Deklaration zusätzlicher Ressourcen für Logs und Benachrichtigungen
Doch eine selbst definierte Ressource hat ohne einen Controllerkeine Bedeutung. Möglicherweise müssen wir auf die Hilfe eines …
4. Identifizierung von Pods
Im vorherigen Punkt haben wir vereinbart, dass ein Cassandra-Knoten einem Pod in Kubernetes entspricht. Aber die IP-Adressen der Pods werden jedes Mal unterschiedlich sein. Die Identifikation eines Knotens in Cassandra erfolgt dabei genau über die IP-Adresse... Das bedeutet, dass nach jeder Löschung eines Pods der Cassandra-Cluster einen neuen Knoten hinzufügen wird.
Es gibt Lösungen, sogar mehrere:
- Wir können die Hosts anhand ihrer UUIDs (die eindeutig die Instanzen von Cassandra identifizieren) oder über die IP-Adressen verfolgen und alles in bestimmten Strukturen/Tabellen speichern. Diese Methode hat zwei Hauptnachteile:
- Das Risiko eines Wettrennens, wenn zwei Knoten gleichzeitig ausfallen. Nach dem Neustart werden die Cassandra-Knoten gleichzeitig eine IP-Adresse aus der Tabelle anfordern und um dieselbe Ressource konkurrieren.
- Wenn ein Cassandra-Knoten seine Daten verloren hat, können wir ihn nicht mehr identifizieren.
- Die zweite Lösung scheint ein kleiner Hack zu sein, ist aber trotzdem sinnvoll: Wir können für jeden Cassandra-Knoten einen Service mit ClusterIP erstellen. Die Probleme dieser Implementierung sind:
- Wenn im Cassandra-Cluster sehr viele Knoten vorhanden sind, müssen wir eine große Anzahl von Services erstellen.
- Die ClusterIP-Funktion wird über iptables realisiert. Dies kann problematisch werden, wenn der Cassandra-Cluster viele (1000… oder sogar 100?) Knoten hat. Obwohl dieses Problem lösen kann.
- Eine dritte Lösung besteht darin, für die Cassandra-Knoten das Knotennetzwerk anstelle des dedizierten Pod-Netzwerks zu verwenden, indem die Einstellung aktiviert wird
hostNetwork: true. Diese Methode bringt bestimmte Einschränkungen mit sich:- Beim Ersetzen von Knoten. Der neue Knoten muss unbedingt die gleiche IP-Adresse haben wie der vorherige (in Clouds wie AWS, GCP ist das praktisch unmöglich);
- Durch die Verwendung des Knotennetzwerks des Clusters beginnen wir, um Netzwerkressourcen zu konkurrieren. Daher wird es problematisch sein, mehr als einen Cassandra-Pod auf einen Clusterknoten zu legen.
5. Backups
Wir möchten eine vollständige Version der Daten eines Cassandra-Knotens nach einem Zeitplan speichern. Kubernetes bietet eine bequeme Möglichkeit mit Hilfe von , aber hier macht uns Cassandra selbst einen Strich durch die Rechnung.
Ich erinnere daran, dass ein Teil der Cassandra-Daten im Speicher gehalten wird. Um ein vollständiges Backup zu erstellen, müssen die Daten aus dem Speicher (Memtables) auf die Festplatte (SSTables). In diesem Moment hört der Cassandra-Knoten auf, Verbindungen anzunehmen, und wird vollständig aus dem Clusterbetrieb genommen.
Danach wird ein Backup gezogen (snapshot) und das Schema wird gespeichert (keyspace). Dabei stellt sich heraus, dass uns das Backup allein nichts nützt: Wir müssen die Datenidentifikatoren, für die der Cassandra-Knoten verantwortlich war, speichern – das sind spezielle Tokens.

Verteilung der Tokens zur Identifizierung, für welche Daten die Cassandra-Knoten verantwortlich sind.
Ein Beispielskript zum Erstellen eines Cassandra-Backups von Google in Kubernetes findet man unter . Der einzige Punkt, den das Skript nicht berücksichtigt, ist das Zurücksetzen der Daten auf den Knoten vor dem Erstellen des Snapshots. Das bedeutet, dass das Backup nicht für den aktuellen Zustand, sondern für einen früheren Zustand erstellt wird. Aber das hilft, den Knoten nicht aus dem Betrieb zu nehmen, was sehr logisch erscheint.
set -eu
if [[ -z "$1" ]]; then
info "Bitte geben Sie einen Keyspace an"
exit 1
fi
KEYSPACE="$1"
result=$(nodetool snapshot "${KEYSPACE}")
if [[ $? -ne 0 ]]; then
echo "Fehler beim Erstellen des Snapshots"
exit 1
fi
timestamp=$(echo "$result" | awk '/Snapshot directory: / { print $3 }')
mkdir -p /tmp/backup
for path in $(find "/var/lib/cassandra/data/${KEYSPACE}" -name $timestamp); do
table=$(echo "${path}" | awk -F "[/-]" '{print $7}')
mkdir /tmp/backup/$table
mv $path /tmp/backup/$table
done
tar -zcf /tmp/backup.tar.gz -C /tmp/backup .
nodetool clearsnapshot "${KEYSPACE}"Beispiel eines Bash-Skripts zum Erstellen eines Backups von einem Cassandra-Knoten.
Fertige Lösungen für Cassandra in Kubernetes
Was wird derzeit verwendet, um Cassandra in Kubernetes bereitzustellen, und welche dieser Lösungen passt am besten zu den spezifischen Anforderungen?
1. Lösungen auf Basis von StatefulSets oder Helm-Charts
Die Nutzung grundlegender Funktionen von StatefulSets zur Bereitstellung eines Cassandra-Clusters ist eine sinnvolle Option. Mithilfe von Helm-Charts und Go-Templates kann den Nutzern eine flexible Benutzeroberfläche zur Bereitstellung von Cassandra bereitgestellt werden.
In der Regel funktioniert das gut… bis etwas Unerwartetes passiert, wie beispielsweise der Ausfall eines Knotens. Die Standardwerkzeuge von Kubernetes können einfach nicht alle oben beschriebenen Besonderheiten berücksichtigen. Darüber hinaus ist dieser Ansatz stark eingeschränkt, was die Erweiterbarkeit für komplexere Anwendungen angeht: Knotenwechsel, Backup, Wiederherstellung, Monitoring usw.
Vertreter:
- ;
- .
Beide Charts sind gleich gut, sind jedoch den oben beschriebenen Problemen ausgesetzt.
2. Lösungen auf Basis von Kubernetes-Operatoren
Diese Optionen sind besonders interessant, da sie umfangreiche Möglichkeiten zur Verwaltung des Clusters bieten. Für den Design-Operator von Cassandra, wie auch für jede andere Datenbank, sieht ein gutes Muster wie folgt aus: Sidecar Controller CRD:

Das Schema zur Verwaltung der Knoten in einem gut gestalteten Cassandra-Operator.
Betrachten wir die bestehenden Operatoren.
1. Cassandra-Operator von instaclustr
- Bereitschaft: Alpha
- Lizenz: Apache 2.0
- Implementiert in: Java
Dies ist ein vielversprechendes und aktiv wachsendes Projekt von einem Unternehmen, das verwaltete Cassandra-Bereitstellungen anbietet. Es verwendet, wie oben beschrieben, einen Sidecar-Container, der Befehle über HTTP entgegennimmt. Da es in Java geschrieben ist, fehlt ihm manchmal die fortgeschrittene Funktionalität der client-go-Bibliothek. Außerdem unterstützt der Operator keine unterschiedlichen Racks für ein Rechenzentrum.
Dafür hat der Operator Vorteile wie die Unterstützung von Monitoring, eine hochgradige Clusterverwaltung mittels CRD und sogar Dokumentationen zur Sicherstellung von Backups.
2. Navigator von Jetstack
- Bereitschaft: Alpha
- Lizenz: Apache 2.0
- Implementiert in: Golang
Ein Operator zur Bereitstellung von DB-as-a-Service. Aktuell werden zwei Datenbanken unterstützt: Elasticsearch und Cassandra. Er bietet interessante Lösungen wie den Datenbankzugriff über RBAC (hierfür wird ein separater navigator-apiserver bereitgestellt). Ein Projekt, das es wert ist, genauer betrachtet zu werden, allerdings wurde der letzte Commit vor anderthalb Jahren gemacht, was sein Potenzial deutlich einschränkt.
3. Cassandra-Operator von vgkowski
- Bereitschaft: Alpha
- Lizenz: Apache 2.0
- Implementiert in: Golang
Eine ernsthafte Betrachtung fand nicht statt, da der letzte Commit im Repository vor über einem Jahr erfolgte. Die Entwicklung des Operators wurde eingestellt: Die letzte unterstützte Kubernetes-Version ist 1.9.
4. Cassandra-Operator von Rook
- Bereitschaft: Alpha
- Lizenz: Apache 2.0
- Implementiert in: Golang
Die Entwicklung dieses Operators verläuft nicht so schnell, wie es wünschenswert wäre. Er hat eine durchdachte CRD-Struktur zur Verwaltung des Clusters und löst das Problem der Knotenidentifikation durch einen Service mit ClusterIP (dieser „Hack“) … aber das ist bisher alles. Monitoring und Backups sind derzeit nicht standardmäßig enthalten (Übrigens haben wir uns um das Monitoring) ). Ein interessanter Punkt ist, dass mit diesem Operator auch ScyllaDB bereitgestellt werden kann.
Hinweis: Wir haben diesen Operator mit kleinen Anpassungen in einem unserer Projekte verwendet. In der gesamten Betriebszeit (~4 Monate) gab es keine Probleme mit dem Operator.
5. CassKop von Orange
- Bereitschaft: Alpha
- Lizenz: Apache 2.0
- Implementiert in: Golang
Der jüngste Betreiber in der Liste: Der erste Commit wurde am 23. Mai 2019 gemacht. Er verfügt bereits jetzt über eine Vielzahl von Funktionen aus unserer Liste, die im Projekt-Repository genauer betrachtet werden können. Der Operator basiert auf dem beliebten operator-sdk. Er unterstützt das Monitoring 'out of the box'. Das Hauptunterscheidungsmerkmal von anderen Operatoren ist die Verwendung , das in Python implementiert ist und für die Kommunikation zwischen den Cassandra-Knoten verwendet wird.
Fazit
Die Anzahl der Ansätze und möglichen Optionen zur Migration von Cassandra nach Kubernetes spricht für sich: Das Thema ist gefragt.
In dieser Phase sollte alles, was oben beschrieben ist, auf eigene Gefahr ausprobiert werden: Keiner der Entwickler garantiert die 100%-ige Funktionalität ihrer Lösung in einer Produktionsumgebung. Viele Produkte sehen jedoch vielversprechend aus, um sie in Entwicklungsumgebungen zu testen.
Ich denke, in Zukunft wird diese Frau auf dem Schiff von Nutzen sein!
P.S.
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «»;
- «».
Quelle: habr.com
