Avant de commencer…#
Comme souvent dans mes articles, je dois vous informer que la démonstration qui sera faite se base sur une nouvelle version du projet Kubeadm avec Vagrant et Ansible, idéal pour réviser votre certification !, n’hésitez pas à consulter cet article pour vous familiariser avec les technologies Vagrant et Ansible.
De plus, une introduction à MetalLB a déjà été effectuée avec l’article suivant : À la découverte de MetalLB. Les principaux concepts ne seront pas réexpliqués dans celui-ci.
Néanmoins, cet article a pour objectif de voir une configuration avancée de MetalLB avec la mise en place du BGP, protocole qui sera expliqué en dessous. C’est généralement la configuration qui simulera le mieux un répartiteur de charge, plutôt que celle que nous avons vu précédement (layer2).
Qu’est-ce que le BGP ?#
Le BGP ou Border Gateway Protocol est un protocole de routage qui permet d’interconnecter plusieurs réseaux entre eux, définis comme des Autonomous Systems (AS).
Il utilise le protocole de transport TCP sur le port 179 afin d’échanger différentes informations de routage. Ce protocole est très connu et pour cause, il est utilisé pour Internet.
Afin de mettre en place ce protocole, l’outil open-source BIRD permettra de transformer une machine virtuelle utilisant un système d’exploitation GNU/Linux en un routeur pour configurer le BGP.
Chaque machine que l’on souhaite déclarer au routeur est appelée peer ou neighboor. Dans notre cas, les noeuds de type worker contenant chaque Pod
speaker de MetalLB seront déclarés.
BGP versus Layer 2#
Le mode BGP de MetalLB permet, lors de la création du répartiteur de charge (load balancer) d’annoncer au routeur avec lequel il établit la connexion, l’IP externe du service. Ce qui permet de rendre accessible ce service depuis le routeur.
Si un poste client est correctement configuré au routeur, il pourra faire un curl
de l’IP externe du service de type LoadBalancer sans aucun souci.
Si notre cluster possède plusieurs noeuds worker et que l’on interconnecte ces workers au routeur, cela permet une véritable répartition de charge, car les routes publiées par MetalLB sont équivalentes les unes aux autres, à l’exception du prochain saut, c’est ce que l’on appelle l’ECMP (Equal-cost multi-path routing). Enfin, c’est le kube-proxy
qui va acheminer le paquet vers le Pod
en question.
Exemple ici avec service de type LoadBalancer
possédant l’IP 172.16.254.1
et avec un cluster possédant trois noeuds worker :
bird> show route stats
172.16.254.1/32 via 10.0.0.51 on enp0s8 [peer1 18:05:44] * (100) [AS22000?]
via 10.0.0.52 on enp0s8 [peer2 18:05:44] (100) [AS22000?]
via 10.0.0.53 on enp0s8 [peer3 18:05:44] (100) [AS22000?]
Les trois noeuds worker (10.0.0.51
, 10.0.0.52
et 10.0.0.53
) sont déclarés comme des routes possibles à emprunter.
Comme vous l’avez vu dans l’article présentant MetalLB, le mode layer2 a le gros avantage de ne pas nécessiter de configuration spécifique, en revanche, il n’y aura qu’un seul noeud worker qui se chargera d’être le point d’entrée du service. Ce qui ne permet pas de redistribuer la charge équitablement sur l’ensemble des noeuds et empêche la mise à l’échelle (scaling).
Comme vous l’aurez compris, le layer2
est réservé aux environnements de test ou PoC (Proof of Concept), tandis que le BGP s’inscrit dans une démarche de production.
Architecture#
Avant de commencer à manipuler le cluster Kubernetes ou les équilibreurs de charge, il est important de parcourir l’architecture réseau du projet.
Comme vous pouvez le voir, il y a deux réseaux :
- Un premier, pour le cluster qui permet de faire tourner les noeuds master/controlplane et le ou les worker(s). La plage réseau utilisée est 10.0.0.0/24 et l’ASN (Autonomous Systems Number) de 22000 ;
- Un second, qui va contenir notre client qui permettra d’exécuter différentes opérations sur le cluster Kubernetes. La plage réseau utilisée est 172.16.0.0/24 et l’ASN est de 11000.
Ces deux réseaux sont reliés par un routeur qui possède deux cartes réseau, une sur le réseau client-network et l’autre sur le réseau k8s-network.
Le projet#
Comme d’habitude, les outils Vagrant et Ansible sont nécessaires sur votre poste de travail si vous souhaitez exécuter ce qui va suivre.
Le dépôt de code est disponible à l’adresse suivante :
Vagrant#
La configuration de Vagrant se trouve comme à son habitude dans le fichier Vagrantfile
, elle se compose de plusieurs parties : routeur, master, workers et client.
Plusieurs réseaux ont été définis avec l’instruction :
router.vm.network "private_network", ip: <plage adresses IP>,
netmask: '255.255.255.0', virtualbox__intnet: '<nom réseau interne>'
Le paramètre virtualbox__intnet
permet de définir un nom pour le réseau interne et interconnecter les autres machines entre elles
L’image et la version de celle-ci sont fixées pour chaque machine virtuelle :
client.vm.box = IMAGE_NAME
client.vm.box_version = IMAGE_VERSION
client.vm.synced_folder '.', '/vagrant', disabled: true
De plus, le répertoire partagé vagrant
est désactivé, car il n’est pas nécessaire pour ce cas d’utilisation.
Ansible#
Le fichier contenant le playbook principal se trouve à la racine du projet avec le nom site.yml
, on retrouve l’ensemble des rôles qui seront joués sur les différentes machines.
Par rapport à la version précédente, deux nouveaux hôtes ont été ajoutés : le router et le client.
- hosts: router
gather_facts: true
become: true
roles:
- { role: bird, tags: bird }
...
- hosts: client
gather_facts: true
become: true
roles:
- { role: client/route-config, tags: client }
- { role: client/kubectl, tags: client }
- { role: client/helm, tags: client }
Sur le router, on installe BIRD et on initialise sa configuration qui sera détaillée dans le prochain point.
Tandis que sur le client, on va venir paramétrer la machine virtuelle avec netplan
qui est dans le rôle client/route-config
qui permet de définir la configuration du réseau avec un fichier YAML.
Comme vous pouvez le voir ici :
network:
version: 2
renderer: networkd # Service réseau utilisé
ethernets:
enp0s8: # Carte réseau concernée
routes:
# Création d'une route pour atteindre le cluster Kubernetes depuis le routeur
- to: {{ kubernetes_network_base }}.0/24 # 10.0.0.0/24 par défaut
via: {{ client_network_base }}.1
# Création d'une route pour atteindre le réseau BGP des load balancers depuis le routeur
- to: {{ bgp_network_base }}.0/24
via: {{ client_network_base }}.1 # 172.16.0.0/24 par défaut
Enfin, deux outils sont installés, kubectl
pour atteindre le cluster Kubernetes et helm
pour installer des charts.
BIRD#
Pour ce qui est de la configuration de BIRD, elle se trouve dans le dossier roles
à l’emplacement roles/bird/templates/bird.conf.j2
:
router id {{ client_network_base }}.1; # Adresse du routeur côté client
protocol direct {
interface "lo"; # Enlève l'interface du routage de BIRD
}
protocol kernel {
persist; # Conserve les règles même en arrêtant le service
scan time 20; # Scanne la table de routage toutes les 20 secondes
merge paths on; # Active l'ECMP
import all;
export all;
}
protocol device {
scan time 10; # Scanne les interfaces toutes les 10 secondes
}
# On configure l'ensemble des noeuds worker
{% for worker_number in range(1, (kubernetes_worker_number | int) + 1) %}
protocol bgp peer{{ worker_number }} {
local as 11000;
neighbor {{ kubernetes_network_base }}.{{ worker_number + 50 }} as 22000;
import all;
export all;
}
{% endfor %}
MetalLB#
Dans le fichier install_metallb.yml
, on retrouve l’initialisation du chart de MetalLB avec une configuration spécifique pour notre architecture :
...
values:
configInline:
peers:
- my-asn: 22000
peer-asn: 11000
peer-address: 10.0.0.1
address-pools:
- name: my-ip-space
protocol: bgp
avoid-buggy-ips: true
addresses:
- 172.16.254.0/24
On réserve la plage d’adresses IP 172.16.254.0/24
pour nos équilibreurs de charge qui seront annoncés au routeur. L’adresse 10.0.0.1
représente l’IP du routeur, my-asn
est l’ASN du réseau client et peer-asn
l’ASN du réseau Kubernetes.
Le paramètre avoid-buggy-ips: true
évite d’utiliser les adresses .0
et .255
du réseau comme c’est indiqué ici.
Pour aller plus loin#
Démonstration#
Pour démarrer le tout, une commande est à exécuter :
vagrant up
Une fois l’ensemble des machines configurées, il ne reste plus qu’à installer MetalLB sur le cluster avec les deux commandes suivantes :
# Installation de la collection kubernetes.core
ansible-galaxy collection install kubernetes.core
# Installation du chart Helm de MetalLB avec la configuration spécifique pour notre cluster
ansible-playbook install_metallb.yml -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
Notre cluster est opérationnel, pour tester la création de load balancer, connectez-vous sur la machine client et exécutez ces commandes :
vagrant ssh client
# Création d'un déploiement nginx de 3 replicas
k create deploy nginx --image=nginx --replicas=3
# Création du service de type LoadBalancer
k expose deploy nginx --type=LoadBalancer --port=80
Une fois cela fait, vous pouvez récupérer l’IP externe de votre service :
vagrant@client:~$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 152m
nginx LoadBalancer 10.102.226.112 172.16.254.1 80:32730/TCP 4s
Il s’agit de 172.16.254.1
dans notre cas, et vous pouvez faire un curl
dessus :
vagrant@client:~$ curl 172.16.254.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Tout est fonctionnel, notre réseau BGP fonctionne à merveille et tout est accessible depuis la machine virtuelle client !
Conclusion#
Comme vous avez pu le voir, la configuration de MetalLB est plus compliquée qu’avec le mode layer2
présenté dans un article précédent. Cependant, cette configuration est beaucoup plus résiliente comme je vous l’ai exposé dans la partie BGP versus Layer 2
.
Vous pouvez donc, avec ce projet, simuler des services de type LoadBalancer
et avoir un résultat similaire aux services managés présents dans le Cloud.
Dans un prochain article, nous compléterons (encore) ce projet avec la mise en place de Traefik en tant qu’Ingress Controller
pour faire sortir vos services du clusters !