Cilium L2 LoadBalancer на bare-metal k3s
Published: 2026-03-29
В облачном Kubernetes LoadBalancer-сервисы автоматически получают внешние IP от провайдера. На bare-metal k3s нет cloud controller. Cilium заполняет этот пробел с помощью L2 announcement — eBPF-альтернативы MetalLB.
Проблема
На bare-metal некому отвечать на ARP-запросы к IP LoadBalancer-сервисов:
bash$ kubectl get svc apisix -n ingress-apisix
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
apisix LoadBalancer 10.96.45.23 <pending> 80:32000/TCP
<pending> — cloud controller не выделил IP.
L2 announcement в Cilium
CiliumLoadBalancerIPPool — диапазон IP:
yamlapiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: dev-pool
spec:
blocks:
- cidr: "172.16.57.192/28" # 14 IP для LoadBalancer dev-кластера
CiliumL2AnnouncementPolicy — какие ноды анонсируют какие сервисы:
yamlapiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: dev-l2-policy
spec:
serviceSelector:
matchLabels:
app.kubernetes.io/managed-by: Helm
nodeSelector:
matchLabels:
kubernetes.io/os: linux
interfaces:
- "eth0"
externalIPs: true
loadBalancerIPs: true
При создании LoadBalancer-сервиса Cilium выбирает IP из пула, назначает его и отвечает на ARP-запросы через eth0.
Расположение объектов
Оба объекта в spoke/:
fluxcd/projects/dev/kustomization/spoke/
├── cilium-lb-pool.yaml
├── cilium-l2-announcement.yaml
├── namespaces.yaml
└── kustomization.yaml
Применяются через dev-spoke Kustomization (не dev-apps) — это обычные Kubernetes-ресурсы, а не HelmRelease.
Yandex Cloud: L2 не нужен
На YC Managed Kubernetes (sre, loadgds, demo) внешние IP выдаёт облачный балансировщик. Объекты Cilium LB pool и L2 announcement намеренно отсутствуют в spoke-оверлеях этих окружений.
Назначить конкретный IP
yamlspec:
values:
service:
loadBalancerIP: "172.16.57.193"
Без этого Cilium назначит следующий свободный IP из пула.
Предварительные требования
yamll2announcements:
enabled: true
l2podAnnouncements:
enabled: true
kubeProxyReplacement: true
k8sServiceHost: "API_SERVER_IP"
k8sServicePort: "6443"
k8sServiceHost обязателен при использовании kube-proxy replacement — Cilium напрямую обращается к API-серверу.
Проверка
bash# Статус пула
kubectl get ciliumloadbalancerippool -A
# Выделенные IP
kubectl get svc -A -o wide | grep LoadBalancer
# Политики L2-анонса
kubectl get ciliuml2announcementpolicy -A
# Детали пула с выделениями
kubectl describe ciliumloadbalancerippool dev-pool
После создания LoadBalancer-сервиса внешний IP появляется в течение нескольких секунд:
bashNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
apisix LoadBalancer 10.96.45.23 172.16.57.193 80:32000/TCP
Cilium vs MetalLB
MetalLB был стандартом до появления нативного L2 в Cilium. Если Cilium уже отвечает за CNI и kube-proxy replacement, добавлять MetalLB — значит держать два сетевых контроллера на каждой ноде.
| Cilium L2 | MetalLB | |
|---|---|---|
| Протокол | ARP/NDP | ARP/NDP или BGP |
| BGP | CiliumBGPPeeringPolicy | Да, нативно |
| Интеграция | Часть Cilium | Отдельный компонент |
| Замена kube-proxy | Тот же компонент | Отдельный |