Migrering af Cassandra til Kubernetes: funktioner og løsninger
Vi støder jævnligt på Apache Cassandra-databasen og behovet for at betjene den i en Kubernetes-baseret infrastruktur. I dette materiale vil vi dele vores vision om de nødvendige trin, kriterier og eksisterende løsninger (herunder en oversigt over operatører) til at migrere Cassandra til K8s.
"Den, der kan regere en kvinde, kan også regere staten"
Hvem er Cassandra? Det er et distribueret lagringssystem designet til at håndtere store mængder data og samtidig sikre høj tilgængelighed uden et enkelt fejlpunkt. Projektet behøver næppe en lang introduktion, så jeg vil kun give de vigtigste træk ved Cassandra, som vil være relevante i forbindelse med en specifik artikel:
Cassandra er skrevet på Java.
Cassandra-topologien omfatter flere niveauer:
Node - en installeret Cassandra-instans;
Rack er en gruppe af Cassandra-forekomster, forenet af nogle karakteristika, placeret i det samme datacenter;
Datacenter - en samling af alle grupper af Cassandra-instanser placeret i ét datacenter;
Cluster er en samling af alle datacentre.
Cassandra bruger en IP-adresse til at identificere en node.
For at fremskynde skrive- og læseoperationer gemmer Cassandra nogle af dataene i RAM.
Nu - til den faktiske potentielle flytning til Kubernetes.
Tjekliste for overførsel
Når vi taler om migreringen af Cassandra til Kubernetes, håber vi, at det med flytningen bliver mere bekvemt at administrere. Hvad vil der kræves til dette, hvad vil hjælpe med dette?
1. Datalagring
Som det allerede er blevet afklaret, gemmer Cassanda en del af dataene i RAM - in Huskes. Men der er en anden del af dataene, der gemmes på disken - i formen SSTable. En enhed føjes til disse data Commit Log — registreringer af alle transaktioner, som også gemmes på disken.
Skriv transaktionsdiagram i Cassandra
I Kubernetes kan vi bruge PersistentVolume til at gemme data. Takket være gennemprøvede mekanismer bliver arbejdet med data i Kubernetes lettere hvert år.
Vi vil allokere vores egen PersistentVolume til hver Cassandra pod
Det er vigtigt at bemærke, at Cassandra selv indebærer datareplikering og tilbyder indbyggede mekanismer til dette. Derfor, hvis du bygger en Cassandra-klynge fra et stort antal noder, er der ingen grund til at bruge distribuerede systemer som Ceph eller GlusterFS til datalagring. I dette tilfælde ville det være logisk at gemme data på værtsdisken vha lokale persistente diske eller montering hostPath.
Et andet spørgsmål er, om du vil oprette et separat miljø for udviklere for hver funktionsgren. I dette tilfælde ville den korrekte tilgang være at rejse én Cassandra-node og gemme dataene i et distribueret lager, dvs. de nævnte Ceph og GlusterFS vil være dine muligheder. Så vil udvikleren være sikker på, at han ikke vil miste testdata, selvom en af Kuberntes-klyndeknuderne går tabt.
2. Overvågning
Det praktisk talt ubestridte valg til implementering af overvågning i Kubernetes er Prometheus (vi talte om dette i detaljer i relateret rapport). Hvordan har Cassandra det med metrik-eksportører til Prometheus? Og hvad er endnu vigtigere, med matchende dashboards til Grafana?
Et eksempel på udseendet af grafer i Grafana for Cassandra
JMX Exporter vokser og udvikler sig, mens Cassandra Exporter ikke har været i stand til at få tilstrækkelig fællesskabsstøtte. Cassandra Exporter understøtter stadig ikke de fleste versioner af Cassandra.
Du kan køre det som en javaagent ved at tilføje et flag -javaagent:<plugin-dir-name>/cassandra-exporter.jar=--listen=:9180.
Der er en til ham passende dashboard, som er inkompatibelt med Cassandra Exporter.
3. Valg af Kubernetes primitiver
I henhold til ovenstående struktur af Cassandra-klyngen, lad os prøve at oversætte alt, hvad der er beskrevet der, til Kubernetes-terminologi:
Cassandra Node → Pod
Cassandra Rack → StatefulSet
Cassandra Datacenter → pool fra StatefulSets
Cassandra Cluster → ???
Det viser sig, at der mangler en ekstra enhed til at administrere hele Cassandra-klyngen på én gang. Men hvis noget ikke eksisterer, kan vi skabe det! Kubernetes har en mekanisme til at definere sine egne ressourcer til dette formål - Brugerdefinerede ressourcedefinitioner.
Erklærer yderligere ressourcer til logfiler og advarsler
Men Custom Resource i sig selv betyder ikke noget: det kræver trods alt controller. Du skal muligvis søge hjælp Kubernetes operatør...
4. Identifikation af bælg
I afsnittet ovenfor blev vi enige om, at én Cassandra-node vil svare til én pod i Kubernetes. Men podernes IP-adresser vil være forskellige hver gang. Og identifikationen af en node i Cassandra er baseret på IP-adressen... Det viser sig, at efter hver fjernelse af en pod, vil Cassandra-klyngen tilføje en ny node.
Der er en vej ud og ikke kun én:
Vi kan føre optegnelser efter værtsidentifikatorer (UUID'er, der unikt identificerer Cassandra-forekomster) eller efter IP-adresser og gemme det hele i nogle strukturer/tabeller. Metoden har to hovedulemper:
Risikoen for, at en racetilstand opstår, hvis to noder falder på én gang. Efter stigningen vil Cassandra noder samtidig anmode om en IP-adresse fra bordet og konkurrere om den samme ressource.
Hvis en Cassandra-node har mistet sine data, vil vi ikke længere være i stand til at identificere dem.
Den anden løsning virker som et lille hack, men ikke desto mindre: vi kan oprette en tjeneste med ClusterIP for hver Cassandra-node. Problemer med denne implementering:
Hvis der er mange noder i en Cassandra-klynge, bliver vi nødt til at oprette en masse tjenester.
ClusterIP-funktionen implementeres via iptables. Dette kan blive et problem, hvis Cassandra-klyngen har mange (1000... eller endda 100?) noder. Selvom afbalancering baseret på IPVS kan løse dette problem.
Den tredje løsning er at bruge et netværk af noder til Cassandra noder i stedet for et dedikeret netværk af pods ved at aktivere indstillingen hostNetwork: true. Denne metode pålægger visse begrænsninger:
For at udskifte enheder. Det er nødvendigt, at den nye node skal have samme IP-adresse som den forrige (i skyer som AWS, GCP er dette næsten umuligt at gøre);
Ved at bruge et netværk af klynge noder begynder vi at konkurrere om netværksressourcer. Derfor vil det være problematisk at placere mere end én pod med Cassandra på én klyngenode.
5. Sikkerhedskopier
Vi ønsker at gemme en fuld version af en enkelt Cassandra-nodes data på en tidsplan. Kubernetes giver en praktisk funktion ved hjælp af CronJob, men her sætter Cassandra selv en eger i vores hjul.
Lad mig minde dig om, at Cassandra gemmer nogle af dataene i hukommelsen. For at lave en fuld sikkerhedskopi skal du bruge data fra hukommelsen (Memtables) flyt til disk (SSTables). På dette tidspunkt holder Cassandra-knuden op med at acceptere forbindelser og lukker fuldstændigt ned fra klyngen.
Herefter fjernes sikkerhedskopien (snapshot) og skemaet er gemt (tasterum). Og så viser det sig, at bare en sikkerhedskopi ikke giver os noget: vi skal gemme de dataidentifikatorer, som Cassandra-knuden var ansvarlig for - disse er specielle tokens.
Distribution af tokens for at identificere, hvilke data Cassandra noder er ansvarlige for
Et eksempel på script til at tage en Cassandra backup fra Google i Kubernetes kan findes på dette link. Det eneste punkt, som scriptet ikke tager højde for, er at nulstille data til noden, før det tages snapshot. Det vil sige, at sikkerhedskopieringen ikke udføres for den aktuelle tilstand, men for en tilstand lidt tidligere. Men dette hjælper med ikke at tage noden ud af drift, hvilket virker meget logisk.
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}"
Et eksempel på et bash-script til at tage en sikkerhedskopi fra en Cassandra-node
Klare løsninger til Cassandra i Kubernetes
Hvad bruges i øjeblikket til at implementere Cassandra i Kubernetes, og hvilken af disse passer bedst til de givne krav?
1. Løsninger baseret på StatefulSet- eller Helm-diagrammer
Brug af de grundlæggende StatefulSets-funktioner til at køre en Cassandra-klynge er en god mulighed. Ved at bruge Helm-diagrammet og Go-skabelonerne kan du give brugeren en fleksibel grænseflade til at implementere Cassandra.
Dette fungerer normalt fint... indtil der sker noget uventet, såsom en knudefejl. Standard Kubernetes-værktøjer kan simpelthen ikke tage højde for alle de funktioner, der er beskrevet ovenfor. Derudover er denne tilgang meget begrænset med hensyn til, hvor meget den kan udvides til mere komplekse anvendelser: nodeudskiftning, backup, gendannelse, overvågning osv.
Begge diagrammer er lige gode, men er underlagt de ovenfor beskrevne problemer.
2. Løsninger baseret på Kubernetes Operator
Sådanne muligheder er mere interessante, fordi de giver rigelige muligheder for at styre klyngen. For at designe en Cassandra-operatør, som enhver anden database, ser et godt mønster ud som Sidecar <-> Controller <-> CRD:
Nodestyringsskema i en veldesignet Cassandra-operatør
Dette er virkelig et meget lovende og aktivt udviklende projekt fra en virksomhed, der tilbyder administrerede Cassandra-implementeringer. Den bruger, som beskrevet ovenfor, en sidevognsbeholder, der accepterer kommandoer via HTTP. Skrevet i Java, mangler det nogle gange den mere avancerede funktionalitet i client-go-biblioteket. Operatøren understøtter heller ikke forskellige racks til ét datacenter.
Men operatøren har sådanne fordele som støtte til overvågning, klyngestyring på højt niveau ved hjælp af CRD og endda dokumentation til at lave sikkerhedskopier.
En erklæring designet til at implementere DB-as-a-Service. Understøtter i øjeblikket to databaser: Elasticsearch og Cassandra. Den har så interessante løsninger som databaseadgangskontrol via RBAC (til dette har den sin egen separate navigator-apiserver). Et interessant projekt, der ville være værd at se nærmere på, men den sidste commit blev lavet for halvandet år siden, hvilket klart reducerer potentialet.
De betragtede det ikke "seriøst", da den sidste commit til depotet var for mere end et år siden. Operatørudvikling er opgivet: den seneste version af Kubernetes rapporteret som understøttet er 1.9.
En operatør, hvis udvikling ikke går så hurtigt, som vi gerne ville. Det har en gennemtænkt CRD-struktur til klyngestyring, løser problemet med at identificere noder ved hjælp af Service med ClusterIP (det samme "hack")... men det var alt for nu. Der er i øjeblikket ingen overvågning eller sikkerhedskopier ud af boksen (i øvrigt er vi til overvågning tog det selv). Et interessant punkt er, at du også kan implementere ScyllaDB ved hjælp af denne operatør.
NB: Vi brugte denne operatør med mindre ændringer i et af vores projekter. Der blev ikke bemærket problemer i operatørens arbejde i hele driftsperioden (~4 måneders drift).
Den yngste operatør på listen: den første commit blev foretaget den 23. maj 2019. Allerede nu har det i sit arsenal et stort antal funktioner fra vores liste, hvoraf flere detaljer kan findes i projektarkivet. Operatøren er bygget på basis af det populære operatør-sdk. Understøtter overvågning ud af æsken. Den største forskel fra andre operatører er brugen CassKop plugin, implementeret i Python og brugt til kommunikation mellem Cassandra noder.
Fund
Antallet af tilgange og mulige muligheder for at overføre Cassandra til Kubernetes taler for sig selv: emnet er efterspurgt.
På dette stadium kan du prøve noget af ovenstående på egen risiko og risiko: ingen af udviklerne garanterer 100 % drift af deres løsning i et produktionsmiljø. Men allerede nu ser mange produkter lovende ud at prøve at bruge i udviklingsbænke.
Jeg tror, at denne kvinde på skibet i fremtiden vil komme til nytte!