Настройка отказоустойчивого кластера MySQL с синхронной мульти-master репликацией с помощью Galera в Ubuntu, Debian и CentOS Linux

Синхронная мульти-master репликация в MySQL появилась сравнительно недавно, однако была востребована довольно давно. До этого момента пользователям приходилось использовать либо репликацию в режиме Master-Slave и переключать операции записи на ведомый сервер при отказе главного сервера, либо использовать довольно сложный вариант с Master-Master репликацией на основании двунаправленной Master-Slave репликации.

Стоит отметить, что в конфигурации Master-мульти-Slave реализация автоматического переключения становится сложнее и не всегда возможна без человеческого вмешательства. Более того, репликация Master-Slave является не полностью синхронной, отчего могут быть проблемы с потерей данных при выходе сервера Master из строя, если ведомый сервер существенно отстал.

С появлением Galera все стало намного проще. Для надежной работы вам необходимо эксплуатировать нечетное количество серверов, например 3 или 5 для защиты от партиционирования, так, чтобы при ситуации, когда кластер «рассыпался» Galera могла принять обоснованное решение о том, является ли наблюдаемый сегмент кластера целостным.

В самом простом случае вес каждого узла является равным 1, соответственно, если для наблюдаемой части кластера сумма весов узлов > N/2, эта часть кластера считается работоспособной. Например, для 3х узлов:

Ситуация 1: отказ узла MySQL
Решение: два оставшихся узла могут работать, т.к. 2 > 3/2

Ситуация 2: отказ сети узла MySQL
Решение 1: два оставшихся узла могут работать, т.к. 2 > 3/2
Решение 2: узел с отказавшей сетью не может работать, т.к. 1 < 3/2

Ситуаций 3: отказ центрального коммутатора
Решение: кластер неработоспособен, т.к. для каждого узла 1 < 3/2

В общем случае, для безотказной работы кластера Galera вам необходимо обеспечить надежность:

  • избыточность сетевой инфраструктуры (стеки, MLAG, OSPF);
  • качественное энергоснабжение и разделение на независимые домены отказа, например, 3 VM в одном сервере не могут считаться надежным решением для формирования кластера Galera.

В более сложных топологиях, узлы кластера могут иметь различные веса. Общая формула кворума выглядит следующим образом:

Подробнее за деталями обратитесь к оригинальной статье о Galera.

При использовании Galera стоит помнить, что чем больше в кластере узлов, тем шире будут нужны каналы обмена данными, поскольку репликация осуществляется дублированием отправки пакетов от каждого узла каждому. Если у вас много узлов и интенсивный обмен трафиком между ними, рекомендуется переключиться на репликацию по multicast, если ваша сеть это поддерживает.

Конечно, помимо достоинств у кластера Galera есть и ограничения (перевод фрагмента с официального сайта MariaDB):

  • Репликация поддерживается только для таблиц в формате InnoDB;
  • Не поддерживается репликация явных блокировок, включая LOCK TABLES, FLUSH TABLES {явный список таблиц} WITH READ LOCK, (GET_LOCK (), RELEASE_LOCK (),…); Правильное использование транзакций позволяет преодолеть эти ограничения, однако, если движок использует вышеприведенные операции, они должны выполняться но одном и том же узле. Операции глобальной блокировки, такие как FLUSH TABLES WITH READ LOCK поддерживаются.
  • Все таблицы должны иметь первичный ключ (поддерживаются первичные ключи на нескольких столбцах). Операции DELETE не поддерживаются в таблицах без первичного ключа. Кроме того, строки в таблицах без первичного ключа могут извлекаться на разных узлах в разном порядке.
  • Общий журнал запросов и журнал медленных запросов не могут быть перенаправлены в таблицу. Если вы включите эти журналы, то вы должны отправлять вывод записей журналов в файл, установив log_output = FILE.
  • Транзакции XA не поддерживаются.
  • Хотя Galera явно не ограничивает размер транзакции, набор записей обрабатывается как один резидентный буфер памяти, и в результате очень большие транзакции (например, LOAD DATA) могут отрицательно повлиять на производительность узла. Чтобы избежать этого, системные переменные wsrep_max_ws_rows и wsrep_max_ws_size ограничивают размер строки в транзакции 128 КБ, а максимальный размер транзакции составляет 1 ГБ. При необходимости пользователи могут увеличить эти ограничения. В будущих версиях будет добавлена поддержка фрагментации транзакций.

