Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen

Mit der Datenbank Apache Cassandra und der Notwendigkeit ihrer Nutzung in einer Infrastruktur auf Basis von Kubernetes haben wir regelmäßig zu tun. In diesem Material teilen wir unsere Sichtweise zu den erforderlichen Schritten, Kriterien und bestehenden Lösungen (einschließlich einer Übersicht über Operatoren) für die Migration von Cassandra nach K8s.

„Wer eine Frau beherrschen kann, kann auch einen Staat führen“

Wer ist Cassandra eigentlich? Es handelt sich um ein verteiltes Speichersystem, das für die Verwaltung großer Datenmengen konzipiert ist und dabei hohe Verfügbarkeit ohne Single Point of Failure gewährleistet. Das Projekt benötigt wohl keine lange Vorstellung, daher nenne ich nur die grundlegenden Merkmale von Cassandra, die in Bezug auf den spezifischen Artikel relevant sind:

  • Cassandra ist in Java geschrieben.
  • Die Topologie von Cassandra umfasst mehrere Ebenen:
    • Node – eine gestartete Instanz von Cassandra;
    • Rack – eine Gruppe von Cassandra-Instanzen, die nach einem bestimmten Kriterium im selben Rechenzentrum verbunden sind;
    • Datacenter – die Gesamtheit aller Gruppen von Cassandra-Instanzen, die sich in einem Rechenzentrum befinden;
    • Cluster – die Gesamtheit aller Rechenzentren.
  • Zur Identifikation eines Knotens verwendet Cassandra die IP-Adresse.
  • Zur Beschleunigung von Lese- und Schreibvorgängen speichert Cassandra einen Teil der Daten im Arbeitsspeicher.

Jetzt kommen wir zum eigentlichen potenziellen Umzug nach Kubernetes.

Checkliste für die Migration

Wenn es um die Migration von Cassandra nach Kubernetes geht, hoffen wir, dass die Verwaltung mit dem Umzug einfacher wird. Was wird dafür benötigt, was wird dabei helfen?

1. Speicher für Daten

Wie bereits erwähnt, speichert ein Teil der Daten von Cassandra im Arbeitsspeicher – in der Memtable. Es gibt jedoch auch einen anderen Teil der Daten, der auf der Festplatte gespeichert wird – in Form von SSTable. Zu diesen Daten kommt die Entität Commit Log – Aufzeichnungen über alle Transaktionen, die ebenfalls auf der Festplatte gespeichert werden.

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Schema der Schreibtransaktionen in Cassandra

In Kubernetes können wir PersistentVolumes zur Datenspeicherung verwenden. Dank bewährter Mechanismen wird die Arbeit mit Daten in Kubernetes von Jahr zu Jahr einfacher.

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Jeden Pod mit Cassandra werden wir ein eigenes PersistentVolume zuweisen.

Es ist wichtig zu beachten, dass Cassandra von sich aus die Replikation von Daten vorsieht und hierfür integrierte Mechanismen anbietet. Daher ist es nicht notwendig, verteilte Systeme wie Ceph oder GlusterFS für die Speicherung von Daten zu verwenden, wenn Sie einen Cassandra-Cluster aus vielen Knoten zusammenstellen. In diesem Fall ist es sinnvoll, die Daten auf der Festplatte des Knotens mithilfe von lokalen persistente Disks oder Mounts hostPath.

Eine andere Frage ist, wenn Sie für jede Feature-Branch eine separate Umgebung für Entwickler schaffen möchten. In diesem Fall wäre der richtige Ansatz, einen Cassandra-Knoten hochzufahren und die Daten in einem verteilten Speicher zu speichern, d.h. die genannten Ceph und GlusterFS werden Ihre Optionen sein. Dann kann der Entwickler sicher sein, dass er auch bei einem Ausfall eines der Knoten im Kubernetes-Cluster keine Testdaten verliert.

2. Überwachung

