Trouver un titre court pour résumer ce billet n’a pas été facile.
Pour détailler un peu plus, je vous propose de mettre un place un Load-balancing croisé entre une ferme de serveurs Apache et une ferme de serveurs Tomcat, le tout bien sûr contrôlé par un système de répartition de charges et d’ip virtuelle.
(nb. Le terme « ferme » désigne un ensemble de serveurs (2 minimum 😉 ). Et puis, Apache… Indien, Plumes, Poule, Ferme, la boucle est bouclée 😀 … Bon ok, je –>[] )
Un dessin valant mieux qu’un long discours, voici ce que nous allons mettre en place
Oui je sais, je suis un grand malade…
Le principe est le suivant : Nous avons une IP Virtuelle, affectée par le protocole CARP sur un ensemble de 2 serveurs OpenBSD. Nous utilisons « Hoststated » qui est un « vérificateur d’état » pour Packet Filter qui nous permettra de load balancer nos frontaux Apache.
Bien évidemment, j’utilise ce moyen car nous n’avons pas tous un ALTEON chez soi…
En dessous nous aurons notre ferme Apache en mode proxy qui fera du load-balancing sur nos tomcats qui eux, ecouteront en http.
(dans cette architecture, on laisse les Tomcats gérér les contenus dynamiques ET statiques).
- Configuration des Tomcats
Que ce soit un tomcat 5 ou 6, la configuration du connecteur Coyote se fait tout simplement dans le fichier server.xml dans le répertoire conf.
Exemple :
<Connector port="60001"
minProcessors="5"
maxProcessors="64"
enableLookups="false"
scheme="http"
proxyName="monappli.blog.guiguiabloc.fr"
proxyPort="80"
acceptCount="100" debug="0" connectionTimeout="1000"
useURIValidationHack="false" disableUploadTimeout="true" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="guiguiabloc-a">
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Alias>monappli.blog.guiguiabloc.fr</Alias>
<Context path="/guiguiabloc"
docBase="/opt/tomcat-guiguiabloc/webapps/guiguiabloc"
debug="0" privileged="false">
A adapter bien sûr à votre appli tomcat.
Ce qui est important c’est :
- Le « Connector Port » qui est le port http sur lequelle écoute le tomcat (identique sur le deuxième serveur tomcat), ici 60001.
- la JVMRoute qui nous servira pour le balancing (ici « guiguiabloc-a » pour le serveur Tomcat-A et « guiguiabloc-b » pour le serveur Tomcat-B).
Le reste est a peu près classique, il est bon de rajouter un « proxyName » qui nous aidera lorsque l’appli est mal codée… (qui a dit, cela arrive souvent ???)
Il faut créer une page des test dans notre webapp, qui servira au redirecteur principal a vérifier que le Tomcat répond correctement :
Exemple de page test.jsp :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <%@ page session="false" %> <HTML> <HEAD> <% response.setHeader("Pragma","No-cache"); response.setDateHeader("Expires",0); response.setHeader("Cache-Control","no-cache"); %> </HEAD> <BODY> Cette page est utilisée par le frontal pour tester si Tomcat est lancé </BODY> </HTML>
Vérifier que cela fonctionne avec un navigateur :
lynx http://monappli.blog.guiguiabloc.fr:60001/guiguiabloc/test.jsp
- Configuration des frontaux Apache
Pré-requis, compiler les modules proxy (j’utilise la version 2.2.8 d’Apache).
N’oubliez pas de les inclure dans votre httpd.conf :
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
Etc… (modules mod_rewrite, mod_unique_id, mod_setenvif, mod_logio, mod_log* et d’autres sont nécessaire et dépendent de la façon dont vous travaillez avec Apache).
Un petit peaufinage en fin de fichier :
ProxyReceiveBufferSize 65536
# Capacity configuration
ServerLimit 4096
MaxClients 4096
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
Ne reste qu’a configurer vos vhost :
<VirtualHost 192.168.1.1:80> l’ip du web-a
ServerName monappli.blog.guiguiabloc.fr
<Proxy balancer://lbguigui> Un nom unique pour identifier le loadbalancer
BalancerMember http://l’adresse ip du tomcat-a:60001 route=guiguiabloc-a retry=1
connecttimeout=2000 timeout=30 loadfactor=100
BalancerMember http://l’adresse ip du tomcat-b:60001 route=guiguiabloc-b retry=1
connecttimeout=2000 timeout=30 loadfactor=100
</Proxy>
ProxySet balancer://lbguigui stickysession=JSESSIONID|jsessionid nofailover=on
maxattempts=1
Pour des applis PHP, remplacer « JSESSIONID » par « PHPSESSIONID » (si vous gérez les sessions)
On positionne le « nofailover=on » car on ne fait pas de réplication de session
Si vous voulez plus d’informations, je vous invite à consulter le site d’Apache, surtout LA .
Le Proxy via le module Rewrite :
RewriteEngine On
RewriteRule ^(.*)$ $1 [E=request-uri:$1,C]
RewriteRule ^(/.*)$ balancer://lbguigui$1 [P,L]
RewriteRule ^(.*)$ / [R=permanent,L]
PS: Je vous conseille fortement de vous définir votre propre format de log afin de tracer au mieux vos sessions.
Exemple de création de format de Log et de modification des en-têtes :
LogFormat "%h %l %u %t \"%r\" %>s %B \" %{Referer}i\" \"%{User-Agent}i\" I=%I O=%O %{X-Guiguiabloc-Diag}o h=web-a r=%{BALANCER_SESSION_ROUTE}e w=%{BALANCER_WORKER_NAME}e D D=%D C=%X s=%{REDIRECT_STATUS}e XFF=\"%{X-Forwarded-For}i\"" GuiguiablocLogFormat CustomLog logs/monappli.blog.guiguiabloc.fr/access.log GuiguiablocLogFormat ErrorLog logs/monappli.blog.guiguiabloc.fr/error.log Header set X-Guiguiabloc-Diag "%t %D" Header set X-Guiguiabloc-Route "h=web-a r=%{BALANCER_SESSION_ROUTE}e w=%{BALANCER_WORKER_NAME}e" Header set X-Guiguiabloc-URI "u=%{request-uri}e" Header append Vary User-Agent RequestHeader set Host monappli.blog.guiguiabloc.fr ProxyPreserveHost On Header set Server "MA FERME H-A" Header set Cache-Control no-cache="Set-Cookie,Set-Cookie2" env=cacheable-maxage Header append Cache-Control private env=cacheable-private Header append Cache-Control max-age=%{cacheable-maxage}e env=cacheable-maxage Header set Cache-Control no-store env=!cacheable-maxage Header append Cache-Control no-cache env=!cacheable-maxage Header unset Set-Cookie env=cacheable-maxage Header unset Set-Cookie2 env=cacheable-maxage Header unset ETag
Lire cette partie chez Apache.
- Configuration de la VIP et du load-balancer
J’avais déjà expliquer dans un billet précédent la configuration d’une VIP avec CARP sous OpenBSD, je ne reviendrais donc pas dessus.
La configuration du Load Balancing avec PacketFilter est on ne peut plus simple (comme toujours avec ce bijou 🙂 ) :
webA = "192.168.1.1" webB = "192.168.1.2" rdr on $ext from any to $VIP port 80 -> {$webA $webB}
table <serveursWeb> persist rdr on $ext from any $VIP port 80 -> <serveursWeb>
Ne reste qu’a ajouter nos serveurs Apache dans la table « serveursWeb » :
pfctl -t serveursWeb -T a 192.168.1.1
pfctl -t serveurWeb -T a 192.168.1.2
Afin de vérifier l’état de nos serveurs Web, nous allons utiliser « Hoststated« . (Sa mise en oeuvre est extrèmement simpliste).
Une petite ancre bien placée dans notre pf.conf :
rdr-anchor "hoststated/*"
Puis on renseigne notre fichier /etc/hoststated.conf :
webA="192.168.1.1" webB="192.168.1.2" interval 5 # vérifiation toutes les 5 secondes table serveursWeb { check http "/test.jsp" code 200 timeout 300 real port 80 host $webA host $webB } service www { virtual ip $VIP port 80 table serveursWeb }
Vous pouvez vérifier l’état de vos serveurs Apache en temps réel :
# hoststatectl show Type Id Name Status service 0 www active table 0 serveursWeb active (2 hosts up) host 1 192.168.1.1 down host 0 192.168.1.2 up
Magique non ?
Le schéma du début avec les IP utilisées pour vous aider à comprendre l’architecture :
J’ai bien évidemment survoler toutes les possibilités que vous offre ce type d’architecture. Je laisse votre esprit désormais en ébullition pour peaufiner le travail et vous plonger dans les MAN et fichiers de configuration des différents outils utilisés ici afin de l’adapter à vos besoins.
Bonjour,
je tenais à te féliciter concernant ce site très riche en informations, et tes tutos très bien expliqués.
Concernant ce tuto, sur le loadbalancer tomcat, je voulais savoir, pourquoi tu avais choisi le mod_proxy et non le mod_jk ?? soucis de performance ??
et surtout bonne continuation
Merci d’avance
Merci Kidrek 🙂
Je préfère utiliser les connecteurs http qu’AJP pour des soucis de perf effectivement et de l’affinité de sessions.
De plus, le load-balancing AJP ne me semble pas au top (à confirmer)
Bref, je préfère une chaîne http de bout en bout qui au niveau des switchs applicatifs (altéons ou OpenBSD (voir tuto plus récent) est beaucoup plus simple à contrôler.