Architetture di bilanciamento del carico del server, Parte 1: bilanciamento del carico a livello di trasporto

Le server farm raggiungono un'elevata scalabilità e un'elevata disponibilità tramite il bilanciamento del carico del server, una tecnica che fa apparire la server farm ai client come un singolo server. In questo articolo in due parti, Gregor Roth esplora le architetture di bilanciamento del carico dei server, concentrandosi sulle soluzioni open source. La parte 1 tratta le nozioni di base sul bilanciamento del carico del server e discute i pro ei contro del bilanciamento del carico del server a livello di trasporto. La Parte 2 copre le architetture di bilanciamento del carico del server a livello di applicazione, che risolvono alcune delle limitazioni delle architetture discusse nella Parte 1.

La barriera all'ingresso per molte società Internet è bassa. Chiunque abbia una buona idea può sviluppare una piccola applicazione, acquistare un nome di dominio e configurare alcuni server basati su PC per gestire il traffico in entrata. L'investimento iniziale è piccolo, quindi il rischio di avvio è minimo. Ma un'infrastruttura a basso costo di successo può diventare rapidamente un problema serio. Un singolo server che gestisce tutte le richieste in arrivo potrebbe non essere in grado di gestire volumi di traffico elevati una volta che l'azienda diventa popolare. In tali situazioni le aziende spesso iniziano a scalare : aggiornano l'infrastruttura esistente acquistando una scatola più grande con più processori o aggiungono più memoria per eseguire le applicazioni.

L'aumento di scala, tuttavia, è solo una soluzione a breve termine. Ed è un approccio limitato perché il costo dell'aggiornamento è sproporzionatamente alto rispetto ai guadagni in termini di capacità del server. Per questi motivi, le società Internet di maggior successo seguono un approccio scale out . I componenti dell'applicazione vengono elaborati come più istanze nelle server farm, basate su hardware e sistemi operativi a basso costo. Man mano che il traffico aumenta, vengono aggiunti i server.

L'approccio della server farm ha le sue esigenze uniche. Sul lato software, è necessario progettare le applicazioni in modo che possano essere eseguite come più istanze su server diversi. A tale scopo, suddividendo l'applicazione in componenti più piccoli che possono essere distribuiti in modo indipendente. Questo è banale se i componenti dell'applicazione sono senza stato. Poiché i componenti non mantengono alcuno stato di transazione, ognuno di essi può gestire le stesse richieste allo stesso modo. Se è necessaria più potenza di elaborazione, è sufficiente aggiungere altri server e installare i componenti dell'applicazione.

Un problema più impegnativo si verifica quando i componenti dell'applicazione sono stateful. Ad esempio, se il componente dell'applicazione contiene i dati del carrello, una richiesta in arrivo deve essere instradata a un'istanza del componente dell'applicazione che contiene i dati del carrello del richiedente. Più avanti in questo articolo, parlerò di come gestire tali dati di sessione dell'applicazione in un ambiente distribuito. Tuttavia, per ridurre la complessità, i sistemi applicativi basati su Internet di maggior successo cercano di evitare i componenti delle applicazioni stateful ogni volta che è possibile.

Sul lato infrastruttura, il carico di elaborazione deve essere distribuito tra il gruppo di server. Questo è noto come bilanciamento del carico del server. Le tecnologie di bilanciamento del carico riguardano anche altri domini, ad esempio la diffusione del lavoro tra componenti come collegamenti di rete, CPU o dischi rigidi. Questo articolo si concentra sul bilanciamento del carico del server.

Disponibilità e scalabilità

Il bilanciamento del carico del server distribuisce le richieste di servizio su un gruppo di server reali e fa sembrare quei server come un unico grande server per i client. Spesso dozzine di server reali sono dietro un URL che implementa un singolo servizio virtuale.

Come funziona? In un'architettura di bilanciamento del carico del server ampiamente utilizzata, la richiesta in arrivo viene diretta a un servizio di bilanciamento del carico del server dedicato che è trasparente per il client. In base a parametri come la disponibilità o il carico del server corrente, il sistema di bilanciamento del carico decide quale server deve gestire la richiesta e la inoltra al server selezionato. Per fornire all'algoritmo di bilanciamento del carico i dati di input richiesti, il sistema di bilanciamento del carico recupera anche le informazioni sullo stato e sul carico dei server per verificare che possano rispondere al traffico. La figura 1 illustra questa classica architettura di bilanciamento del carico.

L'architettura del load-dispatcher illustrata nella Figura 1 è solo uno dei diversi approcci. Per decidere quale soluzione di bilanciamento del carico è la migliore per la tua infrastruttura, devi considerare disponibilità e scalabilità .

La disponibilità è definita dal tempo di attività , il tempo tra i guasti. (Il tempo di inattività è il tempo necessario per rilevare il guasto, ripararlo, eseguire il ripristino richiesto e riavviare le attività.) Durante il tempo di attività il sistema deve rispondere a ciascuna richiesta entro un tempo prestabilito e ben definito. Se questo tempo viene superato, il client lo vede come un malfunzionamento del server. L'alta disponibilità, fondamentalmente, è ridondanza nel sistema: se un server si guasta, gli altri assumono il carico del server guasto in modo trasparente. Il guasto di un singolo server è invisibile al client.

Scalabilità significa che il sistema può servire un singolo cliente, così come migliaia di client simultanei, soddisfacendo i requisiti di qualità del servizio come il tempo di risposta. Sotto un carico maggiore, un sistema ad alta scalabilità può aumentare il throughput in modo quasi lineare in proporzione alla potenza delle risorse hardware aggiuntive.

Nello scenario in Figura 1, si ottiene un'elevata scalabilità distribuendo la richiesta in ingresso sui server. Se il carico aumenta, è possibile aggiungere altri server, a condizione che il bilanciamento del carico non diventi il ​​collo di bottiglia. Per raggiungere la disponibilità elevata, il sistema di bilanciamento del carico deve monitorare i server per evitare di inoltrare richieste a server sovraccarichi o inattivi. Inoltre, anche il bilanciatore del carico stesso deve essere ridondante. Discuterò questo punto più avanti in questo articolo.

Tecniche di bilanciamento del carico del server

In generale, le soluzioni di bilanciamento del carico del server sono di due tipi principali:

  • Il bilanciamento del carico a livello di trasporto, come l'approccio basato su DNS o il bilanciamento del carico a livello TCP / IP, agisce indipendentemente dal payload dell'applicazione.
  • Il bilanciamento del carico a livello di applicazione utilizza il payload dell'applicazione per prendere decisioni sul bilanciamento del carico.

Le soluzioni di bilanciamento del carico possono essere ulteriormente classificate in bilanciatori del carico basati su software e bilanciatori del carico basati su hardware. I bilanciatori del carico basati su hardware sono scatole hardware specializzate che includono circuiti integrati specifici dell'applicazione (ASIC) personalizzati per un uso particolare. Gli ASIC consentono l'inoltro ad alta velocità del traffico di rete senza l'overhead di un sistema operativo generico. I sistemi di bilanciamento del carico basati su hardware vengono spesso utilizzati per il bilanciamento del carico a livello di trasporto. In generale, i bilanciatori del carico basati su hardware sono più veloci delle soluzioni basate su software. Il loro svantaggio è il loro costo.

A differenza dei sistemi di bilanciamento del carico hardware, i sistemi di bilanciamento del carico basati su software vengono eseguiti su sistemi operativi standard e componenti hardware standard come i PC. Le soluzioni basate su software vengono eseguite all'interno di un nodo hardware di bilanciamento del carico dedicato come nella Figura 1 o direttamente nell'applicazione.

Bilanciamento del carico basato su DNS

Il bilanciamento del carico basato su DNS rappresenta uno dei primi approcci al bilanciamento del carico del server. Il DNS (Domain Name System) di Internet associa gli indirizzi IP a un nome host. Se digiti un nome host (come parte dell'URL) nel tuo browser, il browser richiede che il server DNS risolva il nome host in un indirizzo IP.

L'approccio basato su DNS si basa sul fatto che il DNS consente di assegnare più indirizzi IP (server reali) a un nome host, come mostrato nell'esempio di ricerca DNS nel Listato 1.

Listato 1. Esempio di ricerca DNS

>nslookup amazon.com Server: ns.box Address: 192.168.1.1 Name: amazon.com Addresses: 72.21.203.1, 72.21.210.11, 72.21.206.5

Se il server DNS implementa un approccio round robin, l'ordine degli indirizzi IP per un determinato host cambia dopo ogni risposta DNS. Di solito i client come i browser tentano di connettersi al primo indirizzo restituito da una query DNS. Il risultato è che le risposte a più client vengono distribuite tra i server. A differenza dell'architettura di bilanciamento del carico del server nella Figura 1, non è richiesto alcun nodo hardware di bilanciamento del carico intermedio.

DNS è una soluzione efficiente per il bilanciamento del carico del server globale, in cui il carico deve essere distribuito tra data center in posizioni diverse. Spesso il bilanciamento del carico del server globale basato su DNS viene combinato con altre soluzioni di bilanciamento del carico del server per distribuire il carico all'interno di un data center dedicato.

Sebbene facile da implementare, l'approccio DNS presenta gravi inconvenienti. Per ridurre le query DNS, il client tende a memorizzare nella cache le query DNS. Se un server diventa non disponibile, la cache del client e il server DNS continueranno a contenere un indirizzo di server inattivo. Per questo motivo, l'approccio DNS fa poco per implementare l'alta disponibilità.

Bilanciamento del carico del server TCP / IP

I servizi di bilanciamento del carico del server TCP / IP operano su una commutazione di livello di basso livello. Un popolare sistema di bilanciamento del carico del server di basso livello basato su software è Linux Virtual Server (LVS). I server reali appaiono al mondo esterno come un unico server "virtuale". Le richieste in arrivo su una connessione TCP vengono inoltrate ai real server dal load balancer, che esegue un kernel Linux con patch per includere il codice IP Virtual Server (IPVS).

Per garantire un'elevata disponibilità, nella maggior parte dei casi vengono configurati una coppia di nodi di bilanciamento del carico, con un nodo di bilanciamento del carico in modalità passiva. Se un bilanciamento del carico non riesce, il programma heartbeat che viene eseguito su entrambi i sistemi di bilanciamento del carico attiva il nodo di bilanciamento del carico passivo e avvia l'acquisizione dell'indirizzo IP virtuale (VIP). Mentre l'heartbeat è responsabile della gestione del failover tra i bilanciatori del carico, vengono utilizzati semplici script di invio / attesa per monitorare lo stato di salute dei real server.

La trasparenza per il client si ottiene utilizzando un VIP assegnato al sistema di bilanciamento del carico. Se il client invia una richiesta, prima il nome host richiesto viene tradotto nel VIP. Quando riceve il pacchetto di richiesta, il load balancer decide quale real server deve gestire il pacchetto di richiesta. L'indirizzo IP di destinazione del pacchetto di richiesta viene riscritto nell'IP reale (RIP) del server reale. LVS supporta diversi algoritmi di pianificazione per la distribuzione delle richieste ai real server. È spesso impostato per utilizzare la pianificazione round-robin, simile al bilanciamento del carico basato su DNS. Con LVS, la decisione sul bilanciamento del carico viene presa a livello TCP (Livello 4 del modello di riferimento OSI).

Dopo aver ricevuto il pacchetto di richiesta, il server reale lo gestisce e restituisce il pacchetto di risposta. Per forzare la restituzione del pacchetto di risposta tramite il bilanciamento del carico, il server reale utilizza il VIP come percorso di risposta predefinito. Se il sistema di bilanciamento del carico riceve il pacchetto di risposta, l'IP di origine del pacchetto di risposta viene riscritto con VIP (OSI Model Layer 3). Questa modalità di instradamento LVS è denominata instradamento NAT (Network Address Translation). La Figura 2 mostra un'implementazione LVS che utilizza il routing NAT.

LVS supporta anche altre modalità di routing come Direct Server Return. In questo caso il pacchetto di risposta viene inviato direttamente al client dal real server. Per fare questo, il VIP deve essere assegnato anche a tutti i real server. È importante rendere il VIP del server irrisolvibile per la rete; in caso contrario, il bilanciamento del carico diventa irraggiungibile. Se il sistema di bilanciamento del carico riceve un pacchetto di richiesta, l'indirizzo MAC (OSI Model Layer 2) della richiesta viene riscritto al posto dell'indirizzo IP. Il real server riceve il pacchetto di richiesta e lo elabora. In base all'indirizzo IP di origine, il pacchetto di risposta viene inviato direttamente al client, bypassando il bilanciamento del carico. Per il traffico Web, questo approccio può ridurre drasticamente il carico di lavoro del bilanciatore. In genere, vengono trasferiti molti più pacchetti di risposta rispetto ai pacchetti di richiesta. Ad esempio, se richiedi una pagina Web, spesso viene inviato un solo pacchetto IP. Se viene richiesta una pagina Web più grande,sono necessari diversi pacchetti IP di risposta per trasferire la pagina richiesta.

Caching

Le soluzioni di bilanciamento del carico del server di basso livello come LVS raggiungono il loro limite se è richiesto il caching a livello di applicazione o il supporto della sessione dell'applicazione. La memorizzazione nella cache è un importante principio di scalabilità per evitare costose operazioni che recuperano ripetutamente gli stessi dati. Una cache è un archivio temporaneo che contiene dati ridondanti risultanti da una precedente operazione di recupero dati. Il valore di una cache dipende dal costo per recuperare i dati rispetto alla percentuale di risultati e alla dimensione della cache richiesta.

In base all'algoritmo di pianificazione del bilanciamento del carico, le richieste di una sessione utente vengono gestite da server diversi. Se una cache viene utilizzata sul lato server, le richieste vaganti diventeranno un problema. Un approccio per gestire questo è posizionare la cache in uno spazio globale. memcached è una popolare soluzione di cache distribuita che fornisce una grande cache su più macchine. È una cache distribuita e partizionata che utilizza hashing coerente per determinare il server della cache (daemon) per una data voce della cache. In base al codice hash della chiave della cache, la libreria client associa sempre lo stesso codice hash allo stesso indirizzo del server della cache. Questo indirizzo viene quindi utilizzato per memorizzare la voce della cache. La Figura 3 illustra questo approccio alla memorizzazione nella cache.

Il Listato 2 utilizza spymemcached, un memcachedclient scritto in Java, per memorizzare nella cache i HttpResponsemessaggi su più macchine. La spymemcachedlibreria implementa la logica client richiesta che ho appena descritto.

Listato 2. cache HttpResponse basata su memcached