Migracja Cassandry do Kubernetes: funkcje i rozwiązania

Migracja Cassandry do Kubernetes: funkcje i rozwiązania

Regularnie spotykamy się z bazą danych Apache Cassandra i koniecznością obsługi jej w infrastrukturze opartej na Kubernetesie. W tym materiale podzielimy się naszą wizją niezbędnych kroków, kryteriów i istniejących rozwiązań (w tym przegląd operatorów) migracji Cassandry do K8s.

„Kto potrafi rządzić kobietą, może także rządzić państwem”

Kim jest Cassandra? Jest to rozproszony system pamięci masowej przeznaczony do zarządzania dużymi wolumenami danych przy jednoczesnym zapewnieniu wysokiej dostępności bez pojedynczego punktu awarii. Projektu nie trzeba długo przedstawiać, dlatego podam jedynie główne cechy Cassandry, które będą istotne w kontekście konkretnego artykułu:

  • Cassandra jest napisana w Javie.
  • Topologia Cassandry obejmuje kilka poziomów:
    • Węzeł - jedna wdrożona instancja Cassandra;
    • Rack to grupa instancji Cassandry, połączonych jakąś cechą, zlokalizowana w tym samym centrum danych;
    • Datacenter - zbiór wszystkich grup instancji Cassandry zlokalizowanych w jednym centrum danych;
    • Klaster to zbiór wszystkich centrów danych.
  • Cassandra używa adresu IP do identyfikacji węzła.
  • Aby przyspieszyć operacje zapisu i odczytu, Cassandra przechowuje część danych w pamięci RAM.

Teraz - do faktycznego potencjalnego przejścia na Kubernetes.

Lista kontrolna do przeniesienia

Skoro mowa o migracji Cassandry do Kubernetesa, mamy nadzieję, że wraz z przeprowadzką zarządzanie nią stanie się wygodniejsze. Co będzie do tego potrzebne, co w tym pomoże?

1. Przechowywanie danych

Jak już wyjaśniono, Cassanda przechowuje część danych w pamięci RAM - w Memtable. Ale jest jeszcze jedna część danych zapisywana na dysku - w formie SSTable. Do tych danych dodawana jest jednostka Dziennik zatwierdzeń — zapisy wszystkich transakcji, które są również zapisywane na dysku.

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Napisz diagram transakcji w Cassandrze

W Kubernetesie możemy używać PersistentVolume do przechowywania danych. Dzięki sprawdzonym mechanizmom praca z danymi w Kubernetesie z roku na rok staje się łatwiejsza.

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Przydzielimy własny PersistentVolume do każdego kapsuły Cassandra

Należy zauważyć, że sama Cassandra implikuje replikację danych, oferując do tego wbudowane mechanizmy. Dlatego jeśli budujesz klaster Cassandra z dużej liczby węzłów, nie ma potrzeby używania systemów rozproszonych, takich jak Ceph lub GlusterFS do przechowywania danych. W takim przypadku logiczne byłoby przechowywanie danych na dysku hosta za pomocą lokalne dyski stałe lub montaż hostPath.

Kolejnym pytaniem jest to, czy chcesz utworzyć osobne środowisko dla programistów dla każdej gałęzi funkcji. W tym przypadku właściwym podejściem byłoby podniesienie jednego węzła Cassandra i przechowywanie danych w magazynie rozproszonym, tj. wspomniane Ceph i GlusterFS będą Twoimi opcjami. Wtedy programista będzie miał pewność, że nie straci danych testowych nawet w przypadku utraty jednego z węzłów klastra Kuberntes.

2. Monitorowanie

Praktycznie niekwestionowanym wyborem do wdrożenia monitoringu w Kubernetesie jest Prometheus (mówiliśmy o tym szczegółowo w powiązany raport). Jak Cassandra radzi sobie z eksporterami metryk dla Prometheusa? A co jeszcze ważniejsze, dzięki pasującym dashboardom dla Grafany?

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Przykład wyglądu wykresów w Grafanie dla Cassandry

