
С базата данни Apache Cassandra и необходимостта от нейната експлоатация в инфраструктура, базирана на Kubernetes, се сблъскваме редовно. В този материал ще споделим нашето виждане за необходимите стъпки, критерии и съществуващи решения (включващи преглед на операторите) за мигриране на Cassandra в K8s.
„Който може да управлява жена, ще се справи и с държавата“
Коя е Cassandra? Това е разпределена система за съхранение, предназначена за управление на големи обеми данни, като същевременно осигурява висока наличност без единична точка на отказ. Проектът вероятно не се нуждае от дълго представяне, затова ще изброя само основните характеристики на Cassandra, които ще бъдат актуални в контекста на конкретната статия:
- Cassandra е написана на Java.
- Топологията на Cassandra включва няколко нива:
- Node — един развернат екземпляр на Cassandra;
- Rack — група екземпляри на Cassandra, обединени по някакъв признак, които се намират в един и същ дата център;
- Datacenter — съвкупност от всички групи екземпляри на Cassandra, намиращи се в един дата център;
- Cluster — съвкупност от всички дата центрове.
- За идентификация на възел, Cassandra използва IP адрес.
- За бързината на операциите по запис и четене, част от данните Cassandra съхранява в оперативната памет.
Сега — към самия потенциален преход в Kubernetes.
Check-list за преместване
Като говорим за миграцията на Cassandra в Kubernetes, се надяваме, че с преместването ще управляването ѝ стане по-удобно. Какво е необходимо за това, какво ще помогне?
1. Хранилище за данни
Както бе уточнено, част от данните на Cassandra се съхраняват в оперативната памет — в Memtable. Но има и друга част от данните, която се записва на диск, — под формата на SSTable. Към тези данни се добавя сущността Commit Log — записи за всички транзакции, които също се запазват на диск.

Схема на транзакциите при запис в Cassandra
В Kubernetes можем да използваме PersistentVolume за съхранение на данни. Благодарение на отработените механизми, работата с данни в Kubernetes става все по-лесна с всяка изминала година.

На всеки pod с Cassandra ще отделим свой PersistentVolume
Важно е да се изтъкне, че Cassandra сама по себе си предвижда репликация на данни, предлагайки вградени механизми за това. Следователно, ако изграждате Cassandra клъстер от голям брой възли, няма нужда да използвате разпределени системи за съхранение на данни като Ceph или GlusterFS. В този случай логично ще бъде данните да се съхраняват на диска на възела чрез или монтиране на hostPath.
Друг въпрос е, ако искате да създадете отделна среда за разработчици за всяка feature-ветка. В този случай правилният подход ще бъде да се стартира един възел Cassandra, а данните да се съхраняват в разпределено хранилище, т.е. споменатите Ceph и GlusterFS ще станат ваша опция. Тогава разработчикът ще бъде сигурен, че няма да загуби тестовите данни дори при загуба на един от възлите на Kubernetes клъстера.
2. Мониторинг
Практически без алтернатива за реализация на мониторинг в Kubernetes е Prometheus (подробно сме говорили за това в ). Каква е ситуацията с Cassandra по отношение на експортерите на метрики за Prometheus? И, което е дори по-важно, с подходящите dashboard’и за Grafana?

Примерен външен вид на графики в Grafana за Cassandra
Има само два експортьора: и .
Избрахме първия, защото:
- JMX Exporter се развива и променя, докато Cassandra Exporter не успя да получи значителна подкрепа от общността. Cassandra Exporter все още не поддържа повечето версии на Cassandra.
- Може да го стартирате като javaagent, добавяйки флага
-javaagent:<името-на-директорията>\/cassandra-exporter.jar=--listen=:9180. - Има , който не е съвместим с Cassandra Exporter.
3. Избор на примитиви на Kubernetes
Според изложената по-горе структура на Cassandra клъстера, ще се опитаме да преведем всичко описано в терминологията на Kubernetes:
- Cassandra Възел → Pod
- Cassandra Рафт → StatefulSet
- Cassandra Център за данни → пул от StatefulSets
- Cassandra Клъстер → ???
Получава се, че липсва някаква допълнителна същност, за да управлява целия клъстер Cassandra наведнъж. Но ако нещо липсва, можем да го създадем! В Kubernetes за това е предназначен механизмът за определяне на собствени ресурси — .