Fast die einzige Wahl für die Implementierung der Überwachung in Kubernetes ist Prometheus (detailliert darüber haben wir in entsprechendem Bericht erzählt). Wie steht es um Cassandra mit Metrik-Exportern für Prometheus? Und, was sogar noch wichtiger ist, mit passenden Dashboards dafür in Grafana?

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Beispiel für das äußere Erscheinungsbild von Grafiken in Grafana für Cassandra

Es gibt nur zwei Exporteure: jmx_exporter und cassandra_exporter.

Wir haben uns für den ersten entschieden, weil:

  1. JMX Exporter wächst und entwickelt sich weiter, während Cassandra Exporter nicht die nötige Unterstützung aus der Community erhalten konnte. Der Cassandra Exporter unterstützt bisher nicht die meisten Versionen von Cassandra.
  2. Man kann ihn als javaagent starten, indem man das Flag hinzufügt -javaagent:\/cassandra-exporter.jar=--listen=:9180.
  3. Für ihn gibt es ein angemessenes Dashboard, das nicht mit Cassandra Exporter kompatibel ist.

3. Auswahl der Kubernetes-Primitiven

Gemäß der oben genannten Struktur des Cassandra-Clusters versuchen wir, alles, was dort beschrieben ist, in die Terminologie von Kubernetes zu übersetzen:

  • Cassandra-Knoten → Pod
  • Cassandra-Rack → StatefulSet
  • Cassandra-Datacenter → Pool aus StatefulSets
  • Cassandra-Cluster → ???

Es fehlt also eine zusätzliche Entität, um den gesamten Cassandra-Cluster auf einmal zu verwalten. Aber wenn etwas fehlt, können wir es schaffen! In Kubernetes gibt es dafür den Mechanismus zur Definition eigener Ressourcen — Custom Resource Definitions.

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Deklaration zusätzlicher Ressourcen für Logs und Benachrichtigungen

Aber selbst eine benutzerdefinierte Ressource hat keinen Sinn: denn dafür braucht man einen Controller.. Es könnte notwendig sein, auf die Hilfe eines Kubernetes-Operators zurückzugreifen

4. Identifizierung von Pods

Im obigen Punkt haben wir vereinbart, dass ein Cassandra-Knoten einem Pod in Kubernetes entspricht. Aber die IP-Adressen der Pods werden jedes Mal unterschiedlich sein. Und die Identifizierung eines Knotens in Cassandra erfolgt genau auf der Grundlage der IP-Adresse… Das bedeutet, dass nach jeder Löschung eines Pods der Cassandra-Cluster einen neuen Knoten hinzufügen wird.

Es gibt Lösungen, und zwar nicht nur eine:

  1. Wir können die Identifizierung anhand der Host-IDs (UUIDs, die die Instanzen von Cassandra eindeutig identifizieren) oder anhand der IP-Adressen führen und all dies in bestimmten Strukturen/Tabellen speichern. Diese Methode hat zwei wesentliche Nachteile:
    • Das Risiko eines Wettlaufs, wenn zwei Knoten gleichzeitig ausfallen. Nach dem Wiederhochfahren werden die Cassandra-Knoten gleichzeitig IP-Adressen aus der Tabelle anfordern und um dieselbe Ressource konkurrieren.
    • Wenn ein Cassandra-Knoten seine Daten verloren hat, können wir ihn nicht mehr identifizieren.
  2. Die zweite Lösung scheint ein kleiner Hack zu sein, aber dennoch: Wir können für jeden Cassandra-Knoten einen Service mit ClusterIP erstellen. Probleme dieser Implementierung:
    • Wenn im Cassandra-Cluster sehr viele Knoten vorhanden sind, müssen wir sehr viele Services erstellen.
    • Die Möglichkeit von ClusterIP wird über iptables realisiert. Dies kann ein Problem werden, wenn im Cassandra-Cluster viele (1000… oder sogar 100?) Knoten vorhanden sind. Obwohl die Lastverteilung auf IPVS dieses Problem lösen kann.
  3. Die 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 hat bestimmte Einschränkungen:
    • Bei der Ersetzung von Knoten. Der neue Knoten muss unbedingt die gleiche IP-Adresse wie der vorherige haben (das ist in Cloud-Umgebungen wie AWS, GCP praktisch unmöglich);
    • Wenn wir das Knotennetzwerk des Clusters verwenden, fangen wir an, um Netzwerkressourcen zu konkurrieren. Folglich wird es problematisch, mehr als einen Pod mit Cassandra 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 der Verwendung von CronJob, aber hier stellt uns Cassandra selbst ein Bein.