Jest tylko dwóch eksporterów: jmx_eksporter и casandra_eksporter.

Sami wybraliśmy ten pierwszy, ponieważ:

  1. JMX Exporter rośnie i rozwija się, podczas gdy Cassandra Exporter nie była w stanie uzyskać wystarczającego wsparcia społeczności. Cassandra Exporter nadal nie obsługuje większości wersji Cassandry.
  2. Możesz uruchomić go jako javaagent, dodając flagę -javaagent:<plugin-dir-name>/cassandra-exporter.jar=--listen=:9180.
  3. Jest jeden dla niego odpowiedni pulpit nawigacyjny, który jest niezgodny z Cassandra Exporter.

3. Wybór prymitywów Kubernetes

Zgodnie z powyższą strukturą klastra Cassandra spróbujmy przełożyć wszystko, co jest tam opisane na terminologię Kubernetesa:

  • Węzeł Cassandra → Pod
  • Cassandra Rack → StatefulSet
  • Centrum danych Cassandra → pula z StatefulSets
  • Gromada Kasandry → ???

Okazuje się, że brakuje jakiegoś dodatkowego podmiotu, który zarządzałby jednocześnie całym klastrem Cassandra. Ale jeśli coś nie istnieje, możemy to stworzyć! Kubernetes posiada mechanizm definiowania w tym celu własnych zasobów - Niestandardowe definicje zasobów.

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Deklarowanie dodatkowych zasobów dla logów i alertów

Ale sam zasób niestandardowy nic nie znaczy: w końcu wymaga kontroler. Być może będziesz musiał szukać pomocy Operator Kubernetesa...

4. Identyfikacja strąków

W powyższym akapicie ustaliliśmy, że jeden węzeł Cassandra będzie równy jednemu podowi w Kubernetesie. Ale adresy IP kapsuł będą za każdym razem inne. A identyfikacja węzła w Cassandrze odbywa się na podstawie adresu IP... Okazuje się, że po każdym usunięciu poda klaster Cassandra doda nowy węzeł.

Istnieje wyjście, i nie tylko jedno:

  1. Możemy prowadzić rejestry według identyfikatorów hostów (uUID, które jednoznacznie identyfikują instancje Cassandry) lub według adresów IP i przechowywać to wszystko w niektórych strukturach/tabelach. Metoda ma dwie główne wady:
    • Ryzyko wystąpienia sytuacji wyścigu, jeśli dwa węzły spadną jednocześnie. Po wzroście węzły Cassandra będą jednocześnie żądać adresu IP z tabeli i konkurować o ten sam zasób.
    • Jeśli węzeł Cassandra utraci swoje dane, nie będziemy już w stanie go zidentyfikować.
  2. Drugie rozwiązanie wydaje się małym hackiem, ale mimo to: możemy stworzyć usługę z ClusterIP dla każdego węzła Cassandra. Problemy z tą implementacją:
    • Jeśli w klastrze Cassandra znajduje się wiele węzłów, będziemy musieli utworzyć wiele usług.
    • Funkcja ClusterIP jest implementowana poprzez iptables. Może to stanowić problem, jeśli klaster Cassandra ma wiele (1000… a nawet 100?) węzłów. Chociaż równoważenie w oparciu o IPVS może rozwiązać ten problem.
  3. Trzecie rozwiązanie polega na wykorzystaniu sieci węzłów dla węzłów Cassandry zamiast dedykowanej sieci podów poprzez włączenie ustawienia hostNetwork: true. Metoda ta nakłada pewne ograniczenia:
    • Do wymiany jednostek. Konieczne jest, aby nowy węzeł miał taki sam adres IP jak poprzedni (w chmurach takich jak AWS, GCP jest to prawie niemożliwe);
    • Wykorzystując sieć węzłów klastra, zaczynamy konkurować o zasoby sieciowe. Dlatego umieszczenie więcej niż jednego kapsuły z Cassandrą w jednym węźle klastra będzie problematyczne.

