Содержание статьи
Допустим, у нас есть один физический довольно мощный сервер. Конкретные характеристики приводить не буду, представим, что это современный сервер со средними или чуть выше средних параметрами. Ты хочешь его использовать в качестве веб-сервера для своего сайта, но в то же время нужно обеспечить его отказоустойчивость, а физический сервер всего один.
Классическая схема отказоустойчивости реализуется при наличии хотя бы двух физических серверов. Один — основной, второй — резервный. Понятно, что если что-то происходит с основным, то в дело вступает резервный. В нашем случае, поскольку нет второго физического сервера, мы будем использовать два виртуальных. Расчет довольно прост: если произойдет какой-то программный глюк на основном виртуальном сервере, то будет работать резервный.
Недостаток такого вида отказоустойчивости в том, что если что-то случится с железом, то тебе уже ничто не поможет. Но, как говорится, это лучше, чем ничего. К тому же приведенные в статье инструкции ты сможешь использовать для настройки полноценной отказоустойчивости, когда у тебя появится второй физический сервер.
Выбор решения виртуализации
Какое решение будем использовать для виртуализации? Первое, что приходит на ум, — это старый добрый OpenVZ. Добрый-то он добрый, но уж очень старый, я бы даже сказал — древний, а его ядро вообще родом из мезозойской эры. Другими словами, если у тебя современный дистр, то, установив OpenVZ, ты сделаешь эдакий downgrade. Но выход есть: наследник OpenVZ — Virtuozzo.
Преимущество Virtuozzo в том, что устанавливается он на голое железо и представляет собой отдельный дистрибутив Linux (Virtuozzo Linux), который уже оптимизирован для задач виртуализации и хостинга. Все, что нужно, — взять и установить его на машину, которая будет сервером виртуализации.
Недостаток в том, что Virtuozzo — решение коммерческое и придется немного потратиться. Дополнительную информацию ты сможешь найти на официальной страничке продукта.
Получение trial-версии
Прежде чем покупать Virtuozzo, ты можешь его попробовать, чтобы окончательно решить, понравится он тебе или нет. Заходи на их сайт и жми кнопку Try Virtuozzo 7 Now
. Далее нужно заполнить несложную форму и нажать кнопку Submit
. После этого ты попадешь на страничку, на которой можно скачать ISO-шник и документацию. Линк на ISO-шник и документацию также будет продублирован на твой email, если ты захочешь скачать Virtuozzo и/или документацию позже.
Триальная лицензия дается во время установки Virtuozzo 7 на хост, поэтому важно наличие интернет-соединения. Trial-версия работает 30 дней — думаю, этого вполне достаточно, чтобы принять решение о покупке.
Создание виртуальных серверов
Нам нужно создать два контейнера (естественно, после установки Virtuozzo Linux на физический компьютер), а затем установить в них все самое необходимое. Сначала создадим сами контейнеры:
# prlctl create first --vmtype ct --ostemplate centos-6-x86_64
# prlctl create second --vmtype ct --ostemplate centos-6-x86_64

Как ты уже догадался, это будут контейнеры для первого и второго серверов. После создания контейнеров нужно их настроить:
# prlctl set <контейнер> --cpus 1
# prlctl set <контейнер> --memsize 1G --swappages 512M
# prlctl set first --hostname first.example.com
# prlctl set first --ipadd 192.168.52.101
# prlctl set second --hostname second.example.com
# prlctl set second --ipadd 192.168.52.102

Здесь нужно ввести актуальные параметры. Я выделил каждому виртуальному серверу по одному процессору и гигабайту оперативки, своп — 512 Мбайт (на всякий случай). Можно было бы выделить и 512 Мбайт оперативки — большинство сайтов работают на виртуальном хостинге с худшими характеристиками. Также доменные имена и IP-адреса нужно указать собственные, у меня демонстрационные.
Запускаем виртуальный сервер:
# prlctl start <контейнер>
Устанавливаем пароль root для обоих контейнеров:
# prlctl exec <контейнер> passwd
Подключаемся к серверу по SSH:
# ssh <IP-адрес>
По сути, на данный момент у тебя уже есть два полноценных сервера, которые осталось только настроить.

Настройка сети
Прежде чем приступить к настройке серверов, нужно настроить сетку, иначе в виртуальном сервере yum работать не будет и софт ты все равно не установишь. На данный момент мы установили только доменные имена и IP-адреса виртуальных узлов. Но этого мало. Нужно настроить NAT, разрешить доступ к виртуальным серверам извне и настроить DNS.
Начнем с настройки NAT. В Virtuozzo Linux пакет iptables-services установлен по умолчанию, а IPv4-форвардинг включен (в файле cat /proc/sys/net/ipv4/ip_forward
есть единичка), поэтому никакие подготовительные действия не понадобятся: сразу приступаем к настройке правил iptables. Для NAT определенного контейнера используется команда
# iptables -t nat -A POSTROUTING -s src_net -o if -j SNAT --to ip_address
Здесь вместо src_net
нужно указать IP-адрес подсетки контейнера, вместо if
— интерфейс, а вместо ip_address
— внешний IP-адрес твоего аппаратного узла. Но можно сделать проще и ввести команду
# iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE
Ошибка?
Если система будет ругаться на отсутствие таблицы NAT, не обращай внимания. Видимо, есть какой-то конфликт между версией iptables и ядра, разбираться не стал, но самое интересное, что правила работают :).
В этом случае все IP-адреса будут транслироваться SNAT. Именно эту команду я и ввел на рис. 4.

На радостях запускаем контейнер и... обнаруживаем, что пропинговать-то узел по IP мы можем, а вот DNS настроить забыли. Ничего, все это решается командой
# prlctl set <контейнер> --nameserver 8.8.8.8
Пока используем OpenDNS, ведь свой мы еще не настроили. Теперь нужно убедиться, что мы можем пропинговать узел по его имени:
# ping mail.ru

Вот, теперь все в порядке и можно приступить к установке софта. Только не забудь настроить доступ к виртуальным серверам извне, иначе никто не сможет к ним достучаться:
# iptables -t nat -A PREROUTING -p tcp -d ip_address --dport port_num \
-i ens33 -j DNAT --to-destination ve_address:dst_port_num
Здесь ve_address
— это IP-адрес контейнера, dst_port
— TCP-порт, ip_address
— внешний (публичный) IP-адрес твоего узла, а port_num
— TCP-порт аппаратного узла, который будет использоваться для интернет-соединений к приватным контейнерам. Обрати внимание, что данное правило сделает сервис, который раньше висел на порту с заданным номером (port_num), недоступным. Также обрати внимание, что трансляция SNAT, которую мы делали раньше, тоже необходима.
Если ты хочешь, чтобы порт 80 был доступен на аппаратном узле, а доступ к виртуальным серверам реализован через порт 8080, используй такие правила:
# iptables -t nat -A PREROUTING -p tcp -d ip_address --dport 8080 \
-i eth0 -j DNAT --to-destination ve_address:80
# iptables -t nat -A POSTROUTING -s ve_address -o eth0 -j SNAT --to ip_address
Тогда «достучаться» до виртуальных серверов можно будет так: http://ip_address:8080/.
Осталось только сохранить правила iptables:
# service iptables save
# service iptables restart
Теперь у наших виртуальных серверов есть доступ к интернету, они могут пропинговать друг друга и к ним можно обратиться извне (рис. 6, 7). По сути, они мало чем отличаются от обычных интернет-серверов.


Настройка DNS
Далее нужно установить на физический компьютер пакет bind — это DNS-сервер. Пакет хоть и имеет версию 9, но называется просто bind. Каталог /etc/bind
отсутствует (вместо него — каталог /etc/named
, но он пуст), а конфиг самого сервера находится в /etc/named.conf
. Это я описываю отличия настройки пакета bind в Virtuozzo Linux от привычных дистрибутивов. Во всем остальном bind настраивается так же, как в любом другом дистрибутиве Linux.
Твоя цель — создать две A-записи на www.example.com в зоне example.com:
@ IN A 192.168.52.101
www IN A 192.168.52.101
www IN A 192.168.52.102
first IN A 192.168.52.101
second IN A 192.168.52.102
Полные конфиги не стану приводить по двум причинам. Первая — все они стандартны и только будут занимать место (да и Google не любит дублирование контента :)). Вторая — все равно у тебя будет реальная зона, у меня же она демонстрационная. Если ты повторяешь все написанное в статье в VMware/Windows (то есть «физический» сервер с Virtuozzo Linux — это виртуальная машина VMware), то можно для обеспечения отказоустойчивости добавить в C:\Windows\System32\drivers\etc\hosts
строки
192.168.52.101 www.example.com
192.168.52.102 www.example.com
Это тоже две A-записи, но только в Windows. После этого введи команду ipconfig /flushdns.
В Linux проверить наш сценарий также можно без DNS-сервера — отредактировав файл /etc/hosts
и добавив в него те же записи. Но на практике тебе будет нужен DNS-сервер, делегирующий твой домен. Так что управление доменом придется перетащить к себе или же к регистратору, где есть вменяемая панель управления зоной — с возможностью добавления необходимых тебе записей.
Проверка отказоустойчивости
Для начала расскажу, почему все это будет работать. Современные браузеры, получив несколько A-записей, пытаются зайти на сайт, используя первый IP-адрес. Если он недоступен, они используют второй и так далее. Другими словами, таких вот серверов у тебя может быть даже не два, а гораздо больше. Например, у Google их одиннадцать, а у Mail.Ru — четыре (рис. 8).

Чтобы проверить отказоустойчивость, нам нужно сделать разным контент на первом и втором серверах. Ведь сейчас открывается одна и та же страничка (Apache 2 Test Page), и мы просто-напросто не будем знать, с какого сервера получен ответ. Поэтому, чтобы не использовать дополнительные инструменты для отслеживания пакетов, проще всего зайти на второй сервер и отредактировать файл /var/www/html/index.html
. Пусть его содержимое будет вот таким простым:
<h1>Second server</h1>
После этого нужно остановить первый сервер и обратиться к www.example.com. Но обо всем по порядку:
ssh 192.168.52.102
[root@second]: echo "<h1>Second server</h1>" > /var/www/html/index.html
[root@second]: exit
prlctl stop first
Первая команда обеспечивает вход на второй сервер. Вторую мы уже вводим в SSH-сеансе со вторым сервером. Она добавляет «контент» в index.html. Затем мы выходим со второго сервера и останавливаем первый.

Осталось открыть твой любимый браузер и ввести URL http://www.example.com. У тебя должна появиться страничка с надписью Second server (рис. 10). Если ты увидел тестовую страничку Apache, то что-то ты сделал не так. Копать нужно в сторону DNS (ну или ты забыл снести тестовый контент на втором сервере).

Настройка синхронизации
По сути, поставленная задача, а именно отказоустойчивость работает. Но нам нужно еще настроить синхронизацию контента, чтобы все работало как следует.
Если у тебя только HTML-контент или же движок, который хранит данные в файлах, то все очень просто. Достаточно только настроить синхронизацию каталогов двух веб-серверов (кстати, сами серверы httpd на виртуальных серверах оказались уже установленными — очень удобно). Если же контент хранится в базе данных MySQL или какой-либо другой, то нужно настраивать репликацию БД, однако это тема для отдельной статьи. Поэтому есть два выхода — или разбираться с репликацией MySQL, или писать сценарий на PHP, который будет делать импорт/экспорт БД на второй сервер. Как по мне, проще и эффективнее использовать уже существующие механизмы репликации, а не изобретать велосипед заново.
Сейчас мы рассмотрим, как синхронизировать HTML-контент. На обоих серверах нужно установить lsyncd (если не требуется двусторонняя синхронизация, то достаточно установить пакет только на первом):
rpm -ivh http://mirror.yandex.ru/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
yum install lsyncd
Первая команда добавляет нужный репозиторий, вторая устанавливает lsyncd. Эти команды надо вводить на всех виртуальных серверах. При создании виртуальных серверов я использовал шаблоны CentOS. Если ты выбрал Debian-совместимый шаблон, то для установки lsyncd используй apt-get:
# apt-get install lsyncd
Репозиторий подключать не требуется, если ты выбрал шаблон Ubuntu или Debian начиная с версии 7. Там lsyncd уже находится в репозитории.
После установки lsyncd нужно создать каталоги для хранения логов и временных файлов (создай эти каталоги на двух серверах, если в будущем понадобится двусторонняя синхронизация):
# mkdir -p /var/log/lsyncd
# mkdir -p /var/www/temp
Теперь отредактируем основной файл конфигурации первого сервера /etc/lsyncd.conf
:
settings {
logfile = "/var/log/lsyncd/lsyncd.log",
statusFile = "/var/log/lsyncd/lsyncd.status",
nodaemon = true
}
sync {
default.rsyncssh,
source="/var/www/html",
host="second.example.com",
targetdir="/var/www/html",
rsync = {
archive=true,
compress=true,
temp_dir="/var/www/temp",
update=true,
links=true,
times=true,
protect_args=true
},
delay=5,
ssh = {
port = 22
}
}
В host нужно указать второй сервер, в source — папку на текущем сервере (она будет одинакова на обоих серверах), в targetdir — папку на удаленном сервере. Параметр delay задает время синхронизации в секундах (значение по умолчанию — 10 с). Параметр nodaemon после отладки следует установить в true, чтобы lsyncd запускался в режиме демона.
Перед проверкой надо создать SSH-ключи, поскольку lsyncd нужно, чтобы был предоставлен доступ к каждому из серверов по ключу. На обоих серверах введи команду
# ssh-keygen
На вопросы можно отвечать, просто нажав Enter. С сервера first добавляем ключ сервера second:
# ssh-copy-id root@second.example.com
Аналогично с сервера second добавь ключ на сервер first:
# ssh-copy-id root@first.example.com
Теперь подключись с сервера first к серверу second и наоборот. Пароль запрашиваться не будет. На вопросы при подключении отвечай yes.
Вот теперь на сервере first создай тестовый файл:
# touch /var/www/html/test
Далее запусти lsyncd вручную:
# lsyncd /etc/lsyncd.conf
Проверь, что все синхронизировалось корректно. Если все прошло хорошо, то на втором сервере в /var/www/html ты увидишь созданный файл test.
Осталось изменить значение nodaemon в конфиге на false и добавить lsyncd в автозагрузку:
systemctl start lsyncd.service
systemctl enable lsyncd.service
Первая команда включает автозагрузку сервиса, а вторая запускает сам сервис.
Примечания
Наша схема синхронизации работает в одном направлении. При желании можно сделать и двухстороннюю синхронизацию (SSH уже настроен, осталось настроить lsyncd), но она нужна далеко не всегда.
Обычно имеется одно хранилище контента (первый сервер) и каждые 5–10 с выполняется репликация со вторым сервером. Если сервер обновляется не очень часто, можно вообще производить синхронизацию вручную, после его обновления, или же установить интервал не в пять секунд, а в 3600 (синхронизация один раз в час). В данном примере пять секунд использовались, чтобы меньше ждать при тестировании.
Собственно, на этом все! Отказоустойчивость настроена, синхронизация (хоть и частичная, без репликации БД) — тоже.