Ich erinnere daran, dass ein Teil der Daten von Cassandra 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 schaltet sich vollständig aus dem Clusterbetrieb aus.

Danach wird ein Backup (Snapshot) erstellt und das Schema (keyspace). Und hier stellt sich heraus, dass ein einfaches Backup uns nichts nützt: Wir müssen die Daten-IDs speichern, für die der Cassandra-Knoten verantwortlich war – das sind spezielle Tokens.

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Die Verteilung der Tokens zur Identifizierung, welche Daten von den Cassandra-Knoten verwaltet werden

Ein Beispielskript für das Backup von Cassandra von Google in Kubernetes finden Sie unter diesem Link. Der einzige Punkt, den das Skript nicht berücksichtigt, ist der Reset der Daten auf dem Knoten vor der Erstellung des Snapshots. Das bedeutet, dass das Backup nicht für den aktuellen Zustand, sondern für einen etwas früheren Zustand durchgeführt wird. Aber das hilft, den Knoten nicht aus dem Betrieb zu nehmen, was als 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 für die Sicherung eines Cassandra-Knotens

Fertige Lösungen für Cassandra in Kubernetes

Was wird zurzeit genutzt, um Cassandra in Kubernetes bereitzustellen, und welche dieser Lösungen passt am besten zu den angegebenen Anforderungen?

1. Lösungen auf Basis von StatefulSets oder Helm-Charts

Die grundlegenden Funktionen der StatefulSets zur Ausführung des Cassandra-Clusters zu nutzen, ist eine gute Option. Mit Helm-Chart und Go-Templates kann man dem Nutzer eine flexible Schnittstelle zur Bereitstellung von Cassandra bieten.

In der Regel funktioniert das ganz gut… bis etwas Unerwartetes passiert – zum Beispiel, wenn ein Knoten ausfällt. Die Standardmittel von Kubernetes können einfach nicht all diese beschriebenen Besonderheiten berücksichtigen. Darüber hinaus ist dieser Ansatz in Bezug auf die Erweiterbarkeit für komplexere Anwendungen, wie Knotenaustausch, Backup, Wiederherstellung, Überwachung usw., sehr eingeschränkt.

Vertreter:

Beide Charts sind gleichermaßen gut, sind jedoch anfällig für die oben beschriebenen Probleme.

2. Lösungen auf Basis von Kubernetes Operators

Solche Optionen sind interessanter, da sie umfangreiche Möglichkeiten zur Verwaltung des Clusters bieten. Für das Design des Cassandra-Operators, wie auch für jede andere Datenbank, sieht ein gutes Muster so aus: Sidecar Controller CRD:

Migration von Cassandra in Kubernetes: Besonderheiten und Lösungen
Schema zur Verwaltung von Knoten in einem gut gestalteten Cassandra-Operator

Lassen Sie uns die vorhandenen Operatoren betrachten.

1. Cassandra-Operator von Instaclustr

  • GitHub
  • Bereitschaft: Alpha
  • Lizenz: Apache 2.0
  • Implementiert in: Java

Dies ist ein wirklich vielversprechendes und aktiv wachsendes Projekt von einem Unternehmen, das verwaltete Cassandra-Deployments anbietet. Es verwendet, wie oben beschrieben, einen Sidecar-Container, der Befehle über HTTP entgegennimmt. Es ist in Java geschrieben, daher fehlt manchmal die fortschrittlichere Funktionalität der client-go Bibliothek. Außerdem unterstützt der Operator keine unterschiedlichen Racks für ein Datacenter.

