Pull to refresh

Доступная отказоустойчивость для вашего сайта

Reading time 27 min
Views 5.6K

Доступная отказоустойчивость для вашего сайта

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

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

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

А есть ли способы защитить ваш интернет-магазин или другой сервис от таких проблем?

Да, конечно, есть, и не один, но также есть и множество нюансов.

К сожалению, обычно отказоустойчивое решение стоит очень и очень дорого. Даже в простых конфигурациях ежемесячные расходы могут достигать 100–200 тысяч рублей и больше. Немало средств придется потратить и на первоначальную настройку. Но есть и недорогие решения.

Эта статья поможет вам настроить доступный вариант отказоустойчивости, созданный на базе технологии VRRP (Virtual Router Redundancy Protocol) и сервиса keepalived.

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

Наивная реализация отказоустойчивости

Если мы защищаем наш интернет-магазин от отказа сервера или проблем в датацентре, не будет ли достаточно просто арендовать резервный сервер в другом датацентре, а затем настроить репликацию данных?

На рис. 1 показан такой вариант, когда серверы расположены в разных датацентрах и у них разные адреса IP (адреса IP показаны только для примера).

Рис. 1. Основной и резервный сервер в разных датацентрах
Рис. 1. Основной и резервный сервер в разных датацентрах

Здесь сервер m01host играет роль основного сервера (MASTER-сервер), а сервер s01host — резервного (BACKUP-сервер). Если в проекте используется СУБД, то настраивается репликация базы данных с основного сервера на резервный. Репликацию файлов можно организовать, например, при помощи rsync.

Если основной сервер интернет-магазина или дата-центр, в котором этот сервер расположен, выйдут из строя, то резервный сервер будет содержать актуальные данные. Теперь достаточно изменить запись A домена сайта интернет-магазина в DNS, указав там адрес IP резервного сервера вместо основного и, казалось бы, интернет-магазин сможет снова работать.

Но тут есть нюанс. Проблема в том, что на распространение изменений в DNS может уйти очень много времени, от нескольких часов до 2–3  дней. И все это время сайт интернет-магазина будет недоступен всем или части пользователей.

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

А можно ли каким-либо образом обеспечить переключение сайта с основного сервера на резервный без изменений в DNS?

Да, такая возможность имеется, и она реализуется различными способами.

Реальные способы обеспечения отказоустойчивости

Существуют различные отказоустойчивые решения, допускающие быстрое переключение сайта с отказавшего сервера на работоспособный, например, отказоустойчивые облака или отказоустойчивые балансировщики.

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

Но тут нужно учесть три неприятных момента.

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

Во-вторых, скорость доступа к сетевым дисковым устройствам обычно ниже скорости доступа к локальным дискам SSD и NVMe, что может отрицательно сказаться на производительности СУБД. Да, в облаках есть скоростные сетевые диски, но они стоят дорого.

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

Еще есть решение на базе отказоустойчивых балансировщиков нагрузки. Такие балансировщики могут исключить направление трафика на отказавшие узлы, однако сами по себе они не решают всех проблем, в частности проблемы отказоустойчивости базы данных.

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

Но самым бюджетным и достаточно надежным способом реализации отказоустойчивости сайта, работающего на одном выделенном сервере, оказалось использование VRRP и keepalived.

Применение VRRP и keepalived

VRRP — это сетевой протокол увеличения доступности маршрутизаторов, выполняющих роль шлюза по умолчанию. Если кратко, то VRRP совместно с сервисом keepalived обеспечит быстрый перенос адреса IP с главного сервера на резервный при отказе главного сервера.

Так как адрес IP при этом останется тем же самым, то никаких настроек в DNS изменять не потребуется. Такой адрес IP еще называют «плавающим», потому что он может переходить с одного сервера на другой, или виртуальным (Virtual IP, VIP).

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

А есть ли проблемы при использовании VRRP и keepalived?

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

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

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

Также вы можете заказать пару серверов и недорогую отказоустойчивую сеть с плавающим IP в компании Servers.ru, которая позволит защитить ваш интернет-магазин от отказа сервера или дата-центра с помощью keepalived.

Тестирование перед заказом сети VRRP

Если вы собираетесь испытать сервис keepalived, то для этого можно создать две виртуальные машины при помощи, например, VMware Workstation Pro. При этом на время отладки вам не придется арендовать у провайдера два сервера и сеть.

В процессе тестирования вы сможете настроить файлы конфигурации keepalived и вспомогательные скрипты. А когда все будет готово, закажите серверы и сеть VRRP у провайдера. Селектел, например, выделит вам подсеть /29 из шести адресов IP:

  • служебный адрес IP;

  • широковещательный адрес;

  • четыре свободных адреса IP

Вам также будет предоставлена маска подсети 255.255.255.248 и адрес шлюза по умолчанию.

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

Установка keepalived

Расскажем о том, как провести установку и отладку конфигурации keepalived на виртуальных машинах VMware Workstation Pro.

Сразу заметим, что обычно провайдеры могут объединить в сеть VRRP только физические серверы, а не виртуальные.

Итак, с помощью VMware Workstation Pro создадим две виртуальные машины Debian 11 и установим на них сервис keepalived.

Используем следующее распределение адресов и настройки сети:

  • 192.168.0.182: мастер-узел m01host (MASTER);

  • 192.168.0.181: резервный узел s01host (BACKUP);

  • 192.168.0.179: плавающий IP (виртуальный IP, VIP);

  • 192.168.0.1: шлюз по умолчанию и сервер DNS;

  • 255.255.255.0: маска сети

Если это возможно, при создании виртуальных машин в VMware Workstation Pro выделите для них по 8 Гбайт оперативной памяти, по 80 Гбайт дисковой памяти и по четыре ядра процессора (или задайте больше ресурсов, если это требует ваш сайт).

Обязательно укажите Network connection как Bridged.

Установка необходимых пакетов и модулей

Установите на главном и резервном серверах Exim для отправки сообщений об изменении состояния keepalived по электронной почте:

# apt-get install exim4

Разрешите отправку почты через интернет:

# dpkg-reconfigure exim4-config

Для тестирования репликации базы данных установите на обоих серверах MariaDB:

# apt install mariadb-server

Задайте пароль пользователя root для главного сервера:

# mysql -u root

> ALTER USER 'root'@'localhost' IDENTIFIED BY 'master_password';
> FLUSH PRIVILEGES;

Также аналогичным образом задайте пароль пользователя root для резервного сервера:

> ALTER USER 'root'@'localhost' IDENTIFIED BY 'slave_password';

Настройте безопасность MariaDB на каждом сервере, разрешив доступ к СУБД через сокеты:

# mysql_secure_installation

Установите пакеты gcc и make:

# apt install gcc make

Также вам потребуются модули Perl, которые можно установить при помощи cpan:

  • File::Slurp

  • File::Copy

  • Sys::Hostname

  • Data::Dumper

И, наконец, установите сервис keepalived в ОС Debian 11 на главном и резервном сервере:

# apt install keepalived

При необходимости на сайте https://keepalived.readthedocs.io/en/latest/installing_keepalived.html вы найдете инструкции по установке keepalived для этой и других ОС.

Конфигурирование keepalived на главном сервере

После установки основных пакетов и модулей необходимо настроить конфигурацию keepalived, отредактировав файл /etc/keepalived/keepalived.conf на главном и резервном сервере.

Для главного сервера конфигурация должна быть такой (адреса электронной почты и адреса IP показаны только для примера):

global_defs {
  notification_email {
   admin@domain.ru
  }
  notification_email_from admin-vrrp@domain.ru
  smtp_server 127.0.0.1
  smtp_connect_timeout 60
  router_id master
}
vrrp_instance my_site {
  #state MASTER
  interface ens33
  virtual_router_id 77
  priority 180
  nopreempt
  advert_int 5
  smtp_alert
  notify /home/frolov/keepalivednotify.pl root
  virtual_ipaddress {
    192.168.0.197/24
  }
}

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

Укажите здесь адрес электронной почты системного администратора notification_email, а также адрес notification_email_from, от имени которого будет приходить почта.

Также необходимо задать в параметре smtp_server адрес IP сервера SMTP и таймаут при отправке почты smtp_connect_timeout.

Необязательный параметр router_id предназначен для идентификации узла. По умолчанию здесь используется имя хоста, а мы указали строку «master».

Блок vrrp_instance задает параметры работы с VRRP. Строка после vrrp_instance используется для идентификации блока VRRP, и здесь можно указать, например, название сайта интернет-магазина. Это название должно быть одинаковым в файле конфигурации как для главного, так и для резервного сервера. Мы указали строку «my_site».

Параметр state задает начальное состояние узла при запуске keepalived. Он может иметь значение «MASTER» для основного сервера или «BACKUP» для резервного. Причины, по которой эта строка закрыта символом комментария, будут рассмотрены позже, при описании схем переключения с главного сервера на резервный и обратно.

Параметр interface задает имя сетевого интерфейса на сервере. Вы можете узнать его, например, командой ip -a:

# ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:34:5f:6c brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    inet 192.168.0.181/24 brd 192.168.0.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe34:5f6c/64 scope link
       valid_lft forever preferred_lft forever

В данном случае используется имя «ens33».

При настройке keepalive обязательно уточните имена сетевых интерфейсов на ваших серверах. При ошибке перенос виртуального адреса IP с главного сервера на резервный работать не будет.

Параметр virtual_router_id задает идентификатор блока VRRP, и он также должен быть одинаковый для главного и резервного серверов.

При помощи параметра priority нужно задать приоритет блока VRRP (от 1 до 255). Для главного сервера этот приоритет должен быть выше, чем для резервного. Если в сети VRRP два или несколько узлов, то в состояние MASTER переводится узел с наибольшим значением приоритета.

Если задан параметр nopreempt, параметр state должен иметь значение BACKUP, или он не должен указываться. Мы задаем этот параметр для того, чтобы предотвратить автоматическое переключение главного сервера в состояние MASTER, если этот сервер был какое-то время недоступен.

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

Параметр smtp_alert активирует отправку сообщения об изменении состояния по электронной почте. Параметры отправки задаются в ранее описанном блоке global_defs.

Если при изменении состояния узла нужно выполнять какие-либо действия, следует задать в параметре notify пусть к скрипту. Этот скрипт будет запущен, если состояние узла изменится. В параметре notify можно указать имя пользователя операционной системы, с правами которого будет запущен скрипт. Наш скрипт будет изменять файл конфигурации MariaDB, поэтому тут нужны права пользователя root.

И, наконец, блок virtual_ipaddress задает виртуальный адрес IP, который будет переходить с главного сервера на резервный, если главный сервер окажется недоступен.

Описание перечисленных выше параметров можно найти в документации keepalived.

Конфигурация keepalived резервного сервера

На резервном сервере подготовьте в файле /etc/keepalived/keepalived.conf другую конфигурацию (адреса электронной почты и адреса IP показаны только для примера):

global_defs {
  notification_email {
    admin@domain.ru
  }
  notification_email_from admin-vrrp@domain.ru
  smtp_server 127.0.0.1
  smtp_connect_timeout 60
  router_id backup
}
vrrp_instance my_site {
  state BACKUP
  interface ens33
  virtual_router_id 77
  priority 100
  nopreempt
  advert_int 5
  smtp_alert
  notify /home/frolov/keepalivednotify.pl root
  virtual_ipaddress {
    192.168.0.197/24
  }
}

Как видите, здесь параметр state задает начальное состояние узла как BACKUP. В параметре router_id узел идентифицируется строкой «backup».

Кроме того, приоритет для резервного сервера priority равен 100, он имеет меньшее значение, чем для главного (там указано 180, максимальное значение — 255).

В остальном файл конфигурации резервного сервера аналогичен файлу главного сервера.

Настройка репликации базы данных

В рабочем режиме база данных и файлы реплицируются с главного сервера на резервный. Однако при изменении состояния резервного сервера с MASTER на BACKUP необходимо отключить репликацию базы данных. Этим занимается упомянутый выше скрипт /home/frolov/keepalivednotify.pl.

Для тестирования мы создадим на основном и резервном сервере базу данных my_shop_db и настроим ее репликацию.

Подробнее о настройке репликации и ее мониторинга вы можете узнать из моей статьи «Репликация MySQL и MariaDB: мониторинг с помощью Zabbix».

Подключитесь на главном и резервном серверах к MariaDB как пользователь root:

# mysql -u root -p

Если вы делаете это в Debian, то пароль указывать не нужно, так как авторизация выполняется через сокет.

После этого на обоих серверах создайте базу данных my_shop_db и пользователя shop_db_user:

> CREATE DATABASE my_shop_db;
> CREATE USER 'shop_db_user'@'%' IDENTIFIED BY 'db_password';
> GRANT ALL PRIVILEGES ON my_shop_db.* TO 'shop_db_user'@'%' WITH GRANT OPTION;

Не забудьте изменить здесь пароль пользователя. Также задайте минимально необходимые права доступа пользователя к базе данных.

Далее на главном сервере добавьте в созданную базу данных таблицу goods и вставьте в эту таблицу две записи:

> use my_shop_db;
> create table goods(id int, name varchar(80));
> INSERT INTO goods VALUES (1, "sony123");
> INSERT INTO goods VALUES (2, "sony456");

Посмотрите содержимое таблицы:

> select * from goods;
+------+---------+
| id   | name    |
+------+---------+
|    1 | sony123 |
|    2 | sony456 |
+------+---------+


Итак, вы создали базы данных, теперь нужно отредактировать файлы конфигурации MariaDB /etc/mysql/mariadb.conf.d/50-server.cnf на обоих серверах для включения репликации.

Конфигурация MariaDB для сервера MASTER

Прежде всего, найдите в файле конфигурации параметр bind-address и закройте его символом комментария:

# bind-address            = 127.0.0.1

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

Далее добавьте в файл конфигурации MariaDB следующие строки:

### Replica master
server-id=182
log_bin=/var/log/mysql/mysql-bin.log
expire_logs_days=5
max_binlog_size=50M
sync-binlog=0
binlog_format=mixed
binlog-do-db=my_shop_db
innodb_flush_log_at_trx_commit=0
innodb_flush_method=O_DIRECT

Добавленные параметры описаны в статье "Репликация MySQL и MariaDB: мониторинг с помощью Zabbix".

Обратите внимание, что параметр server-id должен быть разным на главном и резервном сервере, а в параметре binlog-do-db следует указать имя реплицируемой базы данных.

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

# systemctl restart mysql
# systemctl status mysql

Конфигурация MariaDB для сервера BACKUP

В файле конфигурации MariaDB резервного сервера обязательно нужно закрыть символом комментария строку bind-address:

#bind-address            = 127.0.0.1

Добавьте в файл конфигурации строки:

### Replication Slave
server-id = 181
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 5
max_binlog_size = 50M
sync-binlog=0
binlog_format=mixed
relay-log = /var/log/mysql/mysql-relay-bin.log
replicate-do-db = my_shop_db
report-host=s01host.itmatrix.ru
slave_sql_verify_checksum=0
#skip_slave_start = 1 # prevent restart slave after failure

Здесь при помощи параметра replicate-do-db задается имя реплицируемой базы данных. Параметр report-host позволяет узнать имя сервера реплики с помощью команды «show slave hosts».

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

Создание пользователя репликации

Чтобы настроить репликацию, создайте пользователя repl_user на главном и резервном серверах:

> CREATE USER repl_user;
> GRANT REPLICATION SLAVE ON *.* TO repl_user IDENTIFIED BY 'repl_password';
> FLUSH PRIVILEGES;

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

Запуск репликации

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

> use my_shop_db;
> FLUSH TABLES WITH READ LOCK;

Далее в этом же окне посмотрите статус базы на главном сервере:

> SHOW MASTER STATUS;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      328 | my_shop_db   |                  |
+------------------+----------+--------------+------------------+

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

Теперь откройте еще одно консольное окно на главном сервере и запустите дамп базы данных:

$ mysqldump -ushop_db_user -pdb_password -hlocalhost --opt --quote-names --default-character-set=cp1251 my_shop_db  > my_shop_db.sql

Параметры дампа задайте исходя из нужной вам кодировки.

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

> UNLOCK TABLES;

Консольное окно со статусом базы данных пока не закрывайте.

Далее перенесите дамп на сервер реплики (то есть на резервный сервер):

# scp -v my_shop_db.sql frolov@192.168.0.181:/home/frolov/

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

$ mysql -ushop_db_user -pdb_password -hlocalhost my_shop_db < my_shop_db.sql

Теперь проверьте состояние репликации. Оно должно быть таким, как показано ниже:

> SHOW SLAVE STATUS\G
Empty set (0.000 sec)

После восстановления дампа базы почистите журналы в /var/log/mysql и запустите репликацию:

> RESET MASTER;

> CHANGE MASTER TO MASTER_HOST='192.168.0.182', MASTER_USER='repl_user', MASTER_PASSWORD='repl_password', MASTER_LOG_FILE = 'mysql-bin.000001', MASTER_LOG_POS = 328;

> START SLAVE;

Теперь снова проверьте состояние репликации:

> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                   Master_Host: 192.168.0.182
                   Master_User: repl_user
                   Master_Port: 3306
                 Connect_Retry: 60
               Master_Log_File: mysql-bin.000001
           Read_Master_Log_Pos: 328
                Relay_Log_File: mysql-relay-bin.000002
                 Relay_Log_Pos: 551
         Relay_Master_Log_File: mysql-bin.000001
              Slave_IO_Running: Yes
             Slave_SQL_Running: Yes
               Replicate_Do_DB: my_shop_db
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 328
               Relay_Log_Space: 856
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: No
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
               Master_SSL_Cert:
             Master_SSL_Cipher:
                Master_SSL_Key:
         Seconds_Behind_Master: 0  Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 182
                Master_SSL_Crl:
            Master_SSL_Crlpath:
                    Using_Gtid: No
                   Gtid_IO_Pos:
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: optimistic
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 0
1 row in set (0.000 sec)

 Если репликация работает, значения параметров Slave_IO_Running и Slave_SQL_Running должны быть равны Yes, а значение параметра Seconds_Behind_Master должно быть нулевым.

Теперь получите на главном сервере список узлов репликации:

> show slave hosts;
+-----------+---------------------+------+-----------+
| Server_id | Host                | Port | Master_id |
+-----------+---------------------+------+-----------+
|       181 | s01host.itmatrix.ru | 3306 |       182 |
+-----------+---------------------+------+-----------+

В этом списке должен быть резервный сервер s01host.itmatrix.ru.

Скрипт keepalivednotify.pl

Теперь подготовим скрипт /home/frolov/keepalivednotify.pl, который будет запускаться сервисом keepalived при изменении состояния сервера (MASTER или BACKUP).

Задачи этого скрипта:

  • сохранение текущего состояния сервера (MASTER или BACKUP) в файле /home/frolov/node_keepalive_state.txt;

  • ведение файла журнала /home/frolov/node_keepalive_log.txt, отражающего действия, предпринятые скриптом keepalivednotify.pl при изменении состояния сервера;

  • переключение режима репликации MariaDB при переходе сервера из состояния MASTER в состояние BACKUP

Параметры скрипта keepalivednotify.pl

Когда сервис keepalived запускает скрипт, указанный в параметре notify файла конфигурации /etc/keepalived/keepalived.conf, то он передает ему три параметра. Это строка идентификатора блока VRRP, заданная в параметре vrrp_instance файла конфигурации keepalived, группа (если она задана в параметре vrrp_sync_group), а также состояние узла (MASTER или BACKUP).

Программа получает эти параметры и сохраняет их в соответствующих переменных:

my $instance = $ARGV[0];
my $group = $ARGV[1];
my $state = $ARGV[2];

Файлы состояния и журнала

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

my $datestring = localtime();
my $host = hostname;

Полученная информация добавляется в соответствующие файлы:

my $log_file = '/home/frolov/node_keepalive_log.txt';
open($fh, '>>:encoding(UTF-8)', $log_file) or die "Can't open file '$log_file'";
my $node_keepalive_state_file = '/home/frolov/node_keepalive_state.txt';
my $state_str = $instance.'_'.$group.'_'.$state.','.$host.','.$datestring."\n";
write_file($node_keepalive_state_file, $state_str);
print $fh "$datestring: $host ($instance $group) -> $state\n";

Действия при изменении состояния сервера

Далее программа keepalivednotify.pl анализирует имя сервера, на котором она запущена, и его состояние, предпринимая соответствующие действия:

if($host eq 's01host.itmatrix.ru' and $state eq 'MASTER')   # BACKUP go to MASTER
{
  my $rc = stop_replica($conf);
  print $fh "$datestring: $host ($instance $group) -> Stop MySQL Replication\n";
}
elsif($host eq 's01host.itmatrix.ru' and $state eq 'BACKUP')   # BACKUP return from MASTER to BACKUP
{
  print $fh "$datestring: $host ($instance $group) -> BACKUP node return from MASTER to BACKUP\n";
}
elsif($host eq 's01host.itmatrix.ru' and $state eq 'STOP')   # BACKUP return from MASTER to BACKUP
{
  print $fh "$datestring: $host ($instance $group) -> BACKUP node goto STOP\n";
}
elsif($host eq 'm01host.itmatrix.ru') # MASTER state
{
  print $fh "$datestring: $host ($instance $group) -> MASTER node goto $state\n";
}
else
{
  print $fh "$datestring: $host ($instance $group) -> Invalid host $host or invalid state $state\n";
}
close $fh;

Остановка репликации и замена файла конфигурации Maria DB

Если состояние резервного узла стало MASTER, то резервный сервер стал основным. В этом случае с помощью функции stop_replica скрипт keepalivednotify.pl останавливает репликацию и меняет файл конфигурации MariaDB таким образом, чтобы после перезагрузки резервного сервера репликация не запустилась вновь автоматически.

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

Функция stop_replica получает в качестве параметра ссылку на хэш, содержащий информацию, необходимую для отключения репликации и для манипуляций с файлами конфигурации MariaDB:

my $conf=
{
  'mysql_cmd' => '/usr/bin/mysql -s -uroot -pwJzBruD6WOzV9VCx -e ',
  'mysql_config_file' => '/etc/mysql/mariadb.conf.d/50-server.cnf',
  'mysql_master_config_file' => '/home/frolov/vrrp/mysql_config/50-server.cnf_master',
  'mysql_slave_config_file' => '/home/frolov/vrrp/mysql_config/50-server.cnf_slave',
  'mysql_slave_no_replication_config_file' => '/home/frolov/vrrp/mysql_config/50-server.cnf_slave_no_replication',
};

Для рабочего проекта в этом хэше необходимо заменить пароль root для подключения к MariaDB. Лучше всего хранить эти данные не в тексте скрипта, а в отдельном файле, например, формата JSON.

Получив управление, функция stop_replica прежде всего останавливает репликацию с помощью команды «stop slave IO_THREAD»:

my ($conf) = @_;
my $rc = {};
my $cmd = $conf->{mysql_cmd}."\'stop slave IO_THREAD'";
my @rqout = ();
@rqout = split /\n/, `$cmd`;

Далее функция дожидается завершения исполнения сервером реплики всех команды из журнала relay в своей базе при помощи функции relay_log_done.

Если выполнение команд завершено, функция останавливает репликацию и копирует новый файл конфигурации Maria DB:

if(relay_log_done($conf))
{
  $cmd = $conf->{mysql_cmd}."\'STOP SLAVE'";
  @rqout = ();
  @rqout = split /\n/, `$cmd`;
  $cmd = $conf->{mysql_cmd}."\'RESET MASTER'";
  @rqout = ();
  @rqout = split /\n/, `$cmd`;

  if(!copy($conf->{ 'mysql_slave_no_replication_config_file'}, $conf->{'mysql_config_file'}))
  {
    $rc->{ 'err_message' } = "MySQL config file copy failed: $!";
    $rc->{ 'rc' } = 'err';
    return($rc);
  }
  $rc->{ 'rc' } = 'ok';
}

Что касается функции relay_log_done, то она выполняет несколько попыток обнаружить строку «Slave has read all relay log; waiting for more updates» в результатах выдачи команды «SHOW PROCESSLIST»:

sub relay_log_done
{
  my ($conf) = @_;
  my $cmd = $conf->{mysql_cmd}."\'SHOW PROCESSLIST'".'|grep "Slave has read all relay log; waiting for more updates"';
  my @rqout;
  for(my $i=0; $i <= 5; $i++)
  {
    @rqout = ();
    @rqout = split /\n/, `$cmd`;
    if(defined $rqout[0]) { return 1; }
    sleep(3);
  }
  return 0;
}

В рабочем проекте записывайте все ошибки, полученные от функций в файл журнала, или обрабатывайте их каким-либо иным способом.

Исходный текст скрипта keepalivednotify.pl можно загрузить здесь.

При использовании этого скрипта в рабочих проектах внесите в него необходимые изменения, в частности, измените пароль доступа к Maria DB.

Также не забудьте сделать файл keepalivednotify.pl исполняемым:

# chmod +x /home/frolov/keepalivednotify.pl

Управление репликацией файлов

О том, как настроить репликацию файлов при помощи rsync и мониторинг этой репликации читайте в статье «Репликация файлов через rsync: мониторинг с помощью Zabbix», написанной мной для блога FirstVDS.

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

Для проверки скрипт анализирует содержимое файла состояния /home/frolov/node_keepalive_state.txt, а также имя hostname сервера.

В нашем примере hostname резервного сервера — s01host.itmatrix.ru. Если при этом файл состояния содержит строку INSTANCE_my_site_BACKUP, то можно запускать репликацию файлов:

my $node_keepalive_state_file = '/home/frolov/node_keepalive_state.txt';
my $state_file_content = read_file($node_keepalive_state_file);
my $host = hostname;
if($host eq 's01host.itmatrix.ru' and $state_file_content=~/INSTANCE_my_site_BACKUP/)
{
  # Запуск репликации файлов 
  …
}

Когда резервный сервер перейдет в состояние MASTER, репликация файлов запускаться не будет, так как в файле состояния будет строка «INSTANCE_my_site_MASTER».

Управление пакетными заданиями

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

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

В качестве решения можно предложить запуск через crontab пакетной программы с правами пользователя root, которая будет определять, доступен ли на сервере виртуальный IP. Если виртуальный IP доступен, то сервер находится в состоянии MASTER, и программа запускает необходимые пакетные задания интернет-магазина (или другого сервиса).

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

my $node_keepalive_state_file = '/home/frolov/node_keepalive_state.txt';
my $state_file_content = read_file($node_keepalive_state_file);
my $ipa_cmd = 'ip a | grep 192.168.0.197';
my @ipaout = ();
@ipaout = split /\n/, `$ipa_cmd`;
my $vip_is_up=0;
if($ipaout[0]) { $vip_is_up=1; }
if($state_file_content=~/INSTANCE_my_site_MASTER/ and $vip_is_up == 1)
{
  my $cmd = 'sudo -u siteuser /usr/bin/perl /home/siteuser/data/www/my-site.ru/cgi-bin/batch_job.pl >/dev/null 2>&1';
  my @rqout = ();
  @rqout = split /\n/, `$cmd`;
}

Для рабочего проекта тут нужно как минимум заменить адрес виртуального IP и путь к пакетной программе вашего интернет-магазина.

Запуск отказоустойчивой системы

При первом запуске keepalived на главном сервере он перейдет в состояние BACKUP, а через некоторое время — в состояние MASTER. Серверу будет выделен виртуальный адрес IP, в нашем случае 192.168.0.197.

Если теперь запустить keepalive на резервном сервере, то этот сервер останется в состоянии BACKUP, потому что его приоритет, заданный параметром  priority, ниже, чем у главного сервера.

Проверка переключения виртуального адреса IP

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

Предполагая, что главный сервер находится в состоянии MASTER, а резервный — в состоянии BACKUP, остановите сервис keepalived на главном сервере. Так вы смоделируете выход из строя главного сервера.

Через некоторое время резервный сервер перейдет в состояние MASTER и получит виртуальный IP, как и должно быть.

Обратное переключение

Теперь предположим, что главный сервер снова оказался доступен. Запустите на нем сервис keepalived.