5. Kopie zapasowe

Chcemy zapisać pełną wersję danych pojedynczego węzła Cassandra zgodnie z harmonogramem. Kubernetes zapewnia wygodną funkcję korzystania CronJob, ale tutaj sama Cassandra wbija szprychy w nasze koła.

Przypomnę, że Cassandra przechowuje część danych w pamięci. Do wykonania pełnej kopii zapasowej potrzebne są dane z pamięci (Tablice pamięci) przenieś na dysk (Tabele SST). W tym momencie węzeł Cassandra przestaje akceptować połączenia, całkowicie zamykając klaster.

Następnie kopia zapasowa jest usuwana (migawka) i schemat zostaje zapisany (spacja klawiszowa). A potem okazuje się, że samo wykonanie kopii zapasowej nic nam nie daje: musimy zapisać identyfikatory danych, za które odpowiadał węzeł Cassandra – są to specjalne tokeny.

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Dystrybucja tokenów w celu identyfikacji, za jakie dane odpowiadają węzły Cassandra

Przykładowy skrypt do pobrania kopii zapasowej Cassandry z Google w Kubernetes można znaleźć pod adresem link. Jedynym punktem, którego skrypt nie bierze pod uwagę, jest resetowanie danych do węzła przed wykonaniem migawki. Oznacza to, że kopia zapasowa jest wykonywana nie dla stanu bieżącego, ale dla stanu nieco wcześniejszego. Pomaga to jednak nie wyłączać węzła, co wydaje się bardzo logiczne.

set -eu

if [[ -z "$1" ]]; then
  info "Please provide a keyspace"
  exit 1
fi

KEYSPACE="$1"

result=$(nodetool snapshot "${KEYSPACE}")

if [[ $? -ne 0 ]]; then
  echo "Error while making snapshot"
  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}"

Przykład skryptu bash do wykonywania kopii zapasowej z jednego węzła Cassandra

Gotowe rozwiązania dla Cassandry w Kubernetesie

Czego obecnie używa się do wdrożenia Cassandry w Kubernetesie i który z nich najlepiej odpowiada danym wymaganiom?

1. Rozwiązania oparte na wykresach StatefulSet lub Helm

Dobrym rozwiązaniem jest użycie podstawowych funkcji StatefulSets do uruchomienia klastra Cassandra. Korzystając z szablonów wykresu Helm i Go, możesz zapewnić użytkownikowi elastyczny interfejs do wdrażania Cassandry.

Zwykle działa to dobrze... dopóki nie wydarzy się coś nieoczekiwanego, na przykład awaria węzła. Standardowe narzędzia Kubernetes po prostu nie są w stanie uwzględnić wszystkich opisanych powyżej funkcji. Ponadto podejście to ma bardzo ograniczone możliwości rozszerzenia w przypadku bardziej złożonych zastosowań: wymiana węzłów, tworzenie kopii zapasowych, odzyskiwanie, monitorowanie itp.

Przedstawiciele:

Obydwa wykresy są równie dobre, ale są obarczone problemami opisanymi powyżej.

2. Rozwiązania oparte na Operatorze Kubernetes

Opcje takie są o wiele bardziej interesujące, ponieważ dają szerokie możliwości zarządzania klastrem. Do zaprojektowania operatora Cassandra, jak każdej innej bazy danych, dobry wzorzec wygląda tak: Sidecar <-> Controller <-> CRD:

Migracja Cassandry do Kubernetes: funkcje i rozwiązania
Schemat zarządzania węzłami w dobrze zaprojektowanym operatorze Cassandra

Przyjrzyjmy się istniejącym operatorom.

1. Cassandra-operator z instaclustr

  • GitHub
  • Gotowość: Alfa
  • Licencja: Apache 2.0
  • Zaimplementowano w: Java

