
Z bazą danych Apache Cassandra i koniecznością jej eksploatacji w ramach infrastruktury opartej na Kubernetes spotykamy się regularnie. W tym materiale podzielimy się naszym spojrzeniem na niezbędne kroki, kryteria oraz istniejące rozwiązania (w tym przegląd operatorów) związane z migracją Cassandry do K8s.
„Kto może zarządzać kobietą, poradzi sobie i z państwem”
Kim właściwie jest Cassandra? To rozproszony system zarządzania danymi, zaprojektowany do obsługi dużych ilości danych, zapewniając jednocześnie wysoką dostępność bez pojedynczego punktu awarii. Projekt prawdopodobnie nie wymaga długiego przedstawiania, dlatego przedstawię tylko główne cechy Cassandry, które będą istotne w kontekście tego artykułu:
- Cassandra jest napisana w Java.
- Topologia Cassandry obejmuje kilka poziomów:
- Węzeł — jeden uruchomiony egzemplarz Cassandry;
- Rack — grupa egzemplarzy Cassandry, połączonych według określonego kryterium, znajdująca się w jednym centrum danych;
- Centrum danych — zbiorowość wszystkich grup egzemplarzy Cassandry, znajdujących się w jednym centrum danych;
- Klaster — zbiorowość wszystkich centrów danych.
- Aby zidentyfikować węzeł, Cassandra używa adresu IP.
- Dla szybkości operacji zapisu i odczytu część danych Cassandra przechowuje w pamięci operacyjnej.
Teraz przejdźmy do potencjalnej migracji do Kubernetes.
Lista kontrolna do przeniesienia
Mówiąc o migracji Cassandry do Kubernetes, mamy nadzieję, że z nową lokalizacją zarządzanie nią stanie się łatwiejsze. Czego potrzebujemy, co nam w tym pomoże?
1. Przechowywanie danych
Jak już wspomniano, część danych Cassandry przechowuje w pamięci operacyjnej — w Memtable. Ale jest też inna część danych, która jest zapisywana na dysku — w postaci SSTable. Do tych danych dodawana jest jednostka Commit Log — zapisy dotyczące wszystkich transakcji, które także są zapisywane na dysku.

Schemat transakcji zapisu w Cassandrze
W Kubernetes możemy używać PersistentVolume do przechowywania danych. Dzięki sprawdzonym mechanizmom, praca z danymi w Kubernetes staje się z każdym rokiem coraz prostsza.

Każdemu podowi z Cassandrą przydzielimy własny PersistentVolume.
Warto zauważyć, że Cassandra sama w sobie zakłada replikację danych, oferując w tym celu wbudowane mechanizmy. Dlatego, jeśli tworzysz klaster Cassandry z dużą liczbą węzłów, nie ma potrzeby korzystania z rozproszonych systemów przechowywania danych, takich jak Ceph czy GlusterFS. W takim przypadku logiczne będzie przechowywanie danych na dysku węzła za pomocą lub montowania hostPath.
Inna kwestia, jeśli chcesz tworzyć osobne środowisko dla każdego feature branch dla deweloperów. W takim przypadku właściwym podejściem będzie uruchomienie jednego węzła Cassandry i przechowywanie danych w rozproszonym magazynie, tj. wspomniane Ceph i GlusterFS będą Twoją opcją. Wówczas deweloper będzie pewny, że nie straci danych testowych, nawet w przypadku utraty jednego z węzłów klastra Kubernetesa.
2. Monitorowanie
Praktycznie bezalternatywnym wyborem dla realizacji monitorowania w Kubernetesie jest Prometheus (szczegółowo opisaliśmy to w ). Jak wygląda współpraca Cassandry z eksporterami metryk dla Prometheusa? I, co w niektórych przypadkach może być ważniejsze, z odpowiednimi dashboardami dla Grafany?

Przykład wyglądu wykresów w Grafanie dla Cassandry
Jest tylko dwóch eksporterów: i .
Wybraliśmy pierwszy, ponieważ:
- JMX Exporter rozwija się i ewoluuje, podczas gdy Cassandra Exporter nie uzyskał odpowiedniego wsparcia społeczności. Cassandra Exporter wciąż nie wspiera większości wersji Cassandry.
- Można go uruchomić jako javaagent, dodając flagę
-javaagent:/cassandra-exporter.jar=--listen=:9180. - Dla niego istnieje , który nie jest kompatybilny z Cassandra Exporter.
3. Wybór prymitywów Kubernetes
Zgodnie z powyżej przedstawioną strukturą klastra Cassandry spróbujmy przetłumaczyć wszystko, co tam opisano, na terminologię Kubernetes:
- Cassandra Node → Pod
- Cassandra Rack → StatefulSet
- Cassandra Datacenter → pula StatefulSets
- Cassandra Cluster → ???
Okazuje się, że brakuje jakiegoś dodatkowego bytu, aby zarządzać całym klastrem Cassandry jednocześnie. Ale jeśli czegoś brakuje, możemy to stworzyć! W Kubernetesie do tego służy mechanizm definiowania własnych zasobów — .

