Пользователь
0,0
рейтинг
21 марта 2013 в 13:33

Администрирование → Балансировка входящих соединений на iptables из песочницы

*nix*
Предположим, что у вас есть некий сервис, принимающий входящие соединения и возникла задача балансировки нагрузки и/или отказоустойчивости.

В общем виде схема выглядит так:
клиент ----> балансировщик ---> бэкенд (сервис)


Готовых балансировщиков под конкретные нужды множество. Например, nginx — отличный балансировщик для веб-приложений, haproxy для tcp-соединений.


  • Вы не ищете легких путей
  • Вам скучно и захотелось чего-нибудь новенького
  • Только iptables, только хардкор

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

Рецепт


Для балансировки будем использовать модули statistic, condition и маркировку соединений.
Не все эти модули есть по-умолчанию. В старых версиях linux вместо statistic ищите nth, а condition так возможно придется поставить руками с сайта проекта

Теперь предположим, что ваш сервис — это 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


Заключение


С одной стороны наверное нет веских причин делать именно так, но с другой можно их придумать.
  1. iptables работает на уровне ядра и скорее всего будет быстрее любого другого балансировщика
  2. iptables надежнее. Например, не факт, что когда между балансировщиками произойдет failover, тот же haproxy успешно стартанет, т.к. для старта ему нужно гораздо больше условий. Например, IP-адрес 10.0.0.1 должен быть перехвачен и только после этого можно будет запускать haproxy на 25-м порту. Например, кто-то за это время испортил его файл конфигурации и он вообще не стартанет.
Алексей Ларьков @gmlexx
карма
13,0
рейтинг 0,0
Пользователь

Самое читаемое Администрирование

Комментарии (16)

  • –2
    Скажите пожалуйста, зачем?
    или просто just for fun?
    • +3
      Видимо — потому что могу!
      Ну и кроме того всегда полезно знать на что способен инструмент
    • +2
      Тут вопрос скорее не зачем, а как это можно сделать если надо.
      Вот, например, так.
      • –3
        Я просто не увидел чем он круче haproxy, вот и спросил :)
  • +3
    Вот еще парочка методов описана ru.scribd.com/doc/78302933/Load-Balancing-Through-IP-Tables
    Но пока что лучше ipvsadm + keepalived
    ip_vs тоже работает на уровне ядра
    • 0
      Ещё
      iptables -m cluster


      Готовый пример в мануале iptables есть.
  • 0
    Спасибо, интересная статья. Лучше знать больше, чем меньше.
  • 0
    В LARTC для этого, кажется, используется набор утилит iproute, tc в частности.
  • +3
    nginx — отличный балансировщик для веб-приложений, haproxy для tcp-соединений.

    NGINX тоже умеет балансировать TCP, причем делает это довольно хорошо
    github.com/yaoweibin/nginx_tcp_proxy_module
    • 0
      спасибо за полезную ссылку
    • 0
      не торопитесь применять,
      не production ready

      Поимел проблемы на сервисе, через который ходит 80Mbps
  • 0
    Спасибо.
    Я использовал этот метод в течение несколько месяцев для балансировки HTTPS до HAProxy 1,5 года назад :)
    Метод простой и надежный, но не определяет доступность хостов автоматически.
    Поэтому через некоторое время завели nginx как https proxy перед HAProxy. :)
    • 0
      Вообще NGINX тоже умеет определять или живой бекенд, например в случае с HTTPS вот так:
      check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;
      с другой стороны, haproxy дает информацию в info-страничке о состоянии бекенда даже если на него совсем нет конектов (кроме тестовых), что несомненно тоже довольно удобно.
      • 0
        я имел виду, что iptables не умеет определять доступность бэкенда :)
  • +1
    Извиняюсь за.
    А как посмотреть пост вместе с уехавшими за границу nowrap-строками?
    • 0
      Поправил

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.