
Con il database Apache Cassandra e la necessità di gestirlo all'interno di un'infrastruttura basata su Kubernetes, ci confrontiamo regolarmente. In questo articolo condivideremo la nostra visione dei passi necessari, dei criteri e delle soluzioni esistenti (inclusa una panoramica degli operatori) per la migrazione di Cassandra in K8s.
«Chi può gestire una donna, può gestire anche uno stato»
Chi è Cassandra? È un sistema distribuito di archiviazione, progettato per gestire grandi volumi di dati, garantendo al contempo alta disponibilità senza un singolo punto di guasto. Il progetto non ha bisogno di lunghe presentazioni, quindi menzionerò solo le caratteristiche principali di Cassandra che saranno rilevanti per questo articolo:
- Cassandra è scritta in Java.
- La topologia di Cassandra include diversi livelli:
- Node — un'istanza distribuita di Cassandra;
- Rack — un gruppo di istanze di Cassandra, unite da qualche criterio, situate nello stesso data center;
- Datacenter — l'insieme di tutti i gruppi di istanze di Cassandra che si trovano in un singolo data center;
- Cluster — l'insieme di tutti i data center.
- Per identificare un nodo, Cassandra utilizza un indirizzo IP.
- Per velocizzare le operazioni di scrittura e lettura, parte dei dati di Cassandra viene memorizzata in memoria volatile.
Ora passiamo al potenziale trasferimento in Kubernetes.
Checklist per il trasferimento
Parlando della migrazione di Cassandra a Kubernetes, speriamo che trasferirei la gestione diventi più semplice. Cosa serve per questo e cosa può aiutare?
1. Storage per i dati
Come già indicato, parte dei dati di Cassandra viene memorizzata in memoria volatile — in Memtable. Ma c'è anche un'altra parte dei dati che viene salvata su disco, sotto forma di SSTable. A questi dati si aggiunge l'entità Commit Log — registrazioni di tutte le transazioni, che vengono anch'esse salvate su disco.

Schema delle transazioni di scrittura in Cassandra
In Kubernetes possiamo utilizzare PersistentVolume per la memorizzazione dei dati. Grazie a meccanismi collaudati, lavorare con i dati in Kubernetes diventa ogni anno più semplice.

Ad ogni pod di Cassandra assegneremo il proprio PersistentVolume.
È importante notare che Cassandra implica già la replica dei dati, fornendo meccanismi integrati per questo. Pertanto, se stai costruendo un cluster Cassandra con un gran numero di nodi, non è necessario utilizzare sistemi distribuiti come Ceph o GlusterFS per l'archiviazione dei dati. In questo caso, è logico memorizzare i dati sul disco del nodo tramite o montaggio hostPath.
Un'altra questione è se desideri creare un ambiente separato per gli sviluppatori per ogni ramo della feature. In tal caso, l'approccio corretto sarebbe avviare un nodo Cassandra e memorizzare i dati in uno storage distribuito, quindi Ceph e GlusterFS diventerebbero una tua opzione. In questo modo, lo sviluppatore sarà certo di non perdere i dati di test anche in caso di perdita di uno dei nodi del cluster Kubernetes.
2. Monitoraggio
Una scelta praticamente senza alternative per implementare il monitoraggio in Kubernetes è Prometheus (di cui abbiamo parlato in dettaglio nel ). Come se la cava Cassandra con gli exporter di metriche per Prometheus? E, cosa forse ancora più importante, con i dashboard adeguati per Grafana?

Esempio dell'aspetto dei grafici in Grafana per Cassandra
Ci sono solo due esportatori: e .
Abbiamo scelto il primo perché:
- JMX Exporter è in crescita e si sta sviluppando, mentre Cassandra Exporter non ha ricevuto il giusto supporto dalla comunità. Cassandra Exporter non supporta ancora la maggior parte delle versioni di Cassandra.
- Può essere avviato come javaagent aggiungendo il flag
-javaagent:/cassandra-exporter.jar=--listen=:9180. - Per esso c'è un , che non è compatibile con Cassandra Exporter.
3. Scelta dei primitivi Kubernetes
Secondo la struttura del cluster Cassandra descritta sopra, proviamo a tradurre tutto ciò che è descritto nella terminologia di Kubernetes:
- Cassandra Node → Pod
- Cassandra Rack → StatefulSet
- Cassandra Datacenter → un pool di StatefulSet
- Cassandra Cluster → ???
Sembra che manchi qualche entità aggiuntiva per gestire l'intero cluster Cassandra contemporaneamente. Ma se qualcosa manca, possiamo crearlo! In Kubernetes, esiste un meccanismo per definire risorse personalizzate — .

