Load Balancing, ridondanza, scalabilità: una panoramica

Load Balancing

Esempio di architettura con load balancer tra client e web server

 

Il bilanciamento del carico (load balancing) è un processo che si occupa di suddividere le richieste tra risorse multiple in base ad alcune metriche (casuale, casuale in base alla capacità delle macchine etc.) ed allo stato delle risorse disponibili.

Un setup ideale dovrebbe garantire il bilanciamento ad ogni livello dell’infrastruttura (utente-web server ; web server – piattaforma interna ; piattaforma interna – database), in modo da ottenere ridondanza e scalabilità completa: la prima consente di non risentire dell’eventuale perdita di uno o più server (la capacità resasi indisponibile sarà sottratta al quantitativo totale senza però creare altri problemi); la seconda permette invece di aumentare linearmente la capacità mediante l’aggiunta di hardware (es: se ad una macchina se ne aggiunge una seconda avremo un incremento del 50% e così via, in questi casi si parla allora di scalabilità orizzontale).

Iniziamo con il vedere quali sono le principali modalità grazie alle quali è possibile implementare il bilanciamento di carico.

Load Balancing con hardware dedicato

Si tratta di una soluzione di alta gamma ma estremamente costosa. L’acquisto di un load balancer hardware dedicato (es: Citrix NetScaler) è in grado di risolvere un vasta gamma di problematiche rivelandosi tuttavia molto care e difficili da configurare. Persino le compagnie di livello enterprise e con budget “quasi illimitati” cercano di utilizzare di rado tali soluzioni , destinandole magari ad uno dei soli livelli elencati in precedenza (ad esempio come primo punto di contatto tra le richieste degli utenti ed il web server) ed affiancandole ad altre soluzioni di bilanciamento.

Load Balancing via software

I software load balancer rappresentano invece un approccio ibrido e meno costoso rispetto alla proposta precedente, lasciando anche ampia libertà di manovra nel caso si voglia passare, in tempi successivi, ad hardware dedicato o altro. E’ per questa serie di motivi che per la maggior parte dei sistemi si consiglia di cominciare con un bilanciamento software. HAProxy è un esempio di load balancing via software: viene eseguito su ciascun box  ed ogni servizio che si intende bilanciare dispone di una porta locale diretta (es: le macchine possono essere accessibili tramite localhost:9000 ; il pool di lettura database tramite localhost:9001 e così via). HAProxy gestisce lo stato di salute, rimuove e restituisce le macchine a determinati pool in base alla configurazione corrente, bilanciando allo stesso tempo le macchine presenti in quei pool.

Caching, un alleato per il bilanciamento di carico

Il caching consiste nel pre-calcolo di risultati, nella pre-generazione di indici complessi e archiviazione di copie di dati alle quali si accede frequentemente in un backend più veloce. Il caching permette di ottimizzare l’utilizzo delle risorse che già si hanno a disposizione e bisognerebbe pensarvi ancora prima di affrontare la questione del bilanciamento di carico – se ben studiata, una strategia di caching consentirà di risparmiare tempo prezioso.

Anche in questo caso abbiamo due tipologie principali da analizzare, il caching delle applicazioni ed il caching del database – buona parte dei sistemi è solita adottarle entrambe. Il primo richiede l’integrazione esplicita all’interno del codice stesso dell’applicazione: sarà quest’ultimo a controllare periodicamente se un valore è in cache e, nel caso in cui non lo sia, andrà a recuperarlo dal database, per poi scriverlo nella cache. Il secondo è in grado di eseguire molto rapidamente il codice di un’applicazione senza che si debba intervenire direttamente come nel caso precedente. Un esperto (database architect) è in grado di aumentare sensibilmente le performance grazie a tale strategia.

 

Caching

Cache Database

Caching

Cache Applicazioni

 

Cache in memoria

Redis e Memcached sono entrambi esempi di in-memory cache, tra le più performanti soluzioni di chaching utilizzabili grazie all’archiviazione dei dati in memoria (RAM) – più veloce dei classici hard disk di diversi ordini di grandezza. Il caching in memoria comporta tuttavia la disponiblità di meno memoria nella RAM e sul disco, situazione che deve essere affrontata mantenendo solo un sottoinsieme di dati nella cache in memoria, un obiettivo raggiungibile grazie ad una strategia LRU o least recently used (meno usato di recente) – LRU lavora considerando solamente i dati usati più di frequente avvantaggiandoli rispetto a quelli meno consultati.

Content Distribution Networks (CDN)

Una particolare tipologia di cache adottata su siti che distribuiscono un’elevata quantità di media statici. Le CDN hanno il compito di rendere disponibili questi ultimi al di fuori degli application server dell’azienda (ottimizzati solitamente per servire pagine dinamiche) e fornire distribuzione geografica, al fine di agevolare il caricamento degli asset sui server con un minore impiego di risorse.

Una tipico setup CDN funziona nel seguente modo: un utente richiede alla CDN l’elaborazione di un determinato “frammento” di media statico che sarà o servito immediatamente (se disponibile localmente) o in seguito (dovrà essere prima individuato, spostato nella cache locale e servito infine all’utente che ha inviato la richiesta). Nel caso in cui un sito non sia abbastanza grande da poter disporre di una CDN propria, è possibile predisporre il portale ad una futura transizione servendo i contenuti su un sottodominio separato, usando un server HTTP abbastanza leggero come Nginx ed attivare in tempi successivi il DNS dai server ad una CDN.

Elaborazione Offline

I sistemi più complessi possono ad un certo punto non essere più in grado di effettuare esclusivamente elaborazioni online e per questo devono ricorrere alle elaborazioni offline – sia per evitare tempi di latenza inaccettabili sia perchè tali elaborazioni hanno spesso cadenza periodica. Una prima soluzione è quella della coda messaggi che permette alle  applicazioni web di pubblicare velocemente messaggi in coda ed avere altri processi che eseguono l’elaborazione al di fuori della timeline legata alla richiesta del client; o ancora di dividere il lavoro tra commesse offline ed online a seconda dell’interfaccia alla quale è esposto l’utente.

Un seconda soluzione è invece l’aggiunta del supporto a MapReduce mediante Hadoop, HBase o Hive. L’implementazione di un livello MapReduce è ideale per l’elaborazione di grandi set di dati (es: generazione report di analisi) in tempi accettabili.

Livello di piattaforma

Buona parte delle applicazioni parte stabilendo una comunicazione diretta tra applicazione web e database: sebbene tale approccio si riveli quasi sempre adeguato, vi sono alcune buone ragioni che suggeriscono di adottare un livello aggiuntivo di piattaforma che operi da intermediario tra le applicazioni ed il database: la prima è che tale separazione tra i due livelli originari consente di scalare queste parti in maniera indipendente. Implementando una nuova API è possibile ad esempio aggiungere un server di piattaforma senza dover necessariamente aumentare inutilmente la capacità per il tier di applicazione. Specializzare il ruolo dei server apre le porte ad un livello di ottimizzazione della configurazione che non è disponibile per macchine general purpose.

Livello di piattaforma

Esempio di architettura con livello di piattaforma

La seconda è che un livello di piattaforma può rivelarsi ideale per riutilizzare una qualsiasi infrastruttura con altri prodotti o interfacce (applicazioni, API, app mobile etc.) in quanto risparmia la fatica di riscrivere del codice per la gestione delle operazioni di caching, del database e via dicendo.

La terza è la possibilità di scalare in modo più semplice un’organizzazione. Se usata al meglio la piattaforma espone un’interfaccia product-agnostic che maschera i dettagli di implementazione, permettendo a più team indipendenti di sviluppare utilizzando le capacità della piattaforma, oltre che implementare e ottimizzare la piattaforma stessa per altri scopi.