Не так даўно я сутыкнуўся з вельмі нестандартнай задачай налады маршрутышчацыі для MetalLB. Усё б нічога, т.я. звычайна для MetalLB не патрабуецца ніякіх дадатковых дзеянняў, але ў нашым выпадку маецца дастаткова вялікі кластар з вельмі няхітрай канфігурацыяй сеткі.
У дадзеным артыкуле я распавяду як наладзіць source-based і policy-based routing для вонкавай сеткі вашага кластара.
Я не буду падрабязна спыняцца на ўсталёўцы і наладзе 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.
Дадзеныя паводзіны можна наладжваць, напрыклад улучыўшы strict 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-запытаў, а робіць гэта самастойна ў user-space, такім чынам дадзеная опцыя не паўплывае на працу 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
яна будзе змяшчаць толькі два маршруты неабходных для адпраўкі пакета ў адказ праз гейтвей. 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: Калі спатрэбіцца policy-based routing
Тапалогія сеткі як і ў папярэднім прыкладзе, але дапусцім вы хочаце таксама мець магчымасць звяртацца да знешніх адрасоў пула 1.2.3.0/24
з вашых подаў:
Асаблівасць заключаецца ў тым, што пры звароце да любога адрасу ў 1.2.3.0/24
, пакет у адказ трапляючы на ноду і маючы адрас крыніцы ў дыяпазоне 1.2.3.0/24
будзе паслухмяна адпраўлены ў eth0.100
, Але мы вось хочам каб Kubernetes перанакіраваў яго ў наш першы пад, які і згенераваў першапачатковы запыт.
Вырашыць дадзеную праблему аказалася няпроста, але гэта стала магчымым дзякуючы policy-based routing:
Для большага разумення працэсу прывяду блок схему 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 існуе так званы reverse path filter, які псуе ўсю маліну ажыццяўляе простую праверку: для ўсіх уваходных пакетаў ён змяняе адрас крыніцы пакета з адрасам адпраўніка і правярае ці можа пакет сысці праз той жа інтэрфейс на які быў атрыманы , калі не, то адфільтроўваевет яго.
Праблема ў тым, што ў нашым выпадку ён будзе працаваць некарэктна, але мы можам адключыць яго:
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