Cilium Cilium

Service-IPs per BGP ankündigen

Kein Cloud-Load-Balancer? Cilium kann Service-IPs stattdessen per BGP direkt in dein physisches Netzwerk ankündigen.

15 min Experte

Der Service-Typ LoadBalancer eines Cloud-Anbieters funktioniert, weil die Cloud eine API hat, um bei Bedarf einen externen Load Balancer bereitzustellen. Auf Bare Metal oder in jeder Umgebung ohne diese API bleibt ein LoadBalancer-Service für immer im Status Pending, solange nichts anderes diese Rolle übernimmt. Das BGP Control Plane von Cilium ist eine Möglichkeit, diese Rolle auszufüllen: Es routet die IP eines Services direkt in dein physisches Netzwerk, indem Cluster-Knoten BGP direkt mit vorgelagerten Routern sprechen. Diese Anleitung behandelt die vier Ressourcen, die das konfigurieren, und wie du von beiden Seiten prüfst, dass eine BGP-Session tatsächlich steht.

Wann BGP das richtige Werkzeug ist

BGP-Peering von Cluster-Knoten zu vorgelagerten Routern ist eine echte Netzwerkintegration. Sie erfordert Abstimmung mit den Verantwortlichen für das physische (oder virtuelle) Netzwerk, eine Router-Konfiguration auf deren Seite, die zu deiner Peer-Konfiguration auf der Kubernetes-Seite passt, und echte betriebliche Vertrautheit mit BGP-Konzepten wie Autonomen Systemen und Route Advertisement. Das ist das richtige Werkzeug für einen On-Premises- oder Bare-Metal-Cluster, dessen Service-IPs aus dem restlichen Netzwerk tatsächlich routbar sein sollen, so wie bei einem Cloud-LoadBalancer. Für einfachere Anforderungen ist Ciliums L2-Announcements-Funktion (Ankündigung der IP eines Services per ARP/NDP im lokalen Netzsegment, ohne Routingprotokoll) eine leichtgewichtigere Alternative, die du zuerst in Betracht ziehen solltest, wenn dein Cluster in einem einzelnen Netzsegment liegt und kein Layer-3-Routing zwischen Segmenten oder Rechenzentren braucht.

Die vier Ressourcen

Vier CiliumBGP*-Custom-Resources arbeiten zusammen:

  • CiliumLoadBalancerIPPool definiert einen IP-Adressbereich, den Cilium LoadBalancer-Services zuweisen kann.
  • CiliumBGPAdvertisement definiert, was per BGP angekündigt wird, hier Service-IPs aus diesem Pool.
  • CiliumBGPPeerConfig definiert, wie BGP gesprochen wird: Timer, Graceful-Restart-Verhalten, Adressfamilien.
  • CiliumBGPClusterConfig verbindet alles: welche Knoten teilnehmen (über einen Label-Selector), ihre lokale AS-Nummer und mit welchen vorgelagerten Peers (per IP und AS-Nummer) sie sich verbinden.

Den IP-Pool definieren

apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: apps-pool
spec:
  allowFirstLastIPs: "No"
  blocks:
    - cidr: "10.10.211.0/24"

allowFirstLastIPs: "No" schließt die Netzwerk- und Broadcast-Adresse (.0 und .255) aus dem vergebbaren Bereich aus, eine gängige Konvention aus der klassischen Subnetz-Vergabe. Passe das CIDR an den Bereich an, den dein Netzwerkteam dafür tatsächlich bereitgestellt hat. Aus deren Sicht muss es echter, routbarer Adressraum sein und kein beliebiger privater Bereich, den du ohne Absprache gewählt hast.

Definieren, was angekündigt wird

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPAdvertisement
metadata:
  name: bgp-advertisements
  labels:
    advertise: bgp
spec:
  advertisements:
    - advertisementType: Service
      service:
        addresses:
          - LoadBalancerIP
      selector:
        matchExpressions:
          - key: io.kubernetes.service.namespace
            operator: Exists

Das kündigt die LoadBalancerIP passender Services an. Das Feld labels (advertise: bgp) ist das, was die Peer-Konfiguration unten referenziert, um beides zu verknüpfen – nicht ein Label an den Services selbst.

Peer-Verhalten definieren

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeerConfig
metadata:
  name: bgp-peer-config
spec:
  timers:
    holdTimeSeconds: 9
    keepAliveTimeSeconds: 3
  gracefulRestart:
    enabled: true
    restartTimeSeconds: 15
  families:
    - afi: ipv4
      safi: unicast
      advertisements:
        matchLabels:
          advertise: bgp

Das families.advertisements.matchLabels verbindet diese Peer-Konfiguration über das Label mit der oben definierten Advertisement-Ressource.

Alles verbinden: Cluster-Konfiguration und Knotenauswahl

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPClusterConfig
metadata:
  name: bgp-cluster-config
spec:
  nodeSelector:
    matchLabels:
      role: bgp-node
  bgpInstances:
    - name: instance-1
      localASN: 65011
      peers:
        - name: leaf-switch-1a
          peerASN: 65002
          peerAddress: "10.10.210.2"
          peerConfigRef:
            name: bgp-peer-config
        - name: leaf-switch-1b
          peerASN: 65002
          peerAddress: "10.10.210.3"
          peerConfigRef:
            name: bgp-peer-config

nodeSelector beschränkt das BGP-Peering auf eine bewusst gewählte Teilmenge von Knoten, nämlich die mit dem Label role: bgp-node, statt dass jeder Knoten im Cluster versucht, sich mit vorgelagerten Routern zu verbinden:

kubectl label node worker-4 worker-5 worker-6 role=bgp-node

Ein sinnvoller Bereich sind zwei bis acht Peering-Knoten: genug, um einen Single Point of Failure zu vermeiden, ohne dass dein Netzwerkteam BGP-Sessions gegen jeden Knoten eines Clusters mit womöglich Dutzenden oder Hunderten Knoten konfigurieren und pflegen muss. Wie viele sinnvoll sind, hängt vom physischen Aufbau deines Clusters ab. Bei Verteilung über mehrere Racks oder Reihen spricht in der Regel viel dafür, von Knoten in jedem davon aus zu peeren, statt alle BGP-Sessions an einem Ort zu bündeln.

Wende alle vier Ressourcen an und prüfe dann von der Kubernetes-Seite:

kubectl apply -f bgp-pool.yaml -f bgp-advertisement.yaml -f bgp-peer-config.yaml -f bgp-cluster-config.yaml

Prüfen, dass die Session tatsächlich steht

Aus Sicht eines Knotens, mit Ciliums eigener Debug-CLI im Cilium-Pod auf einem BGP-gelabelten Knoten:

kubectl -n kube-system exec <cilium-pod-on-bgp-node> -- cilium-dbg bgp peers

Achte auf den Session-Zustand established. Alles andere – idle, connect, active – bedeutet, dass die Session nicht zustande gekommen ist. Die häufigsten Ursachen sind eine nicht passende AS-Nummer zwischen beiden Seiten, eine nicht erreichbare Peer-Adresse oder dass der vorgelagerte Router noch gar nicht für diesen Peer konfiguriert ist. Bestätige, dass die Peer-Konfiguration auf der Netzwerkseite genau zu dem passt, was du hier deklariert hast, bevor du annimmst, dass die Konfiguration auf der Kubernetes-Seite falsch ist.

Bestätigen, dass eine Adresse tatsächlich angekündigt wird

Erstelle einen LoadBalancer-Service, der aus dem Pool zieht:

kubectl get svc -o wide

Seine EXTERNAL-IP sollte eine Adresse aus dem CIDR des Pools sein. Prüfe am vorgelagerten Router (oder an dem, was dahinter sitzt – einer Firewall oder einem Core-Router), dass diese Adresse als gelernte /32-Route per BGP erscheint, angekündigt von den Adressen der Peering-Knoten. Wenn genau diese /32 auftaucht und du den Service darüber erfolgreich erreichst, ist das die echte Ende-zu-Ende-Bestätigung, dass IP-Pool-Zuweisung, BGP-Ankündigung und das Routing des physischen Netzwerks korrekt zusammenspielen – und nicht nur, dass die Ressourcen auf der Kubernetes-Seite ohne Fehler angewendet wurden.

Beliebte Anleitungen