Cilium Cilium

Annoncer les IP de Service avec BGP

Pas de répartiteur de charge cloud ? Cilium peut annoncer les IP de Service directement à votre réseau physique via BGP.

15 min Avancé

Le type de Service LoadBalancer d’un fournisseur cloud fonctionne parce que le cloud dispose d’une API pour provisionner un répartiteur de charge externe à la demande. Sur du bare metal, ou dans tout environnement sans cette API, un Service LoadBalancer reste indéfiniment en Pending tant que quelque chose d’autre ne remplit pas ce rôle. Le plan de contrôle BGP de Cilium est une façon de le faire : il route l’IP d’un Service directement dans votre réseau physique, en faisant dialoguer en BGP les nœuds du cluster avec les routeurs en amont. Ce guide présente les quatre ressources qui le configurent, et comment vérifier qu’une session BGP est réellement établie des deux côtés.

Quand BGP est le bon outil

Le peering BGP entre des nœuds de cluster et des routeurs en amont est une véritable intégration réseau : elle demande de se coordonner avec les personnes qui gèrent le réseau physique (ou virtuel), une configuration côté routeur qui correspond à votre configuration de peer côté Kubernetes, et une réelle familiarité opérationnelle avec les concepts BGP comme les systèmes autonomes et l’annonce de routes. C’est le bon outil pour un cluster on-premises ou bare metal dont les IP de Service doivent être réellement routables depuis le reste de votre réseau, comme le ferait un LoadBalancer cloud. Pour des besoins plus simples, la fonctionnalité d’annonces L2 de Cilium (annoncer l’IP d’un Service via ARP/NDP sur le segment réseau local, sans protocole de routage) est une alternative plus légère à envisager en premier si votre cluster vit sur un seul segment réseau et n’a pas besoin de routage de couche 3 entre segments ou datacenters.

Les quatre ressources

Quatre ressources personnalisées CiliumBGP* fonctionnent ensemble :

  • CiliumLoadBalancerIPPool définit une plage d’adresses IP que Cilium peut attribuer aux Services LoadBalancer.
  • CiliumBGPAdvertisement définit ce qui est annoncé via BGP, ici les IP de Service tirées de ce pool.
  • CiliumBGPPeerConfig définit comment dialoguer en BGP : timers, comportement de graceful restart, familles d’adresses.
  • CiliumBGPClusterConfig assemble le tout : quels nœuds participent (via un sélecteur de labels), leur numéro d’AS local, et à quels pairs en amont (par IP et numéro d’AS) ils se connectent.

Définir le pool d’IP

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

allowFirstLastIPs: "No" exclut les adresses de réseau et de broadcast (.0 et .255) de la plage attribuable, une convention courante héritée de l’allocation traditionnelle de sous-réseaux. Adaptez le CIDR à la plage que votre équipe réseau vous a réellement allouée pour cet usage ; il doit s’agir d’un espace d’adressage réel et routable de leur point de vue, pas d’une plage privée arbitraire choisie sans concertation.

Définir ce qui est annoncé

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

Ceci annonce la LoadBalancerIP des Services correspondants. Le champ labels ici (advertise: bgp) est ce que la configuration de peer ci-dessous référence pour relier les deux, et non un label sur les Services eux-mêmes.

Définir le comportement des pairs

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

Le families.advertisements.matchLabels ici relie cette configuration de peer à la ressource d’annonce définie plus haut, en faisant correspondre son label.

Tout assembler : configuration du cluster et sélection des nœuds

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 limite le peering BGP à un sous-ensemble délibéré de nœuds, ceux labellisés role: bgp-node, plutôt que de laisser chaque nœud du cluster tenter de dialoguer avec les routeurs en amont :

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

Une plage raisonnable est de deux à huit nœuds de peering : assez pour éviter un point de défaillance unique, sans obliger votre équipe réseau à configurer et maintenir des sessions BGP vers chaque nœud d’un cluster qui peut en compter des dizaines ou des centaines. Le nombre adéquat dépend de la disposition physique de votre cluster : s’il s’étend sur plusieurs racks ou rangées, il est généralement préférable de faire le peering depuis des nœuds de chacun, plutôt que de concentrer toutes les sessions BGP au même endroit.

Appliquez les quatre ressources, puis vérifiez du côté Kubernetes :

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

Vérifier que la session est bien établie

Du point de vue d’un nœud, en utilisant la CLI de debug propre à Cilium dans le Pod Cilium qui s’exécute sur un nœud labellisé BGP :

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

Cherchez un état de session established. Tout autre état (idle, connect, active) signifie que la session n’est pas réellement établie ; les causes les plus courantes sont une incohérence de numéro d’AS entre les deux côtés, une adresse de pair injoignable, ou le routeur en amont qui n’est simplement pas encore configuré pour attendre ce pair. Confirmez que la configuration du pair côté réseau correspond exactement à ce que vous avez déclaré ici avant de supposer que la configuration côté Kubernetes est fautive.

Confirmer qu’une adresse est bien annoncée

Créez un Service LoadBalancer qui puise dans le pool :

kubectl get svc -o wide

Son EXTERNAL-IP doit être une adresse du CIDR du pool. Depuis le routeur en amont (ou depuis ce qui se trouve au-delà : un pare-feu, un routeur de cœur), vérifiez que cette adresse apparaît comme une route /32 apprise via BGP, annoncée depuis les adresses des nœuds de peering. Voir apparaître cette /32 précise, et atteindre avec succès le Service par ce biais, est la véritable confirmation de bout en bout que l’allocation du pool d’IP, l’annonce BGP et le routage propre du réseau physique sont tous correctement reliés, et pas seulement que les ressources côté Kubernetes se sont appliquées sans erreur.

Guides populaires