Dostrajanie routingu dla MetalLB w trybie L2

Dostrajanie routingu dla MetalLB w trybie L2
Niedawno stanąłem przed bardzo nietypowym zadaniem ustawienia routingu dla MetalLB. Wszystko byłoby dobrze, bo... Zwykle MetalLB nie wymaga żadnych dodatkowych działań, jednak w naszym przypadku mamy do czynienia z dość dużym klastrem z bardzo prostą konfiguracją sieci.

W tym artykule powiem Ci, jak skonfigurować routing oparty na źródłach i zasadach dla sieci zewnętrznej Twojego klastra.

Nie będę wchodził w szczegóły dotyczące instalacji i konfiguracji MetalLB, ponieważ zakładam, że masz już pewne doświadczenie. Proponuję przejść od razu do rzeczy, czyli ustawienia routingu. Mamy więc cztery przypadki:

Przypadek 1: Gdy nie jest wymagana żadna konfiguracja

Spójrzmy na prosty przypadek.

Dostrajanie routingu dla MetalLB w trybie L2

Dodatkowa konfiguracja routingu nie jest wymagana, jeśli adresy wydawane przez MetalLB znajdują się w tej samej podsieci, co adresy Twoich węzłów.

Na przykład masz podsieć 192.168.1.0/24, ma router 192.168.1.1, a Twoje węzły otrzymają adresy: 192.168.1.10-30, to dla MetalLB możesz dostosować zakres 192.168.1.100-120 i miej pewność, że będą działać bez dodatkowej konfiguracji.

Dlaczego? Ponieważ Twoje węzły mają już skonfigurowane trasy:

# 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

A adresy z tego samego zakresu będą je ponownie wykorzystywać bez żadnych dodatkowych działań.

Przypadek 2: Gdy wymagane jest dodatkowe dostosowanie

Dostrajanie routingu dla MetalLB w trybie L2

Powinieneś skonfigurować dodatkowe trasy, gdy Twoje węzły nie mają skonfigurowanego adresu IP lub trasy do podsieci, dla której MetalLB nadaje adresy.

Wyjaśnię trochę bardziej szczegółowo. Ilekroć MetalLB wyprowadza adres, można go porównać do prostego przypisania, takiego jak:

ip addr add 10.9.8.7/32 dev lo

Zwróć uwagę na:

  • a) Adres jest przypisany z prefiksem /32 czyli trasa nie zostanie dla niej automatycznie dodana do podsieci (to tylko adres)
  • b) Adres jest dołączony do dowolnego interfejsu węzła (na przykład pętli zwrotnej). Warto w tym miejscu wspomnieć o cechach stosu sieciowego Linux. Nieważne do jakiego interfejsu dodasz adres, jądro zawsze będzie przetwarzać żądania arp i wysyłać odpowiedzi arp do któregokolwiek z nich, takie zachowanie jest uznawane za prawidłowe i co więcej, jest dość powszechnie stosowane w tak dynamicznym środowisku jak Kubernetes.

To zachowanie można dostosować, na przykład włączając ścisłe arp:

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

W takim przypadku odpowiedzi arp zostaną wysłane tylko wtedy, gdy interfejs będzie jawnie zawierał określony adres IP. To ustawienie jest wymagane, jeśli planujesz używać MetalLB, a Twój kube-proxy działa w trybie IPVS.

Jednak MetalLB nie używa jądra do przetwarzania żądań arp, ale robi to sam w przestrzeni użytkownika, więc ta opcja nie będzie miała wpływu na działanie MetalLB.

Wróćmy do naszego zadania. Jeśli w Twoich węzłach nie ma trasy dla wydanych adresów, dodaj ją wcześniej do wszystkich węzłów:

ip route add 10.9.8.0/24 dev eth1

Przypadek 3: Gdy potrzebujesz routingu opartego na źródle

Będziesz musiał skonfigurować routing oparty na źródle, jeśli pakiety będą odbierane przez oddzielną bramę, a nie tę skonfigurowaną domyślnie, dlatego pakiety odpowiedzi również powinny przechodzić przez tę samą bramę.

Na przykład masz tę samą podsieć 192.168.1.0/24 dedykowane dla Twoich węzłów, ale chcesz nadawać adresy zewnętrzne za pomocą MetalLB. Załóżmy, że masz wiele adresów w podsieci 1.2.3.0/24 zlokalizowane w sieci VLAN 100 i chcesz ich używać do zewnętrznego dostępu do usług Kubernetes.

Dostrajanie routingu dla MetalLB w trybie L2

Podczas kontaktu 1.2.3.4 będziesz wysyłał żądania z innej podsieci niż 1.2.3.0/24 i poczekaj na odpowiedź. Węzeł będący aktualnie węzłem głównym dla adresu wydanego przez MetalLB 1.2.3.4, odbierze pakiet z routera 1.2.3.1, ale odpowiedź dla niego musi koniecznie przebiegać tą samą drogą, przez 1.2.3.1.

Ponieważ nasz węzeł ma już skonfigurowaną bramę domyślną 192.168.1.1, to domyślnie odpowiedź trafi do niego, a nie do 1.2.3.1, dzięki któremu otrzymaliśmy paczkę.

Jak sobie poradzić z tą sytuacją?

W takim przypadku należy przygotować wszystkie swoje węzły w taki sposób, aby były gotowe do obsługi adresów zewnętrznych bez dodatkowej konfiguracji. Oznacza to, że w powyższym przykładzie musisz wcześniej utworzyć interfejs VLAN na węźle:

ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up

A następnie dodaj trasy:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Pamiętaj, że trasy dodajemy do osobnej tablicy routingu 100 będzie zawierać tylko dwie trasy niezbędne do wysłania pakietu odpowiedzi przez bramę 1.2.3.1, znajdujący się za interfejsem eth0.100.

Teraz musimy dodać prostą regułę:

ip rule add from 1.2.3.0/24 lookup 100

co wyraźnie mówi: jeśli adres źródłowy pakietu znajduje się w 1.2.3.0/24, musisz skorzystać z tablicy routingu 100. Opisaliśmy już w nim trasę, która go przeprowadzi 1.2.3.1

Przypadek 4: Gdy potrzebujesz routingu opartego na zasadach

Topologia sieci jest taka sama jak w poprzednim przykładzie, ale załóżmy, że chcesz mieć również dostęp do adresów puli zewnętrznych 1.2.3.0/24 z twoich kapsuł:

Dostrajanie routingu dla MetalLB w trybie L2

Osobliwością jest to, że podczas uzyskiwania dostępu do dowolnego adresu w 1.2.3.0/24, pakiet odpowiedzi trafia do węzła i ma adres źródłowy w zakresie 1.2.3.0/24 zostanie posłusznie wysłany eth0.100, ale chcemy, aby Kubernetes przekierował go do naszego pierwszego poda, który wygenerował pierwotne żądanie.

Rozwiązanie tego problemu okazało się trudne, ale stało się możliwe dzięki routingowi opartemu na zasadach:

Aby lepiej zrozumieć proces, oto schemat blokowy netfilter:
Dostrajanie routingu dla MetalLB w trybie L2

Najpierw, podobnie jak w poprzednim przykładzie, utwórzmy dodatkową tablicę routingu:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Dodajmy teraz kilka reguł do 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

Reguły te będą oznaczać połączenia przychodzące do interfejsu eth0.100, oznaczając wszystkie pakiety znacznikiem 0x100, odpowiedzi w ramach tego samego połączenia będą również oznaczone tym samym tagiem.

Teraz możemy dodać regułę routingu:

ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100

Oznacza to, że wszystkie pakiety mają adres źródłowy 1.2.3.0/24 i oznacz 0x100 należy poprowadzić za pomocą tabeli 100.

Tym samym inne pakiety odbierane na innym interfejsie nie podlegają tej regule, co pozwoli na ich trasowanie przy użyciu standardowych narzędzi Kubernetes.

Jest jeszcze jedna rzecz, w Linuksie istnieje tak zwany filtr odwrotnej ścieżki, który psuje całą malinkę, dokonuje prostego sprawdzenia: dla wszystkich przychodzących pakietów zamienia adres źródłowy pakietu z adresem nadawcy i sprawdza, czy pakiet może opuścić ten sam interfejs, na którym został odebrany; jeśli nie, zostanie odfiltrowany.

Problem w tym, że w naszym przypadku nie będzie to działać poprawnie, ale możemy to wyłączyć:

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter

Należy pamiętać, że pierwsze polecenie kontroluje globalne zachowanie rp_filter; jeśli nie jest wyłączone, drugie polecenie nie będzie miało żadnego efektu. Jednakże pozostałe interfejsy pozostaną z włączoną opcją rp_filter.

Aby nie ograniczać całkowicie działania filtra, możemy zastosować implementację rp_filter dla netfiltera. Używając rpfilter jako modułu iptables, możesz skonfigurować dość elastyczne reguły, na przykład:

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

włącz rp_filter w interfejsie eth0.100 dla wszystkich adresów z wyjątkiem 1.2.3.0/24.

Źródło: www.habr.com

Dodaj komentarz