Affiner le routage pour MetalLB en mode L2

Affiner le routage pour MetalLB en mode L2
Il n'y a pas si longtemps, j'ai été confronté à une tâche très inhabituelle : configurer le routage pour MetalLB. Tout irait bien, parce que... Habituellement, MetalLB ne nécessite aucune action supplémentaire, mais dans notre cas, nous avons un cluster assez grand avec une configuration réseau très simple.

Dans cet article, je vais vous expliquer comment configurer le routage basé sur la source et basé sur la stratégie pour le réseau externe de votre cluster.

Je n'entrerai pas dans les détails de l'installation et de la configuration de MetalLB, car je suppose que vous avez déjà une certaine expérience. Je propose d'aller droit au but, à savoir la mise en place du routage. Nous avons donc quatre cas :

Cas 1 : Lorsqu’aucune configuration n’est requise

Regardons un cas simple.

Affiner le routage pour MetalLB en mode L2

Une configuration de routage supplémentaire n'est pas requise lorsque les adresses émises par MetalLB se trouvent dans le même sous-réseau que les adresses de vos nœuds.

Par exemple, vous disposez d'un sous-réseau 192.168.1.0/24, il a un routeur 192.168.1.1, et vos nœuds reçoivent des adresses : 192.168.1.10-30, puis pour MetalLB vous pouvez ajuster la plage 192.168.1.100-120 et assurez-vous qu'ils fonctionneront sans aucune configuration supplémentaire.

Pourquoi donc? Parce que vos nœuds ont déjà des routes configurées :

# ip route
default via 192.168.1.1 dev eth0 onlink 
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10

Et les adresses de la même plage les réutiliseront sans aucune action supplémentaire.

Cas 2 : Lorsqu'une personnalisation supplémentaire est requise

Affiner le routage pour MetalLB en mode L2

Vous devez configurer des routes supplémentaires chaque fois que vos nœuds n'ont pas d'adresse IP configurée ou de route vers le sous-réseau pour lequel MetalLB émet des adresses.

Je vais vous expliquer un peu plus en détail. Chaque fois que MetalLB génère une adresse, elle peut être comparée à une simple affectation telle que :

ip addr add 10.9.8.7/32 dev lo

Faire attention à:

  • a) L'adresse est attribuée avec un préfixe /32 c'est-à-dire qu'une route ne sera pas automatiquement ajoutée au sous-réseau correspondant (c'est juste une adresse)
  • b) L'adresse est attachée à n'importe quelle interface de nœud (par exemple bouclage). Il convient de mentionner ici les fonctionnalités de la pile réseau Linux. Quelle que soit l'interface à laquelle vous ajoutez l'adresse, le noyau traitera toujours les requêtes arp et enverra des réponses arp à chacune d'entre elles, ce comportement est considéré comme correct et, de plus, est assez largement utilisé dans un environnement aussi dynamique que Kubernetes.

Ce comportement peut être personnalisé, par exemple en activant l'arpège strict :

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

Dans ce cas, les réponses arp ne seront envoyées que si l'interface contient explicitement une adresse IP spécifique. Ce paramètre est requis si vous prévoyez d'utiliser MetalLB et que votre proxy Kube s'exécute en mode IPVS.

Cependant, MetalLB n'utilise pas le noyau pour traiter les requêtes arp, mais le fait lui-même dans l'espace utilisateur, cette option n'affectera donc pas le fonctionnement de MetalLB.

Revenons à notre tâche. Si la route pour les adresses émises n'existe pas sur vos nœuds, ajoutez-la au préalable à tous les nœuds :

ip route add 10.9.8.0/24 dev eth1

Cas 3 : lorsque vous avez besoin d'un routage basé sur la source

Vous devrez configurer le routage basé sur la source lorsque vous recevez des paquets via une passerelle distincte, et non celle configurée par défaut. Par conséquent, les paquets de réponse doivent également passer par la même passerelle.

Par exemple, vous avez le même sous-réseau 192.168.1.0/24 dédié à vos nœuds, mais vous souhaitez émettre des adresses externes à l'aide de MetalLB. Supposons que vous ayez plusieurs adresses d'un sous-réseau 1.2.3.0/24 situés dans le VLAN 100 et que vous souhaitez les utiliser pour accéder aux services Kubernetes en externe.

Affiner le routage pour MetalLB en mode L2