Обявяване на допълнителни ресурси за логове и уведомления
Но сам по себе си Custom Resource нищо не значи: за него е нужен контролер. Може да се наложи да потърсите помощ от …
4. Идентификация на pod’ове
В по-горе се споразумяхме, че един възел Cassandra ще отговаря на един pod в Kubernetes. Но IP адресите на pod-овете всеки път ще бъдат различни. А идентификацията на възела в Cassandra се извършва именно на база на IP адреса… Оказва се, че след всяко изтриване на pod, кластерът Cassandra ще добавя нов възел.
Има изход, и дори не един:
- Можем да водим отчет по идентификаторите на хостовете (UUID, които уникално идентифицират инстанциите на Cassandra) или по IP адреси и да запазим всичко това в определени структури/таблици. Методът има два основни недостатъка:
- Риск от възникване на условие на гонка при падане на два възела. След рестартиране възлите Cassandra едновременно ще започнат да искат IP адрес от таблицата и ще съревновават за един и същи ресурс.
- Ако възел Cassandra е загубил данните си, вече няма да можем да го идентифицираме.
- Второто решение изглежда като малък трик, но все пак: можем да създадем Service с ClusterIP за всеки възел Cassandra. Проблемите на тази реализация:
- Ако в кластера Cassandra има много възли, ще трябва да създадем много Service-и.
- Възможността ClusterIP е реализирана чрез iptables. Това може да стане проблем, ако в кластера Cassandra има много (1000... или дори 100?) възли. Въпреки че може да реши този проблем.
- Третото решение е да използваме мрежата от възли на Cassandra вместо специалната мрежа на pod-овете, като активираме настройката
хостоваМрежа: true. Този метод налага определени ограничения:- На замяната на възлите. Необходимо е новият възел задължително да има същия IP адрес, какъвто е имал предишният (в облаци като AWS, GCP е практически невъзможно да се направи);
- Използвайки мрежата от възли на клъстера, започваме да се състезаваме за мрежови ресурси. Следователно, да разположим на един клъстерен възел повече от един pod с Cassandra ще бъде сложно.
5. Резервни копия
Искаме да архивираме пълната версия на данните от един възел Cassandra в определено време. Kubernetes предлага удобна възможност с помощта на , но тук самата Cassandra ни създава трудности.
Да припомня, че част от данните Cassandra съхранява в паметта. За да направим пълно резервно копие, е необходимо данните от паметта (Memtables) да бъдат прехвърлени на диска (SSTables). В този момент възелът Cassandra спира да приема връзки, напълно изключвайки се от работата на клъстера.
След това се прави резервно копие (snapshot) и се запазва схемата (keyspace). И тук се оказва, че просто резервното копие не е достатъчно: нужно е да се запазят идентификаторите на данните, за които отговарят възлите на Cassandra - това са специални токени.

Распределение на токените за идентификация на данните, за които отговарят възлите на Cassandra
Примерен скрипт за създаване на резервно копие на Cassandra от Google в Kubernetes можете да намерите на . Единственият проблем, който скриптът не отчита, е нулирането на данните на възела преди създаването на snapshot. Тоест, резервното копие се прави не за текущото състояние, а за състоянието малко по-рано. Но това помага да не изключваме възела, което изглежда логично.
set -eu
if [[ -z "$1" ]]; then
info "Моля, предоставете keyspace"
exit 1
fi
KEYSPACE="$1"
result=$(nodetool snapshot "${KEYSPACE}")
if [[ $? -ne 0 ]]; then
echo "Грешка при създаването на 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}"Примерен bash-скрипт за създаване на резервно копие от един възел на Cassandra
Готови решения за Cassandra в Kubernetes
Какви решения се използват в момента за разгръщане на Cassandra в Kubernetes и кое от тях най-добре отговаря на зададените изисквания?
1. Решения на базата на StatefulSet или Helm-чарти
Използването на основни функции на StatefulSets за стартиране на кластера Cassandra е добро решение. С помощта на Helm-чарти и шаблони Go може да се предостави на потребителя гъвкав интерфейс за разгръщане на Cassandra.
Обикновено това работи добре… докато не се случи нещо неочаквано - например, повреда на възел. Стандартните средства на Kubernetes просто не могат да вземат предвид всички описани по-горе особености. Освен това, този подход е много ограничен в това, колко може да бъде разширен за по-сложни употреби: замяна на възли, резервно копие, възстановяване, мониторинг и т.н.
Представители:
- ;
- .
И двата чарта са еднакво добри, но са подложени на описаните по-горе проблеми.
2. Решения на базата на Kubernetes Operator
Тези опции са по-интересни, тъй като предоставят широки възможности за управление на кластера. За проектиране на оператора на Cassandra, подобно на всяка друга база данни, добър шаблон изглежда като Sidecar Controller CRD:

Схема за управление на възлите в правилно проектиран оператор на Cassandra
Нека разгледаме съществуващите оператори.
1. Cassandra-оператор от instaclustr
- Състояние: Alpha
- Лиценз: Apache 2.0
- Изграден на: Java
Това е наистина многообещаващ и активно развиващ се проект от компания, която предлага управлявани инсталации на Cassandra. Както беше споменато по-горе, той използва sidecar-контейнер, който приема команди чрез HTTP. Написан е на Java, затова понякога му липсва по-усъвършенствана функционалност от библиотеката client-go. Освен това, операторът не поддържа различни Racks за един Datacenter.
Затова операторът има предимства като поддръжка на мониторинг, високо ниво на управление на клъстера с помощта на CRD и дори документация за създаване на резервни копия.
2. Navigator от Jetstack
- Състояние: Alpha
- Лиценз: Apache 2.0
- Изграден на: Golang
Оператор, създаден за инсталиране на DB-as-a-Service. В момента поддържа две бази данни: Elasticsearch и Cassandra. Предлага интересни решения, като контрол на достъпа до базата данни чрез RBAC (за това се стартира собствен navigator-apiserver). Интересен проект, който заслужава внимание, но последният комит е направен преди една година и половина, което очевидно намалява потенциала му.
3. Cassandra-оператор от vgkowski
- Състояние: Alpha
- Лиценз: Apache 2.0
- Изграден на: Golang
Не го разглеждат „сериозно“, тъй като последният комит в репозитория е направен преди повече от година. Развитието на оператора е спряно: последната версия на Kubernetes, обявена като поддържана, е 1.9.
4. Cassandra-оператор от Rook
- Състояние: Alpha
- Лиценз: Apache 2.0
- Изграден на: Golang
Оператор, чиято разработка не прогресира толкова бързо, колкото би трябвало. Има добре обмислена структура на CRD за управление на клъстера, решава проблема с идентификацията на възлите чрез Service с ClusterIP (този хак) ... но засега това е всичко. В момента не притежава функции за мониторинг и резервни копия (между другото, за мониторинга ние ). Интересен факт е, че с помощта на този оператор може да се инсталира и ScyllaDB.
NB: Този оператор с малки подобрения ние използвахме в един от нашите проекти. Проблеми в работата на оператора през целия период на експлоатация (~4 месеца работа) не са били забелязани.
5. CassKop от Orange
- Състояние: Alpha
- Лиценз: Apache 2.0
- Изграден на: Golang
Най-младият оператор в списъка: първият комит е направен на 23 май 2019 година. Вече има в арсенала си много функции от нашия списък, с които можете да се запознаете в репозитория на проекта. Операторът е изграждан на популярния operator-sdk. Поддържа мониторинг „извън кутията“. Основната разлика от другите оператори е използването на , реализиран на Python и използван за комуникация между възлите на Cassandra.
Изводи
Броят на подходите и възможните варианти за миграция на Cassandra в Kubernetes говори сам за себе си: темата е актуална.
На този етап, опитването на нещо от описаното по-горе става на ваша отговорност: нито един от разработчиците не гарантира 100%-ова функционалност на решението си в продукционна среда. Но вече много продукти изглеждат обещаващи, за да се опитате да ги използвате в разработка.
Мисля, че в бъдеще тази жена на кораба ще бъде на място!
P.S.
Прочетете също в нашия блог:
- «»;
- «»;
- «»;
- «».
Източник: habr.com