Der Operator hat jedoch Vorteile wie Unterstützung für Monitoring, hochrangige Clusterverwaltung durch CRD und sogar Dokumentation zur Erstellung von Backups.

2. Navigator von Jetstack

  • GitHub
  • Bereitschaft: Alpha
  • Lizenz: Apache 2.0
  • Implementiert in: Golang

Ein Operator, der für die Bereitstellung von DB-as-a-Service konzipiert ist. Derzeit unterstützt er zwei Datenbanken: Elasticsearch und Cassandra. Er beinhaltet interessante Lösungen wie Zugriffskontrolle auf die Datenbank über RBAC (dazu wird ein separater navigator-apiserver bereitgestellt). Ein interessantes Projekt, das es wert ist, betrachtet zu werden, allerdings wurde der letzte Commit vor anderthalb Jahren gemacht, was definitiv sein Potenzial beeinträchtigt.

3. Cassandra-Operator von vgkowski

  • GitHub
  • Bereitschaft: Alpha
  • Lizenz: Apache 2.0
  • Implementiert in: Golang

Es wurde nicht ernsthaft in Betracht gezogen, da der letzte Commit im Repository vor mehr als einem Jahr erfolgte. Die Entwicklung des Operators wurde eingestellt: Die letzte Version von Kubernetes, die als unterstützt angegeben ist, ist 1.9.

4. Cassandra-Operator von Rook

  • GitHub
  • Bereitschaft: Alpha
  • Lizenz: Apache 2.0
  • Implementiert in: Golang

Die Entwicklung des Operatormoduls verläuft nicht so schnell, wie man es sich wünschen würde. Es verfügt über eine durchdachte CRD-Struktur zur Verwaltung des Clusters und löst das Problem der Identifizierung von Knoten mithilfe von Service mit ClusterIP (das ist der berühmte "Hack")… aber das ist bisher alles. Monitoring und Backups sind derzeit nicht out-of-the-box verfügbar (übrigens haben wir uns um das Monitoring selbst gekümmert ) Ein interessanter Punkt ist, dass man mit diesem Operator auch ScyllaDB bereitstellen kann.NB: Wir haben diesen Operator mit kleinen Anpassungen in einem unserer Projekte verwendet. Während der gesamten Nutzungsdauer von etwa 4 Monaten gab es keine Probleme mit dem Betrieb des Operators.

5. CassKop von Orange

5. CassKop von Orange

  • GitHub
  • Bereitschaft: Alpha
  • Lizenz: Apache 2.0
  • Implementiert in: Golang

Der jüngste Operator in der Liste: Der erste Commit wurde am 23. Mai 2019 durchgeführt. Schon jetzt verfügt er über eine große Anzahl von Funktionen aus unserer Liste, die im Repository des Projekts näher beschrieben werden. Der Operator basiert auf dem beliebten operator-sdk. Unterstützt das Monitoring «out of the box». Der Hauptunterschied zu anderen Operatoren ist die Verwendung des CassKop-Plugins, das in Python implementiert ist und für die Kommunikation zwischen Cassandra-Knoten verwendet wird.

Das DBMS Tarantool ist ein attraktives, zukunftsträchtiges Produkt zur Erstellung von hochbelasteten Anwendungen.

Die Anzahl der Ansätze und möglichen Optionen, Cassandra in Kubernetes zu migrieren, spricht für sich selbst: Das Thema ist gefragt.

In diesem Stadium kann man die oben beschriebenen Ansätze auf eigenes Risiko ausprobieren: Keiner der Entwickler garantiert eine 100%-ige Funktionalität ihrer Lösung in einer Produktionsumgebung. Aber viele Produkte sehen bereits vielversprechend aus, um sie in Entwicklungsständen einzusetzen.

Ich denke, dass diese Frau auf dem Schiff in Zukunft von Nutzen sein wird!

P.S.

Lesen Sie auch in unserem Blog:

Quelle: habr.com

60GB SSD 8Gb DDR4