
Un bilanciatore di carico di livello 4 orizzontalmente scalabile con Direct Server Return per Linux che utilizza XDP/eBPF
Questo README è in fase di aggiornamento per riflettere le modifiche recenti – alcune informazioni potrebbero non corrispondere al codice attuale. Questa iterazione del codice non è ancora pronta per ambienti di produzione – utilizza una release v0.2 per ambienti reali
Un bilanciatore di carico di livello 4 (L4LB) orizzontalmente scalabile con Direct Server Return (DSR) per Linux che utilizza XDP/eBPF.
Se pensi che possa essere utile o hai domande/suggerimenti, sentiti libero di contattarmi a [email protected] o aprire un'issue su GitHub.
Ora supporta IPv6 e distribuzione a livello 3 (noto anche come tunneling)! La libreria XVS è stata aggiornata per includere queste funzionalità e inoltre elimina la necessità di eseguire controlli di salute da un namespace di rete, semplificando notevolmente il codice. Questo porrà fine al requisito che tutti i backend condividano una VLAN con il bilanciatore di carico.
Le restrizioni attuali del codice impediscono di abilitare il tunneling per servizio. Utilizzare l'opzione -tunnel consente di abilitare globalmente il tunneling di livello 3 utilizzando uno schema singolo (IP-in-IP, GRE, FOU o GUE). In futuro, il codice verrà aggiornato per consentire la configurazione del tunneling a livello di servizio.
Il bilanciamento del carico di livello 2 continuerà ad essere supportato – la ragione principale per cui è stato avviato il progetto è stata la mancanza di supporto di livello 2 da parte del bilanciatore di carico Katran di Facebook.
Un file di configurazione di esempio per IPv6/L3 è incluso – seguirà una documentazione più dettagliata.
VC5 è un bilanciatore di carico di rete progettato per sostituire gli apparecchi hardware legacy. Permette di distribuire servizi con indirizzi IP virtuali (VIP) a gruppi di server backend ("reali"). I server reali possono eseguire i servizi stessi o agire come proxy per un altro livello di server (ad es. HAProxy che funge da router HTTP/offload SSL di livello 7 quando è necessario prendere decisioni a livello applicativo). L'unico requisito è che i VIP siano configurati su un dispositivo loopback su ciascun server reale, ad es.: ip addr add 192.168.101.1/32 dev lo
I servizi e i server reali sono specificati in un file di configurazione, insieme alle definizioni dei controlli di salute. Quando i server backend superano i controlli e sono disponibili in numero sufficiente per fornire un servizio, gli indirizzi IP virtuali vengono annunciati ai router tramite BGP.
Ora è supportata la distribuzione del traffico sia a livello 2 che a livello 3. La distribuzione a livello 2 richiede che i server reali condividano una VLAN con il bilanciatore di carico; quando riceve un pacchetto da distribuire, il bilanciatore aggiorna gli indirizzi hardware ethernet nel pacchetto utilizzando l'indirizzo MAC del server reale come destinazione e il proprio indirizzo MAC come sorgente, e inoltra il pacchetto attraverso l'interfaccia appropriata, aggiornando l'ID VLAN 802.1Q se i pacchetti sono con tag VLAN.
La distribuzione a livello 3 richiede che i pacchetti vengano incapsulati in un protocollo di tunneling indirizzato all'IP del server reale e inoltrati tramite un router (a meno che il server e il bilanciatore di carico non condividano una VLAN). Se, una volta incapsulato, un pacchetto supera la dimensione massima di trasmissione della rete, viene inviato un messaggio ICMP alla sorgente con indicazioni sull'MTU appropriato da utilizzare. I server backend devono solo decapsulare i pacchetti – non è richiesta la tunneling bidirezionale con i bilanciatori di carico.
Un server con un'interfaccia di rete da 10 Gbit/s dovrebbe essere in grado di supportare un servizio HTTP con larghezza di banda in egress superiore a 100 Gbit/s grazie alla natura asimmetrica della maggior parte del traffico Internet. Per servizi più piccoli, una o due macchine virtuali modeste saranno probabilmente sufficienti per gestire un servizio che genera alcuni gigabit/s di traffico in uscita.
Se un'istanza non è sufficiente, è possibile aggiungere più server per scalare orizzontalmente la capacità (e fornire ridondanza) utilizzando la funzionalità ECMP del router. Sono supportate interfacce in bonding 802.3ad e trunking VLAN 802.1Q (vedi directory examples/).
Non sono richiesti moduli del kernel o configurazioni complesse, anche se per ottenere le migliori prestazioni si consiglia un driver per scheda di rete con supporto della modalità nativa XDP (ad es.: mlx4, mlx5, i40e, ixgbe, ixgbevf, nfp, bnxt, thunder, dpaa2, qede). Un elenco completo è disponibile nella pagina di supporto dei driver del progetto XDP.
Per ottenere i migliori risultati, si consiglia di disabilitare/disinstallare irqbalance.
Dovrai selezionare un IP primario da passare al bilanciatore. Viene utilizzato come ID del router BGP.
Un semplice esempio su un server con una singola interfaccia ethernet senza tag:
apt-get install git make libelf-dev golang-1.20 libyaml-perl libjson-perl ethtool (o l'equivalente della tua distribuzione)ln -s /usr/lib/go-1.20/bin/go /usr/local/bin/go (assicurati che il binario Go sia nel tuo PATH)git clone https://github.com/davidcoles/vc5.gitcd vc5/cmdcp config.sample.yaml config.yaml (modifica config.yaml in base alle tue esigenze)make (scarica la libreria libbpf, compila il binario e il file JSON di configurazione)./vc5 10.1.10.100 config.json eth0 (modifica utilizzando l'indirizzo IP del tuo server e l'interfaccia ethernet)ip addr add 192.168.101.1/32 dev lo)È quasi certamente più semplice utilizzare il binario dell'ultima release di Github (compilato per x86-64). Questo sarà stato testato in produzione, quindi dovrebbe essere affidabile. Assicurati che la tua configurazione sia compatibile con questa versione utilizzando lo script config.pl della release con tag (oppure, ovviamente, puoi creare il tuo JSON di configurazione come preferisci).
Se aggiorni il file di configurazione YAML e rigeneri il JSON (make config.json), puoi ricaricare la nuova configurazione inviando un segnale SIGINT (Ctrl-C) o SIGUSR2 al processo. SIGQUIT (Ctrl-) o SIGTERM faranno sì che il processo termini correttamente le connessioni BGP ed esca.
Un esempio più complesso con un dispositivo ethernet in bonding LACP composto da due interfacce (Intel X520 10 Gbps sul mio server di test), con modalità driver XDP nativa abilitata e VLAN con tag:
Voce vlans in config.yaml:
vlans:
10: 10.1.10.0/24
20: 10.1.20.0/24
30: 10.1.30.0/24
Riga di comando:
./vc5 -n 10.1.10.100 config.json enp130s0f0 enp130s0f1
Il binario rileverà le tue interfacce VLAN cercando dispositivi con indirizzi IP inclusi nei prefissi VLAN nel file di configurazione. Se utilizzi interfacce fisiche separate senza tag, ora dovrebbe funzionare in modo trasparente senza alcuna configurazione aggiuntiva: elenca semplicemente tutte le interfacce sulla riga di comando in modo che il codice eBPF venga caricato in ciascuna di esse.
Poiché lo stato della connessione viene tracciato per core (BPF_MAP_TYPE_LRU_PERCPU_HASH), dovresti assicurarti che RSS (Receive Side Scaling) instradi costantemente i pacchetti di un flusso allo stesso core CPU in caso di modifica dell'interfaccia selezionata dallo switch quando la topologia LACP cambia. Disabilita irqbalance, assicurati che le impostazioni del canale siano le stesse su ciascuna interfaccia (ethtool -l/-L) e che l'indirizzamento dell'hash del flusso RSS corrisponda (ethtool -x/-X).
La configurazione può essere testata avviando una connessione di lunga durata (ad es. utilizzando iperf con l'opzione -t) verso un insieme di server backend, quindi disabilitando il backend scelto con un asterisco dopo l'indirizzo IP nel file di configurazione, determinando quale interfaccia sta ricevendo il flusso sul bilanciatore (ad es. watch -d 'cat /proc/interrupts | grep enp130s0f' e cercando il contatore IRQ in rapido aumento) e quindi rimuovendo questa interfaccia dal LACP (ifenslave -d bond0 enp130s0f0). Dovresti vedere il flusso spostarsi sull'altra interfaccia di rete ma colpire comunque lo stesso core.
Quando si utilizzano backend in più sottoreti, per ottenere le migliori prestazioni dovresti assicurarti che tutte le VLAN siano con tag su una singola interfaccia trunk (LACP bonded se hai più di un'interfaccia fisica) con le mappature ID sottorete/VLAN specificate nella sezione vlans del file di configurazione.
Se ciò non è possibile (ad esempio, creare interfacce trunk su vSphere non è semplice), puoi assegnare ciascuna sottorete a un'interfaccia senza tag diversa:
./vc5 10.1.10.100 config.json eth0 eth1 eth2
Un buon riepilogo dei concetti utilizzati è discusso nel talk di Patrick Shuff "Building a Billion User Load Balancer" e nel talk di Nitika Shirokov su Katran
Una console web di base e un server di metriche Prometheus sono inclusi: 
È ora incluso il supporto sperimentale per il logging su Elasticsearch (direttamente al tuo cluster, senza bisogno di raccogliere log di sistema). Ogni sonda verso i server backend viene registrata, quindi se un server si guasta puoi vedere esattamente quale errore è stato restituito, oltre a molte altre condizioni. Ciò richiederà molti perfezionamenti e una denominazione più sensata dei parametri di log, ecc. (se hai suggerimenti, contattaci), ma dovrebbe portare alla possibilità di ottenere buone informazioni su cosa sta succedendo nel sistema – il mio primo tentativo molto inesperto di creare una dashboard Kibana come esempio: 
È stato testato principalmente con server backend Icecast e client che ricevono un mix di flussi a basso e alto bitrate (48 kbps - 192 kbps).
Sembra che un guest VMWare (4 core, 8 GB) che utilizza il driver generico XDP possa supportare 100.000 client simultanei, 380 Mbps/700 kpps di traffico attraverso il bilanciatore e 8 Gbps di traffico dai backend direttamente ai client.
Su un singolo processore Intel Xeon Gold 6314U (2,30 GHz, 32 core fisici, con hyperthreading abilitato per 64 core logici) e una scheda ethernet Intel 10G 4P X710-T4L-t, sono riuscito a eseguire 700.000 flussi a 2 Gbps/3,8 Mpps di traffico in ingresso e 46,5 Gbps in uscita. Il server era inattivo per oltre il 90%. Sfortunatamente non avevo le risorse disponibili per creare più client/server.
Esistono tre modalità operative: semplice, VLAN e multi-NIC. Nella modalità semplice, tutti gli host devono trovarsi sulla stessa sottorete dell'indirizzo primario del bilanciatore. Nella modalità VLAN (abilitata dichiarando voci nella sezione "vlans" del file di configurazione YAML/JSON), le voci del server devono corrispondere a una voce di sottorete CIDR VLAN. Le interfacce con tag VLAN devono essere create nel sistema operativo e avere un indirizzo IP assegnato all'interno della sottorete. Nella modalità multi-NIC, alle sottoreti vengono assegnati ID nello stesso modo delle VLAN, ma viene utilizzato bpf_redirect() per inviare il traffico attraverso l'interfaccia configurata in modo appropriato (invece di modificare l'ID VLAN e utilizzare XDP_TX).
In modalità VLAN, tutto il traffico per il bilanciatore deve trovarsi su una VLAN con tag (nessun push o pop di 802.1Q è effettuato – per ora).