HowTo: деплой Apache Cassandra DB и компонентов для её мониторинга

от автора

Привет! Меня зовут Сергей Тетерюков, и я работаю инженером инфраструктуры и автоматизации в X5 Tech. Недавно я написал для коллег обзорную статью о БД Apache Cassandra DB и её деплое, и теперь хочу поделиться ей с вами.

Краткое и поверхностное описание Cassandra DB

Apache Cassandra — высокопроизводительная, отказоустойчивая распределённая не реляционная (NoSQL) БД, позволяющая создать надёжное масштабируемое хранилище данных.

Cassandra использует язык CQL, синтаксис которого аналогичен SQL.

Компоненты cassandra db:

  • Нода(Node) или узел – основной элемент хранилища cassandra db. 

  • Дата-центр (Data-center) – состоит из одной или нескольких нод, связанных между собой. Может быть физическим или виртуальным.

    Деление на дата-центры позволяет распределять рабочую нагрузку, обеспечивает те самые reliability и fault tolerance.

    В каждый дата-центр обычно входят несколько нод, как минимум одна из которых выполняет роль раздающей (Seed); рекомендуется делать более одной seed-ноды на один дата-центр.

  • Кластер (Cluster) – объединение дата-центров.

Для более детального изучения архитектуры и того, как работает Cassandra рекомендую ознакомиться с основными разделами из официальной документации:

Рассмотрим практическую продуктовую задачу

  1. Задеплоить кластер Apache Cassandra DB в нескольких окружениях (например, dev, stage, и prod).

  2. Установить компоненты, необходимые для мониторинга Cassandra DB (экспортеры для сбора метрик состояния и ресурсов серверов, на которых развернуты ноды БД кассандры, и непосредственно метрик самой БД).

Варианты реализации деплоя

  1. В докер-контейнерах.
    Плюсы:
    — Есть готовые докер-образы, как для Cassandra DB, так и для node-exporter, cassandra-exporter => минимум настроек.

    Минусы (выявленные методом проб и ошибок на личном опыте):
    — Требуется подготовка хостов — установка и настройка docker и сопутствующих пакетов.
    — Для более гибкой настройки потребуется модификация образов.
    — Для успешного объединения отдельных нод Cassandra DB в кластер могут потребоваться дополнительные танцы с бубном: так, например, у меня при одновременном запуске контейнеров ноды как минимум одна из нод не могла корректно получить токены, и не проходила проверку для получения статуса UN (Up, Normal). Пришлось настроить ожидание с запуском каждого следующего контейнера только после завершения запуска и инициализации предыдущего, начиная с seed-ноды.
    — Сложности с настройками контейнеров экспортеров (например, node-exporter официально не рекомендуется деплоить в докер-контейнере, т.к. для корректной работы ему требуется доступ к системе хоста).
    — Сложности взаимодействия контейнеров БД и экспортеров между собой. У меня, например, так и не удалось подружить контейнер cassandra-exporter с контейнером самой cassandra-db.

  2. В k8s-кластере.
    Плюсы:
    — Как и в случае с docker: готовые докер-образы всех компонентов.
    — Возможности настроить автомасштабирование.
    — Высокая доступность и отказоустойчивость.
    — GAP-стек хорошо адаптирован для мониторинга kubernetes, в т.ч. есть готовые решения в виде helm-chart’ов.

    Минусы:
    — Дополнительные сложности, связанные с реализацией деплоя stateful-приложения.
    — Для более гибкой настройки потребуется модификация образов.
    — Необходимо будет решать проблемы с объединением cassandra-узлов в единый cassandra-кластер и их идентификацией (для которой требуются ip-адреса узлов).
    — Масса сложностей (согласно информации с просторов сети) с настройками cassandra-exporter’а, чтобы он мог корректно забирать метрики.

    В сети можно найти несколько готовых helm-chart’ов для cassandra-db, например: один, второй (более не поддерживается разработчиками). Также есть несколько kubernetes-операторов разной степени готовности и поддержки, со своими плюсами и минусами.

  3. Установка пакетов на хостах.
    Плюсы:

    — Отсутствие проблем взаимодействия компонентов между собой, описанных для реализации в докер-контейнерах.
    — Возможность более гибкой настройки компонентов.

    Минусы:
    —Требуются дополнительные настройка для организации работы cassandra как сервиса с автозапуском при старте хоста.

