
Сегодня я расскажу о том, как быстро собрать отказоустойчивый кластер с балансировкой нагрузки с помощью keepalived на примере DNS-серверов.
Итак, предположим, что у нас есть сервер, который должен работать без перебоев. Его нельзя просто так взять и выключить посреди рабочего дня — клиенты или пользователи не поймут. Тем не менее любой сервер время от времени надо обслуживать, ставить обновления, менять аппаратную конфигурацию или сетевые настройки. Кроме того, нужно быть готовым к росту нагрузки, когда мощности сервера перестанет хватать. У сервера должна быть предусмотрена возможность быстрого масштабирования. Если вы озаботились этими проблемами, то вам нужно организовать отказоустойчивый кластер.
Ч��ще всего такие задачи приходится делать с HTTP-серверами. Я сегодня покажу сборку кластера на примере DNS, потому что опробовал технологию именно на нем, но с минимальными изменениями то же самое можно сделать и с серверами, работающими по TLS или HTTP.
Лирическое отступление о проблемах DNS
В принципе, DNS задуман так, чтобы необходимости городить огород с кластерами не возникало. В каждой зоне можно прописать множество NS-записей, в каждой сети можно раздать список DNS-серверов с помощью DHCP. DNS-сервера умеют реплицировать зоны, поэтому они хорошо масштабируются. Однако на практике это плохо работает. Когда я попробовал добавить 2-й DNS-сервер, то обнаружил, что
- Половина пользователей сидят со статическими настроенными адресами DNS-серверов. Ставят себе 4 восьмерки в качестве primary и локальный DNS в качестве secondary.
- Многие Linux-сервера не умеют из коробки корректно обновлять настройки DNS. Там такой зоопарк из glibc, nsswitch.conf, resolv.conf, NetworkManager, resolvconf, systemd-resolved, hosts, dhclient.conf и т. п., которые конфликтуют между собой, что рассчитывать на автоматическое обновление по DHCP просто не приходится.
- Windows шлет запросы одновременно на все сервера, но обязательно дожидается ответа или таймаута от 1-го
- Linux сначала обращается к 1-му DNS в списке и только в случае ошибки переходит к следующему.
Если долгое время в сети используется DNS-сервер с определенным IP, то он оказывается прописан в десятках разных мест. Например:
- Директива resolver в nginx.conf
- daemon.json в docker
- В настройках docker-контейнеров
- В конфигах модных нынче систем вроде kubernetes или openshift во внутренних файлах на каждой ноде и еще там же в конфигах dnsmasq.
- В конфигах почтовых серверов.
- В настройках VPN-серверов.
Никакими плейбуками и DHCP-декларациями это все обновить просто нереально. Поэтому было бы очень хорошо, если бы собираемый кластер можно было повесить на 1 уже существующий IP. Тогда для пользователей ничего не изменится, и софт перенастраивать не понадобится.
Поэтому я выбрал решение с keepalived и протоколом VRRP.
Подготовка
Собирая кластер из нескольких серверов, желательно уметь их различать на стороне клиента, чтобы не бегать искать по логам серверов, какой запрос, куда попал. Я для этого в bind9 завел вот такую зону.
$ORIGIN load.balance.
$TTL 1h
load.balance. 86400 IN SOA ns.mydomain.ru. dnsmaster.mydoamin.ru. (
1603065098 3600 1800 604800 30
)
load.balance. IN NS ns.mydomain.ru.
health IN TXT =nameserver-1=На 1-м сервере ресурсная запись TXT для health.load.balance содержит текст =nameserver-1=, на 2-м сервере — =nameserver-2=, и т. д. Таким образом, отправляя запрос в кластер, я по ответу могу определить, какой сервер мне ответил, что очень удобно для отладки.
Если у вас HTTP-сервер, то поместите эту информацию в HTTP-заголовок. Например, для nginx я использую вот такую директиву
add_header serega-trace "$hostname" always;Убедитесь, что ваши сетевые файерволлы не блокируют IP-трафик протокола 112 по адресу 224.0.0.18. Этот адрес будут использовать ваши сервера, чтобы договариваться между собой о том, кто из них MASTER, а кто BACKUP.
iptables -t filter -I INPUT -p vrrp -d 224.0.0.18 -j ACCEPT
iptables -t filter -I OUTPUT -p vrrp -d 224.0.0.18 -j ACCEPTПри организации DNS нужно выделить мастер-сервер, на который не будут идти клиентские запросы. DNS-мастер используется только для управления. Он реплицирует свои зоны на slave-сервера, которые уже видны пользователям, и именно на них идет нагрузка. Далее речь будет идти о кластеризации именно DNS-slave.
Уровень 1 (Easy)
Если вам просто достаточно резервирования на случай аварии, то рекомендую рассмотреть самый быстрый и простой вариант. 2 одинаковых сервера разделяют между собой общий виртуальный IP (далее буду называть его VIP). У кого в данный момент в сетевом интерфейсе прописан VIP, тот сервер и работает. 2-й сервер следит за мастером (имеется в виду мастер VRRP, а не DNS), и как только обнаруживает, что он перестал вещать, сразу объявляет мастером себя и поднимает VIP на своем сетевом интерфейсе.
Меня приятно удивило то, как быстро удалось поднять кластер по такому варианту. Несмотря на то, что раньше я никогда не имел дело с keepalived, уже через час все работало. Так что если вам нужно решить задачу быстро, то уровень 1 вам подойдет в самый раз.
Сбор информации
Для успешного развертывания вам понадобится собрать следующую информацию. Здесь я привожу значения для примера. У вас эти значения должны быть свои.
| Параметр | Возможное значение | Описание |
|---|---|---|
| vip | 10.2.1.5 | виртуальный IP, на который шлют запросы клиенты |
| dev0 | eth0 | 1-й сетевой интерфейс на узлах кластера |
| ip01 | 10.2.1.2 | IP 1-го узла кластера на 1-м сетевом интерфейсе |
| ip02 | 10.2.1.3 | IP 2-го узла кластера на 1-м сетевом интерфейсе |
| net0 | 10.2.1.0/24 | подсеть, которой принадлежат ip01 и ip02 |
Установите keepalived и snmpd. SNMP ставить необязательно, но мне кажется, что, если серверов с виртуальными IP будет много, он будет полезен.
setenforce 0 # если вдруг у вас selinux
dnf install -y keepalived nmap-ncat net-snmp net-snmp-utils
systemctl enable keepalived
systemctl enable snmpdNetcat нужен для диагностики и healthcheck-ов.
В файл /etc/sysconfig/keepavlied добавьте опцию -x. Она нужна для взаимодействия keepalived с snmpd. Если вы не собираетесь поднимать SNMP, то этот шаг можете пропустить.
Отредактируйте файл /etc/snmp/snmpd.conf следующим образом:
master agentx
rocommunity publicВ /etc/keepalived положите вот такой скрипт keepalived-notify.sh:
#!/bin/sh
umask -S u=rwx,g=rx,o=rx
exec echo "[$(date -Iseconds)]" "$0" "$@" >>"/var/run/keepalived.$1.$2.state"Этот скрипт будет вызываться демоном keepalived при изменении состояния кластера. Он запишет в каталог /var/run статусный файл, который удобно использовать для диагностики.
Отредактируйте основной конфигурационный файл keepalived.conf следующим образом:
global_defs {
enable_script_security
}
vrrp_script myhealth {
script "/bin/nc -z -w 2 127.0.0.1 53"
interval 10
user nobody
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 5
priority 100
advert_int 1
nopreempt
notify /etc/keepalived/keepalived-notify.sh root
authentication {
auth_type PASS
auth_pass KPSjXfRG
}
virtual_ipaddress {
10.2.1.5
}
track_script {
myhealth
}
}Блок global_defs содержит единственную необходимую настройку enable_script_security, которая по умолчанию отключена.
Блок vrrp_script описывает скрипт, который демон keepalived будет использовать для определения работоспособности своего сервера. Если этот скрипт вернет ошибку, то демон перейдет в состояние FAIL, и не будет претендовать на роль MASTER. В этом же блоке описывается периодичность выполнения healthchek-ов и указывается пользователь, от имени которого запускается скрипт. В нашем случае используется утилита netcat, которая устанавливает соединение c локальным DNS-сервером по TCP-порту 53. Можно использовать разные проверки, например, прозвонить UDP-порт 53 утилитой dig.
В блоке VRRP_INSTANCE задаются настройки 1 экземпляра сервера с виртуальным IP.
- state задает начальное состояние сервера BACKUP или MASTER. В режиме nopreempt единственное допустимое значение — BACKUP.
- interface указывает, на каком сетевом интерфейсе будет поднят VIP
- virtual_router_id уникальный идентификатор роутера VRRP. Возможные значения от 1 до 255. У всех узлов кластера это значение должно быть одинаковым. Рекомендуется в качестве router_id использовать последний байт VIP, чтобы не запутаться, когда у вас таких виртуальных адресов будет много.
- priority задает приоритет данного экземпляра при выборе мастера. Мастером назначается сервер, у которого значение параметра priority выше. Если у нескольких серверов priority одинаковый, то мастер будет выбран случайным образом.
- advert_int определяет, с какой периодичностью мастер должен сообщать остальным о себе. Если по истечению данного периода сервера не получат от мастера широковещательное уведомление, то они инициируют выборы нового мастера.
- nopreempt означает, что если мастер пропал из сети, и был выбран новый мастер с меньшим приоритетом, то по возвращении старшего мастера, он останется в состоянии BACKUP. Т. е. если вы перезагрузили мастер, то он больше мастером не станет, пока новый мастер не отвалится. Если вы предпочитаете, чтобы мастером был какой-то конкр��тный сервер, то замените настройку nopreempt на preempt_delay.
- notify задает хук-скрипт, который будет вызываться при каждом изменении состояния сервера, и имя пользователя, от имени которого данный скрипт будет выполняться.
- authentication задает пароль, длиной до 8 символов, который будет защищать кластер от случайных коллизий с другими серверами в локальной сети.
- virtual_ipaddress задает VIP
- track_script указывает на описание скрипта, осуществляющего healthcheck.
Выполните указанные настройки на обоих серверах кластера и запустите сервисы:
systemctl start snmpd
systemctl start keepalivedПроверьте логи и статусные файлы на наличие ошибок.
journalctl -u snmpd
journalctl -u keepalived
tail /var/run/keepalived.INSTANCE.VI_1.stateЕсли у вас используется selinux, не забудьте по данным audit.log обновить политики и вернуть enforcing mode командой set enforce 1.
Проверка
Если в логах ошибок нет, можно проверять. Поскольку мы кластеризовали DNS, то будем использовать dig. Для проверки HTTP-сервера подойдет curl.
Запустите на клиенте такой скрипт:
while true; do
dig -4 +short +notcp +norecurse +tries=1 +timeout=1 \
-q health.load.balance. -t txt @10.2.1.5;
sleep 1;
doneЭтот скрипт будет раз в секунду выдавать ресурсную запись health.load.balance/IN/TXT, которая содержит идентификатор сервера. В нашей конфигурации это будет тот сервер, который сейчас VRRP-мастер.
Зайдите на этот сервер и убедитесь, что в файле /var/run/keepalived.INSTANCE.VI_1.state в последней строке указано MASTER.
Перезапустите на мастере сервис keepalived. Если у вас все сделано правильно, то мастер поменяется. На 1-м сервере в файле keepalived.INSTANCE.VI_1.state появятся строки STOP и BACKUP, на втором сервере в этом же файле появится строка MASTER, а клиентский скрипт станет выдавать ресурсную запись с идентификатором нового мастера.
[2020-10-13T10:48:00+00:00] /etc/keepalived/keepalived-notify.sh INSTANCE VI_1 BACKUP 100
[2020-10-13T11:26:29+00:00] /etc/keepalived/keepalived-notify.sh INSTANCE VI_1 MASTER 100"=nameserver-1="
"=nameserver-1="
"=nameserver-1="
"=nameserver-2="
"=nameserver-2="Все, кластер готов. Теперь можно перезагружать сервера по одному в любое время, не нужно откладывать обновления на выходные, не нужно работать по ночам, не нужно рассылать пользователям предупреждения о том, что возможны перебои в связи с регламентными работами.
Если у вас все получилось, значит вы успешно прошли 1-й уровень кластеризации. Он обладает следующими плюсами:
- очень легко и быстро настраивается,
- практически невозможно что-либо сломать,
- на всех узлах кластера одинаковая конфигурация, что сильно сокращает трудоемкость администрирования.
Однако есть у данного варианта развертывания и существенный минус. В один момент времени в кластере работает только один сервер, а остальные, что называется, курят бамбук. Во-первых, это неэффективно с точки зрения использования вычислительных ресурсов. Во-вторых, если ваш сервер перестанет справляться с нагрузкой, то такой простенький кластер вам не поможет.
Чтобы обеспечить не только отказоустойчивость, но и масштабирование, переходите на следующий уровень кластеризации, о котором я расскажу прямо сейчас.
Уровень 2 (c балансировкой нагрузки)
Чтобы задействовать все сервера кластера, нужно научить VRRP-мастер, на сетевом интерфейсе которого прописан VIP, не только принимать трафик для своих локальных сервисов, но и направлять часть трафика на остальные сервера. Демон keepalived как раз умеет это делать.
Отредактируйте конфиг keepalived.conf, приведя его к следующему виду:
global_defs {
enable_script_security
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 5
priority 100
advert_int 1
nopreempt
notify /etc/keepalived/keepalived-notify.sh root
authentication {
auth_type PASS
auth_pass KPSjXfRG
}
virtual_ipaddress {
10.2.1.5
}
}
virtual_server 10.2.1.5 53 {
protocol UDP
delay_loop 10
lvs_sched rr
lvs_method NAT
real_server 10.2.1.2 53 {
DNS_CHECK {
type txt
name health.load.balance
}
}
real_server 10.2.1.3 53 {
DNS_CHECK {
type txt
name health.load.balance
}
}
}
virtual_server 10.2.1.5 53 {
protocol TCP
delay_loop 10
lvs_sched rr
lvs_method NAT
real_server 10.2.1.2 53 {
TCP_CHECK {
connect_timeout 3
}
}
real_server 10.2.1.3 53 {
TCP_CHECK {
connect_timeout 3
}
}
}Начало конфигурации аналогично предыдущему варианту без балансировки, только из блока vrrp_instance исчез track_script, соответственно за ненадобностью был удален блок vrrp_script.
Главное отличие в новой конфигурации заключается в блоках virtual_server. Для DNS требуется 2 виртуальных сервера, для 53-го порта TCP и для 53-го порта UDP. В случае HTTP-сервера аналогично потребуются сервера для 80-го и 443-го портов TCP.
Каждый виртуальный сервер идентифицируется 3 значениями: IP, порт и протокол. IP и порт через пробел указываются в заголовке блока virtual_server, а протокол определяется параметром protocol внутри блока. Допустимые протоколы TCP и UDP. В случае DNS как раз нужны оба.
Параметр delay_loop задает периодичность, с которой балансировщик опрашивает бэкенды для определения их состояния.
Самые важные параметры в виртуальном сервере — это lvs_sched и lvs_method. lvs_sched задает алгоритм, по которому балансировщик определяет, куда отправить очередной IP-пакет. lvs_method задает механизм, который использует балансировщик для направления пакетов в выбранный пункт назначения.
В приведенном примере lvs_sched равен rr, что означает round robin, т. е. балансировка равномерно по очереди. lvs_method используется NAT. Кроме NAT доступны также механизмы DR (direct routing) и TUN (tunneling). На мой взгляд, NAT — единственный рабочий вариант. Остальные методы работают только в очень специфических условиях.
Каждый виртуальный сервер состоит из нескольких реальных серверов, что отражено в соответствующих вложенных блоках real_server. Внутри блока real_server надо описать способ опроса его состояния, т. е. healthcheck.
После такой настройки keepalived работает следующим образом:
- Принимает IP-пакет с адресом получателя равным VIP.
- Выбирает real_server, куда необходимо направить пакет, анализируя протокол, порт, lvs_sched и результаты healthcheck-ов.
- Заменяет в IP-пакете адрес получателя на IP-адрес выбранного реального сервера и отправляет его дальше.
Только для работы этого недостаточно. Выбранный реальный сервер отправит ответный IP-пакет, в котором адрес отправителя будет равен его собственному IP-адресу. Клиент дропнет такой пакет, потому что он отправлял запрос на VIP и ожидает ответы от VIP, а не с IP-адреса реального сервера.
Чтобы решить эту проблему, нужно заставить реальный сервер отправить ответ не на IP клиента, а на IP мастера, чтобы тот выполнил обратную подстановку реального IP на виртуальный.
Для этого понадобится itpables и некоторые настройки ядра.
dnf -y install iptables iptables-services
systemctl enable iptables
systemctl start iptablesecho "net.ipv4.ip_forward=1" >>/etc/sysctl.d/99-sysctl.conf
echo "net.ipv4.vs.conntrack=1" >>/etc/sysctl.d/99-sysctl.conf
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.vs.conntrack=1Добавьте в iptables следующее правило:
iptables -t nat -I POSTROUTING 1 -d 10.2.1.0/24 -j SNAT --to-source 10.2.1.5
service iptables saveДействие SNAT означает, что после маршрутизации в IP-пакете IP-адрес источника будет заменен на IP-адрес to-source. Вместо SNAT можно также использовать действие MASQUERADE, которое делает то же самое, только определяет исходящий IP автоматически.
Поскольку были внесены изменения в такие вещи как iptables и параметры ядра, рекомендуется перезагрузить сервер и убедиться, что ничего из этого не потерялось.
Выполните описанные действия на обоих серверах кластера. Проверьте логи и статусные файлы. Если ошибок нет, то выполните проверку работы кластера способом, описанным выше. Теперь должен получиться вот такой вывод:
"=nameserver-1="
"=nameserver-2="
"=nameserver-1="
"=nameserver-2="
"=nameserver-1="Поскольку в балансировщике задан алгоритм round robin, то сервера отвечают строго по очереди друг за другом.
Остановите named на 2-м сервере, и получите:
"=nameserver-1="
"=nameserver-1="
"=nameserver-1="
"=nameserver-1="
"=nameserver-1="Снова запустите на 2-м сервере named, и на клиенте снова начнется чередование ответов.
Не забудьте про корректировку политик selinux, о которой рассказано выше.
Поздравляю. Только что мы с вами прошли 2-й уровень кластеризации.
Теперь наш кластер не только отказоустойчивый, но и масштабируемый.
Тем не менее, данная конструкция имеет и большой минус. Из-за трансляции адресов на подчиненные сервера запросы приходят с VIP, и невозможно по логам определить, какой запрос от какого клиента пришел. Возможность вычислить клиента по IP очень полезна для решения различных проблем. Поэтому, мой совет — не останавливаться на 2-м уровне и, по возможности, перейти на 3-й.
Уровень 3 (Expert)
3-й уровень кластеризации подразумевает, что вы уже находитесь на 2-м, и все у вас работает как надо. Необходимо только обеспечить проброс клиентского IP-адреса до реальных серверов.
Принцип проброса следующий: все пакеты, приходящие на сервер, содержат IP реального клиента, а ответы на них отправляются на VIP. Т. е. VIP для реального сервера является шлюзом по умолчанию. Причем на этот шлюз направляются даже те пакеты, получатель которых находится в локальной подсети.
Но у серверов уже есть шлюз по умолчанию, и заменить его нельзя. Если убрать стандартный шлюз по умолчанию, то балансировка может быть и будет работать, однако все остальное сетевое взаимодействие у сервера сломается, и на него нельзя будет даже зайти по SSH.
Решение заключается в создании на сервере 2-го сетевого интерфейса. На 1-м сетевом интерфейсе будет производиться стандартный сетевой обмен, а на 2-м как раз и будет настроен VIP в качестве шлюза по умолчанию. Соответственно, у серверов вместе с дополнительным сетевым интерфейсом должен будет появиться и дополнительный IP-адрес.
Итак, добавьте на сервера кластера дополнительные сетевые интерфейсы и назначьте им IP-адреса.
Соберите информацию, необходимую для дальнейшей настройки. В нашем примере будут использоваться следующие значения:
| Параметр | Возможное значение | Описание |
|---|---|---|
| vip | 10.2.1.5 | виртуальный IP, на который шлют запросы клиенты |
| dev0 | eth0 | 1-й сетевой интерфейс на узлах кластера |
| dev1 | eth1 | 2-й сетевой интерфейс на узлах кластера |
| ip01 | 10.2.1.2 | IP 1-го узла кластера на 1-м сетевом интерфейсе |
| ip02 | 10.2.1.3 | IP 2-го узла кластера на 1-м сетевом интерфейсе |
| ip11 | 10.2.1.6 | IP 1-го узла кластера на 2-м сетевом интерфейсе |
| ip12 | 10.2.1.7 | IP 2-го узла кластера на 2-м сетевом интерфейсе |
| net0 | 10.2.1.0/24 | подсеть, которой принадлежат ip01 и ip02 |
Скорректируйте конфигурацию keepalived по указанному образцу.
global_defs {
enable_script_security
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 5
priority 100
advert_int 1
nopreempt
notify /etc/keepalived/keepalived-notify.sh root
authentication {
auth_type PASS
auth_pass KPSjXfRG
}
virtual_ipaddress {
10.2.1.5
}
}
virtual_server 10.2.1.5 53 {
protocol UDP
delay_loop 10
lvs_sched rr
lvs_method NAT
real_server 10.2.1.6 53 {
DNS_CHECK {
connect_ip 10.2.1.2
type txt
name health.load.balance
}
}
real_server 10.2.1.7 53 {
DNS_CHECK {
connect_ip 10.2.1.3
type txt
name health.load.balance
}
}
}
virtual_server 10.2.1.5 53 {
protocol TCP
delay_loop 10
lvs_sched rr
lvs_method NAT
real_server 10.2.1.6 53 {
TCP_CHECK {
connect_ip 10.2.1.2
connect_timeout 3
}
}
real_server 10.2.1.7 53 {
TCP_CHECK {
connect_ip 10.2.1.3
connect_timeout 3
}
}
}От предыдущей конфигурации новая отличается тем, что для реальных серверов указаны IP с дополнительных сетевых интерфейсов, но healthcheck-и отправляются на IP основных сетевых интерфейсов. Обратите внимание на этот моме��т. Все сервера кластера опрашивают друг друга, но на дополнительных сетевых интерфейсах будут настроены маршруты на VIP, поэтому узлы, которые сейчас в состоянии BACKUP, не смогут получить ответ от новых IP-адресов.
Теперь необходимо прописать правильные маршруты для дополнительных сетевых интерфейсов.
Добавьте в файл /etc/iproute2/rt_tables 2 новых таблицы маршрутизации. В примере ниже добавлены таблицы table0 и table1.
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
20 table0
21 table1По документации к NetworkManager и CentOS 8 статические маршруты и правила следует помещать в файлы /etc/syconfig/network-scripts/route-eth0 и rule-eth0. На многих моих серверах именно так и сделано. Только почему-то на серверах, поднятых из одного и того же образа, формат этих файлов оказался разным. На большинстве серверов route-eth0 выглядит так:
192.168.1.0/24 via 192.168.1.1
172.10.1.0/24 via 172.10.1.1но почему-то на моих серверах DNS эти же файлы содержат вот это:
ADDRESS0=192.168.1.0
NETMASK0=255.255.255.0
GATEWAY0=192.168.1.1
ADDRESS1=172.10.1.0
NETMASK1=255.255.255.0
GATEWAY1=172.10.1.1Формат понятен, только совершенно непонятно, как в него поместить имя таблицы маршрутизации. Я не смог разобраться в этом вопросе. Если кто-то знает, в чем дело, поделитесь, пожалуйста, информацией в комментариях.
Поскольку сохранить маршруты в специальных системных файлах не удалось, пришлось сделать костыль.
UPD: в комментах товарищ Bearpuh подсказал, как это лечить.
#!/bin/sh
# https://tldp.org/HOWTO/Adv-Routing-HOWTO/lartc.rpdb.multiple-links.html
vip="10.2.1.5"
dev0="eth0"
ip0="10.2.1.2" # "10.2.1.3"
dev1="eth1"
ip1="10.2.1.6" # "10.2.1.7"
ip route add 10.2.1.0/24 dev "$dev0" src "$ip0" table table0
ip route add default via 10.2.1.1 table table0
ip rule add from "$ip0" table table0
ip route add "$vip/32" dev "$dev1" src "$ip1"
ip route add default via "$vip" table table1
ip route add "$vip/32" dev "$dev1" src "$ip1" table table1
ip rule add from "$ip1" table table1В комментарии в скрипте указаны значения, которые надо скорректировать при переносе скрипта на другой сервер.
Данный скрипт я добавил в автозапуск вместе с сервисом keepalived.
systemctl edit keepalived[Service]
ExecStartPre=/etc/keepalived/routes.shНе самое элегантное решение, но работает нормально, поэтому можете спокойно использовать, если не знаете, как можно сделать лучше.
Принципы задания правил маршрутизации описаны в Linux Advanced Routing & Traffic Control HOWTO. Идея заключается в том, что создается 2 независимые таблицы маршрутизации, в каждой свой шлюз по умолчанию. В зависимости от того, с какого сетевого интерфейса отправляется пакет, с помощью правил ip rule выбирается либо одна таблица, либо другая.
Удалите из iptables правило SNAT в цепочке POSTROUTING, добавленное при прохождении 2-го уровня. Сохраните состояние iptables.
iptables -t nat -D POSTROUTING -d 10.2.1.0/24 -j SNAT --to-source 10.2.1.5
service iptables saveПроверьте результат. Клиент должен получать ответы от каждого сервера поочередно, в логах на серверах должны писаться реальные IP клиентов.
Теперь имеется отказоустойчивый кластер, в котором все живые узлы принимают нагрузку и видят IP-адреса своих клиентов. Единственный небольшой минус у этой конфигурации заключается в том, что есть 1 скрипт, который на разных серверах должен иметь разные параметры. Небольшое усложнение администрирования, которое с лихвой окупается полученными преимуществами.
| Сложность | Масштабирование | Единые настройки | IP клиента | |
|---|---|---|---|---|
| Уровень 1 | Easy | Нет | Да | Да |
| Уровень 2 | Normal | Да | Да | Нет |
| Уровень 3 | Expert | Да | Нет | Да |