Dichiarazione di risorse aggiuntive per log e notifiche
Ma una Custom Resource di per sé non significa nulla: ha bisogno di controllore. Potrebbe essere necessario ricorrere all’aiuto di …
4. Identificazione dei pod
Nel punto precedente abbiamo concordato che un nodo Cassandra corrisponde a un pod in Kubernetes. Tuttavia, gli indirizzi IP dei pod saranno sempre diversi. L'identificazione del nodo in Cassandra avviene proprio sulla base dell'indirizzo IP… Quindi, dopo ogni eliminazione di un pod, il cluster Cassandra aggiungerà un nuovo nodo.
Ci sono soluzioni, e anche più di una:
- Possiamo tenere traccia attraverso identificatori univoci dei dispositivi (UUID) che identificano in modo univoco le istanze di Cassandra, oppure tramite indirizzi IP, memorizzando tutto in strutture/ Tabelle. Il metodo presenta due principali svantaggi:
- Rischio di condizione di competizione in caso di guasto di due nodi contemporaneamente. Dopo il ripristino, i nodi Cassandra richiederanno simultaneamente un indirizzo IP dalla tabella, concorrendo per la stessa risorsa.
- Se un nodo Cassandra perde i propri dati, non saremo più in grado di identificarlo.
- La seconda soluzione sembra un piccolo hack, ma comunque: possiamo creare un servizio con ClusterIP per ogni nodo Cassandra. I problemi di questa implementazione:
- Se nel cluster Cassandra ci sono molti nodi, dovremo creare un numero molto elevato di servizi.
- La possibilità di ClusterIP è realizzata tramite iptables. Questo può diventare un problema se nel cluster Cassandra ci sono molti (1000… o anche 100?) nodi. Anche se può risolvere questo problema.
- La terza soluzione è utilizzare la rete dei nodi per i nodi Cassandra invece di una rete dedicata ai pod attivando l'impostazione
hostNetwork: true. Questo metodo comporta alcune limitazioni:- Sostituzione dei nodi. È necessario che un nuovo nodo abbia necessariamente lo stesso indirizzo IP del precedente (nelle nuvole come AWS, GCP, questo è praticamente impossibile);
- Utilizzando la rete dei nodi del cluster, iniziamo a competere per le risorse di rete. Di conseguenza, posizionare più di un pod con Cassandra su un singolo nodo del cluster sarà problematico.
5. Backup
Vogliamo salvare una versione completa dei dati di un nodo Cassandra secondo un programma. Kubernetes fornisce una comoda opportunità utilizzando , ma qui Cassandra stessa ci crea problemi.
Ricordo che parte dei dati di Cassandra è archiviata in memoria. Per effettuare un backup completo, è necessario trasferire i dati dalla memoria (Memtables) su disco (SSTables). In questo momento il nodo Cassandra smette di accettare connessioni, disattivandosi completamente dal lavoro del cluster.
Dopo di ciò, viene eseguita una copia di backup (snapshot) e viene salvato lo schema (chiave spazio). E qui si scopre che semplicemente avere un backup non ci aiuta: è necessario salvare gli identificatori dei dati per cui era responsabile il nodo Cassandra, ovvero token speciali.