Для production среды рекомендуется деплой БД на хостах, этот вариант и будет рассмотрен далее.

В качестве основного инструмента для деплоя был выбран ansible. Соответственно для каждого компонента (БД и 2 экспортера) у нас будет своя ansible-role.

Общие особенности реализации процесса:

  • ansible.cfg:

[defaults] host_key_checking = False deprecation_warnings = False interpreter_python = /usr/bin/python3 vault_password_file = vault_secret.sh stdout_callback = yaml

Пара пояснений:

  1. [stdout_callback = yaml] — вывод при исполнении роли становится более читаемым;

  2. [vault_password_file = vault_secret.sh] — данный параметр позволит хранить пароль от ansible-vault в качестве переменной окружения и не передавать его при запуске роли.

В скрипт-файле vault_secret.sh:

#!/bin/bash echo $ANSIBLE_VAULT_PASS
  • Организация директории group_vars:

group_vars ├── all │   └── var.yml ├── dev │   ├── var.yml │   └── vault.yml ├── prod │   ├── var.yml │   └── vault.yml └── stage     ├── var.yml     └── vault.yml

Все секреты храним в group_vars/<environment>/vault.yml, шифруемые c помощью ansible-vault.

Например, пароль от учётной записи, под которой будет осуществляться ssh-подключение к хостам — в group_vars/all/var.yml:

ansible_password: "{{ vault_ansible_password }}"

В group_vars/<environment>/vault.yml:

ansible__vault_password: "MySecr3tP@s$w0rd!"

И шифруем:

$ ansible-vault encrypt group_vars/test/vault.yml

После чего файл с паролем выглядит примерно вот так:

$ANSIBLE_VAULT;1.1;AES256 64613635303233376461643834326339343535303763353731643762383136656566666336646636 3866666162383666656137346365663631333532633762610a373733353863666230383630643661 61613262366138373537663866303434643565313262366133383732313062393531636431653862 3263653434313938320a386638653734376330646136303231323966393837333934373764306230 32306266366237393137303961633232666636643062306264633633623930666339326637643835 61396237386263613761626439303831313939366365303431616432656563366261633065636261 32623861653736386266343866386135373866636632316533363531356561643833326636303632 65613230323333383364323938376539303432656538353564383735373237306231366561383637 6338
  • Файл hosts(inventory):

[seeds]     csnd1 ansible_host=10.10.10.10     csnd2 ansible_host=10.10.10.11 [workers]       csnd3 ansible_host=10.10.10.12     [prod:children]     seeds     workers [dc1:children]     seeds     workers [all:vars]     ansible_connection=ssh     ansible_become=yes     ansible_become_user=root
  • Объявление группы ‘seeds’ необходимо для дальнейшего определения seed-нод кластера.

  • Формирование остальных групп — в соответствии с требуемой в вашей задаче реализацией.

Далее рассмотрим роли для деплоя каждого компонента

  1. Ansible-role для Cassandra DB:

    В качестве основы была выбрана готовая роль от LoCP (League of Crafty Programmers Ltd). Было изучено несколько вариантов, но роль LoCP, на мой субъективный взгляд, наиболее понятная и подходящая.

    Структура роли:

roles/cassandra-db ├── defaults │   └── main.yml ├── handlers │   └── main.yml ├── tasks │   ├── assertions.yml │   ├── configure.yml │   ├── hotfixes.yml │   ├── install │   │   ├── Debian.yml │   │   └── RedHat.yml │   ├── join_cluster.yml │   ├── main.yml │   ├── service.yml │   └── variables.yml ├── templates │   ├── cassandra.yaml.j2 │   └── etc │       └── systemd │           └── system │               └── cassandra.service.j2 └── vars     ├── Debian.yml     ├── RedHat.yml     └── main.yml

В целом роль самодостаточная:

  • Позволяет развернуть кластер cassandra как на серверах семейства RedHat, так и Debian.

  • Содержит шаги, необходимые для добавления cassandra в систему в качестве сервиса.

  • Есть таски для добавления новых нод в кластер.

Пояснения и советы:

  • Для корректного создания основного конфигурационного файла cassandra — cassandra.yaml — не забудьте добавить в defaults/main.yml либо в yaml-конфиг, вызывающий роль (по аналогии с примером из README.md данной роли), параметры cassandra_configuration и cassandra_regex_replacements:

cassandra_datacenter1_name: "dc1" cassandra_rack_name: "rack1" seeds: "{{ seeds_list | join(',') }}"     cassandra_regex_replacements:     - path: cassandra-env.sh       line: MAX_HEAP_SIZE="{{ cassandra_max_heapsize_mb }}M"       regexp: ^#MAX_HEAP_SIZE="4G"     - path: cassandra-env.sh       line: HEAP_NEWSIZE="{{ cassandra_heap_new_size_mb }}M"       regexp: ^#HEAP_NEWSIZE="800M"     - path: cassandra-rackdc.properties       line: 'dc={{ cassandra_datacenter1_name }}'       regexp: '^dc='     - path: cassandra-rackdc.properties       line: 'rack={{ cassandra_rack_name }}'       regexp: '^rack='     сassandra_configuration:     authenticator: PasswordAuthenticator     cluster_name: "{{ envname }}-cluster"     commitlog_directory: /data/cassandra/commitlog     commitlog_sync: periodic     commitlog_sync_period_in_ms: 10000     data_file_directories:         - /data/cassandra/data     endpoint_snitch: GossipingPropertyFileSnitch     hints_directory: "/data/cassandra/hints"     listen_address: "{{ ansible_default_ipv4.address }}"     rpc_address: "{{ ansible_default_ipv4.address }}"     partitioner: org.apache.cassandra.dht.Murmur3Partitioner     num_tokens: 256     saved_caches_directory: /data/cassandra/saved_caches     seed_provider:         - class_name: "org.apache.cassandra.locator.SimpleSeedProvider"           parameters:               - seeds: "{{ seeds }}"     start_native_transport: true
  • Значения cassandra_max_heapsize_mb и cassandra_heap_new_size_mb рассчитываются автоматически (на основе фактических технических параметров серверов) в тасках, описанных в tasks/variables.yml

  • seedslist определяется следующей конструкцией в groupvars/all.yml:

seeds_list: "{{ groups['seeds'] | map('extract',hostvars,'ansible_host') | list }}"

Соответственно в hosts(inventory) должна быть группа seeds, которые и будут определены в качестве сидов кластера.

  • В качестве бонуса, если вдруг в вашей задаче требуется первоначальная конфигурация БД, например, добавить пространство ключей (keyspace) и пользователя (user):

    а. Для начала надо дождаться, что кластер поднялся и слушает порты для подключения к нему (9042) и взаимодействия нод между собой (7000). Для этого можно добавить следующую таску после всех задач конфигурации и запуска:

- name: Wait for the Cassandra Listener   wait_for:     timeout: 120     host: "{{ ansible_default_ipv4.address }}"     port: "{{ item }}"   loop:     - 9042     - 7000

b. В templates/ добавить шаблон cqlsh-скрипта, например, в моём случае было достаточно такого:

ALTER KEYSPACE system_auth     WITH replication = {         'class': 'NetworkTopologyStrategy',         '{{ cassandra_datacenter1_name }}': {{ cass_dc1_nodes_number }}     }; CREATE ROLE IF NOT EXISTS {{ cassandra_user }}  WITH PASSWORD = '{{ cassandra_user_password }}' AND LOGIN = true; CREATE KEYSPACE IF NOT EXISTS {{ cassandra_keyspace }}      WITH replication = {         'class': 'NetworkTopologyStrategy',         '{{ cassandra_datacenter1_name }}': {{ cass_dc1_nodes_number }}     };

Не забыть объявить необходимые переменные:

cassandra_user: "test_user" cassandra_keyspace: "test_keyspace" cassandra_datacenter1_name: "dc1" cassandra_rack_name: "rack1" cass_dc1_nodes_number: "{{ groups['dc1'] | length }}"   cqlsh_script_template: "cqlsh_script.txt.j2" cqlsh_script_path: "/root/cqlsh_script.txt"

cassandra_user_password — добавить в ansible-vault

c.    Скопировать скрипт на одну ноду кластера, выполнить его и после удалить:

- block:    - name: Apply cqlsh_script     template:       src: "{{ cqlsh_script_template }}"       dest: "{{ cqlsh_script_path }}"       owner: root       group: root       mode: '0644'    - name: add DB data (keyspace, user)     shell: "cqlsh {{ ansible_default_ipv4.address }}  -u cassandra -p cassandra -f  {{ cqlsh_script_path }}"    - name: Remove cqlsh_script file     file:       path: "{{ cqlsh_script_path }}"       state: absent  when: inventory_hostname == groups['seeds'][0]
  1. 2.    Ansible-role для node-exporter’а:

    Основа — готовая роль под авторством cloudalchemy. Структура роли:

roles/node-exporter ├── defaults │   └── main.yml ├── handlers │   └── main.yml ├── tasks │   ├── configure.yml │   ├── install.yml │   ├── main.yml │   ├── preflight.yml │   └── selinux.yml ├── templates │   ├── config.yaml.j2 │   └── node_exporter.service.j2 └── vars     └── main.yml

Необходимости в модификации данной роли почти нет (за исключением моментов, связанных с особенностями инфраструктуры (например, доступность внешних репозиториев из внутренней сети вашей компании), например, node_exporter_repo_url), собственная документация роли, на мой взгляд исчерпывающая.

Пояснения и советы:

  • Роль адаптирована под дистрибутивы семейств RHEL, Fedora, ClearLinux.

  • Node-exporter будет функционировать в качестве systemd service unit.

  • Предусмотрены два варианта установки экспортера:

    a.    Бинарник скачивается из репозитория, адрес которого передаётся в переменной node_exporter_repo_url. В случае, если ваше окружение имеет ограничения в доступе к официальному репозиторию, следует заменить его на адрес своего, доступного репозитория.

    b.    Бинарник заранее помещается в директорию с файлами роли (roles/node-exporter/files), либо по другому удобному вам пути, который далее передаётся в переменной node_exporter_binary_local_dir

Выбирайте более подходящий для вас вариант, но не передавайте одновременно значения и в node_exporter_repo_url, и в node_exporter_binary_local_dir, т.к. они отвечают за взаимозаменяемые сценарии.

  • Для изменения версии экспортера — поменять значение параметра node_exporter_version в defaults/main.yml. Здесь же найдутся параметры для более гибкой настройки конфигурации (config.yaml) — параметры:

node_exporter_tls_server_config: {} node_exporter_http_server_config: {} node_exporter_basic_auth_users: {}
  • При необходимости внести кастомные изменения в конфигурационные файлы настройки экспортера (config.yaml) и настройки сервиса (node_exporter.service): шаблоны файлов расположены в каталоге templates/.

  • Если по каким-то причинам вам потребуется поменять порт, на котором будет работать экспортер — меняйте переменную node_exporter_web_listen_address (стандартное значение «0.0.0.0:9100»). Аналогично с эндпоинтом для метрик — переменная node_exporter_web_telemetry_path (стандартное значение «/metrics»).

3.Ansible-role для cassandra-exporter’а:

В качестве основы была выбрана роль от criteo. Струтура:

roles/cassandra-exporter ├── README.md ├── defaults │   └── main.yml ├── files │   └── config.yml ├── handlers │   └── main.yml ├── tasks │   └── main.yml └── templates     ├── cassandra_exporter.sh.j2     └── etc         └── systemd             └── system                 └── prometheus-cassandra-exporter.service.j2

Изначально эта роль является развитием оригинального JMX exporter, но доработана для более лёгкой интеграции с Cassandra DB. По сути, установки экспортера с помощью этой роли достаточно, чтобы после настройки прометеуса на забор метрик с этого экспортера и установки в графане этого дашборда получить исчерпывающий набор информативных графиков для мониторинга нашего кассандра-кластера в разрезе работы БД.

Также важно отметить, что в рамках этой роли экпортер устанавливается в формате standalone, т.е. как отдельный компонент. Если у вас на проекте предполагается высокая нагрузка на cassandra DB, что закономерно повлечёт за собой увеличение количества метрик, то возможно вам стоит рассмотреть установку cassandra-exporter как java-agent (что позволит ускорить сбор метрик и снизить нагрузку на систему; из минусов — необходимость встраивать агент в конфигурацию cassandra DB, и, как следствие, потенциальные проблемы с совместимостью); в этом случае советую ознакомиться со следующей реализацией экспортера.

