Міграція Cassandra в Kubernetes: особливості та рішення

Міграція Cassandra в Kubernetes: особливості та рішення

З базою даних Apache Cassandra та необхідністю її експлуатації в рамках інфраструктури на базі Kubernetes ми зустрічаємося регулярно. У цьому матеріалі поділимося своїм баченням необхідних кроків, критеріїв та існуючих рішень (включаючи огляд операторів) для міграції Cassandra до K8s.

«Хто може керувати жінкою, впорається і з державою»

Хто ж така Cassandra? Це розподілена система зберігання, призначена для управління великими обсягами даних, що забезпечує високу доступність без жодної точки відмови. Проект навряд чи потребує довгої вистави, тому наведу лише основні особливості Cassandra, що будуть актуальні в розрізі конкретної статті:

  • Кассандра написана на Java.
  • Топологія Cassandra включає декілька рівнів:
    • Node – один розгорнутий екземпляр Cassandra;
    • Rack - група екземплярів Cassandra, об'єднаних за якоюсь ознакою, що знаходиться в одному дата-центрі;
    • Datacenter - сукупність всіх груп екземплярів Cassandra, що знаходяться в одному дата-центрі;
    • Cluster – сукупність усіх дата-центрів.
  • Для ідентифікації вузла Cassandra використовує IP-адресу.
  • Для швидкості операцій запису та читання частина даних Cassandra зберігає в оперативній пам'яті.

Тепер — до потенційного переїзду в Kubernetes.

Check-list для перенесення

Говорячи про міграцію Cassandra до Kubernetes, ми сподіваємося, що з переїздом керувати їй стане зручніше. Що для цього буде потрібно, що в цьому допоможе?

1. Сховище для даних

Як уже уточнювалося, частина даних Cassanda зберігає в оперативній пам'яті Memtable. Але є й інша частина даних, що зберігається на диск, - у вигляді SSTable. До цих даних додається сутність Журнал фіксації — записи про всі транзакції, які також зберігаються на диск.

Міграція Cassandra в Kubernetes: особливості та рішення
Схема транзакцій запису до Cassandra

У Kubernetes ми можемо використовувати для зберігання даних PersistentVolume. Завдяки відпрацьованим механізмам працювати з даними в Kubernetes з кожним роком стає все простіше.

Міграція Cassandra в Kubernetes: особливості та рішення
Кожному pod'у з Cassandra ми виділимо свій PersistentVolume

Cassandra сама по собі має на увазі реплікацію даних, пропонуючи для цього вбудовані механізми. Тому, якщо ви збираєте кластер Cassandra з великої кількості вузлів, то немає необхідності використовувати для зберігання даних розподілені системи типу Ceph або GlusterFS. У цьому випадку логічно зберігатиме дані на диску вузла за допомогою локальних персистентних дисків або монтування hostPath.

Інше питання, якщо ви хочете створювати для кожної feature-гілки окреме оточення для розробників. І тут правильним підходом підніматиме один вузол Cassandra, а дані зберігати у розподіленому сховищі, тобто. згадані Ceph та GlusterFS стануть вашою опцією. Тоді розробник буде впевнений, що не втратить тестові дані навіть за втрати одного з вузлів Kuberntes-кластера.

2. Моніторинг

Практично безальтернативним вибором для реалізації моніторингу в Kubernetes є Prometheus (Докладно ми про це розповідали в відповідній доповіді). Як у Cassandra з експортерами метрик для Prometheus? І, що в чомусь навіть головніше, з придатними для них dashboard'ами для Grafana?

Міграція Cassandra в Kubernetes: особливості та рішення
Приклад зовнішнього вигляду графіків у Grafana для Cassandra

Експортерів лише два: jmx_exporter и cassandra_exporter.

Ми вибрали для себе перший, бо:

  1. JMX Exporter зростає та розвивається, в той час як Cassandra Exporter не зміг отримати належної підтримки спільноти. Cassandra Exporter досі не підтримує більшість версій Cassandra.
  2. Можна запустити його як javaagent за допомогою додавання прапора -javaagent:<plugin-dir-name>/cassandra-exporter.jar=--listen=:9180.
  3. Для нього є адекватний dashboad, який несумісний із Cassandra Exporter.

3. Вибір примітивів Kubernetes

Згідно з вищевикладеною структурою кластера Cassandra, спробуємо перевести все, що там описано, у термінологію Kubernetes:

  • Cassandra Node → Pod
  • Касандра Рак → StatefulSet
  • Cassandra Datacenter → пул з StatefulSets
  • Cassandra Cluster → ???

