от Lucifer

Поради една или друга причина ми се наложи да правя Load Balance между два доставчика. Реших да си спестя голяма част от работата и да го оравновеся на всяка нова връзка.

Ровейки из StackOverflow и ServerFault попаднах на различни инструменти, които позволяват това да се направи … и разбира се опитах … и се провалих с гръм и трясък. Вторият опит беше по … читав, след като схванах къде бъркам … но трябваше да ревъртна защото mail server-а ми (docker image) не можеше да праща мейли. Днес реших и този проблем.

За този материал съм се водил от следните линкове:

https://serverfault.com/questions/93678/load-balancing-nat-ing-multiple-isp-connections-on-linux

linux-ip.net/html/routing-tables.html

И от резултатите от използването на този tool:

https://lstein.github.io/Net-ISP-Balance/

Постановка

Имам малък сървър, който го играе едновременно border device на домашната ми мрежа и web/mail/xmpp хостинг сървър на няколко малки и средни проекта, за които се грижа (един от тях е този блог). Mail server-a от доста време е на docker, но на скоро започнах да качвам и част от нещата които хоствам в docker (нападна ме един WordPress вирус и реших че капсулация на проектите е най-добре).

Доставчика ми за последните 10 години е A1 (бившите SpectrumNet) и почти съм нямал проблеми с тях. Преди известно време обаче ми се наложи да си пусна един Vivacom-ски нет по друг повод и на друг адрес, но после ми увисна не използваем. За това реших да си го закача като втори доставчик.

Подход

Тъй като няма как да агрегирам двата доставчика, мога да им направя Load balance на ниво заявка – всяка отделна конекция минава през различен доставчик. Докато се рових обаче попаднах на интересен подход … по-скоро два допълващи се такива – ip route nexthop и iptables mangle.

ip route nexthop

На практика основната работа се върши от ip route nexthop, който е един много кратък и приятен ред:

ip route add default scope global nexthop via $ISP1_GW dev eth0 weight 1 nexthop via $ISP2_GW dev eth2 weight 1

Дори само този ред ви позволява да подкарате и двата доставчика паралелно, макар, че не е нито фин нито красив метод да се направи това. Още повече че този тип заявки не гарантират NAT-инг на мрежата зад border устройството.

След малко повече четене, се оказа че по-добър вариант е да се създадат две отдел route таблици, които да съдържат пътищата които ще се използват от цялата система.

По подразбиране ip route show показва следното:

default via 78.83.64.1 dev eth0 proto dhcp src 78.83.66.198 metric 202
default via 212.5.146.1 dev eth2 proto dhcp src 212.5.147.104 metric 203
78.83.64.0/20 dev eth0 proto dhcp scope link src 78.83.66.198 metric 202
127.0.0.0/8 dev lo scope link
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev eth3 proto kernel scope link src 192.168.0.254
192.168.10.0/24 via 192.168.10.2 dev tun0
192.168.10.2 dev tun0 proto kernel scope link src 192.168.10.1
212.5.146.0/23 dev eth2 proto dhcp scope link src 212.5.147.104 metric 203

За да можем да разделим трафика обаче, трябва да разделим в две отделни групи и съответно да сложим нужните route-ове. Стъпките са следните:

  1. Флъшваме всички routes
  2. Флъшваме всички rules
  3. Добавяме default rules обратно
  4. Флъшваме двете таблици (за всеки случай)
  5. Добавяме всички нужни route-ове в main таблицата
  6. Добавяме всички нужни route-ове в таблицата на ISP1
  7. Маркираме пакетите за таблица на ISP1
  8. Добавяме всички нужни route-ове в таблицата на ISP2
  9. Маркираме пакетите за таблица на ISP2
  10. Добавяме ip route nexthop пътищата …

Или целият код изглежда по този начин:

ip route flush all
ip rule flush
# Add default rules
ip rule add from all lookup main pref 32766
ip rule add from all lookup default pref 32767
# Flush both route tables
ip route flush table A1
ip route flush table VIVACOM
# Add main table routes
ip route add 78.83.64.0/20 dev eth0 src $ISP1_IP
ip route add 192.168.0.0/24 dev eth3 src 192.168.0.254
ip route add 212.5.146.0/23 dev eth2 src $ISP2_IP
ip route add 127.0.0.0/8 dev lo scope link
ip route add 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
ip route add 192.168.10.0/24 via 192.168.10.2 dev tun0
ip route add 192.168.10.2 dev tun0 proto kernel scope link src 192.168.10.1
#add routes to A1 table with the default gateway
ip route add table A1 default dev eth0 via $ISP1_GW
ip route add table A1 78.83.64.0/20 dev eth0 src $ISP1_IP
ip route add table A1 192.168.0.0/24 dev eth3 src 192.168.0.254
ip route add table A1 212.5.146.0/23 dev eth2 src $ISP2_IP
ip route add table A1 127.0.0.0/8 dev lo scope link
ip route add table A1 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
ip route add table A1 192.168.10.0/24 via 192.168.10.2 dev tun0
ip route add table A1 192.168.10.2 dev tun0 prot kernel scope link src 192.168.10.1
#Add rule for A1 table and mark packets for it
ip rule add from $ISP1_IP table A1
ip rule add fwmark 1 table A1
#Add routes for VIVACOM table with default garway
ip route add table VIVACOM default dev eth2 via $ISP2_GW
ip route add table VIVACOM 78.83.64.0/20 dev eth0 src $ISP1_IP
ip route add table VIVACOM 192.168.0.0/24 dev eth3 src 192.168.0.254
ip route add table VIVACOM 212.5.146.0/23 dev eth2 src $ISP2_IP
ip route add table VIVACOM 127.0.0.0/8 dev lo scope link
ip route add table VIVACOM 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
ip route add table VIVACOM 192.168.10.0/24 via 192.168.10.2 dev tun0
ip route add table VIVACOM 192.168.10.2 dev tun0 proto kernel scope link src 192.168.10.1
#Add rule for VIVACOM and mark packets
ip rule add from $ISP2_IP table VIVACOM
ip rule add fwmark 2 table VIVACOM
#This is where magic happens - this alternates the packets from different ISPs
ip route add default scope global nexthop via $ISP1_GW dev eth0 weight 1 nexthop via $ISP2_GW dev eth2 weight 1

И всички пътища са добавени за да бъдат използвани от всички таблици. И всяка сесия сменя позицията си … Но това създава малко проблеми … все пак трябва и NAT …

iptables mangle

За NAT или Network Address Translation се грижи iptables и по специално специалната mangle таблица, както и –mark опцията на iptables. Ето и как изглежда идеята зад промените на iptables:

  1. Създаваме таблица и маркер за ISP1
  2. Създаваме таблица и маркер за ISP2
  3. Всички нови завки идващи от LAN мрежата в PREROUTING таблицата биват маркирани съответно за ISP1 или ISP2
  4. Всички остановени и свързани връзки им се премахва conn-mark-а
  5. ВАЖНО!!! Тъй като докера е работи зад http reverse proxy – всички нови заявки от докер картите биват подавани към ISP1
  6. Всички входящи заявки към ISP1 картата се маркират за ISP1
  7. Всички входящи заявки към ISP2 картата се маркират за ISP2

Ето и как изглежда кода:

#CONNTRACK for load balancing (PREROUTING)
iptables -t mangle -N A1
iptables -t mangle -A A1 -j MARK --set-mark 1
iptables -t mangle -A A1 -j CONNMARK --save-mark
iptables -t mangle -N VIVACOM
iptables -t mangle -A VIVACOM -j MARK --set-mark 2
iptables -t mangle -A VIVACOM -j CONNMARK --save-mark
iptables -t mangle -A PREROUTING -i $LAN -s 192.168.0.0/24 -m conntrack --ctstate NEW -m statistic --mode random --probability 1 -j VIVACOM
iptables -t mangle -A PREROUTING -i $LAN -s 192.168.0.0/24 -m conntrack --ctstate NEW -m statistic --mode random --probability 0.5 -j A1
iptables -t mangle -A PREROUTING -i $LAN -s 192.168.0.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate NEW -m statistic --mode random --probability 1 -j A1
iptables -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i $WAN -m conntrack --ctstate NEW -j A1
iptables -t mangle -A PREROUTING -i $WAN -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i $WAN2 -m conntrack --ctstate NEW -j VIVACOM
iptables -t mangle -A PREROUTING -i $WAN2 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark

Специфики

OpenVPN

Ако и вие като мен имате OpenVPN инсталиран, добавте следното към конфигурацията на сървъра:

multihome

multihome позволява връзването в такава ситуация …

DNS

От време на време отварянето на интернет страници ще има проблем. Предполагам, че това се получава заради загубата на DNS заявка – тъй като всеки ISP си има DNS – ако не клиента не може да останови връзка с DNS-а защото минава през другия route … ще дропва страници … За сега не съм намерил решение на този проблем.

Симетричност

Поакзаният по-горе метод предполага, че и двата доставчика предоставят еднакви скорости. Моля ви анстройте си weight в ip route частта и probability в iptable частта …

DNS/HTTP Roundrobin

Този метод не покрива DNS и HTTP Server round-robin – сървърите ви продължават да използват 1 IP …

MASQUARADIN

Не забравяйте да си добавите POSTROUTING MASQUAREDE правилата и за двата доставчика:

iptables -A POSTROUTING -t nat -o $WAN -j MASQUERADE
iptables -A POSTROUTING -t nat -o $WAN2 -j MASQUERADE

Торренти

Не мисля че има домашен потребител у нас който да няма торенти. Ако искате да изпозлвате и двата доставчика – добавте си port forwarding и за двете карти:

iptables -A FORWARD -p tcp -i $WAN -d 192.168.0.1 --dport 12666 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp -i $WAN --dport 12666 -j DNAT --to 192.168.0.1:12666
iptables -A FORWARD -p tcp -i $WAN2 -d 192.168.0.1 --dport 12666 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp -i $WAN2 --dport 12666 -j DNAT --to 192.168.0.1:12666

Еми това е от мен … както винаги това ръководство е написано по-скоро за да не забравя как съм го направил аз, но може да е полезно за някого … Ако имате въпроси или предложения – не се притеснявайте да питате … ако някой намери решение за DNS проблема … също да пише …

Ваш,

Lucifer

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *

 

Този сайт използва Akismet за намаляване на спама. Научете как се обработват данните ви за коментари.