Пояснения и советы:

  • cassandra-exporter будет функционировать в качестве systemd service unit.

  • Версия экспортера задаётся в defaults/main.yml в переменной cassandra_exporter_version. Актуальную версию можно посмотреть здесь.

  • Порт, на котором будет висеть экспортер задаётся в defaults/main.yml в переменной cassandra_exporter_listen_port (значение по умолчанию в роли — 8080, но вообще стандартный порт для cassandra-exporter — 9500). Этот же порт следует прописывать в конфиге прометеуса для стягивания метрик.

  • Бинарник и конфиг-файл скачиваются из репозитория, адреса передаются в переменных cassandra_exporter_binary_url и cassandra_exporter_config_url. В случае, если ваша инфраструктура имеет ограничения в доступе к официальному репозиторию, следует заменить значения переменных на адреса своего, доступного репозитория.

  • Если вам потребуется, чтобы установочный бинарник и/или файл конфигурации сразу были в проекте, и не нужно было их скачивать в процессе отработки роли, то потребуются следующие доработки роли:

    — Закинуть эти файлы (по аналогии с node-exporter’ом,) в директорию с файлами роли (roles/cassandra-exporter/files) либо в другую директорию проекта.
    — Добавить переменные cassandra_exporter_binary, cassandra_exporter_config, в которых будут указаны пути до файлов в проекте (если файлы будут помещены в roles/cassandra-exporter/files, то в значениях переменных достаточно будет указать только имена этих файлов, например: ‘cassandra_exporter.jar’, ‘config.yml’).
    — Добавить следующие исполнительные таски:

- name: 'copy cassandra exporter binary'   copy:     src: '{{ cassandra_exporter_binary }}'     dest: '{{ cassandra_exporter_dist_dir }}/cassandra.jar'     owner: '{{ cassandra_exporter_user }}'     group: '{{ cassandra_exporter_group }}'     mode: '0644'   when: cassandra_exporter_binary is defined

А в таске ‘download cassandra exporter binary’ добавить условие выполнения:

when: cassandra_exporter_binary is not defined
- name: 'сopy cassandra exporter config'   copy:     src: '{{ cassandra_exporter_config }}'     dest: '{{ cassandra_exporter_config_dir }}/config.yml'     owner: '{{ cassandra_exporter_user }}'     group: '{{ cassandra_exporter_group }}'     mode: '0644'   when: cassandra_exporter_config is defined

Переменные cassandra_exporter_dist_dir и cassandra_exporter_config_dir — уже заданы в роли.

Конфиг-файл экспортера советую именно таким образом держать в проекте, чтобы при необходимости иметь возможность вносить доработки/изменения через код.

Итог

Имеем набор ролей, который позволит тремя командами:

$ ansible-playbook -i ansible/hosts ansible/install-cassandra-db.yml $ ansible-playbook -i ansible/hosts ansible/install-cassandra-exporter.yml $ ansible-playbook -i ansible/hosts ansible/install-node-exporter.yml

Развернуть полный набор — кластер БД cassandra и его мониторинг как на уровне ресурсов системы, так и на уровне самой БД. Все три компонента установятся на хостах в качестве systemd service unit’ов, перезапускаемых при старте системы, так что в случае перезагрузки хостов, не потребуется ручного запуска.

При установке желательно придерживаться последовательности:

  1. cassandra-db

  2. cassandra-exporter — в этом случае cassandra-exporter сразу найдёт запущенный сервис; node-exporter от остальных компонентов не зависит, и его можно устанавливать в любой момент.

В заключение, для проверки работы каждого компонента:

  1. БД должна быть доступна при обращении к любой из нод кластера на порт 9042.

    — Для удалённого подключения через консоль нам понадобится клиент cqlsh:

$ cqlsh <cassandra_node1_ip-address>:9042 -u my_cass_user

Более подробно о работе с клиентом cqlsh.

— Для диагностики кластера при локальном подключении к хостам-нодам cassandra можно использовать инструмент — nodetool, устанавливается автоматически вместе с cassandra-db. Более подробно о работе с nodetool.

  1. Метрики node-exporter должны быть доступны при обращении к каждому хосту-ноде кластера на порт 9100,

  2. Метрики cassandra-exporter — на порт 9500.


ссылка на оригинал статьи https://habr.com/ru/company/X5Group/blog/653961/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *