Mi è capitato di dover bilanciare con un reverse proxy Apache 2.2 alcuni server fisici, prevalentemente Sharepoint. Subito ho pensato al modulo mod_proxy_balancer di Apache 2.2 ed alla sua capacità di gestire in maniera integrata a mod_proxy il bilanciamento e la persistenza della connessione client, in modo tale da non invalidarne la sessione.

Di seguito i passi di esempio per la sua implementazione.

In pratica si definisce a livello di VirtualHost una risorsa bilanciata:

<Proxy balancer://sitobilanciato>
 BalancerMember http://server-a.sito.lan/  route=uno
 BalancerMember http://server-b.sito.lan/  route=due
 ProxySet lbmethod=byrequests stickysession=BALANCER nofailover=On timeout=3
 </Proxy>

A questo punto, in giro per la configurazione si può decidere di fare reverse proxy del path /pluto verso i server reali, path interno /caio per dire… Se la configurazione “standard” prevederebbe la redirezione del path esterno verso uno dei due server interni:

ProxyPass            /pluto     http://server-a.sito.lan/pippo/
 ProxyPassReverse     /pluto     http://server-a.sito.lan/pippo/

La configurazione bilanciata varia semplicemente così:

ProxyPass            /pluto     balancer://sitobilanciato/caio/
 ProxyPassReverse     /pluto     balancer://sitobilanciato/caio/

in una maniera che definirei semplicissima. La persistenza della sessione viene garantita da Apache, basta che nel cookie definito in stickysession, quindi BALANCER, sia presente il nome del server o meglio la parola chiave definita in route per ciascun server bilanciato. Questo approccio, l’unico che ho trovato in rete, è quasi sempre applicabile perché basta convincere l’applicazione, o il server web residente sul server reale, a scrivere quel cookie e riportare la stringa definita per ciascun server. Meno facile se l’applicazione non è in grado, non è omogenea e non è sotto il nostro controllo. Da qui l’idea di spiegare ad Apache di creare, in autonomia, questo cookie modificando la definizione del pool proxy come segue:

<Proxy balancer://sitobilanciato>
 BalancerMember http://server-a.sito.lan/  route=uno
 BalancerMember http://server-b.sito.lan/  route=due
 ProxySet lbmethod=byrequests stickysession=BALANCER nofailover=On timeout=3
 Header always add Set-Cookie
 "BALANCER=Balancer.%{BALANCER_WORKER_ROUTE}e;
 path=/; domain=.sitarello.it"
 </Proxy>

Questa notazione (attenzione, la riga Header è unica), valida probabilmente da Apache 2.2.10,  è un concentrato di concetti: tramite mod_header apache aggiunge ad ogni response una riga di header che altro non fa che ribadire (inviare) un cookie al client, un cookie proprio per BALANCER. Se questa è la prima risorsa richiesta dal client (ad esempio la pagina HTML), il server Apache invierà questo nuovo cookie, che verrà reinviato dal client per ogni successiva richiesta; a fronte di questo cookie Apache rispetterà la persistenza della corrispondenza tra Client e BalancerMember.

Si ma cosa ci scrive il server in questo cookie? Beh, quanto richiesto e cioè una stringa seguita da un punto ed uno degli identificatori di BalancerMember (parola chiave route). Ma uno a caso? No, alla prima richiesta del client mod_proxy_balancer decide a che server reale inviare la richiesta e sempre mod_proxy_balancer guardacaso valorizza la variabile di ambiente BALANCER_WORKER_ROUTE, dando implicitamente indicazione al client di richiedere, alla prossima richiesta, di parlare “proprio con quel server reale”. Questo approccio ha due vantaggi:

  • il bilanciamento è persistente anche se abbiamo un pool di Reverse Proxy Apache bilanciati tra loro a servire le richieste
  • basta verificare, con un analizzatore HTTP come Live HTTP Headers o HTTPFox, che il cookie inviato e ricevuto siano identici per stabilire che la persistenza è stata mantenuta

In tema di bilanciamento ecco una piccola nota: capita spesso di dover fare richieste HTTP verso server interni (BalancerMember) che implementano virtualhost basati su nome. Se questo nome corrisponde con il nome pubblico del sito (ad esempio il sito si chiama www.sitarello.it e i server web interni accettano richieste proprio per questo vhost) basta utilizzare:

ProxyPreserveHost On

Se invece questi fantastici server web implementano altri vhost del tutto differenti esiste un particolare accorgimento: mantenere l’indicazione di cui sopra e poi, con una sezione Location, modificare l’header della richiesta originaria, in modo che venga passata “modificata” al server reale interno, ad esempio:

<Location />
 RequestHeader set Host www.sitobilanciato.lan
 </Location>

In questo modo si accontenta veramente tutti senza particolare scopo e con la possibilità di modificare l’header in relazione al path richiesto e quindi (probabilmente) alle regole di ProxyPass interne che potrebbero redirigere verso server interni singoli o in pool.