
Un équilibreur de charge de couche 4 horizontalement évolutif avec retour direct du serveur (DSR) pour Linux, utilisant XDP/eBPF.
Ce README est actuellement en cours de mise à jour pour refléter les changements récents – certaines informations peuvent ne pas correspondre au code actuel. Cette itération du code n'est pas encore prête pour une utilisation en conditions réelles – utilisez une version v0.2 pour la production
Un équilibreur de charge de couche 4 (L4LB) pour Linux, horizontalement évolutif, à retour direct du serveur (DSR), utilisant XDP/eBPF.
Si vous pensez que cela peut être utile ou si vous avez des questions/suggestions, n'hésitez pas à me contacter à [email protected] ou à ouvrir une issue GitHub.
Prend désormais en charge IPv6 et la distribution en couche 3 (alias tunnellisation) ! La bibliothèque XVS a été mise à jour pour inclure ces fonctionnalités et élimine également la nécessité d'exécuter les contrôles de santé depuis un espace de noms réseau, ce qui simplifie considérablement le code. Cela mettra fin à l'exigence selon laquelle tous les backends partagent un VLAN avec l'équilibreur de charge.
Les restrictions actuelles du code signifient que l'activation de la tunnellisation sur une
base par service n'est pas prise en charge. L'utilisation de l'option -tunnel permet
d'activer globalement une tunnellisation de couche 3 à l'aide d'un seul schéma
(IP-in-IP, GRE, FOU ou GUE). À l'avenir, le code sera mis à jour
pour permettre de configurer la tunnellisation au niveau du service.
L'équilibrage de charge de couche 2 continuera d'être pris en charge – la raison principale du lancement du projet était l'absence de prise en charge de la couche 2 par l'équilibreur de charge Katran de Facebook.
Un exemple de fichier de configuration IPv6/L3 est inclus – une meilleure documentation suivra.
VC5 est un équilibreur de charge réseau conçu pour remplacer les
anciennes appliances matérielles. Il permet de distribuer des services dotés d'adresses
IP virtuelles (VIP) vers des ensembles de serveurs backend (« réels »).
Les serveurs réels peuvent exécuter les services eux-mêmes ou agir comme
proxys pour une autre couche de serveurs (par ex. HAProxy servant de routeur HTTP de couche 7 /
déchargement SSL lorsque des décisions de niveau applicatif doivent être
prises). La seule exigence étant que les VIP doivent être configurées sur un
périphérique loopback de chaque serveur réel, par ex. : ip addr add 192.168.101.1/32 dev lo
Les services et les serveurs réels sont spécifiés dans un fichier de configuration, ainsi que les définitions des contrôles de santé. Lorsque les serveurs backend réussissent les contrôles et qu'ils sont suffisamment disponibles pour fournir un service, les adresses IP virtuelles sont annoncées aux routeurs via BGP.
La distribution du trafic en couche 2 comme en couche 3 est désormais prise en charge. La distribution en couche 2 exige que les serveurs réels partagent un VLAN avec l'équilibreur de charge ; à la réception d'un paquet à distribuer, l'équilibreur de charge met à jour les adresses matérielles Ethernet du paquet pour utiliser l'adresse MAC du serveur réel comme destination et sa propre adresse MAC comme source, puis transmet le paquet via l'interface appropriée, en mettant à jour l'ID VLAN 802.1Q si les paquets sont tagués VLAN.
La distribution en couche 3 exige que les paquets soient encapsulés dans un protocole de tunnellisation adressé à l'IP du serveur réel et transmis via un routeur (sauf si le serveur et l'équilibreur de charge partagent un VLAN). Si, une fois encapsulé, un paquet dépasse la taille maximale de transmission du réseau, un message ICMP est envoyé à la source avec une recommandation sur la MTU appropriée à utiliser. Les serveurs backend n'ont besoin que de décapsuler les paquets – la tunnellisation bidirectionnelle avec les équilibreurs de charge n'est pas requise.
Un serveur doté d'une interface réseau 10 Gbit/s devrait être capable de prendre en charge un service HTTP avec une bande passante sortante supérieure à 100 Gbit/s, en raison de la nature asymétrique de la plupart du trafic Internet. Pour des services plus petits, une ou deux machines virtuelles modestes suffiront probablement pour un service générant plusieurs gigabit/s de trafic sortant.
Si une instance ne suffit pas, d'autres serveurs peuvent être ajoutés pour faire évoluer la capacité horizontalement (et fournir une redondance) à l'aide de la fonctionnalité ECMP de votre routeur. Les interfaces agrégées 802.3ad et le trunking VLAN 802.1Q sont pris en charge (voir le répertoire examples/).
Aucun module noyau ni configuration complexe n'est requis, bien que pour des performances optimales, un pilote de carte réseau avec prise en charge du mode natif XDP soit recommandé (par ex. : mlx4, mlx5, i40e, ixgbe, ixgbevf, nfp, bnxt, thunder, dpaa2, qede). Une liste complète est disponible sur la page de prise en charge des pilotes du projet XDP.
Pour de meilleurs résultats, vous devriez désactiver/désinstaller irqbalance.
Vous devrez sélectionner une IP principale à passer à l'équilibreur. Elle est utilisée comme ID de routeur BGP.
Un exemple simple sur un serveur avec une seule interface Ethernet non taguée :
apt-get install git make libelf-dev golang-1.20 libyaml-perl libjson-perl ethtool (ou l'équivalent de votre distribution)ln -s /usr/lib/go-1.20/bin/go /usr/local/bin/go (assurez-vous que le binaire Go est dans votre PATH)git clone https://github.com/davidcoles/vc5.gitcd vc5/cmdcp config.sample.yaml config.yaml (modifiez config.yaml selon vos besoins)make (télécharge la bibliothèque libbpf, compile le binaire et le fichier de configuration JSON)./vc5 10.1.10.100 config.json eth0 (adaptez avec l'adresse IP et l'interface Ethernet de votre serveur)ip addr add 192.168.101.1/32 dev lo)Il est presque certainement plus simple d'utiliser le binaire de la dernière version GitHub (compilé pour x86-64). Il aura été testé en production et devrait donc être fiable. Assurez-vous que votre configuration est compatible avec cette version en utilisant le script config.pl de la version taguée (ou, bien sûr, vous pouvez construire votre propre configuration JSON comme vous le préférez).
Si vous mettez à jour le fichier de configuration YAML et régénérez le JSON (make config.json), vous pouvez recharger la nouvelle configuration en envoyant un
SIGINT (Ctrl-C) ou SIGUSR2 au processus. SIGQUIT (Ctrl-\) ou SIGTERM
entraînera l'arrêt gracieux des connexions BGP et la sortie du processus.
Un exemple plus complexe avec un périphérique Ethernet agrégé LACP composé de deux interfaces (Intel X520 10Gbps sur mon serveur de test), avec le mode pilote XDP natif activé et des VLAN tagués :
Entrée vlans de config.yaml :
vlans:
10: 10.1.10.0/24
20: 10.1.20.0/24
30: 10.1.30.0/24
Ligne de commande :
./vc5 -n 10.1.10.100 config.json enp130s0f0 enp130s0f1
Le binaire détectera vos interfaces VLAN en recherchant les périphériques dont les adresses IP sont contenues dans les préfixes VLAN du fichier de configuration. Si vous utilisez des interfaces physiques distinctes non taguées, cela devrait désormais fonctionner de manière transparente sans configuration supplémentaire ; il suffit de lister toutes les interfaces en ligne de commande afin que le code eBPF soit chargé dans chacune d'elles.
Étant donné que l'état des connexions est suivi par cœur (BPF_MAP_TYPE_LRU_PERCPU_HASH), vous devez vous assurer que RSS (Receive Side Scaling) achemine systématiquement les paquets d'un flux vers le même cœur CPU dans le cas où votre commutateur sélectionnerait une interface différente lorsque la topologie LACP change. Désactivez irqbalance, assurez-vous que les réglages de canal sont identiques sur chaque interface (ethtool -l/-L) et que l'indirection du hash de flux RSS correspond (ethtool -x/-X).
Le montage peut être testé en démarrant une connexion de longue durée
(par ex. avec iperf et l'option -t) vers un ensemble de serveurs backend, puis en
désactivant le backend choisi avec un astérisque après l'adresse IP dans le
fichier de configuration, en déterminant quelle interface
reçoit le flux sur l'équilibreur de charge (par ex. watch -d 'cat /proc/interrupts | grep enp130s0f' et en surveillant le compteur IRQ qui
augmente rapidement), puis en retirant cette interface de LACP
(ifenslave -d bond0 enp130s0f0). Vous devriez voir le flux se déplacer vers
l'autre interface réseau mais continuer d'atteindre le même cœur.
Lorsque vous utilisez des backends dans plusieurs sous-réseaux, pour des performances
optimales, vous devez vous assurer que tous les VLAN sont tagués sur une seule interface trunk
(agrégée LACP si vous avez plus d'une interface physique) avec les
correspondances sous-réseau/ID VLAN spécifiées dans la section vlans du fichier
de configuration.
Si cela n'est pas possible (par exemple, créer des interfaces trunk sur vSphere n'est pas simple), vous pouvez alors affecter chaque sous-réseau à une interface non taguée différente :
./vc5 10.1.10.100 config.json eth0 eth1 eth2
Un bon résumé des concepts utilisés est présenté dans la conférence de Patrick Shuff « Building a Billion User Load Balancer » et la conférence de Nitika Shirokov sur Katran
Une console web basique et un serveur de métriques Prometheus sont inclus : 
Un support expérimental d'Elasticsearch pour la journalisation (directement vers votre
cluster, sans avoir besoin de collecter les journaux système) est désormais inclus. Chaque sonde
vers les serveurs backend est journalisée : si l'un d'eux tombe, vous pouvez voir
précisément quelle erreur a été renvoyée, ainsi que toutes sortes d'autres
conditions. Cela demandera beaucoup d'affinage et un nommage plus judicieux
des paramètres de journalisation, etc. (si vous avez des idées, n'hésitez pas à me
contacter), mais cela devrait permettre d'obtenir de bonnes informations
sur ce qui se passe dans le système – ma toute première tentative, très maladroite,
de création d'un tableau de bord Kibana, en exemple : 
Cela a été principalement testé avec des serveurs backend Icecast et des clients recevant un mélange de flux à bas et haut débit (48kbps - 192kbps).
Il semble qu'une machine invitée VMware (4 cœurs, 8 Go) utilisant le pilote XDP générique prenne en charge 100 000 clients simultanés, 380 Mbps/700 Kpps à travers l'équilibreur de charge et 8 Gbps de trafic des backends directement vers les clients.
Sur un seul CPU Intel Xeon Gold 6314U (non virtualisé) (2,30 GHz, 32 cœurs physiques, avec hyperthreading activé pour 64 cœurs logiques) et une carte Ethernet Intel 10G 4P X710-T4L-t, j'ai pu exécuter 700 000 flux à 2 Gbps/3,8 Mpps de trafic entrant et 46,5 Gbps de trafic sortant. Le serveur était inactif à plus de 90 %. Malheureusement, je ne disposais pas des ressources nécessaires pour créer davantage de clients/serveurs.
Il existe trois modes de fonctionnement : simple, VLAN et multi-NIC. En mode simple, tous les hôtes doivent se trouver sur le même sous-réseau que l'adresse principale de l'équilibreur de charge. En mode VLAN (activé en déclarant des entrées dans la section « vlans » du fichier de configuration YAML/JSON), les entrées de serveurs doivent correspondre à une entrée de sous-réseau VLAN/CIDR. Les interfaces taguées VLAN doivent être créées dans le système d'exploitation et avoir une adresse IP affectée dans le sous-réseau. En mode multi-NIC, les sous-réseaux reçoivent des identifiants de la même manière que les VLAN, mais bpf_redirect() est utilisé pour envoyer le trafic via l'interface configurée de manière appropriée (plutôt que de modifier l'ID VLAN et d'utiliser XDP_TX).
En mode VLAN, tout le trafic destiné à l'équilibreur de charge doit être sur un VLAN tagué (aucune insertion ni suppression de 802.1Q n'est effectuée – pour l'instant).