Distribuzione dei token per identificare a quali dati sono responsabili i nodi Cassandra
Un esempio di script per effettuare un backup di Cassandra da Google in Kubernetes può essere trovato su . L'unico punto che lo script non considera è il ripristino dei dati nel nodo prima di eseguire lo snapshot. Quindi, il backup viene eseguito non per lo stato attuale, ma per uno stato precedente. Ma questo aiuta a non disattivare il nodo, il che appare molto logico.
set -eu
if [[ -z "$1" ]]; then
info "Si prega di fornire una chiave spazio"
exit 1
fi
KEYSPACE="$1"
result=$(nodetool snapshot "${KEYSPACE}")
if [[ $? -ne 0 ]]; then
echo "Errore durante la creazione dello snapshot"
exit 1
fi
timestamp=$(echo "$result" | awk '/Directory di snapshot: / { 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}"Esempio di script bash per effettuare un backup da un nodo Cassandra
Soluzioni pronte per Cassandra in Kubernetes
Cosa si utilizza attualmente per implementare Cassandra in Kubernetes e quale di queste opzioni è più adatta ai requisiti specificati?
1. Soluzioni basate su StatefulSet o chart Helm
Utilizzare le funzionalità di base di StatefulSets per avviare un cluster Cassandra è una buona opzione. Con un chart Helm e i modelli Go, è possibile fornire all'utente un'interfaccia flessibile per l'implementazione di Cassandra.
Di solito funziona bene... finché non accade qualcosa di inaspettato, come il guasto di un nodo. Gli strumenti standard di Kubernetes non possono tenere conto di tutte le peculiarità sopra descritte. Inoltre, questo approccio è molto limitato nella misura in cui può essere esteso per usi più complessi: sostituzione dei nodi, backup, ripristino, monitoraggio, ecc.
Rappresentanti:
- ;
- .
Entrambi i chart sono validi, ma sono soggetti ai problemi descritti in precedenza.
2. Soluzioni basate su Kubernetes Operator
Queste opzioni sono più interessanti perché offrono ampie possibilità di gestione del cluster. Per progettare un operatore Cassandra, come per qualsiasi altro database, un buon modello appare come Sidecar Controller CRD:

Schema di gestione dei nodi in un operatore Cassandra ben progettato
Consideriamo gli operatori esistenti.
1. Cassandra-operator di instaclustr
- Pronto: Alpha
- Licenza: Apache 2.0
- Realizzato in: Java
Questo è un progetto davvero promettente e in rapida espansione da parte di un'azienda che offre distribuzioni gestite di Cassandra. Come descritto sopra, utilizza un contenitore sidecar che riceve comandi tramite HTTP. È scritto in Java, quindi a volte manca di funzionalità più avanzate della libreria client-go. Inoltre, l'operatore non supporta diversi rack per un centro dati.
Tuttavia, l'operatore ha vantaggi come il supporto per il monitoraggio, la gestione del cluster a livello elevato tramite CRD e persino documentazione per la creazione di backup.
2. Navigator di Jetstack
- Pronto: Alpha
- Licenza: Apache 2.0
- Realizzato in: Golang
Operatore progettato per il deployment di DB-as-a-Service. Attualmente supporta due database: Elasticsearch e Cassandra. Include soluzioni interessanti, come il controllo degli accessi al database tramite RBAC (per questo viene sollevato un proprio navigator-apiserver separato). Un progetto interessante da tenere d'occhio, anche se l'ultimo commit risale a un anno e mezzo fa, il che ne riduce chiaramente il potenziale.
3. Cassandra-operator di vgkowski
- Pronto: Alpha
- Licenza: Apache 2.0
- Realizzato in: Golang
Non è stato preso in considerazione «seriamente», poiché l'ultimo commit nel repository risale a più di un anno fa. Lo sviluppo dell'operatore è stato abbandonato: l'ultima versione di Kubernetes dichiarata come supportata è 1.9.
4. Cassandra-operator di Rook
- Pronto: Alpha
- Licenza: Apache 2.0
- Realizzato in: Golang
Operatore il cui sviluppo non procede con la rapidità desiderata. Ha una struttura CRD ben progettata per la gestione del cluster e risolve il problema dell'identificazione dei nodi usando Service con ClusterIP (quel «hack»), ma per ora è tutto. Attualmente mancano monitoraggio e backup di default (a proposito, per il monitoraggio ci ). È interessante notare che con questo operatore è possibile anche deployare ScyllaDB.
NB: Questo operatore, con alcune modifiche, è stato utilizzato in uno dei nostri progetti. Non sono stati riscontrati problemi nel funzionamento dell'operatore durante il periodo di utilizzo (~4 mesi di attività).
5. CassKop di Orange
- Pronto: Alpha
- Licenza: Apache 2.0
- Realizzato in: Golang
Il più giovane operatore della lista: il primo commit è stato effettuato il 23 maggio 2019. Attualmente ha già a disposizione un gran numero di funzionalità dalla nostra lista, che possono essere consultate nel repository del progetto. L'operatore è costruito sulla base del popolare operator-sdk. Supporta il monitoraggio "out of the box". La principale differenza rispetto agli altri operatori è l'utilizzo , implementato in Python e utilizzato per la comunicazione tra i nodi di Cassandra.
Conclusioni
Il numero di approcci e possibili varianti per migrare Cassandra su Kubernetes parla chiaro: il tema è molto richiesto.
In questa fase, provare quanto descritto sopra è a proprio rischio e pericolo: nessuno degli sviluppatori garantisce il 100% di funzionamento della propria soluzione in un ambiente di produzione. Ma già ora molti prodotti sembrano promettenti per essere provati in ambienti di sviluppo.
Penso che in futuro questa donna a bordo sarà utile!
P.S.
Leggete anche nel nostro blog:
- «»;
- «»;
- «»;
- «».
Fonte: habr.com