Ogłoszenie dodatkowych zasobów dla logów i powiadomień
Ale sam w sobie Custom Resource nic nie znaczy: potrzebny jest dla niego kontroler. Możliwe, że będziemy musieli skorzystać z pomocy …
4. Identyfikacja podów
W punkcie powyżej uzgodniliśmy, że jeden węzeł Cassandra będzie odpowiadał jednemu podowi w Kubernetes. Jednak adresy IP podów za każdym razem będą różne. Identyfikacja węzła w Cassandra odbywa się właśnie na podstawie adresu IP... Oznacza to, że po każdym usunięciu podu klaster Cassandra doda nowy węzeł.
Są różne rozwiązania:
- Możemy prowadzić ewidencję według identyfikatorów hostów (UUID, które jednoznacznie identyfikują instancje Cassandra) albo według adresów IP, zachowując to wszystko w odpowiednich strukturach/tabelach. Metoda ta ma dwie główne wady:
- Ryzyko wystąpienia warunku wyścigu podczas awarii dwóch węzłów jednocześnie. Po wznowieniu oba węzły Cassandra zaczynają jednocześnie żądać adresu IP z tabeli i konkurować o ten sam zasób.
- Jeżeli węzeł Cassandra utraci swoje dane, nie będziemy mogli go zidentyfikować.
- Drugie rozwiązanie wydaje się prostym hackiem, jednak można je zastosować: możemy tworzyć Service z ClusterIP dla każdego węzła Cassandra. Problemy tej implementacji:
- Jeśli w klastrze Cassandra jest zbyt wiele węzłów, będziemy musieli stworzyć wiele Service’ów.
- Możliwość ClusterIP jest realizowana przez iptables. Może to stać się problemem, jeśli w klastrze Cassandra jest dużo (1000… a może nawet 100?) węzłów. Chociaż może rozwiązać ten problem.
- Trzecie rozwiązanie - wykorzystanie sieci węzłów Cassandra zamiast dedykowanej sieci podów poprzez włączenie ustawienia
hostNetwork: true. Ta metoda nakłada pewne ograniczenia:- Na zastępowanie węzłów. Nowy węzeł musi mieć ten sam adres IP co poprzedni (w chmurach takich jak AWS, GCP jest to praktycznie niemożliwe);
- Korzyując z sieci węzłów klastra, zaczynamy konkurować o zasoby sieciowe. W związku z tym umieszczenie na jednym węźle klastra więcej niż jednego podu Cassandra będzie problematyczne.
5. Kopie zapasowe
Chcemy regularnie zachowywać pełną wersję danych jednego węzła Cassandra. Kubernetes oferuje wygodną możliwość użycia , ale sama Cassandra wprowadza pewne ograniczenia.
Przypomnę, że część danych Cassandra przechowuje w pamięci. Aby wykonać pełną kopię zapasową, należy przenieść dane z pamięci (Memtables) na dysk (SSTables). W tym momencie węzeł Cassandra przestaje przyjmować połączenia, całkowicie wyłączając się z pracy klastra.
Następnie wykonuje się kopię zapasową (snapshot) i zachowuje schemat (keyspace). I tutaj okazuje się, że sama kopia zapasowa nam nic nie daje: musimy zachować identyfikatory danych, za które odpowiadał węzeł Cassandra — to są specjalne tokeny.

Rozdzielenie tokenów do identyfikacji, za jakie dane odpowiadają węzły Cassandra
Przykład skryptu do wykonania kopii zapasowej Cassandra od Google w Kubernetes można znaleźć pod . Jedyny moment, którego skrypt nie uwzględnia, to reset danych na węźle przed wykonaniem snapshotu. Oznacza to, że kopia zapasowa jest wykonywana nie dla bieżącego stanu, ale stanu sprzed chwili. Jednak to pozwala nie wyłączać węzła z pracy, co wydaje się bardzo logiczne.
set -eu
if [[ -z "$1" ]]; then
info "Proszę podać keyspace"
exit 1
fi
KEYSPACE="$1"
result=$(nodetool snapshot "${KEYSPACE}")
if [[ $? -ne 0 ]]; then
echo "Błąd podczas tworzenia snapshotu"
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 wykonania kopii zapasowej z jednego węzła Cassandra
Gotowe rozwiązania dla Cassandra w Kubernetes
Czego w ogóle używa się teraz do wdrożenia Cassandra w Kubernetes i co z tego najbardziej odpowiada zadanym wymaganiom?
1. Rozwiązania oparte na StatefulSet lub wykresach Helm
Użycie podstawowych funkcji StatefulSets do uruchomienia klastra Cassandra to dobry wybór. Przy użyciu wykresu Helm i szablonów Go można dostarczyć użytkownikowi elastyczny interfejs do wdrożenia Cassandra.
Zazwyczaj działa to dobrze… dopóki coś nie pójdzie nie tak — na przykład awaria węzła. Standardowe narzędzia Kubernetes po prostu nie mogą uwzględnić wszystkich wyżej opisanych aspektów. Ponadto, to podejście jest bardzo ograniczone w tym, jak może być rozwijane dla bardziej skomplikowanego użycia: wymiany węzłów, tworzenia kopii zapasowych, przywracania, monitorowania itp.
Reprezentanci:
- ;
- .
Oba wykresy są równie dobre, ale są podatne na opisane powyżej problemy.
2. Rozwiązania oparte na operatorze Kubernetes
Takie opcje są bardziej interesujące, ponieważ oferują szerokie możliwości zarządzania klastrem. Dla zaprojektowania operatora Cassandra, jak i każdej innej bazy danych, dobry wzór wygląda jak Sidecar Controller CRD:

Schemat zarządzania węzłami w dobrze zaprojektowanym operatorze Cassandra
Rozważmy istniejące operatory.
1. Cassandra-operator od instaclustr
- Gotowość: Alpha
- Licencja: Apache 2.0
- Zrealizowany w: Java
To naprawdę obiecujący i aktywnie rozwijający się projekt firmy, która oferuje zarządzane wdrożenia Cassandra. Jak opisano powyżej, wykorzystuje sidecar-kontener, który przyjmuje polecenia za pośrednictwem HTTP. Napisany w Javie, dlatego czasami brakuje mu bardziej zaawansowanej funkcjonalności biblioteki client-go. Operator nie obsługuje również różnych Racków dla jednego Data Center.
Zalety operatora to wsparcie monitoringu, zaawansowane zarządzanie klastrem za pomocą CRD oraz dokumentacja dotycząca tworzenia kopii zapasowych.
2. Navigator od Jetstack
- Gotowość: Alpha
- Licencja: Apache 2.0
- Zrealizowany w: Golang
Operator zaprojektowany do wdrażania DB-as-a-Service. Na chwilę obecną obsługuje dwie bazy danych: Elasticsearch i Cassandra. Posiada ciekawe rozwiązania, takie jak kontrola dostępu do bazy danych przez RBAC (do tego uruchamiany jest oddzielny navigator-apiserver). Interesujący projekt, któremu warto się przyjrzeć, chociaż ostatni commit został wykonany półtora roku temu, co wyraźnie obniża jego potencjał.
3. Cassandra-operator od vgkowski
- Gotowość: Alpha
- Licencja: Apache 2.0
- Zrealizowany w: Golang
Nie zdecydowano się na jego poważne rozważenie, ponieważ ostatni commit w repozytorium miał miejsce ponad rok temu. Rozwój operatora został porzucony: ostatnia wersja Kubernetes, która była zgłaszana jako wspierana, to 1.9.
4. Cassandra-operator od Rook
- Gotowość: Alpha
- Licencja: Apache 2.0
- Zrealizowany w: Golang
Operator, którego rozwój nie postępuje tak szybko, jakbyśmy tego chcieli. Posiada przemyślaną strukturę CRD do zarządzania klastrem, rozwiązuje problem identyfikacji węzłów za pomocą Service z ClusterIP (ten słynny 'hack')… ale na razie to wszystko. Obecnie nie ma monitoringu ani kopii zapasowych (przy okazji, zajęliśmy się monitoringiem sami). ). Interesujący fakt, że przy użyciu tego operatora można również uruchomić ScyllaDB.
NB: Użyliśmy tego operatora z drobnymi poprawkami w jednym z naszych projektów. Nie zauważono żadnych problemów z działaniem operatora podczas całego okresu eksploatacji (~4 miesiące).
5. CassKop od Orange
- Gotowość: Alpha
- Licencja: Apache 2.0
- Zrealizowany w: Golang
Najmłodszy operator na liście: pierwszy commit został wykonany 23 maja 2019 roku. Już teraz ma w swoim arsenale dużą ilość funkcji z naszej listy, z którymi można się zapoznać w repozytorium projektu. Operator oparty na popularnym operator-sdk. Obsługuje monitoring 'z pudełka'. Główną różnicą w porównaniu do innych operatorów jest wykorzystanie , zaimplementowanej w Pythonie i używanej do komunikacji między węzłami Cassandry.
Wnioski
Liczba podejść i możliwych sposobów przeniesienia Cassandry do Kubernetes sama mówi za siebie: temat jest poszukiwany.
Na tym etapie można spróbować czegoś z powyższego na własne ryzyko: żaden z deweloperów nie gwarantuje 100% działania swojego rozwiązania w środowisku produkcyjnym. Jednak już teraz wiele produktów wygląda obiecująco, aby spróbować wykorzystać je w środowiskach deweloperskich.
Myślę, że w przyszłości ta kobieta na statku będzie na miejscu!
P.S.
Przeczytaj także na naszym blogu:
- «»;
- «»;
- «»;
- «».
Źródło: habr.com
