Как я упоминал ранее, для реализации высокой доступности MFA сервиса буду поднимать несколько экземпляров PrivacyIdea серверов. PrivacyIdea все свое состояние хранит в субд. И для решение всей задачи, мне достаточно обеспечить высокую доступность на уровне баз. В документации к сервису предлагается два решения:

  1. Использование централизированной менеджмент системы СУБД (Например: Mariadb Galera, Pgpool);
  2. Использование два инстанса MySQL в режиме master-master.

Разумеется в моем случаи целесобразнее будет использование второго варианта. Вообщем я взялся за изучения вопроса эсплуатации master-to-master субд. И мнения у большинства - нигативные, из основных притензий к такому подходу, это нарватся на рассихронизацию данных в обоих бд. Также при вставке уникального значения столбца в обе базы, приведет к вылету инстансов или к прерыванию репликации. Выход из подобных ситуаций это создание active/passive кластера. Мы добавляем выше какой либо балансировщик (keepalived, например) и пишем данные в одну мастер ноду вторая же нода используется как горячий бекап. ariadb-cluster-schemes.png В случаи падения первого мастера, мы продолжаем писать уже во второй мастер. mariadb-cluster-schemes2.png

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

Данная схема закрывает вопрос этот вопрос, имхо.

Установка MariaDB

Для начала на оба инстанса проинсталируем пакеты с MariaDB:

[All servers]# yum install mariadb mariadb-server mariadb-devel

Запускаем оба инстанса, и инициализируем сценарий настройки безопасности:

[All servers]# systemctl enable --now mariadb
[All servers]# mysql_secure_installation

Открываем порт, для межсетевого взаимодействия двух нод:

[All servers]# firewall-cmd --add-port=3306/tcp --permanent
[All servers]# firewall-cmd --reload

Настройка репликации

На первой мастер ноде отредактируем конфиг - mariadb:

[root@mfa01 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
--
[mysqld]
...
bind-address=*
server-id=1
log_bin=mysql-bin
binlog-format=ROW

В конфиге под контекстом [mysqld], добавляем настройки:

  • bind-address - с какой адреса принимает подключения инстанс бд.
  • server-id - уникальный id идентификатор ноды, которая участвует в репликации.
  • log_bin - в значении этой директивы, указываем куда хотим писать журнал с событиями, в которых описываются изменения базы данных.
  • binlog-format - указывается тип журналирования, в моей случаи тип строка.

На второй мастер ноде добавляем аналогичные директивы:

[root@mfa02 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
--
[mysqld]
...
bind-address=*
server-id=2
log_bin=mysql-bin
binlog-format=ROW

Из отличий, меняем только id (server-id) во второй ноде.

На обоих нодах перезапускаем сервис:

[All servers]# systemctl restart mariadb

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

MariaDB [(none)]> stop slave;
MariaDB [(none)]> grant replication slave on *.* to 'repl'@'%' identified by 'PASSWORD';
MariaDB [(none)]> flush privileges;

Смотрим статус мастера, в этом выводе для нас важны данные поля Position и File, Эти значения будут использоваться для последующей конфигурации второго мастера:

MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      655 |              |                  |
+------------------+----------+--------------+------------------+

Откючаемся от базы, и делаем фулл бекап для дальнейшего восставновления на втором мастере:

[root@mfa01 ~]# mysqldump --all-databases -u root -p > master_backup.sql

Этот дайм закидываем на вторую ноду, и восстанавливаем:

[root@mfa02 ~]# mysql -u root -p < master_backup.sql

И перезапускаем инстанс:

[root@mfa02 ~]# systemctl restart mariadb

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

MariaDB [(none)]> stop slave;
MariaDB [(none)]> change master to master_host='192.168.120.44', master_user='repl', master_password='PASS', master_log_file='mysql-bin.000001', master_log_pos=655;

Обратите внимание, что в директивах:

  • master_host - указывается ip первого мастера
  • master_user/master_password - логин/пароль пользователя с правами репликации, которого создали ранее.
  • master_log_file - название журнала (Я просил запомнить)
  • master_log_pos - номер позиции.

Ну и теперь запускаем слейва:

MariaDB [(none)]> start slave;

Посмотреть статус реплицируемой ноды, можно через команду:

MariaDB [(none)]> show slave status\G;

В выводе этой команды, на данный момент можно обратить внимание на два параметра:

  • Slave_IO_Running - поток input/output, соединение с мастер нодой. (Значение - yes, говорит нам об успешности)
  • Slave_SQL_Running - sql поток с мастером. (Также в значении - yes, говорит об успешности)
  • Slave_SQL_Running_State - обычно отображается сообщение об текущем состоянии. mariadb-cluster-repl.png Сейчас схема работы такая, первая нода в роли мастера, вторая нода как обычная реплика. То есть репликация реализова, как бы в одну сторону. mariadb-cluster-repl2.png На стороне первой ноды поменяем мастера, что бы репликация работала в двунаправленном режиме. mariadb-cluster-repl3.png Для этого на стороне второго мастера вызываем команду - show status master, для получения данных имени лог файла и номера позиции:
MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000002 |      342 |              |                  |
+------------------+----------+--------------+------------------+

Теперь подключаемся к первому инстансу, и меняем настройки мастера:

MariaDB [(none)]> stop slave;
MariaDB [(none)]> change master to master_host='192.168.120.46', master_user='repl', master_password='PASSWORD', master_log_file='mysql-bin.000002', master_log_pos=342;
MariaDB [(none)]> start slave;

Как вы уже поняли, в значение мастер хоста указан ip второго мастера и имя/позиция журнала тоже от второй ноды.

Тестируем репликацию

На этом с настройкой бд все.

Можем протестировать работу репликацию на двух нодах, путем поочередного создания баз или табличек: mariadb-cluster-test1.png Я создал базу на первой ноде, и потчи сразу вывел список баз на второй ноде. Реплика отработала, база отобразилась.

Повторяю действия в обратном порядке: mariadb-cluster-test1.png И обратный сценарий тоже отработал корректно.

Настройка виртуального IP

Виртуальный (или плавающий IP) нам нужен для создания единой точки доступа к бекандам. Вообщем на два сервера устанавливаем keepalived:

[All servers]# yum install keepalived -y 

Редактируем конфиг сервиса:

[root@mfa01 ~]# cat /etc/keepalived/keepalived.conf
---
global_defs {
    enable_script_security
}

vrrp_script database_check {
    script "/usr/bin/systemctl is-active mariadb >/dev/null && echo 1 || echo 0"
    interval 5
}

vrrp_instance pi-cluster {
    state MASTER
    interface ens192
    virtual_router_id 254
    priority 100
    advert_int 2
    nopreempt
    authentication {
        auth_type PASS
        auth_pass PASSWORD
    }
    virtual_ipaddress {
        192.168.120.45
    }
    track_script {
        postgresql_check
    }
}

Конфиг получился достаточно скромный. Из основного, я добавил проверку database_check доступности базы, с интервалом 5 секунд. В главном контексте я определяю состояние инстанса (первая нода будем мастером), приоритет ноды и etc.

Конфиг на второй ноде:

global_defs {
    enable_script_security
}

vrrp_script database_check {
    script "/usr/bin/systemctl is-active mariadb >/dev/null && echo 1 || echo 0"

vrrp_instance pi-cluster {
    state BACKUP
    interface ens192
    virtual_router_id 254
    priority 50
    advert_int 2
    nopreempt
    authentication {
        auth_type PASS
        auth_pass PASSWORD
    }
    virtual_ipaddress {
        192.168.120.45
    }
    track_script {
        postgresql_check
    }
}

В конфигурации второй ноды, отличие только в состоянии (тут оно - Backup), и более низком приоритете.

Есть один важный момент, я добавил опцию nopreempt. Преставим такую ситуацию, что между нодами флапнула сетка и виртуальный ip перетек на второю ноду. Какое-то время прошло и первая нода вновь стала доступна, виртуальный ip откатился обратно. И как бы это нормальное поведение keepalived. Но проблема в том, что в период недоступности первой ноды, на второй ноде уже накопилась какая-то дельта данных в базе. И при быстром откате на первую ноду, мы теряем данные. То есть как бы откатываемся на прошлое состояние.

Такой косяк может произойти, когда база имеет уже жирный размер, и жирный лаг в репликации. И нам получается нужно время, что бы донакатить изменения. Выше упомянутая опция решает эту проблему.

Например, первая нода стала недоступная и через период времени восстановилась, keepalived поменяет состояние этой мастер ноды на Backup и виртуальный адрес не откиниться обратно.

Открываем порт на фаере,

[All servers]# firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
success
[All servers]# firewall-cmd --reload

Ну и запускаем сервис:

[All servers]# systemctl enable --now keepalived

Теперь остается только попробовать различные тесткейсы, так сказать пошатать класстер.


Дополнительные ссылки