
Скоро се сблъсках с доста нетипична задача за настройка на маршрутизация за MetalLB. Няма проблем, тъй като обикновено за MetalLB не са необходими допълнителни действия, но в нашия случай имаме доста голям клъстер с доста проста конфигурация на мрежата.
В тази статия ще разкажа как да настроите маршрутизацията на базата на източника и политиката за външната мрежа на вашия клъстер.
Няма да се спирам подробно на инсталацията и настройката на MetalLB, тъй като предполагам, че вече имате известен опит. Предлагам директно да преминем към същината, а именно настройката на маршрутизацията. И така, имаме четири случая:
Случай 1: Когато настройка не е необходима
Нека разгледаме прост случай.

Допълнителна настройка на маршрутизацията не е необходима, когато адресите, предоставени от MetalLB, се намират в същата подсистема, в която са адресите на вашите нодове.
Например, имате подсистема 192.168.1.0/24, в която има маршрутизатор 192.168.1.1, и вашите нодове получават адреси: 192.168.1.10-30, тогава за MetalLB можете да настроите диапазон 192.168.1.100-120 и да бъдете сигурни, че те ще работят без никаква допълнителна настройка.
Защо е така? Защото вашите нодове вече имат настроени маршрути:
# ip route
default via 192.168.1.1 dev eth0 onlink
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10И адресите от същия диапазон ще ги преизползват без никакви допълнителни усилия.
Случай 2: Когато е необходима допълнителна настройка

Трябва да настроите допълнителни маршрути всеки път, когато вашите нодове нямат настроен IP адреси или маршрут в подсистемата, за която MetalLB предоставя адреси.
Нека обясня по-подробно. Всеки път, когато MetalLB предостави адрес, това може да се сравни с просто назначаване като:
ip addr add 10.9.8.7/32 dev loОбърнете внимание на:
- a) Адресът се назначава с префикс
/32тоест маршрутът в подсистемата за него автоматично няма да бъде добавен (това е просто адрес) - b) Адресът се добавя на произволен интерфейс на нода (например loopback). Тук е важно да се спомене особеностите на мрежовия стек на Linux. Няма значение на кой интерфейс добавите адреса, ядрото винаги ще обработва arp-запитвания и ще изпраща arp-отговори на който и да е от тях, това поведение се счита за правилно и, освен това, е достатъчно широко използвано в такава динамична среда като Kubernetes.
Това поведение може да се конфигурира, например, като се включи строг arp:
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announceВ този случай arp-отговорите ще бъдат изпращани само ако интерфейсът изрично съдържа конкретен IP адрес. Тази настройка е задължителна, ако планирате да използвате MetalLB и вашият kube-proxy работи в режим IPVS.
Въпреки това, MetalLB не използва ядрото за обработка на arp-заявките, а го прави самостоятелно в потребителското пространство, така че тази опция няма да повлияе на работата на MetalLB.
Нека се върнем към нашата задача. Ако маршрута за предоставените адреси на вашите нодове не съществува, добавете го предварително на всички нодове:
ip route add 10.9.8.0/24 dev eth1Случай 3: Когато е необходимо source-based routing
Source-based routing ще трябва да настроите, когато получавате пакети чрез отделен gateway, а не този, който е настроен по подразбиране, съответно отговорните пакети също трябва да преминават през този gateway.
Например, имате все същата подсетка 192.168.1.0/24 отделена за вашите нодове, но искате да предоставяте външни адреси с помощта на MetalLB. Да предположим, че имате няколко адреса от подсетката 1.2.3.0/24 разположени във VLAN 100, и искате да ги използвате за достъп до Kubernetes услуги отвън.

При заявка на 1.2.3.4 вие ще правите запитвания от друга подсетка, а не от 1.2.3.0/24 и ще очаквате отговор. Нодът, който в момента е майстор за предоставения MetalLB адрес 1.2.3.4, ще получи пакет от маршрутизатора 1.2.3.1, но отговорът към него задължително трябва да тръгне по същия маршрут, през 1.2.3.1.
Тъй като нашият нод вече има настроен default gateway 192.168.1.1, по подразбиране отговорът ще отиде при него, а не при 1.2.3.1, през който получихме пакета.
Как да се справим с тази ситуация?
В този случай трябва да подготвите всички ваши нодове, за да бъдат готови да обслужват външни адреси без допълнителна настройка. Тоест, за горепосочения пример, необходимо е предварително да създадете VLAN интерфейс на нода:
ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 upСлед това добавете маршрути:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100Обърнете внимание, че маршрути добавяме в отделна таблица за маршрутизация 100 Тя ще съдържа само два маршрута, необходими за изпращане на отговорния пакет през gateway 1.2.3.1, разположен зад интерфейса eth0.100.
Сега трябва да добавим просто правило:
ip rule add from 1.2.3.0/24 lookup 100което ясно указва: ако адресът на източника на пакета е в 1.2.3.0/24, то трябва да се използва таблица за маршрутизация 100. В нея вече е описан маршрут, който ще го изпрати през 1.2.3.1
Случай 4: Когато е необходимо маршрутизиране на базата на политика
Топологията на мрежата е съща като в предишния пример, но да предположим, че искате също така да имате възможност да се свързвате с външни адреси на пул 1.2.3.0/24 от вашите контейнери:

Спецификата е, че при достъп до който и да е адрес в 1.2.3.0/24, отговорният пакет, достигащ до нода и имащ адрес на източника в диапазона 1.2.3.0/24 ще бъде пратен в eth0.100, но искаме Kubernetes да го пренасочи към нашия първи контейнер, който е генерирал първоначалната заявка.
Решаването на този проблем се оказа сложно, но стана възможно благодарение на маршрутизацията на базата на политика:
За по-добро разбиране на процеса ще представя блок-схема на netfilter:

За начало, както и в предишния пример, ще създадем допълнителна таблица за маршрутизиране:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100Сега да добавим няколко правила в iptables:
iptables -t mangle -A PREROUTING -i eth0.100 -j CONNMARK --set-mark 0x100
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN
iptables -t mangle -A POSTROUTING -j CONNMARK --save-markТези правила ще маркират входящите връзки на интерфейса eth0.100, като помечава всички пакети с етикет 0x100, с този същия етикет ще бъдат помечени и отговорите в рамките на едно и също свързване.
Сега можем да добавим правило за маршрутизиране:
ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100Тоест, всички пакети с адрес на източника 1.2.3.0/24 и етикет 0x100 трябва да бъдат маршрутизирани, използвайки таблицата 100.
Така че другите пакети, получени на друг интерфейс, не попадат под това правило, което ще им позволи да бъдат маршрутизирани с помощта на стандартните средства на Kubernetes.
Има и едно "но", в Linux съществува т.нар. технически филтър за обратен път, който пречи на всичко и извършва проста проверка: за всички входящи пакети, той променя адреса на източника на пакета с адреса на изпращача и проверява дали пакетът може да излезе през същия интерфейс, на който е получен, ако не може, той го отфилтрира.
Проблемът е, че в нашия случай той ще работи некоректно, но можем да го изключим:
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filterОбърнете внимание, първата команда контролира глобалното поведение на rp_filter, ако не бъде изключен, втората команда няма да има никакъв ефект. Въпреки това, останалите интерфейси ще останат с включен rp_filter.
За да не ограничаваме работата на филтъра напълно, можем да използваме реализацията на rp_filter за netfilter. Използвайки rpfilter като модул на iptables, можем да настроим доста гъвкави правила, например:
iptables -t raw -A PREROUTING -i eth0.100 -d 1.2.3.0/24 -j RETURN
iptables -t raw -A PREROUTING -i eth0.100 -m rpfilter --invert -j DROPвключване на rp_filter на интерфейса eth0.100 за всички адреси освен 1.2.3.0/24.
Източник: habr.com
