Ottimizzare grazie a Memcached

theflash_memcachedOttimizzazione server dedicato grazie a MemCached

Spesso si e’ portati a credere che i meccanismi di cache siano una specie di Panacea, e che dalla loro implementazione non si debba temere alcun  problema : ovviamente non è così.
Specialmente se il tuo cloud è dedicato all’erogazione di siti/portali web con volumi importanti l’utilizzo di memcached può sicuramente aumentare le performance di quest’ultimo;  ma può anche introdurre un nuovo point of failure, uno di quelli che non ti aspetti.
Facciamo un esempio, non ti aspetteresti che un’istanza memcached possa rappresentare un collo di bottiglia !, men che meno un pool di istanze bilanciate e, cosa forse ancor più singolare, che lo possano rappresentare a livello di networking. Eppure succede!

Cos’è memcached, e come funziona?
Memcached è un sistema di caching distribuito ed ha una struttura client/server che permette di servire i dati più richiesti direttamente dalla RAM, riducendo cosi al tempo stesso il carico sul database.
Questo può essere di grande aiuto se si ha la necessità di gestire una quantità di dati davvero importante.
Sfruttare elaborazioni già effettuate: questo è il punto di forza di Memcached; infatti raggruppando la cache dei nodi crea una memoria a breve termine che serve per ottimizzare le operazioni ripetute.

Memcached è semplice ma potente. Il suo design semplice promuove un’implementazione veloce, facilità di sviluppo, e risolve molti dei problemi che ci si trova ad affrontare quando si ha a che fare con grandi cache di dati.

Il server funziona ricevendo una serie di coppie chiave-valore e mantenendole in RAM.
In genere è compito della logica applicativa (quindi della programmazione degli scripts php, ruby, phyton ecc…) decidere se effettuare un’operazione di scrittura su memcached oppure di lettura, ed e’sempre compito dell’applicazione computare un hash delle key che invia a memcached e, successivamente, inviare la coppia chiave-valore al server stesso.
Nel caso di un cluster, l’applicazione deve determinare in base all’hash quale nodo memcached contiene quale valore.

Ora, può succedere che alcune coppie chiavi-valore siano molto più utilizzate di altre, queste si definiscono in gergo “hot keys”.
Può succedere inoltre, nel caso che il sito sia molto trafficato, che i front-end web interroghino con preferenza alcune coppie chiave-valore specifiche.
Facciamo un’altro esempio: immaginiamo di dover gestire la home page di un quotidiano nazionale (a me e’ capitato).
Immaginiamo che il codice che muove quel sito interroghi memcached per evitare che, ad ogni richiesta della home page, vengano fatte delle richieste al database.
Immaginiamo infine che la home page del tuo portale riceva un grosso picco di traffico completamente inatteso (per esempio un terremoto).
Man mano che il traffico aumenta, aumentano le richieste che i front-end devono effettuare al back-end memcached per evitare di interrogare continuamente i database.
Per servire un sito di questo tipo, si sono probabilmente predisposti una mezza dozzina di front-end applicativi, o forse di più, e ciascuno di questi chiama il back-end memcache.
Man mano che il traffico da servire lato pubblico scala verso le decine di Mbit/s, il traffico verso il backend memcached aumenta con un moltiplicatore pari al numero di front-end e questo comincia a saturare la scheda di rete del memcached.
Il traffico aumenta fintanto che la scheda di rete si satura, il sistema crolla, con buona pace di ottimizzazioni lato front-end, bilanciatori di carico, e ottimizzazioni lato SQL.

Se avessimo configurato un cluster memcached alle spalle dei front-end il problema potrebbe comunque presentarsi perchè alcuni nodi del cluster potrebbero avere delle “hot-keys” un po più “hot” delle chiavi degli altri nodi.

Ora, potrebbe anche essere che i memcached contengano solo piccole porzioni di dato e quindi il traffico sulla NIC sia molto contenuto, ma se non siete stati voi ad aver scritto l’applicazione, non potrete esserne certi, quindi si dovra’ quantomeno monitorare la saturazione della NIC e l’eventuale presenza di differenti hot keys sui server.

Come uscirne fuori?
Se la situazione non si è ancora deteriorata del tutto, potreste effettuare l’analisi del traffico in uscita dalla NIC con un packet sniffer e cercare di capire quale sia la chiave “incriminata” e i dati ad essa associati.
Ma c’e’ da dire che state facendo passare qualche centinaio di Mbit/s su una NIC, un’attività del genere non deve essere una passeggiata anche solo per la mole di dati che si deve processare.
In alternativa, può venirci in aiuto uno strumento come mctop, che permette di tracciare il numero di chiamate, richieste al secondo, e l’uso della banda per ogni singolo pacchetto.
In questo modo, sara’ molto più facile individuare le key che tendono a saturare la banda ed indicare dove ai programmatori o tecnici dove devono intervenire.