Виходить, що не вистачає якоїсь додаткової сутності, щоб керувати всім кластером Cassandra одразу. Але якщо чогось нема, ми можемо це створити! У Kubernetes для цього призначено механізм визначення власних ресурсів. Custom Resource Definitions.

Міграція Cassandra в Kubernetes: особливості та рішення
Оголошення додаткових ресурсів для логів та оповіщень

Але сам по собі Custom Resource нічого не означає: адже для нього потрібний контролер. Можливо, доведеться вдатися до допомоги Kubernetes-оператора...

4. Ідентифікація pod'ів

Пунктом вище ми погодилися, що один вузол Cassandra дорівнюватиме одному pod'у в Kubernetes. Але IP-адреси у pod'ів щоразу будуть різними. А ідентифікація вузла в Cassandra відбувається саме на основі IP-адреси… Виходить, що після кожного видалення pod'а кластер Cassandra додаватиме новий вузол.

Вихід є, і навіть не один:

  1. Ми можемо вести облік за ідентифікаторами хостів (UUID'ам, які однозначно ідентифікують екземпляри Cassandra) або за IP-адресами і зберігати це все в якихось структурах/таблицях. У методу два основних недоліки:
    • Ризик виникнення умови гонки під час падіння відразу двох вузлів. Після підняття вузли Cassandra одночасно підуть вимагати для себе IP-адресу з таблиці і конкурувати за той самий ресурс.
    • Якщо вузол Cassandra втратив дані, ми більше не зможемо його ідентифікувати.
  2. Друге рішення здається невеликим хаком, проте: ми можемо створювати Service з ClusterIP для кожного вузла Cassandra. Проблеми цієї реалізації:
    • Якщо в кластері Cassandra дуже багато вузлів, нам доведеться створити дуже багато Service'ів.
    • Можливість ClusterIP реалізована через iptables. Це може стати проблемою, якщо в кластері Cassandra багато (1000… чи навіть 100?) вузлів. Хоча балансування на базі IPVS здатна вирішити цю проблему.
  3. Третє рішення - використовувати для вузлів Cassandra мережу вузлів замість виділеної мережі pod'ів за допомогою увімкнення налаштування hostNetwork: true. Цей метод накладає певні обмеження:
    • На заміну вузлів. Потрібно, щоб новий вузол обов'язково мав ту саму IP-адресу, що й попередня (у хмарах на кшталт AWS, GCP це зробити практично неможливо);
    • Використовуючи мережу вузлів кластера, ми починаємо конкурувати за мережеві ресурси. Отже, викласти на один вузол кластера більше одного pod'а з Cassandra буде проблематично.

5. Бекапи

Ми хочемо зберігати повну версію даних одного вузла Cassandra за розкладом. Kubernetes надає зручну можливість з використанням CronJobАле тут палиці в колеса нам вставляє сама Cassandra.

Нагадаю, що частина даних Cassandra зберігає у пам'яті. Щоб зробити повний бекап, потрібні дані з пам'яті (Memtables) перенести на диск (SSTables). У цей момент вузол Cassandra перестає приймати з'єднання, повністю виключаючись із роботи кластера.

Після цього знімається бекап (знімок) та зберігається схема (простір клавіш). І тут з'ясовується, що просто бекап нам нічого не дає: потрібно зберегти ідентифікатори даних, за які відповідав вузол Cassandra, це спеціальні токени.

Міграція Cassandra в Kubernetes: особливості та рішення
Розподіл токенів для ідентифікації за які дані відповідають вузли Cassandra

Приклад скрипту для зняття бекапу Cassandra від Google у Kubernetes можна знайти за цим посиланням. Єдиний момент, який скрипт не враховує, - це скидання даних на вузол перед зняттям snapshot'а. Тобто бекап виконується не для поточного стану, а стану трохи раніше. Але це допомагає не виводити вузол із роботи, що бачиться дуже логічним.

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}"

Приклад bash-скрипту для зняття бекапу з одного вузла Cassandra

Готові рішення для Cassandra в Kubernetes

Що взагалі зараз використовують для розгортання Cassandra у Kubernetes і що з цього найбільше підходить під задані вимоги?

1. Рішення на базі StatefulSet або Helm-Чартів

Використовувати базові функції StatefulSets для запуску кластеру Cassandra є гарним варіантом. За допомогою Helm-чарту та шаблонів Go можна надати користувачеві гнучкий інтерфейс для розгортання Cassandra.

