Loadbalacing

Cet article est une introduction à MetalLB, une solution permettant d’exposer des services si le cluster Kubernetes n’est pas déployé sur une plate-forme en cloud.

Public visé : Administrateurs k8s

Par Abdellah Seddik TAHAR DJEBBAR, Cloud Consultant @Objectif Libre

Introduction

Kubernetes est un des grands sujets d’actualité dans le monde informatique. Malgré son adoption, certaines problématiques se posent toujours, en particulier l’exposition des services vers l’extérieur du cluster. Si votre cluster Kubernetes est déployé sur une plate-forme cloud telle qu’OpenStack, AWS, GCP, alors Kubernetes peut déployer des Load Balancers exposés par la plateforme Cloud. Mais tous les clusters Kubernetes ne sont pas déployés sur des plateformes Cloud. Kubernetes peut également être déployé sur des serveurs Bare metal ou des machines virtuelles en dehors du Cloud. Dans ce cas, les load balancers créés resteront indéfiniment à l’état «pending» et le service ne sera pas rendu. Les administrateurs de clusters Bare metal disposent de deux outils pour amener le trafic des utilisateurs dans leurs clusters, les services de type «NodePort» et «externalIPs». Ces deux options présentent des inconvénients importants pour l’utilisation en production. Pour remédier à cette contrainte, une nouvelle solution appelée MetalLB a été introduite.

MetalLB

MetalLB est une implémentation de Load Balancer pour les clusters Kubernetes Bare Metal, utilisant des protocoles de routage standard.

Principe

Dans un environnement Cloud, la création du Load Balancer et l’attribution de l’adresse IP externe sont effectuées par la plateforme Cloud. MetalLB est responsable de cette attribution dans un cluster Bare metal. Pour cela, un pool d’adresses IP doit être réservé à MetalLB. Une fois que MetalLB a attribué une adresse IP externe à un service, il doit rediriger le trafic de l’IP externe vers le cluster. Pour ce faire, MetalLB utilise des protocoles standard tels que ARP et NDP, ou BGP.

Mode de couche 2 (ARP / NDP)

Dans ce mode, un service appartient à un nœud du cluster. L’adresse de couche 2 (adresse MAC) correspondant à l’IP externe est l’adresse MAC d’un nœud. Pour les autres équipements réseau accédant au service, le nœud a plusieurs adresses.

Architecture

En mode couche 2, MetalLB fonctionne avec deux composants :

  • Cluster-wide controller : ce composant est responsable de la réception des demandes d’allocation.
  • Speaker : le Speaker doit être installé sur chaque nœud du cluster. Il est responsable de la publication de l’adresse de couche 2.

Demo

Commencez par installer MetalLB à l’aide du manifeste fourni :

 $ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml

De nouveaux pods ont été créés, un contrôleur et trois Speakers :

$ kubectl get pod -n metallb-system -o wide
controller-7cc9c87cfb-dqz6z 1/1 Running 0 145m 10.233.70.3 node5 <none> <none>
speaker-2pl5m 1/1 Running 0 145m 192.168.121.170 node3 <none> <none>
speaker-5ndrq 1/1 Running 0 145m 192.168.121.224 node4 <none> <none>
speaker-rln5v 1/1 Running 0 145m 192.168.121.72 node5 <none> <none>

La prochaine étape consiste à configurer MetaLB via une ressource ConfigMap : nous pouvons y définir le mode de fonctionnement (couche 2 ou BGP) et la plage d’adresses IP externes :

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
      - name: my-ip-space
        protocol: layer2
        addresses:
          - 192.168.143.230-192.168.143.250

Dans cette configuration, nous demandons à MetalLB d’attribuer les adresses de la plage 192.168.143.230 à 192.168.143.250, en utilisant le mode de couche 2 (protocol: layer2).

Pour tester notre Load Balancer, nous devons créer un service correspondant :

$ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/tutorial-2.yaml

Nous pouvons maintenant constater qu’un nouveau service de type Load Balancer a été créé et que MetalLB lui a attribué une adresse IP externe à partir du pool spécifié dans la configuration :

$ kubectl get svc nginx
NAME     TYPE         CLUSTER-IP   EXTERNAL-IP     PORT(S)       AGE
nginx    LoadBalancer 10.233.30.62 192.168.143.230 80:31937/TCP 6h26m

Pour accéder au service, le client envoie une requête ARP pour demander l’adresse MAC de l’adresse IP externe, l’un des Speakers répond avec l’adresse MAC de son nœud.

$ kubectl logs -l component=speaker -n metallb-system --since=1m
{"caller":"arp.go:102","interface":"eth2","ip":"192.168.143.230","msg":"got ARP request for service IP, sending response","responseMAC":"52:54:00:a8:63:c5","senderIP":"192.168.143.1","senderMAC":"52:54:00:bd:4a:3e","ts":"2019-04-25T14:21:58.369396026Z"}
{"caller":"arp.go:102","interface":"eth2","ip":"192.168.143.230","msg":"got ARP request for service IP, sending response","responseMAC":"52:54:00:a8:63:c5","senderIP":"192.168.143.1","senderMAC":"52:54:00:bd:4a:3e","ts":"2019-04-25T14:22:29.145677Z"}

L’utilisation du mode Couche 2 pour créer un Load Balancer est très simple, mais elle est également limitée car un service peut être accédé à partir du même nœud. Ainsi, dans un environnement de production, il est préférable d’utiliser le second mode de fonctionnement ou BGP.

Mode BGP

En utilisant le mode BGP, les Speakers établissent un rapprochement BGP avec des routeurs extérieurs au cluster et indiquent à ces routeurs comment transférer le trafic vers les adresses IP de service. L’utilisation de BGP permet un véritable équilibrage de la charge sur plusieurs nœuds et un contrôle du trafic affiné grâce aux mécanismes de politique du protocole.

Démo
Dans cette exemple nous ne montrons pas la configuration du routeur, nous supposons que les routeurs acceptent toutes les connexions BGP provenant des Speakers.

L’architecture utilisée pour cette démo est la suivante :

 

Comme avec le premier mode, installez MetalLB en utilisant le manifeste fourni.

 $ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml

La ressource ConfigMap de configuration de MetalLB est similaire à celle du mode Couche 2 :

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
  peers:
    - my-asn: 64500
      peer-asn: 64500
      peer-address: 192.168.121.10
      address-pools:
        - name: my-ip-space
          protocol: bgp
          addresses:
            - 192.168.143.230-192.168.143.250

En plus du pool d’IP externe, nous devons définir le numéro d’AS qui sera utilisé par les Speakers, ainsi que l’adresse IP des peers BGP distants avec leur numéro AS.

Nous pouvons voir sur le routeur que de nouveaux pairs ont été ajouté à la table des voisins.

R1#show ip bgp summary
BGP router identifier 192.168.143.2, local AS number 64500
BGP table version is 23, main routing table version 23
1 network entries using 144 bytes of memory
1 path entries using 80 bytes of memory
1/1 BGP path/bestpath attribute entries using 136 bytes of memory
0 BGP route-map cache entries using 0 bytes of memory
0 BGP filter-list cache entries using 0 bytes of memory
BGP using 360 total bytes of memory
BGP activity 5/4 prefixes, 13/12 paths, scan interval 60 secs

Neighbor        V  AS    MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
192.168.121.72  4  64500    2      4       23    0    0  00:00:24     0
192.168.121.170 4  64500    2      5       23    0    0  00:00:24     0
192.168.121.224 4  64500    2      4       23    0    0  00:00:24     0

L’étape suivante est de créer un service et de laisser MetalLB faire son travail. Nous utiliserons le même manifeste que précédemment :

 $ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/tutorial-2.yaml

Juste après la création du service, nous pouvons voir dans les logs des Speakers que certains messages ont été envoyés au routeur, annonçant la nouvelle adresse IP externe et indiquant comment l’atteindre :

{"caller":"main.go:229","event":"serviceAnnounced","ip":"192.168.143.230","msg":"service has IP, announcing","pool":"my-ip-space","protocol":"bgp","service":"default/nginx","ts":"2019-04-25T22:14:52.082805682Z"}

{"caller":"main.go:231","event":"endUpdate","msg":"end of service update","service":"default/nginx","ts":"2019-04-25T22:14:52.082823764Z"}

{"caller":"main.go:159","event":"startUpdate","msg":"start of service update","service":"default/nginx","ts":"2019-04-25T22:14:49.878731257Z"}

{"caller":"main.go:172","event":"endUpdate","msg":"end of service update","service":"default/nginx","ts":"2019-04-25T22:14:49.878992728Z"}

{"caller":"main.go:159","event":"startUpdate","msg":"start of service update","service":"default/nginx","ts":"2019-04-25T22:14:49.885773857Z"}

{"caller":"bgp_controller.go:201","event":"updatedAdvertisements","ip":"192.168.143.230","msg":"making advertisements using BGP","numAds":1,"pool":"my-ip-space","protocol":"bgp","service":"default/nginx","ts":"2019-04-25T22:14:49.886003805Z"}

Sur le routeur, nous pouvons voir qu’un nouveau réseau (adresse IP externe) a été ajouté avec trois chemins d’accès, chaque chemin étant représenté par l’un des nœuds :

R1#show ip route bgp
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
+ - replicated route, % - next hop override

Gateway of last resort is not set

    192.168.143.0/32 is subnetted, 1 subnets
B       192.168.143.230 [200/0] via 192.168.121.224, 00:00:15
                        [200/0] via 192.168.121.170, 00:00:15
                        [200/0] via 192.168.121.72, 00:00:15

En utilisant BGP, nous pouvons réaliser un équilibrage de charge à l’aide d’un routeur standard. Cependant, cela comporte également des inconvénients, vous pouvez en savoir plus sur ces limitations et sur la façon de les contourner ici.

Conclusion

Avec MetalLB, il est possible de créer des services kubernetes de type loadbalancer sans déployer votre cluster sur une plate-forme Cloud. MetalLb a deux modes de fonctionnement, un mode L2 simple mais limité qui ne nécessite pas de connaissances approfondies en réseau, et un mode BGP qui est plus robuste et prêt pour la production, mais qui nécessite des compétences pour la gestion du réseau.