- PVSM.RU - https://www.pvsm.ru -
Предположим, что у вас есть некий сервис, принимающий входящие соединения и возникла задача балансировки нагрузки и/или отказоустойчивости.
В общем виде схема выглядит так:
клиент ----> балансировщик ---> бэкенд (сервис)
Готовых балансировщиков под конкретные нужды множество. Например, nginx — отличный балансировщик для веб-приложений, haproxy [1] для tcp-соединений.
На самом деле все зависит от конкретной ситуации, поэтому давайте я просто напишу рецепт и по ходу дела вы решите для себя сами где он может пригодиться.
Для балансировки будем использовать модули statistic, condition и маркировку соединений.
Не все эти модули есть по-умолчанию. В старых версиях linux вместо statistic ищите nth, а condition так возможно придется поставить руками с сайта проекта [2]
Теперь предположим, что ваш сервис — это smtp-сервер.
Внешний ip-адрес пусть будет 10.0.0.1
Внутренние ip-адреса сервисов будут 192.168.0.1 и 192.168.0.2
Создадим в таблице mangle следующий набор правил.
*mangle :MAILMARK - [0:0] -A MAILMARK -j CONNMARK --restore-mark -A MAILMARK -m mark --mark 0x0 -m statistic --mode nth --every 2 -m condition ! --condition mail1up -j MARK --set-mark 1 -A MAILMARK -m mark --mark 0x0 -m condition ! --condition mail2up -j MARK --set-mark 2 -A MAILMARK -m mark --mark 0x0 -m condition ! --condition mail1up -j MARK --set-mark 1 -A MAILMARK -j CONNMARK --save-mark -A PREROUTING -d 10.0.0.1 -p tcp --dport 25 -j MAILMARK COMMIT
а в таблице nat следующий:
-A PREROUTING -d 10.0.0.1 -p tcp --dport 25 -i bond0.204 -m mark --mark 1 -j DNAT --to-destination 192.168.0.1 -A PREROUTING -d 10.0.0.1 -p tcp --dport 25 -i bond0.204 -m mark --mark 2 -j DNAT --to-destination 192.168.0.2
Вот собственно и все, теперь осталось пояснить как это все работает.
Сначала любой входящий пакет на внешний адрес 10.0.0.1 на 25 порт проходит через таблицу mangle и правило маркировки
-A PREROUTING -d 10.0.0.1 -p tcp --dport 25 -j MAILMARK
Для маркировки создана отдельная цепочка MAILMARK, которая работает следующим образом.
Если соединение уже было промаркировано, то маркируем пакет тем же значением, что и соединение.
-A MAILMARK -j CONNMARK --restore-mark
Таким образом пакеты одного соединения в последствии попадут на один бэкенд сервиса.
Если пакет не промаркирован, то маркируем его следующим правилом.
-A MAILMARK -m mark --mark 0x0 -m statistic --mode nth --every 2 -m condition ! --condition mail1up -j MARK --set-mark 1
-m mark --mark 0x0
условие того, что пакет еще не промаркирован
-m statistic --mode nth --every 2
маркируем каждый второй пакет. Т.е. мы балансируем нагрузку очень просто 50/50, но это не единственная возможность (см. random и statistic)
-m condition ! --condition mail1up
условие того, что бэкенд живой
-j MARK --set-mark 1
маркируем пакет меткой со значением 1
Если пакет остался не промаркирован, то маркируем его меткой 2, при условии, что бэкенд живой
-A MAILMARK -m mark --mark 0x0 -m condition ! --condition mail2up -j MARK --set-mark 2
Если пакет все еще остался не промаркирован, то видимо второй бэкенд мертвый, а первая метка не поставилась, т.к. нам либо не повезло с 50/50, либо первый бэкенд тоже умер. На случай если первый бэкенд все же живой маркируем пакет меткой 1
-A MAILMARK -m mark --mark 0x0 -m condition ! --condition mail1up -j MARK --set-mark 1
Последним правилом запоминаем для текущего соединения проставленную метку
-A MAILMARK -j CONNMARK --save-mark
После того, как пакет промаркирован, он будет транслирован на выбранный бэкенд правилами, которые были добавлены в таблицу nat
Осталось осветить еще один момент, связанный с тем, как iptables поймет, что бэкенд живой.
Да никак, это должны сделать вы сами и положить в файлы /proc/net/nf_condition/mail1up и /proc/net/nf_condition/mail2up 0 или 1, в зависимости от того жив бэкенд или мертв.
По-умолчанию при старте iptables в этих файлах будет 0.
Правила, которые приведены, рассчитаны на то, что 0 означает, что бэкенд живой.
Например, для проверки smtp сервера я использую следующий bash-скрипт
#!/bin/bash
get () {
response=$(echo "QUIT" | nc -w 5 $1 25 | head -1 | awk '{print $1}')
if [ "$response" == "220" ]
then
echo "0"
else
echo "1"
fi
}
echo $(get 192.168.0.1) > /proc/net/nf_condition/mail1up
echo $(get 192.168.0.2) > /proc/net/nf_condition/mail2up
С одной стороны наверное нет веских причин делать именно так, но с другой можно их придумать.
Автор: gmlexx
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/iptables/30017
Ссылки в тексте:
[1] haproxy: http://haproxy.1wt.eu/
[2] сайта проекта: http://xtables-addons.sourceforge.net/
[3] Источник: http://habrahabr.ru/post/173713/
Нажмите здесь для печати.