To rzeczywiście bardzo obiecujący i aktywnie rozwijający się projekt firmy oferującej zarządzane wdrożenia Cassandry. Jak opisano powyżej, używa kontenera bocznego, który akceptuje polecenia za pośrednictwem protokołu HTTP. Napisany w Javie, czasami brakuje mu bardziej zaawansowanej funkcjonalności biblioteki klienta. Ponadto operator nie obsługuje różnych stojaków dla jednego centrum danych.

Ale operator ma takie zalety, jak obsługa monitorowania, zarządzanie klastrami wysokiego poziomu za pomocą CRD, a nawet dokumentacja do tworzenia kopii zapasowych.

2. Nawigator z Jetstack

  • GitHub
  • Gotowość: Alfa
  • Licencja: Apache 2.0
  • Wdrożono w: Golang

Instrukcja przeznaczona do wdrażania usługi DB-as-a-Service. Obecnie obsługuje dwie bazy danych: Elasticsearch i Cassandra. Posiada tak ciekawe rozwiązania jak kontrola dostępu do baz danych poprzez RBAC (do tego ma swój własny, oddzielny nawigator-apiserver). Ciekawy projekt, któremu warto byłoby się bliżej przyjrzeć, jednak ostatnie zatwierdzenie miało miejsce półtora roku temu, co wyraźnie ogranicza jego potencjał.

3. Cassandra-operator autorstwa vgkowskiego

  • GitHub
  • Gotowość: Alfa
  • Licencja: Apache 2.0
  • Wdrożono w: Golang

Nie traktowali tego „poważnie”, ponieważ ostatnie zatwierdzenie repozytorium miało miejsce ponad rok temu. Zarzucono rozwój operatora: najnowsza wersja Kubernetes zgłoszona jako obsługiwana to 1.9.

4. Cassandra-operator według Rooka

  • GitHub
  • Gotowość: Alfa
  • Licencja: Apache 2.0
  • Wdrożono w: Golang

Operator, którego rozwój nie postępuje tak szybko, jak byśmy tego chcieli. Ma przemyślaną strukturę CRD do zarządzania klastrami, rozwiązuje problem identyfikacji węzłów za pomocą Service with ClusterIP (ten sam „hack”)… ale to wszystko na razie. Obecnie nie ma gotowego monitorowania ani kopii zapasowych (nawiasem mówiąc, jesteśmy za monitorowaniem sami to wzięliśmy). Ciekawostką jest to, że za pomocą tego operatora możesz także wdrożyć ScyllaDB.

Uwaga: tego operatora z niewielkimi modyfikacjami użyliśmy w jednym z naszych projektów. Przez cały okres eksploatacji (~4 miesiące eksploatacji) nie stwierdzono żadnych problemów w pracy operatora.

5. CassKop od Orange

  • GitHub
  • Gotowość: Alfa
  • Licencja: Apache 2.0
  • Wdrożono w: Golang

Najmłodszy operator na liście: pierwszy commit został wykonany 23 maja 2019 r. Już teraz ma w swoim arsenale dużą liczbę funkcji z naszej listy, o których więcej szczegółów można znaleźć w repozytorium projektu. Operator zbudowany jest w oparciu o popularny operator-sdk. Obsługuje monitorowanie od razu po wyjęciu z pudełka. Główną różnicą w stosunku do innych operatorów jest zastosowanie Wtyczka CassKop, zaimplementowany w Pythonie i używany do komunikacji pomiędzy węzłami Cassandra.

odkrycia

Liczba podejść i możliwych opcji przeniesienia Cassandry na Kubernetes mówi sama za siebie: temat jest pożądany.

Na tym etapie możesz wypróbować dowolne z powyższych na własne ryzyko i ryzyko: żaden z programistów nie gwarantuje 100% działania swojego rozwiązania w środowisku produkcyjnym. Ale już teraz wiele produktów wygląda obiecująco, warto spróbować zastosować je w stołach rozwojowych.

Myślę, że w przyszłości ta kobieta na statku się przyda!

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz