Синхронизация двух серверов Apache + MySQL на FreeBSD

:

В данном обзоре я расскажу о реализации кластера состоящего из двух нод с резервированием популярной связки для веб сервера Apache + MySQL + FreeBSD (или любой Linux).

Коротко о серверах. В каждом сервере находится по две сетевые карты. Одной они подключены — к свитчу, между вторыми короткий патчкорд, чтобы не нагружать наш свитч лишним трафиком.
Соответственно два сетевых интерфейса em0 – для внешки, rl0 – для репликации.
На первом сервере для интерфейса rl0 ставим IP 192.168.0.1 на втором 192.168.0.2.

1. APACHE
Задача — синхронизация файлов виртуальных хостов.
Будем использовать rsync в связке с ssh. Для этого на первом сервере в файле /etc/ssh/sshd_config пишем:

AllowUsers root@192.168.0.2
PermitRootLogin yes

На втором аналогично:
AllowUsers root@192.168.0.1
PermitRootLogin yes

Т е. разрешаем доступ по ssh пользователю root, между серверами. Для каждого виртуального домена используется разные пользователи, соответственно UID у них разные. Можно конечно же создать и отдельного пользователя для репликации файлов, который сможет получить доступ к файлам каждого пользователя, но использовать root-а проще, тем более уязвимости тут нет никакой, т.к. доступ по SSH суперпользователь root имеет только с одного внутреннего IP адреса, что мы явно указали в конфигурационных файлах.

Далее разрешим доступ root без пароля, для этого сгенерируем пару ключей:
ssh-keygen -t rsa (passphrase не указываем)
scp /root/.ssh/id_rsa.pub root@192.168.0.2:/root/.ssh/authorized_keys2

и аналогично на втором сервере:
ssh-keygen -t rsa (passphrase не указываем)
scp /root/.ssh/id_rsa.pub root@192.168.0.1:/root/.ssh/authorized_keys2

Далее на втором сервере создаём, к примеру, в /root/scripts/ файл, назовём его sync.sh:
/usr/local/bin/rsync -e 'ssh -l root -i /root/.ssh/id_rsa' --progress -lzuogthvr --compress-level=9 --delete-after root@192.168.0.1:/opt/vhosts/sitename.ru/ /opt/vhosts/sitename.ru/ >> /root/logs/sync.sitename

sitename.ru – имя виртуального хоста

Разрешим запуск:
chmod +x /root/scripts/sync.sh

Пропишем в /etc/crontab:
0 */1 * * * root /root/scripts/sync.sh >/dev/null 2>&1
В моём примере синхронизация выполняется каждый час.

2. MySQL
На первом сервере в файле my.cnf пишем следующее:
log-bin=mysql-bin
binlog_format=mixed
server-id = 1
slave-compressed = 1
binlog-do-db = base (название бд, которую хотим реплицировать)
replicate-wild-ignore-table=base.chat (в моём примере chat – MEMORY TABLE не содержащая важных данных, игнорируем)

Будем выполнять репликацию под отдельным пользователем, назовём его repluser разрешаем ему доступ с IP 192.168.0.2 и даём глобальные права SELECT, RELOAD, SUPER, REPLICATION SLAVE.

На втором сервере в my.cnf:
max-user-connections = 50
master-host = 192.168.0.1
master-user = repluser (наш пользователь)
master-password = <пароль для repluser>
server-id = 2 (должно отличатся от ID мастера!)
replicate-do-db = base (имя бд)

Дальше
На первом сервере выполняем:
mysql> FLUSH TABLES WITH READ LOCK;
mysql> show master status;

Увидим что-то вроде этого:
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000031 | 2073 | base | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

обязательно сохраняем вывод в текстовый файлик! Далее:
mysqldump -u root -p base > /root/base.db

mysql> UNLOCK TABLES;

переносим полученный дамп на 2-ой сервер.

На втором сервере:
mysql>CREATE DATABASE base;
mysql> USE base;
mysql> SOURCE /root/base.db (путь к нашему дампу)

mysql> CHANGE MASTER TO MASTER_LOG_FILE='srv011-bin.000813';
Query OK, 0 rows affected (0.05 sec)

srv011-bin.000813 — то что писали в текстовый файлик

mysql> CHANGE MASTER TO MASTER_LOG_POS=1156293;
Query OK, 0 rows affected (0.05 sec)

1156293 — оттуда же

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

Репликация работает! Проверим состояние репликации коммандой:

mysql> SHOW SLAVE STATUS\G;

3. Heartbeat
Реализация кластера. Т. к. способом обновления ядра первоначально было выбрано — бинарное обновление, самый удобный вариант CARP отпадал. CARP отличный инструмент, к сожалению, не доступный без пересборки ядра. Значит выбираем heartbeat – довольно известное ПО, к сожалению портированное с linux, из-за чего тянет уйму не нужных зависимостей, но это не так страшно.

cd /usr/ports/sysutils/heartbeat
make && make install && make clean или portmaster sysutils/heartbeat

Переходим к настройке, для этого на первом сервере:
/usr/local/etc/ha.d/authkeys:

auth 1
1 crc

/usr/local/etc/ha.d/ha.cf:

crm off
logfile /var/log/heartbeat.log
keepalive 2
deadtime 10
udpport 694
ucast rl0 192.168.0.2
auto_failback on
node srv1.sitename.ru
node srv2.sitename.ru

rl0 – имя интерфейса на котором выполняем синхронизацию

/usr/local/etc/ha.d/hareresources:
srv1.sitename.ru 212.212.212.212/28/em0
212.212.212.212 — наш белый IP

Аналогично на втором сервере, за исключением IP адреса в /usr/local/etc/ha.d/ha.cf:
ucast rl0 192.168.0.1

Вроде бы всё, но в реализации для FreeBSD вылезла неприятна ошибка, при старте heartbeat не хотел выставлять правильную маску подсети, да к тому же в упор не знал что такое route. К счастью решается довольно просто, идём в:
/usr/local/etc/ocf/resources.d/heartbeat/IPaddr

Ищем строку
CMD="$IFCONFIG $iface inet $ipaddr netmask 255.255.255.255 alias";;

И меняем её на:
CMD="$IFCONFIG $iface $ipaddr netmask $netmask broadcast $broadcast; route add default <IP шлюза по-умолчанию>";;

Теперь всё работает как надо.

Hearbeat можно управлять командами:

Сделать ноду принудительно основной:
/usr/local/lib/heartbeat/hb_takeover
Сделать ноду принудительно запасной:
/usr/local/lib/heartbeat/hb_standby