Отказоустойчивый Kubernetes на BGP. Часть 1: схема и план
Это первая статья серии о том, как с нуля собрать отказоустойчивый кластер Kubernetes, в котором и API-сервер, и сервисы типа LoadBalancer публикуются по BGP. Такой кластер у меня работает на виртуальных машинах Hyper-V, и на нём живёт, в том числе, этот блог.
Серия получилась практической: в каждой части — команды, конфигурация и то, на что я успел наступить.
Серия «Отказоустойчивый Kubernetes на BGP»
- Схема и план (эта статья)
- Готовим узлы Ubuntu 24.04
- BGP на роутере (FRR)
- kube-vip в режиме BGP и первый мастер
- Calico с eBPF и рабочие узлы
- Адреса LoadBalancer из Calico по BGP
- Traefik за BGP и externalTrafficPolicy Local
- Проверка отказоустойчивости и разбор полётов
Что получится в итоге
- три мастера (control plane) и пять рабочих узлов;
- адрес API-сервера (VIP) висит одновременно на всех трёх мастерах, и каждый из них анонсирует его роутеру по BGP — это делает kube-vip;
- адреса сервисов
LoadBalancerвыдаёт и анонсирует Calico — отдельный MetalLB не нужен; - роутер (Linux + FRR) принимает маршруты и раскладывает трафик между узлами по ECMP;
- CNI — Calico с датаплейном eBPF,
kube-proxyв кластере нет; - входящий трафик принимает Traefik, который стоит за адресом из BGP.
Никаких виртуальных адресов «на интерфейсе одного из узлов» и никакого ARP: адрес — это просто маршрут, который есть у роутера, пока есть хотя бы один живой узел, способный обслужить запрос.
Зачем BGP, если есть ARP
У обоих «классических» способов опубликовать адрес — kube-vip в ARP-режиме и MetalLB в L2-режиме — одна и та же особенность: в каждый момент адрес принадлежит одному узлу.
- В ARP-режиме kube-vip узлы выбирают лидера через lease в Kubernetes API, и лидер вешает VIP на свой интерфейс и рассылает gratuitous ARP. Пока лидер жив — весь трафик идёт на него. Если он умер, надо дождаться окончания lease, выбрать нового и обновить ARP-таблицы соседей.
- MetalLB в L2-режиме работает так же: один узел отвечает на ARP за адрес сервиса и принимает на себя весь трафик к нему.
Оба способа требуют, чтобы клиенты и узлы находились в одном L2-сегменте.
При BGP картина другая:
- анонсируют все узлы сразу, роутер видит несколько равноценных маршрутов к одному адресу и раскладывает по ним трафик (ECMP);
- отказ узла — это исчезновение его маршрута: либо сразу (сессия оборвалась), либо по hold-таймеру BGP, либо потому что узел сам снял анонс;
- адресу не нужен L2-сегмент: он может жить в подсети, которой физически не существует.
Цена вопроса
Честно о минусах:
- Нужен роутер, который умеет BGP. У меня это обычный Linux с FRR, но подойдут и MikroTik, и pfSense/OPNsense с плагином FRR, и «взрослое» железо.
- Подсеть для адресов должна быть чисто маршрутизируемой. В BGP-режиме kube-vip не отвечает на ARP, поэтому, если VIP лежит в той же подсети, что и узлы, соседи будут искать его широковещанием и не найдут. Я завёл отдельную
192.168.20.0/24, у которой нет L2-сегмента. - Трафик внутри подсети узлов получается несимметричным (узел идёт к VIP через роутер, а ответ возвращается напрямую). Об этом отдельная часть: без правки файрвола на роутере схема просто не заработает.
- Роутер стал частью control plane. Рабочие узлы ходят к API через него, поэтому перезагрузка роутера роняет их в
NotReady. Раньше, когда VIP был в той же подсети, роутер в этом пути не участвовал.
Схема
интернет / клиенты
│
┌─────────────┴──────────────┐
│ router.example.com │
│ Linux + FRR, AS 65010 │
│ LAN: 192.168.10.1/24 │
└─────────────┬──────────────┘
│ сеть узлов 192.168.10.0/24
┌──────────────────────┼──────────────────────┐
│ │ │
k8s-master-1..3 k8s-node-1..5 (маршрутизируемая
192.168.10.17-19 192.168.10.20-24 подсеть без L2)
kube-vip, AS 65020 Calico (BIRD), AS 65030 192.168.20.0/24
анонсирует анонсирует /32 адресов ├─ .1 VIP API
192.168.20.1/32 LoadBalancer └─ .64/26 пул LoadBalancer
Сессий BGP восемь: по одной между роутером и каждым узлом. Мастера и рабочие узлы между собой по BGP не общаются.
Адресный план
| Что | Значение |
|---|---|
| Подсеть узлов | 192.168.10.0/24, роутер 192.168.10.1 |
Мастера k8s-master-1…3 |
192.168.10.17–19 |
Рабочие узлы k8s-node-1…5 |
192.168.10.20–24 |
| VIP API-сервера | 192.168.20.1 = control.k8s.example.com |
| Пул адресов LoadBalancer | 192.168.20.64/26 |
| Сеть подов | 172.20.0.0/16, блоки по /26 |
| Сеть сервисов | 10.96.0.0/12 |
Пул LoadBalancer начинается с .64, чтобы не пересекаться с VIP, который лежит в той же подсети.
Номера AS
| Кто | AS |
|---|---|
| Роутер | 65010 |
| Мастера (kube-vip) | 65020 |
| Рабочие узлы (Calico) | 65030 |
Все номера из приватного диапазона 64512–65534, и они разные — это сознательное решение:
- роутер и узлы в разных AS — значит, сессии eBGP; при одинаковых AS это был бы iBGP, где ECMP включается отдельной командой (
maximum-paths ibgp), о которой легко забыть и остаться без балансировки; - у мастеров и воркеров разные AS, потому что у них разные роли и политику к ним удобнее применять раздельно; к тому же, одинаковая AS у узла и роутера делала бы невозможным передачу маршрутов обратно в кластер: узел отбросил бы маршрут, увидев в AS_PATH собственный номер.
Пример в документации kube-vip использует одинаковые значения только потому, что там оставлены умолчания.
Версии
| Компонент | Версия |
|---|---|
| ОС | Ubuntu 24.04 |
| Kubernetes (kubeadm) | 1.36 |
| containerd | 2.x из репозитория Docker |
| kube-vip | 1.2.4 |
| Calico | 3.32 (через tigera-operator) |
| FRR на роутере | 8.4 |
| Traefik | 3.7 |
Версии — на момент написания; принцип от них не зависит, но имена полей и флагов могут отличаться.
План серии
- Схема и план — эта статья.
- Готовим узлы Ubuntu 24.04 — сеть, containerd, kubeadm, время, мелочи, о которых лучше знать заранее.
- BGP на роутере (FRR) — конфигурация, фильтры, ECMP и правило файрвола для «разворота» трафика.
- kube-vip в режиме BGP и первый мастер — статический под,
kubeadm init, присоединение остальных мастеров. - Calico с eBPF, рабочие узлы — CNI без
kube-proxy. - Адреса LoadBalancer из Calico по BGP — пул, пиры, аннотация и грабли.
- Traefik за BGP —
externalTrafficPolicy: Localи почему это важно. - Проверка отказоустойчивости и разбор полётов — ломаем узлы, смотрим на роутер, шпаргалка по диагностике.
Что понадобится
- восемь (или меньше — три мастера и хотя бы один воркер) машин с Ubuntu 24.04: у меня виртуальные, по 4 vCPU, 6 ГБ памяти и 60 ГБ диска;
- роутер с Linux (или иным BGP-маршрутизатором), стоящий на пути между узлами и остальными сетями;
- свободная подсеть, которой нет ни у кого в сети: в ней будут жить VIP и адреса сервисов;
- DNS-имя для API (
control.k8s.example.comв моём случае).
Идём дальше: в части 2 подготовим узлы.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.