Безотказный Zabbix: миграция с асинхронной на групповую репликацию

Kate

Administrator
Команда форума

Введение​

Zabbix поддерживает несколько баз данных, но под рассмотрение попали только MySQL и PostgreSQL, как наиболее подходящие под мою установку. PostgreSQL с его repomgr и pgbouncer или каким-нибудь stolon с одной стороны и MySQL Group Replication с другой. Из-за использования MySQL в текущей конфигурации и тяге к стандартной комплектации, выбор пал на второй вариант.

Так что же такое MySQL Group Replication. Как видно из названия, это группа серверов, хранящая одинаковый набор данных. Максимальное количество узлов в группе ограничивается 9-ю. Может работать в режиме single-primary или multi-primary. Но самое интересное всё работает автоматически, будь то выборы нового ведущего сервера, определение поломанного узла, Split-brain или восстановление БД. Поставляется данный функционал в качестве плагинов group_replication и mysql_clone, связь происходит по Group Communication System протоколу в основе которого лежит алгоритм Паксос. Поддерживается данный тип репликации с версий 5.7.17 и 8.0.1.

Моя текущая установка работает на Zabbix 5.0 LTS и MySQL 5.7, миграцию будем проводить с повышением версии MySQL на 8.0, так интереснее ).

Мониторинг репликации​

Да да. Это как TDD, только в администрировании, сначала нужно подготовить мониторинг, чтобы новый кластер сразу попал на радары нашей системы мониторинга и не одна проблема не ускользнула от её зоркого взгляда. Так как у вас еще нет групповой репликации (ГР), то вывод команд указанных ниже будет пустым, поэтому я привожу пример выводов с работающего кластера.

Основным источником информации о статусе узлов служит команда:

SELECT * FROM performance_schema.replication_group_members;
Значение колонки MEMBER_STATE может быть разное. Статусы можно посмотреть на странице официальной документации https://dev.mysql.com/doc/refman/8.0/en/group-replication-server-states.html. Если сервер к примеру корректно перезагружен или выключен, он исчезнет из этой таблицы, поэтому желательно знать общее количество узлов в вашей схеме и следить за их количеством.

Дополнительно нужно следить за производительностью каждого узла:

SELECT * FROM performance_schema.replication_group_member_stats\G
Тут нас интересуют в первую очередь COUNT_TRANSACTIONS_IN_QUEUE, похож на Seconds_Behind_Master в асинхронной репликации. Как видно на третьем сервере количество транзакций в очереди слишком большое, а это повод начать разбираться что же здесь не так.

Резервное копирование​

Я надеюсь, у вас делается регулярное резервное копирование базы данных (БД) и конечно на специально выделенный сервер. Так же желательно убедиться перед миграцией, что на всех узлах с БД есть запас по месту, превышающий в два с половиной раза размер каталога с базой. Это нам понадобится для создания локальной резервной копии, на случай если что-то пойдёт не так, и позволит нам вернуть всё как было. Если у вас файловая система с поддержкой моментальных снимков, не в чём себе не отказывайте, но свободного места должно быть много, так как в самые тяжёлые таблицы будет добавлен дополнительный столбец, а это повлечёт за собой их пересоздание.

План миграции​

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

Если всё идёт гладко:

  1. Пропиливаем необходимые дырки в файрволле (для общения узлов между собой нужно открыть TCP 33061 порт). Выписываем необходимые сертификаты;
  2. Собираем репозиторий с MySQL 8.0 (FreeBSD, Poudriere - у каждого свои причуды);
  3. В системе мониторинга переводим серверы в режим обслуживания, уведомляем всех пользователей Zabbix (чтобы никто не удивлялся);
  4. Выключаем репликацию на всех узлах, которые были Secondary для этого сервера (мы же не хотим, чтобы какие-нибудь наши изменения улетели на действующие сервера). Выключаем репликацию на текущем узле;
  5. Выключаем MySQL 5.7 сервер на первом подопытном узле;
  6. Делаем резервное копирование БД с сохранением атрибутов файлов (сохраняем рядом, чтобы быстро восстановить базу);
  7. Обновляем пакеты на новую версию с новыми зависимостями;
  8. Запускаем MySQL 8.0 сервер (mysql_upgrade не нужен, с 8 версии это действо происходит автоматически);
  9. Добавляем первичные ключи в таблицы, в которых их нет (требования групповой репликации, иначе операции добавления, удаления и т. д. работать не будут). Операция длительная, зависит от размера таблиц и производительности сервера;
  10. Включаем репликацию на этом сервере, отреплецируем все изменения, выключаем репликацию (подтянем изменённые данные с действующего сервера, накопившееся за время наших манипуляций);
  11. Сбрасываем все упоминания об асинхронной репликации на данном сервере (команда RESET SLAVE ALL;);
  12. Настраиваем групповую репликацию и проверяем всё ли работает;
  13. Переключаем Zabbix сервер и Zabbix фронтенд на БД с ГР;
  14. Настраиваем групповую репликацию на других узлах (делаем шаги с 4 по 8, только с удалением каталога с БД перед 8 шагом, т. к. нам нужна чистая установка);
  15. Перенастраиваем мониторинг;
  16. Переделываем Ansible Playbook'и и конфигурационные файлы;
  17. Меняем скрипты и задачи по переключению мастера;
  18. Настраиваем HADNS;
  19. Обновляем документацию;
На непредвиденный случай:

  1. Останавливаем MySQL сервер;
  2. Возвращаем предыдущие версии пакетов;
  3. Удаляем каталог с БД и восстанавливаем из локальной резервной копии, запускаем MySQL сервер;
  4. Настраиваем асинхронную репликацию;
Откатываемся бесконечное количество раз, пока всё не пройдёт гладко.

Далее подробно рассмотрим 9, 12 и 14 шаги.

Шаг 9: Добавление первичных ключей​

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

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

SELECT tables.table_schema , tables.table_name , tables.engine
FROM information_schema.tables
LEFT JOIN (
SELECT table_schema , table_name
FROM information_schema.statistics
GROUP BY table_schema, table_name, index_name HAVING
SUM( case when non_unique = 0 and nullable != 'YES' then 1 else 0 end ) = count(*) ) puks
ON tables.table_schema = puks.table_schema and tables.table_name = puks.table_name
WHERE puks.table_name is null
AND tables.table_type = 'BASE TABLE' AND Engine="InnoDB";
Какой тип ключей и для каких колонок его создать я подсмотрел на форуме Zabbix, вот тут. Чтобы не поломалось обновление Zabbix, в таблице dbversion добавляем первичный ключ по существующему столбцу. Ниже необходимые запросы.

ALTER TABLE history ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE history_uint ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE history_text ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE history_str ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE history_log ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE dbversion ADD PRIMARY KEY (mandatory);
Напоминаю действие занимает большое количество времени на больших таблицах. Надеюсь, когда-нибудь это внесут в схему БД для Zabbix.

Шаг 12: Запуск групповой репликации​

Все настройки, относящиеся к групповой репликации, я собрал в одном месте в конфигурационном файле.

server-id=[номер сервера в кластере по порядку]
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE
transaction_write_set_extraction=XXHASH64

disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
plugin_load_add='group_replication.so;mysql_clone.so'
ssl-ca=/usr/local/etc/ssl/mysql/ca.crt
ssl-cert=/usr/local/etc/ssl/mysql/server.crt
ssl-key=/usr/local/etc/ssl/mysql/server.key
group_replication_ssl_mode=VERIFY_IDENTITY
group_replication_group_name="[одинаковое на всех узлах, генерируем один раз командой SELECT UUID();]"
group_replication_start_on_boot=off # включаем после добавления всех узлов в группу
group_replication_local_address="[полное имя текущего сервера].com:33061"
group_replication_group_seeds="example1.com:33061,example2.com:33061,example3.com:33061"
group_replication_ip_allowlist="2.2.2.2/32,3.3.3.3/32,4.4.4.4/32"
group_replication_member_weight=50

group_replication_recovery_use_ssl=ON
group_replication_recovery_ssl_verify_server_cert=ON
group_replication_recovery_ssl_ca=/usr/local/etc/ssl/mysql/ca.crt
group_replication_recovery_ssl_cert=/usr/local/etc/ssl/mysql/server.crt
group_replication_recovery_ssl_key=/usr/local/etc/ssl/mysql/server.key
Добавляем это всё в my.cnf, дубликаты переменных, которые встречаются в файле до этого, необходимо удалить. Теперь можно перезапустить сервер, либо проверить значение переменных как написано ниже и поменять их вручную. Обратите внимание на переменную group_replication_start_on_boot, она выключена, поэтому при рестарте репликация не запустится.

Проверяем значение переменных командой SHOW VARIABLES LIKE 'binlog_format'; меняем с помощью команды SET GLOBAL binlog_format = RAW; это относится к переменным в верхней части конфига, остальные настройки подтянутся при активации групповой репликации.

Переменные group_replication_ssl_mode и group_replication_recovery_ssl_verify_server_cert установлены в максимально безопасный режим с проверкой сертификата сервера, так что при выписывании сертификата укажите в Subject Alternative Name (SAN) полные имена всех улов кластера, которые есть в group_replication_group_seeds.

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

Создаём пользователя для работы репликации:

SET SQL_LOG_BIN=0;
CREATE USER 'replication'@'%' IDENTIFIED BY '[придумайте пароль]' REQUIRE SSL;
GRANT replication slave ON *.* TO 'replication'@'%';
GRANT BACKUP_ADMIN ON *.* TO 'replication'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
Устанавливаем плагины и проверяем статус:

INSTALL PLUGIN group_replication SONAME 'group_replication.so';
INSTALL PLUGIN clone SONAME 'mysql_clone.so';
SHOW PLUGINS;
Настраиваем пользователя, используемого при восстановлении БД с работающего сервера:

CHANGE REPLICATION SOURCE TO SOURCE_USER='replication', SOURCE_PASSWORD='[придуманный пароль]' \\
FOR CHANNEL 'group_replication_recovery';
Первый запуск группы. Переменную group_replication_bootstrap_group включаем только на первом сервере, на остальных, просто запускаем групповую репликацию:

SET GLOBAL group_replication_bootstrap_group=ON; # выполняем только на первом сервере
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF; # выполняем только на первом сервере
Если никаких ошибок команда не вернула, то можно посмотреть информацию о вновь созданной группе:

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | ce9be252-2b71-11e6-b8f4-00212844f856 |example1.com | 3306 | ONLINE |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
Проверьте дополнительно логи MySQL сервера на содержание в них ошибок.

Шаг 14: Добавление узла в группу​

После того как вы переключили Zabbix сервер и фронтенд на сервер с ГР, можно добавить оставшиеся узлы в кластер. Для этого выключаем MySQL сервер, делаем локальную копию, обновляем пакеты и удаляем текущий каталог с БД.

Запускаем чистую базу данных и проделываем всё тоже самое как в 12-ом шаге, с добавлением специфичных для этого сервера настроек (server-id, group_replication_local_address). Так как группа уже запущена, использовать переменную group_replication_bootstrap_group не нужно.

На этом этапе и будет использоваться так называемый Distributed Recovery механизм в который входит mysql_clone плагин. При подключении к узлу донору в первую очередь он попытается использовать бинарный лог, если информации в нём будет недостаточно, он полностью скопирует базу, включая созданного для репликации пользователя.

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

После добавления оставшихся серверов, поменяйте в конфиге my.cnf значения переменной group_replication_start_on_boot с off на on и перезагрузите MySQL сервер на любом ведомом сервере и проверьте что он остался в группе.

Полезные команды​

SELECT * FROM performance_schema.replication_group_members; - показывает статус всех узлов в группе.

SELECT * FROM performance_schema.replication_group_member_stats\G - показывает производительность каждого отдельного узла.

SELECT group_replication_set_as_primary('[uuid узла]'); - переключение ведущего узла.

Безотказный Zabbix сервер​

А что же с Zabbix сервером спросите вы, если дочитаете до этого момента, а всё просто. Я сделал так чтобы он постоянно следовал за ведущим сервером групповой репликации. В кроне на каждом сервере запускается скрипт, который проверяет что узел сейчас Primary в ГП, если да, то запускает Zabbix сервер, если нет, то останавливает его. Дальше включается в работу HADNS, он проверяет на каком сервере запущен Zabbix и отдает нужный IP адрес для DNS записи.

Заключение​

Возможно, сделано не всё так элегантно как хотелось бы. Вы наверно захотите использовать mysql-shell, mysqlrouter и преобразовать Group Replication в InnoDB Cluster, а может добавить HAProxy, особенно это полезно, когда разворачиваешь Zabbix с нуля. Надеюсь, этот рассказ послужит неплохой отправной точкой и будет полезен. Спасибо за внимание!

Дополнительная литература​

https://dev.mysql.com/doc/refman/8.0/en/group-replication.html

https://blog.zabbix.com/scaling-zabbix-with-mysql-innodb-cluster/8472/

https://en.wikipedia.org/wiki/Paxos_(computer_science)


Источник статьи: https://habr.com/ru/post/557662/
 
Сверху