Зазвичай це працює нормально… поки що не станеться щось несподіване — наприклад, вихід вузла з ладу. Стандартні кошти Kubernetes просто не можуть врахувати всі описані вище особливості. Крім того, цей підхід дуже обмежений у тому, наскільки він може бути розширений для складнішого використання: заміни вузлів, резервного копіювання, відновлення, моніторингу тощо.

Представники:

Обидва чарти однаково хороші, але при цьому схильні до описаних вище проблем.

2. Рішення з урахуванням Kubernetes Operator

Такі опції більш цікаві, тому що надають широкі можливості управління кластером. Для проектування оператора Cassandra, як і будь-якої іншої бази даних, хороший патерн виглядає як Sidecar <-> Controller <-> CRD:

Міграція Cassandra в Kubernetes: особливості та рішення
Схема управління вузлами у правильно спроектованому операторі Cassandra

Розглянемо існуючі оператори.

1. Cassandra-оператор від instaclustr

  • GitHub
  • Готовність: Alpha
  • Ліцензія: Apache 2.0
  • Реалізовано на: Java

Це дійсно дуже перспективний проект, що активно розвивається від компанії, яка пропонує керовані розгортання Cassandra. Він, як і описано вище, використовує sidecar-контейнер, який приймає команди через HTTP. Написаний на Java, тому іноді йому не вистачає більш функціональності бібліотеки client-go. Також оператор не підтримує різні Racks для одного Datacenter.

Зате оператор має такі плюси, як підтримка моніторингу, високорівневого управління кластером за допомогою CRD і навіть документація зі зняття бекапів.

2. Navigator від Jetstack

  • GitHub
  • Готовність: Alpha
  • Ліцензія: Apache 2.0
  • Реалізовано на: Golang

Оператор призначений для розгортання DB-as-a-Service. На даний момент підтримує дві бази даних: Elasticsearch і Cassandra. Має такі цікаві рішення, як контроль доступу до бази даних через RBAC (для цього піднімається свій окремий navigator-apiserver). Цікавий проект, до якого варто було б придивитися, проте останній коміт було зроблено півтора роки тому, що явно знижує його потенціал.

3. Cassandra-оператор від vgkowski

  • GitHub
  • Готовність: Alpha
  • Ліцензія: Apache 2.0
  • Реалізовано на: Golang

Розглядати його «всерйоз» не стали, оскільки останній коміт у репозиторій був понад рік тому. Розробка оператора занедбана: остання версія Kubernetes, заявлена ​​як підтримувана, це 1.9.

4. Cassandra-operator від Rook

  • GitHub
  • Готовність: Alpha
  • Ліцензія: Apache 2.0
  • Реалізовано на: Golang

Оператор, розвиток якого йде не так швидко, як хотілося б. Має продуману структуру CRD для управління кластером, вирішує проблему з ідентифікацією вузлів за допомогою Service з ClusterIP (той самий «хак»)… але поки що це все. Моніторингу та бекапів із коробки зараз немає (до речі, за моніторинг ми взялися самі). Цікавий момент, що за допомогою цього оператора можна розгорнути ScyllaDB.

NB: Цей оператор з невеликими доопрацюваннями ми використовували в одному з наших проектів. Проблем у роботі оператора за весь час експлуатації (~4 місяці роботи) помічено не було.

5. CassKop від Orange

  • GitHub
  • Готовність: Alpha
  • Ліцензія: Apache 2.0
  • Реалізовано на: Golang

Наймолодший оператор у списку: перший коміт було зроблено 23 травня 2019 року. Вже зараз він має у своєму арсеналі велику кількість фіч із нашого списку, докладніше з якими можна ознайомитись у репозиторії проекту. Оператор збудований на базі популярного operator-SDK. Підтримує моніторинг "з коробки". Головною відмінністю від інших операторів є використання плагіна CassKop, реалізований на Python і використовується для комунікації між вузлами Cassandra.

Висновки

Кількість підходів та можливих варіантів перенесення Cassandra у Kubernetes говорить сама за себе: тема затребувана.

На даному етапі пробувати щось із вищеописаного можна на свій страх і ризик: жоден з розробників не гарантує 100% роботу свого рішення в production-середовищі. Але вже зараз багато продуктів виглядають багатообіцяюче, щоб спробувати використовувати їх у стендах для розробки.

Думаю, у майбутньому ця жінка на кораблі прийдеться до місця!

PS

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук