Веб-кластер для бедных

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

Если выделить типовые задачи, которые можно решить путем создания веб-кластера, это:
1) возможность распределить нагрузку по серверам путем обычного round robin на уровне dns;
2) возможность «вынести» сервер поближе к пользователям, когда для одной страны/города, трафик отдается с одного сервера, а для остальных, с другого;
3) возможность делать бэкапы на узле который не участвует в отдаче трафика, но тем не менее, содержит на себе всю актуальную информацию и т.п.

Рассмотрим типовую конфигурацию сайта: php + mysql на каком-нибудь из линуксов.
Итак, имеем два сервера, которые хотим настроить как равноправные узлы кластера (причём эта схема будет работать и на большинстве хостинг-панелях)

Пусть сайты лежат в /var/www, а в качестве СУБД используется Mysql.

Первым делом, нужно настроить синхронизацию файловой системы. Сейчас существует довольно большое количество сетевых файловых систем, которые позволяют «размазать» файлы по серверам, сделать raid поверх сети, но в большинстве случаев, функционал и сложность их настройки будут избыточны для проекта. Одним из удобных и быстрых решений, решающий проблему на базе существующих файловых систем, а не специализированных сетевых, является lsyncd, которое подкупает своей простотой, надежностью и скоростью.

Принцип работы относительно прост, приложение осуществляет живую синхронизацию данных за счет контроля событий в директории на уровне ядра операционной системы (inotify / fsevents) и последующей синхронизации изменений через rsync/ssh. Это дает мгновенную реакцию на событие (утилита получает изменения сразу как они произошли) и через короткий промежуток времени (настраивается в конфигурации) отправляет все изменения на подчиненный сервер. Соответственно, если серверов два, то можно закольцевать, либо, если речь о сервере разработки, то сделать синхронизацию только в одну сторону.

Рассмотрим на примере, основной сервер — node1 (192.168.2.1), дополнительный сервер — node2 (192.168.0.60).

Итак, на примере операционной системы debian и файлов в /var/www примерный план работ будет следующим:

1) добавляем в hosts записи (чтобы проще было в плане контроля)
node1 192.168.2.1
node2 192.168.0.60

2) генерируем открытые ключи на обоих серверах (ssh-keygen и жмем Enter пока не будет выдано приглашение повторно)
3) перекидываем их через между серверами:
с node1
ssh root@node2 'echo ' $(< ~/.ssh/id_rsa.pub) ' >> ~/.ssh/authorized_keys ; chmod 600 ~/.ssh/authorized_keys'
с node2
ssh root@node1 'echo ' $(< ~/.ssh/id_rsa.pub) ' >> ~/.ssh/authorized_keys ; chmod 600 ~/.ssh/authorized_keys'
и проверяем, что по ssh root@node1 / ssh root@node2 можем ходить между серверами
4) apt-get install lsyncd ставим lsyncd на обоих серверах
5) создаем папку конфига, логов и временных файлов
mkdir /etc/lsyncd/ /var/log/lsyncd/ /var/spool/lsyncd/
6) создаем конфигурационный файл на node1
/etc/lsyncd/lsyncd.conf.lua
с содержимым:
settings = {
logfile = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd-status.log",
statusInterval = 10
}

sync {
default.rsyncssh,
source="/var/www",
host="node2",
targetdir="/var/www",
rsync = {
archive = true,
temp_dir = "/var/spool/lsyncd/",
compress = false,
whole_file = false
},
ssh = {
port = 22
}
}

Если версия lsyncd 2.1 и старше (проверить через lsyncd —version) то в блоке settings = { нужно убрать знак равно, чтобы было settings {
Теперь можно запустить синхронизацию
/etc/init.d/lsyncd start

Если всё сделано правильно, то спустя какой-то период времени (зависит от объема информации), файлы будут синхронизированы на node2, после этого, можно добавить конфиг /etc/lsyncd/lsyncd.conf.lua с выше содержимым, только заменив host=»node2″ на host=»node1″ и запустить lsyncd на втором сервере.

Несколько замечаний.
Если делать на живой системе, нужно обратить внимание, чтобы под синхронизацию не попадали файлы кэша, сессий, бэкапов.
Если большое число вложенных директорий, то необходимо дополнительно осуществить тюнинг ядра:
echo "
fs.inotify.max_user_watches = 16777216
fs.inotify.max_queued_events = 65536
" >> /etc/sysctl.conf
echo 16777216 > /proc/sys/fs/inotify/max_user_watches
echo 65536 > /proc/sys/fs/inotify/max_queued_events

Покончив с файлами, можно перейти к репликации mysql