Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen

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.

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen
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.

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen
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 lokalen persistierenden Festplatten 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 entsprechenden Berichten). 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?

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen
Beispiel für das Erscheinungsbild von Grafiken in Grafana für Cassandra

Es gibt nur zwei Exporter: 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 hat. Cassandra Exporter unterstützt nach wie vor 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. Dafür gibt es ein angemessenes Dashboard, 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 — Custom Resource Definitions.

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen
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 Kubernetes-Operators zurückgreifen.

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:

  1. 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.
  2. 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 die Lastverteilung basierend auf IPVS dieses Problem lösen kann.
  3. 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 CronJob, 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.

Migration von Cassandra nach Kubernetes: Besonderheiten und Lösungen
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 diesem Link verfügbar. 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:

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

Betrachten wir die bestehenden Operatoren.

1. Cassandra-Operator von instaclustr

  • GitHub
  • 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

  • GitHub
  • 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

  • GitHub
  • 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

  • GitHub
  • 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) selbst gekümmert). 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

  • GitHub
  • 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 des CassKop-Plugins, 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

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster