Комбинированная балансировка нагрузки интернет-каналов

Предистория



Рано или поздно системный администратор сталкивается с необходимостью распределить трафик по нескольким каналам, при этом естественно желание чтобы каждый канал использовался по максимуму. Столкнувшись с подобной необходимостью, и решив не изобретать велосипед, обратился к помощи поисковиков. Так как сервер у меня на Ubuntu, то обратил свое внимание на статью http://help.ubuntu.ru/wiki/ip_balancing. Реализовал «Способ 1», но при тесте были замечены следующие критичные проблемы: при использовании ссылок на некоторых сайтах они не открывались (например при попытке включить музыку на ресурсе «ВКонтакте»). Причина очевидна — запрос шел через другой канал. Обдумав ситуацию, решил скомбинировать подход к балансировке. Логика проста — больше всего съедает трафика торренты и им подобные программы, поэтому разделяем трафик. В итоге трафик с портами до 11000 распределяем приблизительно равномерно по количеству абонентов — подсетями, трафиком с портами 11000-60000 выравниваем загрузку каналов.

Настройки



Предполагается что созданы таблицы маршрутизации для каждого из каналов, назовем их chan1, chan2, chan3 — три канала соответственно.
Где-нибудь, например в /etc/rc.local добавляем что-то вроде:

ip rule add prio 101 fwmark 1 table chan1
ip rule add prio 102 fwmark 2 table chan2
ip rule add prio 103 fwmark 4 table chan3


Создаем скрипт /etc/rc.balance:

#!/bin/bash

lst='/etc/rc.balance.lst'

########### Flushing ##################
/sbin/iptables -t mangle -F PREROUTING
/sbin/iptables -t mangle -F POSTROUTING
/sbin/iptables -t mangle -F OUTPUT
#######################################

/etc/rc.baltor

/sbin/iptables -t mangle -A PREROUTING -d 172.16.0.0/16 -j RETURN
/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/16 -m state --state INVALID -j DROP

while read net mark
do
    /sbin/iptables -t mangle -A PREROUTING -s $net -m state --state new,related -j CONNMARK --set-mark $mark
done<$lst

/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/16 -p udp --sport 11000:60000 --dport 11000:60000 -m state --state new,related -j BALANCE
/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/16 -p tcp --sport 11000:60000 --dport 11000:60000 -m state --state new,related -j BALANCE

/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/16 -j CONNMARK --restore-mark

exit 0


Создаем список распределения сеток по каналам (во второй колонке — марка):

172.16.0.0/22		1
172.16.4.0/22		2
172.16.8.0/22		4


Скрипт /etc/rc.baltor — правила балансировки:

#!/bin/bash

/sbin/iptables -t mangle -F BALANCE

lst='/etc/rc.cnload.lst'
mrk=1

while read kld
do
    /sbin/iptables -t mangle -A BALANCE -j CONNMARK --set-mark $mrk
    /sbin/iptables -t mangle -A BALANCE -m statistic --mode random --probability 0.$kld -j RETURN
    mrk=`expr $mrk \* 2`
done < $lst

/sbin/iptables -t mangle -A BALANCE -j CONNMARK --set-mark $mrk

exit 0


Скрипт /etc/rc.cnload — расчет вероятности в зависимости от загрузки канала:

#!/bin/bash

cn1=800000 # ширина первого канала в килобитах в секунду
cn2=600000 # второго ..
cn3=400000 # и третьего
if1='eth1' # интерфейс первого канала
if2='eth2' # второго
if3='eth3' # третьего

lst='/etc/rc.cnload.lst'

a1=`ifconfig $if1 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`
a2=`ifconfig $if2 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`
a3=`ifconfig $if3 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`
sleep 20
b1=`ifconfig $if1 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`
b2=`ifconfig $if2 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`
b3=`ifconfig $if3 | grep "RX bytes" | awk '{print $2}' | awk -F: '{print $2}'`

c1=`expr \( $b1 - $a1 \) \* 8 / 20000`
c2=`expr \( $b2 - $a2 \) \* 8 / 20000`
c3=`expr \( $b3 - $a3 \) \* 8 / 20000`

d1=`expr \( $cn1 - $c1 \) \* 100 / $cn1`
d2=`expr \( $cn2 - $c2 \) \* 100 / $cn2`
d3=`expr \( $cn3 - $c3 \) \* 100 / $cn3`

# выполняем корректировку при загрузке одного из каналов более 60%
if [ $d1 -lt "40" -o $d2 -lt "40" -o $d3 -lt "40" ]
then
    e1=`expr 100 \* $d1 / \( $d1 + $d2 + $d3 \)`
    e2=`expr 100 \* $d2 / \( $d2 + $d3 \)`
    f1=`head -n 1 $lst | tail -n 1`
    f2=`head -n 2 $lst | tail -n 1`
    # корректировка если коэффициенты изменились 
    if [ $e1 -ne $f1 -a $e2 -ne $f2 ]
    then
        echo $e1 > $lst
        echo $e2 >> $lst
        /etc/rc.baltor
    fi
fi

exit 0


Добавляем в /etc/rc.local
/etc/rc.balance


и в /etc/crontab
*/1 *	* * *	root    /etc/rc.cnload


Важно, чтобы на момент старта уже существовал файл с коэффициентами /etc/rc.cnload.lst, его можно составить запуском скрипта /etc/rc.cnload.

Заключение



Данный метод упешно реализован в сети с 8000 абонентами. Кроме балансировки используется динамический шейпинг, но это уже тема для другой статьи.

Всем баланса во всем.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 24

    +4
    Интересно, спасибо!
      0
      Спасибо, очень интересная статья! В свое время именно из-за таких сайтов, реагирующих на смену IP отказался от двух каналов, надоело прописывать их в списки исключения. По поводу разделения трафика была идея, но реализовать ее не получилось корректно… Ждем статью и про динамический шейпинг!
        –1
        Разделение трафика по портам не напоминает костыль? А если один канал упадет?

        Может лучше поднять на обоих каналах ВПН с каким-то сервером в ДЦ, который падает крайне редко (и имеет канал шире, чем суммарный в офисе) и пустить трафик через него?

        Тогда не будет проблемы с ip и будет защита от падения канала, заодно и нормальное распределение нагрузки (ваш канал забьется, если все неожиданно начнут вместо торрентов видео какое-то на ютубе смотреть).
          0
          ВПН до еще одного сервера, это по моему еще больший костыль.
          Тогда уж логичнее BGP и AS.
            0
            ВПН универсальне. Я такую схему баллансировки рассматривал при попытке собрать из нескольких телефонов с GPRS и 3G модемов портативную станцию для мобильного интренета с максимальной скоростью.

            BGP это точно такая-же схема, как и ВПН по логике, только маршрутизатор стоит не в офисе, а в качественном датацентре. Но могут быть сложности, при попытке собрать ВГП на основе домашних провайдеров, 3G модемов и подобной мешанины (по факту, эта мешанина может быть надежнее резерва от корпоративного провайдера средней руки за счет отсуствия единой точки отказа). ВПН к удаленному серверу будет через что угодно работать.
              0
              Некоторые клиенты отказываются работать через туннель, например банки с их IPsec.
                0
                Даже, если тунель не на тех машинах, где стоит клиент, а где-то на шлюзе?
                  0
                  Думаю будут сложности с MTU, точно не уверен — не тестировал в таком режиме. Почитать можно тут.
                    0
                    Разве рутер не должен реассемблить пакеты?
              0
              ВGP не позволит выполнить точную балансировку (обычно сетки меньше чем с 24 маской не позволяют раздавать), плюс некоторая инерционность метода.
              0
              Решение для большого количества абонентов, когда начинает работать статистика и к примеру 1000 абонентов не начнут одновременно смотреть ютуб. У меня днем распределяемый трафик (торренты) составляет 25-30% от общей полосы, что вполне достаточно для выравнивания нагрузки учитывая что абоненты предварительно распределены по каналам.
              0
              grep для строки, а не регэкспа? Почему не fgrep?
                +2
                Думаю не критично, но с замечанием согласен, спасибо, учту.
                0
                Балансировка IP-пакетов через линукс — это всегда такой костыль… который работает лишь до достижения определенного объема трафика, а потом наступает северный пушной зверек.
                Самое лучшее, что можно сделать в таком случае — это использовать BGP+аналитику
                1. Устанавливаем BGP сессии по всем доступным каналам с fullview
                2. Дампим трафик и узнаем, на какие автономные сети или просто сети идет больше всего траффика.
                3. эти сетии группируем по кол-ву каналов так, чтобы маршрут до каждой сети был самым коротким по AS-path
                т.е. если допустим по одному каналу сеть 1.0.0.0/24 видна через 3 автономки, а по второму каналу через 6 автономок, то пихать мы будем эту сеть в группу для второго канала. Это не обязательно, но очень желательно, дабы уменьшить задержки на траффик.
                4. формируем префикс листы для приема траффика для каждого канала — так мы сбалансируем исходящий трафик
                для балансировки исходящего трафика так же надо будет проводить аналитику, но это слишком долго описывать в рамках одного коммента.
                  0
                  поправка, дампим не траффик, а собираем по netflow
                    0
                    Думаю каждое решение существует для определенных условий. Что будет если 5-6 каналов? Принимать 6 таблиц fullview? Каковы действия если добавится еще канал? Надо будет заново составлять аналитику?
                    Согласен если каналы не подвергаются особым изменениям, предложенный Вами метод более чем приемлем.
                    Интересует значение «определенного объема трафика», скажем 4Гб/с это ниже этого значения?
                      0
                      6 таблиц фулвью тоже реально, главное оперативной памяти побольше =) Аппаратные железки вроде циски хорошо жуют подобное, а для большого траффика они в самый раз, никакой сервак не сравнится.
                      Аналитику надо проводить скриптом пару раз в месяц и при подкючении нового канала.
                      Определенный объем трафика скорее зависит от типа трафика и кол-ва пакетов. Вот если вы еще мультикаст начнете гонять — станет совсем все печально, ну или множество мелких фрагментированных пакетов обрабатывать\собирать. Но вообще 4 Гб/с(т.е. 2Гб/с на вход и 2Гб/с на выход) линукс точно прожует на какой-нибудь многопроцессорной дуре. Но все равно он будет слабее аппаратной железки, скажем циски 6500/7600 с sup720
                        0
                        В моем случае трафик 3.2Гб/с на вход и 3.2Гб/с на выход на одной сетевой, на второй около 1Гб/с туда и обратно, сервер однопроцессорный, проц — Intel Xeon 4х3.4Ghz, загрузка — 70%. На сервере NAT, BGP, OSPF.
                          0
                          70% — это уже псц, наверняка задержки через этот сервер выростают в районе 5мс. я писал про фулдуплекс на вход\выход. в смысле сколько трафика сервак принимает и отдает в одну сеть и в другие.
                            0
                            в пределах 0,5мс
                        0
                        +не надо забывать про задержки, вносимые на проходящий траффик. Линукс — это же софтроутер, так что не стоит ждать от него чудес.
                        0
                        еще одна опечатка:
                        >для балансировки исходящего трафика так же надо будет проводить аналитику,
                        для балансировки входящего трафика так же надо будет проводить аналитику,
                        +2
                        Есть операционная система RouterOS (MikroTik), на базе Linux, в ней можно сделать балансировку нагрузки с помощью PCC (Per Connection Classifier). Это позволяет с легкостью создать правила подключения клиентов. Думаю в вашем случае подойдет «src address», чтоб не было проблем типа «запрос шел через другой канал». То есть, если клиент попал при подключении на первый канал, он будет оставаться на нём до конца сессии. Документация нам говорит, что таймаут выбирается ядром Linux. Еще можно выбрать тип «dst address» или «both address» (в этом случаи балансировка будет равномерней), тогда если абонент зайдёт на habrahabr.ru, то он пойдет по первому каналу и будет на нем оставаться до смены адреса, а если зайдет на google.com, то по второму (каждый новый dst. адрес — другой канал). Во всех случаях когда адрес не меняется, клиент не будет перебрасываться на другую линию. Реализация этого механизма возможна на Ubuntu, так-как это тоже Linux-based Operating System. Кому интересно вот ссылка на WiKi MikroTik по этой теме PCC. Если кто-нибудь сделает скрипт под Ubuntu, это было бы очень не плохо.
                          +1
                          Спасибо за ссылку, весьма познавательно.

                        Only users with full accounts can post comments. Log in, please.