Non è solo un problema lato server.
Come si puo’ immaginare, questo tipo di problemi va affrontato sia lato sistemi che lato sviluppo
In una situazione di emergenza identificare una “hot key” manualmente, potra’ anche andare bene, ma il punto è che dal lato sviluppo applicativo dovranno mettere mano al codice ed essere in grado di effettuare, per quanto possibile, questo tipo di operazione in autonomia migliorando le fasi di test e di pre produzione.
Diventa cosi’ davvero opportuno avere nella propria infrastruttura, dei sistemi di monitoraggio e di alert, che possano segnalare per tempo gli status ed i possibili rischi di saturazione lato NIC.

Catturare il traffico di rete

redirectRedirect fai da te

Molte volte vi capitera’ (ed a me e’ capitato piu’ volte) di aver bisogno di gestire il traffico di rete ottimizzandolo, filtrandolo e redirezionandolo.
Si pensi, ad esempio, ai test di sviluppo effettuati sulle molte VM in cui si deve tenere conto della quantita’ di Server interessati e del carico di rete da gestire bilanciando quest’ultimo e gestendo le porte interessate.

In questo articolo illustrero’ alcuni dei migliori tra quelli da me usati in ambito OpenSource sono, Rinetd, LVS e Pound, ma l’elenco potrebbe ancora allungarsi, magari per un seguito.

PARTIAMO

rinetd

E’ il piu’ semplice dei tre, dunque partiremo da questo; esso permette di ridirigere una destinazione TCP, definita attraverso una coppia <indirizzo-ip>:<numero-di-porta>, presso un’altra coppia di questi valori. Lo scopo di questo può essere semplicemente quello di dirigere una porta locale verso un’altra porta locale, oppure si può arrivare a intercettare il traffico IP che attraversa un router in modo da ridirigere alcune coppie di indirizzi e porte presso altre destinazioni.

Tutto è composto semplicemente da un daemon, rinetd, che si avvale di un file di configurazione, /etc/rinetd.conf, nel quale si indicano semplicemente le ridirezioni da applicare.

La presenza in funzione di rinetd è incompatibile con altri daemon che stanno in ascolto delle stesse porte che devono essere ridirette, anche se queste sono intese appartenere a host differenti.

Il programma rinetd è il demone che si occupa di ridirigere il traffico TCP in base a quanto contenuto nel file di configurazione /etc/rinetd.conf
E' sufficiente avviarlo e, se il file di configurazione risultera'corretto, iniziare subito a lavorarci. All'avvio, dopo aver letto la configurazione, rinetd deve poter stare in ascolto dell'indirizzo da ridirigere e della porta relativa; qualunque sia l'indirizzo in questione, è necessario che non ci sia già un programma locale che fa la stessa cosa su quella stessa porta; per esempio, non si può tentare di ridirigere il servizio HTTP di un indirizzo qualunque, se questo è presente localmente.

Un esempio di configurazione del file rinetd.conf dovrebbe essere sufficiente a chiarire le idee su questo file. Supponiamo di voler dirottare il traffico diretto verso l’indirizzo IP 10.11.12.13 alla porta 80, in modo che questo vada verso l’indirizzo IP 192.168.1.7, alla porta 80.

120.121.122.123 80 192.168.1.7 80

L’indirizzo da ridirigere, può appartenere a un’interfaccia del nodo presso cui si trova in funzione il demone rinetd,
oppure no, purché i pacchetti diretti a tale indirizzo transitino attraverso il nodo che attua la ridirezione.
Se si vuole apprendere il funzionamento di rinetd senza disporre di una rete vera e propria, basta una direttiva di configurazione simile a quella seguente:

localhost 8888 localhost html

In questo modo, la porta locale 8888 viene ridiretta sulla porta del servizio HTTP (80). Se il servizio HTTP è attivo, si può verificare la ridirezione con un programma di navigazione qualunque, puntando all’URL

http://localhost:8888

Rispetto ai prossimi due tool rinetd non e’ in grado di fungere anche come LoadBalancer.


ipvsadm

Questo servizio aggiorna la tabella d’instradamento IPVS nel kernel. Il demone lvs imposta e gestisce Load Balancer Add-On richiamando ipvsadm per aggiungere, modificare e cancellare le voci all’interno della tabella d’instradamento IPVS. Inoltre ipvsadm fa parte del paccheto LVS  che è una soluzione di bilanciamento del carico avanzato per sistemi Linux.
Si tratta di un progetto open source avviato da Wensong Zhang nel lontano 1998. La missione del progetto è di costruire un server ad alte prestazioni e ad alta disponibilità per Linux utilizzando tecnologie di clustering, offrendo una buona scalabilità, affidabilità e facilità di manutenzione. L’opera principale del progetto LVS è ora quello di sviluppare un software avanzato di bilanciamento del carico IP (IPVS), ed un software di bilanciamento a livello dell’applicazione (KTCPVS), ed i componenti di gestione dei cluster.

Ipvs in pratica

IPVS (IP Virtual Server) implementa un bilanciatore di carico a livello Layer 4 della rete. IPVS in esecuzione su un host si comporta come un sistema di bilanciamento del carico di fronte ad un insieme di server reali in cluster, può indirizzare le richieste per servizi basati si TCP/UDP ai veri server, e fa apparire i servizi dei server reali come un unico servizio virtuale su un unico indirizzo IP.

La componente IPVS è presente in tutti i recenti Kernel, per installare la componente in user-space utilizzate il vostro gestore di pacchetti, ad esempio in Ubuntu:

aptitude install ipvsadm

a questo punto si può creare uno script da far avviare al boot. Io di solito inserisco i comandi all’interno del file
/etc/rc.local.

Prima di tutto dobbiamo resettare l’attuale configurazione con il comando:
ipvsadm -C
Dopodiché iniziamo a dare le regole con i comandi come nell’esempio qui sotto in cui diciamo che le chiamate TCP (parametro -t) all’indirizzo 192.168.10.100 sulla porta 5060 (quella per il protocollo SIP) debbano essere inoltrate alla stessa porta dell’indirizzo 192.168.10.250.  Per reindirizzare una chiamata UDP sostituire il -t con -u.
ipvsadm -A -t 192.168.10.100:5060 -s rr

ipvsadm -a -t 192.168.10.100:5060 -r 192.168.10.250:5060 -m

Naturalmente è possibile catturare il traffico su una porta e inoltrarla ad un’altra con un comando tipo questo:
ipvsadm -A -t 192.168.10.100:88 -s rr
ipvsadm -a -t 192.168.10.100:88 -r 192.168.10.250:80 -m
In questo caso non abbiamo fatto altro che prendere le chiamate alla porta 88 dell’indirizzo 192.168.10.100 e rinviarle al server web dell’IP 192.168.10.250 sulla normale porta 80

Metodi di bilanciamento utilizzati da LVS

In caso si desideri testare il funzionamento di LVS senza la necessita’ di monitorare i servizi e possibile aggiungere e rimuovere nodi con il comando ipvsadm:

ipvsadm -C
ipvsadm -A -t 10.2.1.164:8080 -s lc
ipvsadm -a -t 10.2.1.164:8080 -r 10.2.1.166 -g
ipvsadm -a -t 10.2.1.164:8080 -r 10.2.1.165 -g

Le opzioni utilizzate nelle linee di comando di ipvsadm per l’esempio riportato sono le seguenti:

-C, –clear: cancella la tabella del virtual server.
-A, –add-service: crea un servizio virtuale.
-a, –add-server: aggiunge un nodo ad un servizio virtuale.
-t, –tcp-service: specifica indirizzo ip e numero di porta tcp del servizio virtuale.
-s, –scheduler: specifica l’algoritmo di bilanciamento
-r, –real-server: specifica l’indirizzo ip del nodo reale
-g, –gatewaying: indica il metodo di forwarding direct routing (LVS-DR)

** algoritmi per il bilanciamento che possiamo usare con LVS.

Statici:

– Round Robin

– Weighted Round Robin

– Destination Hashing

– Source Hashing

Dinamici:

– Least-Connection

– Weighted least-connection

– Never queue

– Locality-based least-connection

– Locality-based least-connection with replication scheduling

– Shortest expected delay


pound

Pound è un proxy server di bilanciamento del carico inverso. Accetta richieste da HTTP / HTTPS clienti e li distribuisce a uno o più server web. Le richieste HTTPS vengono decifrati e passati al back-end come semplice protocollo HTTP.

Se più di un server back-end è definita, Pound sceglie uno di loro a caso, sulla base delle priorità definite. Per impostazione predefinita, Pound tiene traccia di associazioni tra client e server back-end (sessioni).

General Principles

In generale, Pound ha bisogno di tre tipi di oggetti definiti, al fine di funzione: ascoltatori , i servizi e back-end .

Ascoltatori
Un ascoltatore è una definizione di come Pound riceve le richieste dai client (browser). Due tipi di ascoltatori può essere definito: normale connessione HTTP ascoltatori e HTTPS (HTTP su SSL / TLS) ascoltatori . Per lo meno un ascoltatore deve definire l’indirizzo e la porta per l’ascolto su, con ulteriori requisiti per HTTPS ascoltatori .

Servizi
Un servizio è la definizione di come le domande trovano risposta. Il servizio può essere definito all’interno di un ascoltatore o al livello superiore (globale). Quando viene ricevuta una richiesta Pound tenta di far corrispondere a ciascun servizio , a sua volta, a partire dai servizi definiti nel ascoltatore stesso e, se necessario, di proseguire con l’ servizi definiti a livello globale. I servizi possono definire le proprie condizioni al quale le domande si può rispondere: in genere si tratta certo URL (solo foto, o un certo percorso) o intestazioni specifiche (come ad esempio l’intestazione Host). Un servizio può anche definire una sessione meccanismo: se definito le richieste future da un determinato cliente sarà sempre la stessa risposta da parte di back-end .

Back-end
Il back-end sono i server reale per il contenuto richiesto. Di per sé, Pound fornisce nessuna risposta – tutti i contenuti devono essere ricevuti da un vero e proprio “web server”. Il back-end definisce come il server dovrebbe essere contattato.

Tre tipi di back-end può essere definito: un “regolare” back-end che riceve le richieste e le risposte restituisce, un “redirect” back-end in questo caso, Pound risponde con una risposta redirect, senza l’accesso a qualsiasi back-end a tutti , o una “emergenza” back-end che sarà usato solo se tutti gli altri backend sono “morti”.

Multiple back-end può essere definito all’interno di un servizio , nel qual caso Pound sarà bilanciamento del carico tra i disponibili back-end .

Se un back-end non riesce a rispondere, sarà considerato “morto”, nel qual caso Pound si ferma l’invio di richieste ad esso. Dead indietro _ e NDS sono periodicamente controllate per la disponibilità, e una volta che rispondono ancora sono “resurected” e le richieste sono inviati di nuovo la loro strada. Se non back-end sono disponibili (nessuno è stato definito, o sono tutti “morti”), allora Pound risponderà con “503 Servizio non disponibile”, senza verificare ulteriori servizi .

Il collegamento tra Pound e il back end- è sempre via HTTP, a prescindere dal protocollo utilizzato tra Pound e il cliente.

Installazione

sudo apt-get install pound

La gestione completa del servizio avviene tramite la configurazione del file /etc/pound/pound.cfg
Esempio 1:

Semplice configurazione HTTP Proxy
Supponiamo di forwardare le richieste http che arrivano dall”IP pubblico 202.54.10.5 all’IP sulla LAN 192.168.1.5 su cui è configurato un web server Apache sulla porta 8080.
Editiamo il file di configurazione di pound di una distro Debian/Ubuntu:

vim /etc/pound/pound.cfg

Questo è l’aspetto del file:

ListenHTTP
Address  202.54.10.5
Port          80
Service
BackEnd
Address  192.168.1.5
Port           8080
End
End
End

Salvare e chiudere il file e restartare Pound:

/etc/init.d/pound restart

Esempio 2
Semplice configurazione HTTP & HTTPS Proxy
In questo esempio vediamo come “proxare” una richiesta http e https dallo stesso IP pubblico 202.54.10.5 a due web server 192.168.1.5 e 192.168.1.6, entrambi sulla porta 80:

ListenHTTP
Address  202.54.10.5
Port          80
End

ListenHTTPs
Address   202.54.10.5
Port           443
Cert           “/etc/ssl/local.server.pem” -–>percorso certificato ssl
End

Service
BackEnd
Address     192.168.1.5
Port              80
Priority       1
Backend
Address     192.168.1.6
Port              80
Priority       3
End
End

Salviamo il file di configurazione e restartiamo pound.

In questo esempio le richieste alla porta 80 all’ ip 202.54.10.5  vengono inoltrate alla porta 80 del webserver 192.168.1.5, mentre le richieste alla porta 443 dall’ ip 202.54.10.5 vengono inoltrate alla porta 80 del web server 192.168.1.6  e in questo caso pound gestisce il certificato ssl, che è possibile generarsi senza alcuna modifica nel backend del web server, che continua a gestire chiamate in http.

PS: e’ possibile inoltre impostare una priorità di inoltro del traffico differente, nel caso si disponga di più server web, cosi’ come indicato dalla voce “Priority” presente nella configurazione del secondo esempio; minore è la cifra, maggiore sarà la priorità assegnata al server.

Buon divertimento !