Отказоустойчивый Kubernetes на BGP. Часть 3: BGP на роутере (FRR)
В третьей части настраиваем сторону роутера. Кластера ещё нет, но роутер можно подготовить заранее: он заранее знает всех будущих соседей и будет ждать, пока они появятся.
Серия «Отказоустойчивый Kubernetes на BGP»
- Схема и план
- Готовим узлы Ubuntu 24.04
- BGP на роутере (FRR) (эта статья)
- kube-vip в режиме BGP и первый мастер
- Calico с eBPF и рабочие узлы
- Адреса LoadBalancer из Calico по BGP
- Traefik за BGP и externalTrafficPolicy Local
- Проверка отказоустойчивости и разбор полётов
В моём случае роутер — обычная машина с Ubuntu 24.04, на которой уже работают маршрутизация и файрвол (iptables). Сеть узлов подключена к его интерфейсу eth1 (адрес 192.168.10.1/24). Если у вас другой роутер — идеи те же, меняется только синтаксис.
Установка FRR
sudo apt-get install -y frr frr-pythontools
sudo sed -i 's/^bgpd=no/bgpd=yes/' /etc/frr/daemons
sudo systemctl enable --now frr
Из всех демонов FRR по умолчанию включён только zebra — он разговаривает с ядром о маршрутах. Строка bgpd=yes включает собственно BGP. Пакет frr-pythontools нужен ради vtysh -b: эта команда применяет frr.conf к работающим демонам без перезапуска службы.
Конфигурация
Вот весь /etc/frr/frr.conf:
frr version 8.4
frr defaults traditional
hostname router.example.com
log syslog informational
service integrated-vtysh-config
!
router bgp 65010
bgp router-id 192.168.10.1
no bgp default ipv4-unicast
bgp graceful-restart
!
neighbor MASTERS peer-group
neighbor MASTERS remote-as 65020
neighbor MASTERS description Kubernetes control plane
neighbor MASTERS timers 10 30
!
neighbor WORKERS peer-group
neighbor WORKERS remote-as 65030
neighbor WORKERS description Kubernetes workers
neighbor WORKERS timers 10 30
!
neighbor 192.168.10.17 peer-group MASTERS
neighbor 192.168.10.18 peer-group MASTERS
neighbor 192.168.10.19 peer-group MASTERS
!
neighbor 192.168.10.20 peer-group WORKERS
neighbor 192.168.10.21 peer-group WORKERS
neighbor 192.168.10.22 peer-group WORKERS
neighbor 192.168.10.23 peer-group WORKERS
neighbor 192.168.10.24 peer-group WORKERS
!
address-family ipv4 unicast
neighbor MASTERS activate
neighbor MASTERS soft-reconfiguration inbound
neighbor MASTERS route-map FROM-CLUSTER in
neighbor MASTERS route-map NOTHING-OUT out
neighbor WORKERS activate
neighbor WORKERS soft-reconfiguration inbound
neighbor WORKERS route-map FROM-CLUSTER in
neighbor WORKERS route-map NOTHING-OUT out
maximum-paths 8
exit-address-family
!
ip prefix-list SERVICES seq 10 permit 192.168.20.0/24 le 32
!
route-map FROM-CLUSTER permit 10
match ip address prefix-list SERVICES
!
route-map NOTHING-OUT deny 10
!
line vty
Применяем:
sudo install -o frr -g frr -m 640 frr.conf /etc/frr/frr.conf
sudo vtysh -b
Что здесь что значит
router bgp 65010 и bgp router-id — наша AS и идентификатор маршрутизатора.
no bgp default ipv4-unicast — соседи не получают обмен IPv4-маршрутами автоматически, каждый включается явно командой activate в блоке address-family. Защита от обмена маршрутами, которых вы не ждёте.
peer-group MASTERS и WORKERS — группы соседей. Мастера (AS 65020, kube-vip) и воркеры (AS 65030, Calico) играют разные роли, и политики к ним удобнее применять раздельно, а не повторять одни и те же строки восемь раз. Сами соседи — отдельными строками neighbor <адрес> peer-group .... Новый узел — одна строка.
timers 10 30 — keepalive 10 секунд, hold 30 секунд вместо стандартных 60/180. Hold-таймер — это максимальное время, за которое роутер обнаружит жёстко умершего соседа (выключили питание, кабель выдернули) и уберёт его маршруты. 180 секунд для API — слишком много.
maximum-paths 8 — включает ECMP. Один и тот же префикс приходит сразу от нескольких узлов: VIP — от трёх мастеров, а позже и адрес сервиса — от нескольких воркеров. Без этой строки роутер выберет один маршрут из равноценных, а остальные оставит про запас. Работает потому, что у нас eBGP: при совпадающих AS пришлось бы писать maximum-paths ibgp.
soft-reconfiguration inbound — роутер хранит принятые маршруты до фильтра. Это важно для диагностики (received-routes и filtered-routes, см. ниже), без неё эти команды выводят пустоту, и легко решить, что сосед ничего не присылает.
ip prefix-list SERVICES + route-map FROM-CLUSTER in — принимаем только 192.168.20.0/24 и подсети внутри неё. Это предохранитель: если Calico однажды начнёт рассказывать роутеру про сеть подов 172.20.0.0/16 (а в какой-то момент это у меня случилось), роутер отбросит эти маршруты, и они не попадут в ядро.
route-map NOTHING-OUT out — роутер кластеру не отдаёт ничего. У узлов есть маршрут по умолчанию через роутер, большего им не нужно.
Ловушка bgp ebgp-requires-policy
По умолчанию (при frr defaults traditional) включена bgp ebgp-requires-policy: eBGP-сосед без входящей и исходящей политики не обменивается маршрутами вообще. Мы это условие выполняем (route-map ... in/out у обеих групп), но обратная сторона такая: опечатка в имени route-map приведёт к тому, что все маршруты будут молча отброшены, без единой ошибки в логе. Если show bgp пуст, смотрим сюда первым делом.
Файрвол: BGP-порт
BGP работает по TCP-порту 179. Если у вас, как и у меня, политика INPUT — DROP, соседям надо его открыть:
sudo iptables -A INPUT -i eth1 -s 192.168.10.0/24 -p tcp --dport 179 -j ACCEPT
Правило нужно держать в сохраняемых правилах (iptables-persistent, netfilter-persistent save) — иначе после перезагрузки роутера сессии не поднимутся.
Самая неочевидная часть: разворот трафика внутри подсети узлов
Теперь о том, без чего вся схема просто не работает. Если вы пропустите этот раздел, с первого взгляда всё будет работать, но ни одно соединение с полезной нагрузкой не будет доходить.
Узлы живут в 192.168.10.0/24, а VIP — в 192.168.20.0/24, которой на канальном уровне не существует. Когда рабочий узел обращается к VIP, он, как и положено, отправляет пакет через шлюз — роутер. Роутер находит в таблице маршрутов 192.168.20.1/32 через мастера и отправляет пакет обратно в тот же интерфейс eth1.
Мастер получает пакет, формирует ответ и видит, что адресат (воркер) находится в его собственной подсети. Поэтому он отвечает напрямую, минуя роутер.
Получается, что роутер видит только половину разговора. Его conntrack создаёт запись по SYN, никогда не видит SYN-ACK и остаётся в состоянии SYN_SENT. Следующий же пакет, уже с данными, попадает под правило, которое есть почти в любом файрволе:
-A FORWARD -m conntrack --ctstate INVALID -j DROP
Выглядит это издевательски:
pingпроходит;- TCP-рукопожатие проходит (ответ идёт мимо роутера и всё равно доходит);
- а первый же пакет с данными пропадает и бесконечно ретранслируется;
- в
tcpdumpна роутере SYN виден дважды (вход и выход), а пакет с данными — один раз.
Лечится правилом, которое разрешает разворот и стоит раньше проверки на INVALID:
sudo iptables -I FORWARD 1 -i eth1 -o eth1 -d 192.168.20.0/24 -j ACCEPT
Правило намеренно узкое: только разворот внутри подсети узлов в сторону подсети сервисов. Всем остальным клиентам оно не нужно: у них обратный путь идёт через роутер, трафик симметричен, и conntrack работает как обычно.
Обратите внимание, что я написал правило на всю подсеть 192.168.20.0/24, а не на один адрес VIP. Адреса LoadBalancer, которые появятся позже, оказываются в точно такой же ситуации, и тогда правило расширять не придётся.
Если файрволом на роутере управляет какой-нибудь агент или генератор правил (у меня так и есть), добавляйте правило через его механизм «пользовательских» правил в начале цепочки FORWARD: иначе при следующей генерации оно пропадёт.
Проверка
Пока ни одного кластерного узла нет, соседи будут в состоянии Active (роутер пытается подключиться, но на другой стороне никто не слушает). Для BGP это нормально.
sudo vtysh -c 'show bgp summary'
Здесь важна колонка State/PfxRcd: число — сессия жива, и столько префиксов принято; слово (Active, Connect, Idle) — сессии нет.
Когда поднимется кластер, для проверки пригодятся ещё несколько команд, и я их оставлю на потом:
sudo vtysh -c 'show bgp ipv4 unicast' # что приняли
sudo vtysh -c 'show bgp ipv4 unicast 192.168.20.1' # кто анонсирует конкретный адрес
ip route show 192.168.20.1 # что попало в ядро
sudo vtysh -c 'show bgp ipv4 unicast neighbors 192.168.10.20 filtered-routes' # что отбросили
В части 8 будет полная шпаргалка, а пока роутер готов: в части 4 поднимем VIP для API-сервера с помощью kube-vip и инициализируем первый мастер.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.