En contactant 1.2.3.4 vous ferez des requêtes à partir d'un sous-réseau différent de celui 1.2.3.0/24 et attendez une réponse. Le nœud qui est actuellement le maître de l'adresse émise par MetalLB 1.2.3.4, recevra le paquet du routeur 1.2.3.1, mais la réponse pour lui doit nécessairement suivre le même chemin, à travers 1.2.3.1.

Puisque notre nœud a déjà une passerelle par défaut configurée 192.168.1.1, alors par défaut la réponse lui sera adressée, et non à 1.2.3.1, par lequel nous avons reçu le colis.

Comment faire face à cette situation ?

Dans ce cas, vous devez préparer tous vos nœuds de manière à ce qu'ils soient prêts à servir des adresses externes sans configuration supplémentaire. Autrement dit, pour l'exemple ci-dessus, vous devez créer à l'avance une interface VLAN sur le nœud :

ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up

Et puis ajoutez des itinéraires :

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Veuillez noter que nous ajoutons des itinéraires à une table de routage distincte 100 il ne contiendra que deux routes nécessaires pour envoyer un paquet de réponse via la passerelle 1.2.3.1, situé derrière l'interface eth0.100.

Nous devons maintenant ajouter une règle simple :

ip rule add from 1.2.3.0/24 lookup 100

qui dit explicitement : si l'adresse source du paquet est dans 1.2.3.0/24, alors vous devez utiliser la table de routage 100. Nous y avons déjà décrit l'itinéraire qui le mènera à travers 1.2.3.1

Cas 4 : lorsque vous avez besoin d'un routage basé sur des politiques

La topologie du réseau est la même que dans l'exemple précédent, mais disons que vous souhaitez également pouvoir accéder aux adresses de pools externes 1.2.3.0/24 depuis vos pods :

Affiner le routage pour MetalLB en mode L2

La particularité est que lors de l'accès à n'importe quelle adresse dans 1.2.3.0/24, le paquet de réponse atteint le nœud et possède une adresse source comprise dans la plage 1.2.3.0/24 sera docilement envoyé à eth0.100, mais nous voulons que Kubernetes le redirige vers notre premier pod, qui a généré la requête d'origine.

Résoudre ce problème s'est avéré difficile, mais cela est devenu possible grâce au routage basé sur des politiques :

Pour une meilleure compréhension du processus, voici un schéma fonctionnel de netfilter :
Affiner le routage pour MetalLB en mode L2

Tout d'abord, comme dans l'exemple précédent, créons une table de routage supplémentaire :

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Ajoutons maintenant quelques règles à iptables :

iptables -t mangle -A PREROUTING -i eth0.100 -j CONNMARK --set-mark 0x100
iptables -t mangle -A PREROUTING  -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark

Ces règles marqueront les connexions entrantes vers l'interface eth0.100, marquant tous les paquets avec l'étiquette 0x100, les réponses au sein de la même connexion seront également marquées avec la même balise.

Nous pouvons maintenant ajouter une règle de routage :

ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100

Autrement dit, tous les paquets ayant une adresse source 1.2.3.0/24 et étiquette 0x100 doit être acheminé à l'aide d'une table 100.

Ainsi, les autres paquets reçus sur une autre interface ne sont pas soumis à cette règle, ce qui permettra de les acheminer à l'aide des outils standards Kubernetes.

Il y a encore une chose, sous Linux il y a ce qu'on appelle un filtre de chemin inverse, qui gâche tout ; il effectue une vérification simple : pour tous les paquets entrants, il change l'adresse source du paquet avec l'adresse de l'expéditeur et vérifie si le paquet peut sortir via la même interface sur laquelle il a été reçu, sinon il le filtrera.

Le problème est que dans notre cas cela ne fonctionnera pas correctement, mais nous pouvons le désactiver :

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter

Veuillez noter que la première commande contrôle le comportement global de rp_filter ; si elle n'est pas désactivée, la deuxième commande n'aura aucun effet. Cependant, les interfaces restantes resteront avec rp_filter activé.

Afin de ne pas limiter complètement le fonctionnement du filtre, nous pouvons utiliser l'implémentation rp_filter pour netfilter. En utilisant rpfilter comme module iptables, vous pouvez configurer des règles assez flexibles, par exemple :

iptables -t raw -A PREROUTING -i eth0.100 -d 1.2.3.0/24 -j RETURN
iptables -t raw -A PREROUTING -i eth0.100 -m rpfilter --invert -j DROP

activer rp_filter sur l'interface eth0.100 pour toutes les adresses sauf 1.2.3.0/24.

Source: habr.com

Ajouter un commentaire