Мы рассмотрим самую простую настройку кластера Galera с тремя узлами, как изображено на следующей картинке:

Для дальнейшей работы рассмотрим как установить MariaDB для различных операционных систем. В этом руководстве мы будем использовать текущую стабильную версию MariaDB версии 10.3.

Установка MariaDB

Установка в Debian 9

Произведем базовую установку MariaDB 10.3, рекомендованную поставщиком для Debian 9 Stretch:

sudo apt-get install -y software-properties-common dirmngr
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xF1656F24C74CD1D8
sudo add-apt-repository 'deb [arch=amd64,i386,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.3/debian stretch main'

sudo apt update
sudo apt install -y mariadb-server

Проверьте корректность установки, соединившись с MariaDB с помощью клиента командной строки:

mysql -uroot -e 'SELECT version();'

+---------------------------------------------+
| version()                                   |
+---------------------------------------------+
| 10.3.13-MariaDB-1:10.3.13+maria~stretch-log |
+---------------------------------------------+

Соединение работает, если вы получили вывод похожий на приведенный выше. Теперь, когда установка MariaDB завершена, можно перейти к настройке кластера.

Установка в Ubuntu 18.04

Произведем базовую установку MariaDB 10.3, рекомендованную поставщиком для Ubuntu Linux 18.04:

sudo apt-get install software-properties-common
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.3/ubuntu bionic main'

sudo apt update
sudo apt install -y mariadb-server

Проверьте корректность установки, соединившись с MariaDB с помощью клиента командной строки:

mysql -uroot -e 'SELECT version();'
+--------------------------------------------+
| version()                                  |
+--------------------------------------------+
| 10.3.13-MariaDB-1:10.3.13+maria~bionic-log |
+--------------------------------------------+

Соединение работает, если вы получили вывод похожий на приведенный выше. Теперь, когда установка MariaDB завершена, можно перейти к настройке кластера.

Установка в Ubuntu 16.04

Произведем базовую установку MariaDB 10.3, рекомендованную поставщиком для Ubuntu Linux 16.04:

sudo apt-get install software-properties-common
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
sudo add-apt-repository 'deb [arch=amd64,arm64,i386,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.3/ubuntu xenial main'

sudo apt update
sudo apt install -y mariadb-server

Проверьте корректность установки, соединившись с MariaDB с помощью клиента командной строки:

mysql -uroot -e 'SELECT version();'
+--------------------------------------------+
| version()                                  |
+--------------------------------------------+
| 10.3.13-MariaDB-1:10.3.13+maria~xenial-log |
+--------------------------------------------+

Соединение работает, если вы получили вывод похожий на приведенный выше. Теперь, когда установка MariaDB завершена, можно перейти к настройке кластера.

Установка в CentOS 7

Произведем базовую установку MariaDB 10.3, рекомендованную поставщиком для CentOS 7. Сначала необходимо добавить репозиторий пакетов MariaDB 10.3 в Yum:

sudo nano /etc/yum.repos.d/mariadb.repo

со следующим содержимым:

# MariaDB 10.3 CentOS repository list - created 2019-03-28 19:13 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

Теперь можно выполнить установку пакетов:

sudo yum install -y MariaDB-server MariaDB-client && 
      sudo systemctl enable mariadb && 
      sudo systemctl start mariadb

Проверьте корректность установки, соединившись с MariaDB с помощью клиента командной строки:

mysql -uroot -e 'SELECT version();'
+-----------------+
| version()       |
+-----------------+
| 10.3.13-MariaDB |
+-----------------+

Соединение работает, если вы получили вывод похожий на приведенный выше. Теперь, когда установка MariaDB завершена, можно перейти к настройке кластера.

Подготовка к настройке кластера

Настройка кластера Galera довольно проста, однако требует выполнения ряда подготовительных действий.

Конфигурация сетевых параметров

Для корректной работы кластера необходим доступ к следующим портам:

  • 3306 (TCP). Стандартный порт MariaDB — для клиентских подключений MySQL и передачи снимков состояния, использующих метод mysqldump. Этот порт можно изменить, указав другое значение.
  • 4567 (TCP/UDP). Порт репликации Galera — для трафика репликации кластера Galera. Репликация в режиме multicast использует оба протокола на этом порту. Порт можно изменить, установив wsrep_node_address.
  • 4568. Порт IST — для инкрементной передачи состояния. Можно изменить, установив ist.recv_addr в wsrep_provider_options.
  • 4444. Порт SST — для всех методов передачи состояния снимком, кроме mysqldump. Может быть изменено путем установки wsrep_sst_receive_address.

Мы считаем, что узлы соединены приватной доверенной сетью, не требующей брэндмауэра. Если у вас используется брэндмауэр, вам необходимо открыть вышеуказанные порты для обеспечения пропуска трафика. Если вы планируете использовать multicast, порт 4567 должен быть открыт как для пропуска трафика UDP, так и для TCP.

Примечание для пользователей Cloud2 NetPoint: В облаке Cloud 2 NetPoint для каждого аккаунта или проекта помимо публичной сети предоставляется еще и приватная сеть, в которую входят только виртуальные машины этого аккаунта. Эта сеть безопасна, работает на скорости 10 Gbit/s, и именно она должна использоваться для настройки кластерных решений вместо публичной сети.

Шифрование трафика кластера

Если ваши узлы кластера Galera будут работать в различных сегментах сети или просто в небезопасной среде, вам необходимо обеспечить шифрование передаваемых между ними данных. Шифрование может быть обеспечено с помощью использования встроенной поддержки SSL. В этой статье такая настройка не рассматривается.

Дополнительные параметры конфигурации MySQL

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

  • default-storage-engine = innodb — кластер Galera работает только с использованием InnoDB, вы должны использовать этот тип таблиц для реализации кластера;
  • log-bin = /var/log/mysql/mysql-bin — задает имя для журнала операций;
  • log-bin-index = /var/log/mysql/mysql-bin.index — задает имя индекса журнала операций;
  • binlog-format = 'ROW' — формат журнала должен быть построчным, это требование для Galera;
  • innodb_autoinc_lock_mode = 2 — режим блокирования, который наилучшим образом подходит для реализации конкурентных генераторов, но может привести к генерации идентификаторов с пропусками;
  • innodb_rollback_on_timeout = 1 — при таймауте транзакции будет осуществлен ее откат;
  • innodb_lock_wait_timeout = 600 — максимальное время ожидания блокировки строк транзакцией;
  • innodb_doublewrite = 1 — двойная запись InnoDB: в буфер двойной записи и в файлы данных;
  • innodb_flush_log_at_trx_commit = 0 — оптимизация, которую не стоит использовать вне кластера; позволяет вызывать синхронизацию не после каждой транзакции, а один раз в секунду;
  • bind-address = 0.0.0.0 — адрес, который слушает MySQL, для разрешения и IP4 и IP6 используйте ::;
  • wsrep_provider = /usr/lib/galera/libgalera_smm.so — путь к библиотеке Galera;
  • wsrep_cluster_address = "gcomm://10.0.0.1,10.0.0.2,10.0.0.3" — адреса узлов кластера, так же может быть адрес мультикаст, если используется репликация по мультикаст;
  • wsrep_on = ON — включает режим репликации;
  • wsrep_cluster_name = "cluster" — задает имя кластера;
  • wsrep_sst_method = rsync — механизм синхронизации состояния узлов после старта (поддерживается rsync, mysqldump, xtrabackup); rsync считается универсальным и подходящим для большинства случаев;
  • wsrep_node_address = "10.0.0.1" — адрес узла, который будет использоваться для репликации;
  • wsrep_node_name = "node1" — имя узла в кластере.

Параметр wsrep_sst_method

Отдельно хотелось бы остановиться на данном параметре. Он определяет способ первоначального переноса состояния между новым узлом и существующим узлом кластера. Существует три варианта, которые отличаются по способу работы:

  • Mysqldump — состояние с удаленного узла переносится на локальный с помощью mysqldump, при этом используется порт 3306 для соединения с удаленным узлом, на время переноса состояния узел донор блокируется. Этот способ считается самым медленным.
  • Rsync — на узле доноре и инициаторе запускается rsync, с помощью которого состояние переносится на инициатор, на время переноса узел донор блокируется. Этот способ является быстрым и оптимален для больших СУБД.
  • Rsync_wan — используется rsync, но для переноса используется алгоритм переноса изменений, что позволяет минимизировать трафик;
  • Xtrabackup — используется утилита Percona Xtrabackup. Способ является быстрым и неблокирующим, однако для его использования требуются дополнительные настройки и установка утилиты xtrabackup.

Мы будем использовать способ rsync, который является рекомендуемым способом по умолчанию.

Общий вид конфигурационного файла galera.cnf

[mysqld]

max_connections=350

log-bin=/var/log/mysql/mysql-bin
log-bin-index=/var/log/mysql/mysql-bin.index
binlog-format = 'ROW'

default-storage-engine=innodb
innodb_autoinc_lock_mode=2
innodb_rollback_on_timeout=1
innodb_lock_wait_timeout=600

#
# не меняйте значение этой переменной при использовании Galera
innodb_doublewrite=1

#
# для ускорения операций
# позволяет не ждать синхронизации с FS
# для большей безопасности можно установить в "1"
innodb_flush_log_at_trx_commit=0

bind-address=0.0.0.0

# Galera Provider Configuration
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://10.0.0.1,10.0.0.2,10.0.0.3"
wsrep_on=ON

# Galera Cluster Configuration
wsrep_cluster_name="cluster"

# Galera Synchronization Configuration
wsrep_sst_method=rsync

# Galera Node Configuration
wsrep_node_address="10.0.0.1"
wsrep_node_name="node1"

Для каждого узла кластера в конфигурационном файле будут меняться только значения параметров wsrep_node_address, wsrep_node_name. Остальные параметры должны быть одними на всех трех узлах.

Настройка с использованием multicast

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

wsrep_provider_options = "gmcast.mcast_addr=224.33.0.6"
wsrep_cluster_address="gcomm://224.33.0.6"

Здесь адрес 224.33.6.1 — доступная вам многоадресная группа, в которой вы будете передавать сообщения кластера.

Другие параметры wsrep_*

Перечисленных выше параметров вполне достаточно для настройки. Если вы хотите узнать о других параметрах, то полный перечень тех, которые относятся к настройкам кластера Galera, находится в официальной документации.

На каждом узле остановите MariaDB, в каталоге /etc/mysql/conf.d создайте файл galera.cnf с указанными выше настройками для Galera:

sudo nano /etc/mysql/conf.d/galera.cnf
sudo systemctl stop mariadb

Не забудьте установить корректные значения параметров wsrep_cluster_address, wsrep_provider_options (multicast группа или другие опции, если используются), wsrep_node_address, wsrep_node_name, wsrep_cluster_name.

Инициализация кластера

Запуск первого узла Galera. Выберите любой из узлов и инициализируйте кластер. Операция не деструктивная, никакие данные не удаляются и не повреждаются:

sudo galera_new_cluster

Запуск оставшихся узлов. Каждый из оставшихся узлов запускается как обычно:

sudo systemctl start mariadb

Проверьте состояние кластера с помощью значения переменных wsrep_*:

$ mysql -uroot -e "SHOW GLOBAL STATUS LIKE 'wsrep_cluster%';"
+--------------------------+--------------------------------------+
| Variable_name            | Value                                |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id    | 5                                    |
| wsrep_cluster_size       | 3                                    |
| wsrep_cluster_state_uuid | 15a6ec3c-648e-11e9-ae72-0715b07d89d9 |
| wsrep_cluster_status     | Primary                              |
| wsrep_cluster_weight     | 3                                    |
+--------------------------+--------------------------------------+


$ mysql -uroot -e "SHOW GLOBAL STATUS LIKE 'wsrep_incoming%'\G"
*************************** 1. row ***************************
Variable_name: wsrep_incoming_addresses
        Value: 10.120.28.219:3306,10.120.29.124:3306,10.120.28.207:3306

Если теперь на одном из узлов выполнить команду sudo systemctl stop mariadb, то вы немедленно увидите как изменится вывод вышеприведенных команд:

$ mysql -uroot -e "SHOW GLOBAL STATUS LIKE 'wsrep_cluster%';"
+--------------------------+--------------------------------------+
| Variable_name            | Value                                |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id    | 6                                    |
| wsrep_cluster_size       | 2                                    |
| wsrep_cluster_state_uuid | 15a6ec3c-648e-11e9-ae72-0715b07d89d9 |
| wsrep_cluster_status     | Primary                              |
| wsrep_cluster_weight     | 2                                    |
+--------------------------+--------------------------------------+

$ mysql -uroot -e "SHOW GLOBAL STATUS LIKE 'wsrep_incoming%'\G"
*************************** 1. row ***************************
Variable_name: wsrep_incoming_addresses
        Value: 10.120.28.219:3306,10.120.29.124:3306

Важно. Когда вы отключаете узлы штатным способом, как сделали выше, кластер понимает, что общий вес кластера надо уменьшить (был 3/3, стал 2/2), поэтому состояние кластера считается целостным, а не деградированным. Если же узлы отключаются аварийно, то вес кластера не уменьшается (был 3/3, стал 2/3).

Проверка репликации данных

На одном из узлов создадим базу данных, таблицу и вставим данные в нее:

$ mysql -uroot 

MariaDB [(none)]> create database test;
Query OK, 1 row affected (0.121 sec)

MariaDB [(none)]> create table test.test (id INT PRIMARY KEY, name VARCHAR(32));
Query OK, 0 rows affected (0.195 sec)

MariaDB [(none)]> insert into test.test values(1, "name");
Query OK, 1 row affected (0.003 sec)

Убедимся, что репликация произошла на оставшиеся узлы, выполнив на каждом из них команду запроса данных:

$ mysql -uroot -e 'select * from test.test;'
+----+------+
| id | name |
+----+------+
|  1 | name |
+----+------+

Аналогичным образом вы можете осуществить проверку и для других операций.

Перезапуск кластера

Если каким-то образом все узлы кластера оказались выключены, вы должны повторить процедуру инициализации. Если серверы MariaDB будут запущены обычным образом, то сборка кластера не произойдет. Первый сервер вы должны запустить с помощью команды sudo galera_new_cluster.

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

Использование ClusterControl для развертывания и управления кластером Galera

Мы рассмотрели настройку кластера Galera в ручном режиме. Кроме этого подхода кластер Galera можно легко развернуть и обслуживать с помощью ClusterControl.

Если вы хотите развернуть кластер Galera с помощью ClusterControl, обратитесь к нашей статье на эту тему.