Предистория
Рано или поздно системный администратор сталкивается с необходимостью распределить трафик по нескольким каналам, при этом естественно желание чтобы каждый канал использовался по максимуму. Столкнувшись с подобной необходимостью, и решив не изобретать велосипед, обратился к помощи поисковиков. Так как сервер у меня на 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 абонентами. Кроме балансировки используется динамический шейпинг, но это уже тема для другой статьи.
Всем баланса во всем.
Автор: Redor