Можно было бы ожидать, что виртуальный IP «перепрыгнет» назад на главный сервер. Но этого не случиться, так как в конфигурации keepalived задан параметр nopreempt.

Зачем используется этот параметр?

Дело в том, что как только виртуальный IP перешел на резервный сервер и тот получил статус MASTER, посетители сайта стали работать уже с резервным сервером, изменяя его базу данных. Если теперь переключить виртуальный IP обратно на главный сервер, все изменения в базе данных резервного сервера будут потеряны.

Именно поэтому обратное переключение на главный сервер всегда выполняется вручную.

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

Контроль трафика keepalived

Для того чтобы понять, как работает keepalived, можно посмотреть широковещательный трафик от этого сервиса с помощью утилиты keepalived.

Установите tcpdump на главном и резервном сервере:

# apt install tcpdump

Далее в отдельных консольных окнах запустите такую команду:

tcpdump -c 10 -i ens33 host 224.0.0.18

Эта команда покажет трафик, приходящий на адрес 224.0.0.18 сетевого интерфейса ens33 (у вас может быть другой интерфейс).

Пока главный и резервный сервер работают в обычном режиме, keepalived сообщает о том, что он жив, рассылая с главного сервера широковещательные запросы такого вида:

13:47:35.948780 IP m02host > 18.0.0.224.in-addr.arpa: VRRPv2, Advertisement, vrid 77, prio 180, authtype none, intvl 5s, length 20

Если же остановить keepalived на главном сервере, то резервный сервер, перестав получать широковещательные пакеты от этого сервера,  переходит в состояние MASTER. Теперь он уже сам начинает рассылать широковещательные пакеты:

13:49:18.344152 IP 192.168.0.181 > 18.0.0.224.in-addr.arpa: VRRPv2, Advertisement, vrid 77, prio 100, authtype none, intvl 5s, length 20

Как видите, в этих пакетах есть идентификатор блока VRRP, заданный параметром virtual_router_id в файле конфигурации keepalived, приоритет priority, а также интервал advert_int.

Таким образом, если серверы с установленным keepalived и одинаковым идентификатором блока VRRP (а их может быть больше двух) перестают получать широковещательные пакеты от главного сервера, то между ними через время, заданное параметром advert_int начинаются новые выборы сервера MASTER. При выборе сравниваются приоритеты, заданные параметром priority.

Заметим, что если на серверах установлен файрволл, необходимо разрешить широковещательный трафик, добавив правила вида:

iptables -A INPUT -I ens33 -d 224.0.0.0/8 -j ACCEPT

Настройка конфигурации NGINX

Очень часто в качестве обратного прокси для сайтов используется NGINX. Заметим, что в конфигурации NGINX для сайта обычно указывается адрес IP сервера, например:

server {
  server_name my-site.ru www.my-site.ru;
  …
  listen xxx.xxx.xxx.xxx:80;
  …
}

Чтобы сайт был доступен на виртуальном IP, на главном и резервном серверах укажите в параметре listen адрес 0.0.0.0:

server {
  server_name my-site.ru www.my-site.ru;
  …
  listen 0.0.0.0:80;
  …
}

После изменения адреса перезапустите сервис NGINX.

В том случае, если на сервере установлена какая-либо панель управления сервером, защитите отредактированный файл конфигурации от изменений с помощью команды chattr:

# chattr +i /etc/nginx/vhosts/siteuser/my-site.ru.conf

Восстановить возможность внесения изменений можно так:

# chattr -i /etc/nginx/vhosts/siteuser/my-site.ru.conf

Если этого не сделать, то любые операции с конфигурацией NGINX для сайта могут привести к восстановлению адреса IP, что, в свою очередь, приведет к недоступности сайта.

О чем еще нужно помнить

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

В частности, вам нужно настроить мониторинг основного и резервного сервера, выполнять резервное копирование данных как на основном, так и на резервном сервере и хранить эти копии отдельно.

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

Мы также рекомендуем настроить мониторинг состояния серверов (MASTER или SLAVE) с помощь Zabbix. В скрипте мониторинга можно контролировать содержимое файла состояния, а также наличие на сервере виртуального IP.

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
-1
Comments 44
Comments Comments 44

Articles