Отказоустойчивый Kubernetes на BGP. Часть 5: Calico с eBPF и рабочие узлы

Три мастера собраны, VIP работает, но кластер без сети. В этой части устанавливаем Calico, переводим его на eBPF вместо kube-proxy и присоединяем рабочие узлы.

Серия «Отказоустойчивый Kubernetes на BGP»

  1. Схема и план
  2. Готовим узлы Ubuntu 24.04
  3. BGP на роутере (FRR)
  4. kube-vip в режиме BGP и первый мастер
  5. Calico с eBPF и рабочие узлы (эта статья)
  6. Адреса LoadBalancer из Calico по BGP
  7. Traefik за BGP и externalTrafficPolicy Local
  8. Проверка отказоустойчивости и разбор полётов

Почему Calico, и почему eBPF

Calico в моём кластере выполняет сразу три задачи:

  1. CNI — сеть подов (VXLAN в режиме CrossSubnet: между узлами одной подсети пакеты идут без инкапсуляции);
  2. сервисы — в режиме eBPF Calico сам обслуживает ClusterIP, NodePort и LoadBalancer, и kube-proxy не нужен;
  3. адреса LoadBalancer по BGP (часть 6).

Режим eBPF я выбрал по двум причинам. Первая — исчезает целый слой правил iptables, которые надо чем-то обновлять: однажды kube-proxy не подхватил смену адреса сервиса на части узлов, и именно это стало главной задержкой переезда. Вторая — eBPF сохраняет адрес клиента для соединений, которые принял другой узел.

Требования: ядро 5.10+ (Ubuntu 24.04 — 6.8), cgroup v2 и смонтированный bpffs — на Ubuntu 24.04 всё это есть из коробки.


Установка оператора

Calico ставится через tigera-operator: он разворачивает и обслуживает все компоненты, а настройка сводится к нескольким ресурсам.

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/operator-crds.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/tigera-operator.yaml

(Файл operator-crds.yaml есть в версиях 3.30 и новее; для старых — достаточно одного tigera-operator.yaml. Именно create, не apply: у CRD слишком большая аннотация.)

Адрес API без kube-proxy

Здесь есть курица и яйцо. Оператор и calico-node — поды с hostNetwork, и в поисках API они обращаются к сервису kubernetes (10.96.0.1). Но этот адрес превращается в адрес мастера только благодаря kube-proxy — которого у нас нет. А eBPF-датаплейн Calico, который должен его заменить, сам требует доступа к API, чтобы запуститься.

Решение из документации Calico — сказать оператору настоящий адрес API через ConfigMap. Указываем имя VIP:

apiVersion: v1
kind: ConfigMap
metadata:
  name: kubernetes-services-endpoint
  namespace: tigera-operator
data:
  KUBERNETES_SERVICE_HOST: control.k8s.example.com
  KUBERNETES_SERVICE_PORT: "6443"
kubectl apply -f kubernetes-services-endpoint.yaml
kubectl -n tigera-operator rollout restart deploy/tigera-operator

Рестарт нужен, чтобы под перечитал переменные окружения.


Installation

apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    linuxDataplane: BPF
    bgp: Disabled
    ipPools:
      - name: default-ipv4-ippool
        cidr: 172.20.0.0/16
        blockSize: 26
        encapsulation: VXLANCrossSubnet
        natOutgoing: Enabled
        nodeSelector: all()
        disableBGPExport: true
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
  name: default
spec: {}

Что тут важно:

  • Пул подов задан явно. Если его не указать, оператор создаст стоковый 192.168.0.0/16, который легко пересечётся с вашей домашней сетью. У нас kubeadm не задавал podSubnet, так что сеть подов известна только Calico.
  • bgp: Disabled пока: сначала я хочу поднять сеть подов, а BGP включим в части 6 вместе с остальной настройкой. Иначе «из коробки» Calico начнёт строить полносвязную сетку BGP между всеми узлами, а нам она не нужна.
  • disableBGPExport: true на пуле подов: когда BGP будет включён, Calico не станет рассказывать роутеру про блоки 172.20.x.x/26. Трафик подов идёт через VXLAN, роутеру про них знать незачем. (Роутер всё равно отбросил бы эти маршруты по prefix-list, но лучше не создавать ему лишней работы.)
  • APIServer разворачивает calico-apiserver — агрегированный API projectcalico.org/v3. Без него kubectl не умеет работать с BGPConfiguration, BGPPeer и обычными IPPool в человекочитаемом виде. Правда, в Calico 3.32 этот компонент объявлен устаревшим: ему на смену идут нативные CRD, но на момент написания он нужен.
kubectl apply -f installation.yaml
kubectl get tigerastatus -w

Через пару минут все компоненты (calico, apiserver, ippools) должны стать Available=True, а три мастера — Ready. CoreDNS перейдёт в Running.


Рабочие узлы

Команду join мы сохранили из вывода kubeadm init. Если токен уже протух (он живёт 24 часа), создаём новый на любом мастере:

kubeadm token create --print-join-command

На каждом из пяти воркеров:

sudo kubeadm join control.k8s.example.com:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Воркеры обращаются к API через роутер: имя ведёт на VIP, VIP — в подсеть, которой нет рядом, и маршрут идёт через 192.168.10.1. Поэтому правило разворота из части 3 должно быть к этому времени на месте: без него join будет подвисать или обрываться.

kubectl get nodes -o wide
kubectl get pods -A -o wide

Проверка eBPF

kubectl -n kube-system get ds                  # kube-proxy нет
kubectl -n calico-system get pods -o wide
kubectl get installation default -o jsonpath='{.status.computed.calicoNetwork.linuxDataplane}'; echo   # BPF

Что видит датаплейн:

kubectl -n calico-system exec ds/calico-node -c calico-node -- calico-node -bpf nat dump | head -20

Здесь все сервисы с их бэкендами. Обычные iptables-save про сервисы уже ничего не скажет.

Проверяем, что у подов работает DNS и доступ в интернет:

kubectl run -it --rm dnscheck --image=busybox:1.37 --restart=Never -- nslookup kubernetes.default
kubectl run -it --rm nettest --image=busybox:1.37 --restart=Never -- wget -qO- -T 5 http://example.com | head -3

Как управлять Calico с оператором

Новичку в оператор-режиме обычно нужно пережить два сюрприза.

Оператор возвращает всё на место. Он непрерывно приводит кластер к описанному состоянию. Редактировать руками daemonset/calico-node или deployment/calico-typha в calico-system бесполезно: оператор молча вернёт их к своему виду за секунды, без ошибки и без следа в логах. Параметры сети (MTU, инкапсуляция, пулы) меняются только через Installation.

Две группы API.

Группа О чём Примеры
operator.tigera.io/v1 как Calico развёрнут Installation, APIServer, TigerStatus
projectcalico.org/v3 как Calico настроен IPPool, BGPConfiguration, BGPPeer, FelixConfiguration

Ресурсы projectcalico.org/v3 правятся напрямую. Исключение — пул подов default-ipv4-ippool: им владеет оператор, он держит его копию в Installation и откатывает правки самого IPPool. Параметры пула меняем только в Installation, и патч должен быть JSON, а не merge: merge заменил бы список пулов целиком.

kubectl patch installation default --type=json \
  -p '[{"op":"replace","path":"/spec/calicoNetwork/ipPools/0/disableBGPExport","value":true}]'

Ещё одна ловушка: IPPool виден через две группы сразу — ippools.projectcalico.org (через агрегированный API, с проверкой) и «сырой» ippools.crd.projectcalico.org. Данные одни и те же, но писать нужно в первую.

Диагностика. Здоровье — первое, что надо смотреть:

kubectl get tigerastatus
kubectl -n tigera-operator logs deploy/tigera-operator --tail=50

tigerastatus пишет что-то расплывчатое вроде «Waiting for Tigera API server», а настоящую причину дают логи оператора.

В логах calico-apiserver будет много level=error с пометкой «Timeout or abort while handling» на GET /apis/projectcalico.org/v3 — это не поломка. Каждые 30 секунд kube-aggregator проверяет доступность APIService и закрывает поток, не дочитав ответ. Если APIService в Available=True, а ошибки только на discovery, всё в порядке.


Память

calico-node в режиме eBPF занимает 200–300 МБ — в его cgroup учитываются карты BPF. Если у вас Hyper-V и вы не добавили nr_cpus (часть 2), сюда ещё добавятся гигабайты per-CPU памяти.


В части 6 включаем BGP в Calico и настраиваем выдачу адресов LoadBalancer.

Оставить комментарий могут только зарегистрированные пользователи.

Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.