Loadbalancing mit Apache2 + Tomcat 6 + mod_proxy

In dem Blogeintrag Loadbalancing mit Apache2 + Tomcat 6 + mod_jk 1.2.X habe ich beschrieben wie ein Loadbalancing mit dem “mod_jk” Module eingerichtet werden kann. Dieser Blogeintrag verfolgt das gleich Ziel, jedoch nicht mit dem “mod_jk” sondern mit dem “mod_proxy” Modul.

Im gegensatz zum “mod_jk” Modul muss das “mod_proxy” Modul nicht von einer externen Quelle heruntergeladen werden, es ist beim apache2 Webserver dabei. Unter Debian muss gegebenenfalls noch folgendes Kommando angesetzt werden.

apt-get install apache-modules

Ein Loadbalancing macht nur Sinn, wenn mindestens zwei Tomcats die gleiche Applikation ausliefern. Der Blogeintrag N Tomcats auf einer Maschine beschreibt wie zwei Tomcats zu konfigurieren sind damit sie parallel auf der selben Maschine laufen.

Gehen wir davon aus das wir eine Webapplikation namens “geo” haben die auf den zwei Tomcats “tomcat1” und “tomcat2” laufen. die “server.xml” von tomcat1 hat folgenden Eintrag:

<Engine name="Catalina"    defaultHost="localhost"    jvmRoute="tomcat1"   >

und die von tomcat2 dementsprechend:

 <Engine name="Catalina"    defaultHost="localhost"    jvmRoute="tomcat2"   >
 

Ganz wichtig hierbei ist das Attribut “jvmRoute”, in dem der Name des jeweiligen Tomcats gesetzt ist. Auf diesen Namen wird aus dem apache2 Webserver heraus referenziert.

In der Konfigurationsdatei des apache2, meisstens “/etc/apache2/httpd.conf” oder “/etc/apache2/apache.conf”, muss das “mod_proxy” Modul geladen werden. Das kann so aussehen:

LoadModule proxy_module libexec/apache2/mod_proxy.so
LoadModule proxy_connect_module libexec/apache2/mod_proxy_connect.so
LoadModule proxy_ftp_module libexec/apache2/mod_proxy_ftp.so
LoadModule proxy_http_module libexec/apache2/mod_proxy_http.so
LoadModule proxy_ajp_module libexec/apache2/mod_proxy_ajp.so
LoadModule proxy_balancer_module libexec/apache2/mod_proxy_balancer.so

Hier wird ein wenig mehr geladen als nur das reine “mod_proxy” Modul. Die Pfade muessen natuerlich an das entsprechende Betriebssystem angepasst sein.

Nach dem erfolgreichen laden des Moduls kann der Loadbalancer konfiguriert werden.

ProxyPass /geo balancer://apacheBalance stickysession=JSESSIONID

<Proxy balancer://apacheBalance>
 BalancerMember ajp://localhost:8009/geo route=tomcat1 loadfactor=50
 BalancerMember ajp://localhost:8010/geo route=tomcat2 loadfactor=50
 </Proxy>

In der ersten Zeile wird der Webserver angewiesen alle Anfragen mit der URI “geo” an den balancer mit dem Namen “apacheBalance” zu delegieren, der im Abschnitt weiter unten definiert ist. Der Balancer kann beliebig viele “BalancerMember” haben und unterstuetzt nicht nur das ajp Protokoll sondern auch http. Das Attribut “route” im BalancerMember referenziert auf den “jvmRoute” des Tomcats, die wir weiter oben eingestellt haben.

Der “loadfactor” muss ein Wert zwischen 0 und 100 sein und legt die Lastverteilung fest. Wenn beide balancer einen Wert von 50 haben, dann wird die Last gleich verteilt. Wuerde der “tomcat1” einen loadfactor von 10 haben und der andere einen loadfactor von 90, dann wuerde “tomcat2” neunmal mehr Last bekommen als “tomcat1”.
Die Applikation ist nun ueber die URL “http://localhost/geo&#8221; erreichbar.

Bei der ersten Anfrage leitet der apache2 auf eines der BalancerMember weiter, z.B. tomcat2. Alle weiteren Anfragen von dem gleichen Client werden ebenfalls an den tomcat2 weitergeleitet, sofern eine HttpSession benutzt wird. Um sich zu merken welche Clients zu welchen Tomcat weitegeleitet werden sollen, legt apache2 eine Cookie beim Cleint an. Dieser Cookie besteht aus der SessionId und dem Namen des jeweiligen Tomcats.

Bei allen weiteren Anfragen wird dieser Cookie mitgeschickt und so weis der apache2 immer genau an welchen Tomcat er die Anfrage weiterleiten soll. Dieses Verhalten macht natuerlich nur Sinn wenn die Webapplikation Kundendaten in einer HttpSession vorhaelt. Wenn das nicht der Fall ist, ist es vollkommen egal an welches der beiden Tomcats die nachfolgenden Anfragen weitergeleitet werden.
Und genau hier liegt der Vorteil vom “mod_proxy” Modul gegenueber dem “mod_jk” Modul. Mod_proxy haengt im Cookie nur dann den Namen des Tomcats an die SessionId wenn die HttpSession nicht leer ist.

Ein weiterer Vorteil des Moduls ist der balancer-manager, eine kleine Webanwendung die den aktuellen Status des Balancers zeigt. Um die Applikation einzubinden muss folgender Eintrag in die apache2 Konfigurationsdatei hinzugefuegt werden.

<Location /balancer-manager>
          SetHandler balancer-manager
</Location>

Nach der Aenderung muss der apache2 natuerlich neu gestartet werden und dannach ist die Anwendung uber die URL “http://localhost/balancer-manager&#8221; erreichbar.

Ueber den Balancer-Manager kann auch zur Laufzeit der loadfactor veraendert werden. Soll einer der beiden Tomcats (tomcat2) aus bestimmten Gruenden vom Netz genommen werden, sollte dieser einen loadfactor von 1 zugewiesen bekommen und der andere einen loadfactor von 99. Damit ist sichergestellt das alle weietern Requests an den Tomcat mit dem loadfactor 99 weitergeleitet werden. Nach einer gewissen Zeit sind auf dem tomcat2 keine HttpSessions mehr offen und er kann problemlos vom Netz genommen werden.

Um den unbefugten Zugriff auf den balancer-manager zu verhindern sollte er durch ein Passwort geschuetzt werden.

<Location /balancer-manager>
          SetHandler balancer-manager

          AuthType Basic
          AuthName "balancer manager "
          AuthUserFile /etc/apache2/htpassbalancer
          Require valid-user
</Location>

Die Passwortdatei kann mit folgendem Kommando angelegt werdne.

htpasswd -c /etc/apache2/htpassbalancer username

Loadbalancing mit Apache2 + Tomcat 6 + mod_jk

Die Nachfolgende Beschreibung zeigt wie ein Apache2 Webserver als Loadbalancer fuer zwei oder mehr Tomcats zu konfigurieren ist. Die Beschreibung bezieht sich wieder auf einen Linux Debian 4.1.X Server mit 2.6.X Kernel, auf dem ein apache2 Webserver, Tomcat 6.0.16 und Java 5.0 installiert und betriebsbereit ist.

Die folgende Graphik veranschaulicht die Systemarchitektur die aufgebaut werden soll.

34_balancer2
34_balancer2

Wie der Apache2 Webserver mit dem Tomcat ueber das mod_jk Modul verbunden wird, habe ich bereits in einem vorhergehenden Blogeintrag beschrieben. Darauf werde ich hier nicht mehr eingehen.

apache2 + tomcat 6 + mod_jk

Ein Loadbalancer macht nur Sinn wenn mehr als ein Tomcat die gleiche Applikation ausliefert. Der Loadbalancer kann die Last auch zwischen 2 Tomcats verteilen die auf unterschiedlichen Maschinen laufen. Wie zwei oder mehr Tomcats auf der selben Maschine eingerichtet werden, zeigt der folgende Blogeintrag:

N Tomcats auf einer Maschine

Wenn Sie die zwei oberen Blogeintraege gelesen und verstanden haben, dann ist es nur noch ein kleiner Schritt zum Loadbalancer. Als erstes muss die /etc/apache2/workers.properties angepasst werden. Hier ist ein Bespiel:

# Tomcat an Java configuration
#

workers.tomcat_home=/opt/tomcat/
workers.java_home=/opt/java/
ps=/

worker.list=loadbalancer

worker.mainworker.type=ajp13
worker.mainworker.port=8009
worker.mainworker.cachesize=20
worker.mainworker.lbfactor=1
worker.mainworker.host=localhost

worker.tomcat2.type=ajp13
worker.tomcat2.port=8010
worker.tomcat2.cachesize=20
worker.tomcat2.lbfactor=1
worker.tomcat2.host=localhost

worker.loadbalancer.type=lb
worker.loadbalancer.balanced_workers=mainworker,tomcat2
worker.loadbalancer.method=Session

Die worker.list verweist auf den worker “loadbalancer”, welcher vom Typ “lb” (loadbalancing) ist und auf die zwei worker “mainworker” und “tomcat2” verweist. Der lbfactor gibt an wieviel Last ein worker uebernehmen soll. Wenn der mainworker einen lbfactor von 5 haette und der “tomcat2” nur einen von 1, dann wuerde der “mainworker” fuenf mal soviele Anfragen zugewiesen bekommen wie der “tomcat2”.
Eine vollstaendige Dokumentation des workers ist hier zu finden:

http://tomcat.apache.org/connectors-doc/reference/workers.html

Die worker Namen “mainworker” und “tomcat2” muessen natuerlich in den jeweiligen <tomcat>/conf/server.xml Dateien zu finden sein. Der eine Tomcat muss dafuer folgende Zeile in seiner server.xml enthalten:

<Engine name="Catalina"   defaultHost="localhost"   jvmRoute="tomcat2"  >

und der andere dementsprechend die Zeile:

<Engine name="Catalina"   defaultHost="localhost"   jvmRoute="mainworker"  >

Zum Schluss muss noch in der Datei /etc/apache2/sites-enabled/000-default der JkMount-Eintrag auf die gewuenschte Applikation angepasst werden:

JkMount /testApp loadbalancer

Wenn alles korrekt konfiguriert ist kann die Applikation durch folgende URL augerufen werden:

http://localhost/testApp/

Der Apache Webserver wird die Anfrage an eines der zwei Tomcats weiterleiten. Wenn der eine Tomcat runtergefahren wird, ist die Applikation immer noch auf dem zweiten Tomcat verfuegbar. Der Webserver leitet dann alle Anfragen automatisch an den einzig noch verbliebenen Tomcat, so das der Benutzer davon nichts mitkriegt.
Auf diese Art und Weise koennen Applikation ausfallsicherer gemacht werden.

Apache2 + Tomcat 6.0.x + mod_jk 1.2.x

Nachfolgend wird hier beschrieben wie man den apache2 Webserver und den Tomcat 6.0.16 Servletcontainer mit dem mod_jk 1.2.26 verbinden kann. Die Beschreibung bezieht sich auf einen Linux Debian 4.1.X Server mit 2.6.X Kernel, auf dem ein apache2 Webserver, Tomcat 6.0.16 und Java 5.0 installiert und betriebsbereit ist.

Der Tomcat liefert standardmaessig seine Applikationen unter dem Port 8080 aus. Diese Eigenschaft hat Nachteile, weil in vielen Firmennetzen der Port 8080 durch eine Firewall geblockt wird. Der Standard HTTP Port 80 dagegen ist in fast allen Netzen offen.
Der Tomcat laesst sich natuerlich auch so konfigurieren das er auf dem Port 80 horcht. Wenn auf dem gleichen Server aber schon ein Webserver installiert ist, kommt es schnell zu Konflikten. Die eleganteste Loesung besteht darin, den Webserver so zu konfigurieren, dass er bestimmte Domainanfragen an den Tomcat weiterleitet und die Antwort vom Tomcat an den Client weiterschickt.

Um den Apache2 Webserver und den Tomcat Sevletcontainer miteinander zu verheiraten muss man das mod_jk 1.2.26 installieren. Die Entwicklung des mod_jk 2.0 wurde wegen zu komplexer Konfiguration eingestellt, statt dessen wurde die alte Version komplett ueberarbeitet. Die aktuelle Version ist NICHT die 2.0 Serie sondern die mod_jk 1.2.26, welche unter folgender URL zum download bereit steht:

http://tomcat.apache.org/download-connectors.cgi

Da es fuer Debian zu Zeit keine mod_jk binary gibt, muss man sich die Source runter laden und compilieren. Nach dem Download sollte man mit folgenden Befehlen das Packet entpacken und in das native Verzeichnis wechseln.

debian:~# tar -xzvf tomcat-connectors-1.2.26-src.tar.gz
debian:~# chmod -R 755 tomcat-connectors-1.2.26-src
debian:~# cd tomcat-connectors-1.2.26-src/native

Anschliessend muessen fuer die Konfiguration, Kompilierung und Installation folgende drei Befehle ausgefuehrt werden.

debian:~# ./configure --with-apxs=/usr/bin/apxs2
debian:~# make
debian:~# make install

Wenn bei den letzten drei Befehlen Fehler auftreten dann liegt das vielleicht daran das Entwickler-Tools auf dem Server fehlen. Folgender Befehl kann Abhilfe schaffen:

debian:~# apt-get install libtool autoconf gcc apache2-prefork-dev g++

Nach der erfolgreichen Installation des mod_jk Modules muss als naechstes der apache2 Webserver konfiguriert werden. Als erstes muss eine workers.properties Datei erstellt werden.

debian:~# emacs /etc/apache2/workers.properties

Meine workers.properties Datei hat folgenden Inhalt

# Tomcat an Java configuration
#
workers.tomcat_home=/opt/tomcat/
workers.java_home=/opt/java/
ps=/worker.list=mainworker

# Definition for local worker using AJP 1.3
#
worker.mainworker.type=ajp13
worker.mainworker.port=8009
worker.mainworker.cachesize=20

Hier wird der aktuelle Pfad zu tomcat und zu java festgelegt und eine worker Liste beschrieben. In den letzten drei Zeilen wird der mainworker konfiguriert. Der Port 8009 ist ein Standardport fuer den ajp13-Connector von Tomcat. Dieser Wert ist nach dem entpacken von Tomcat standardmaessig in der <tomcat>conf/server.xml eingetragen. Wenn hier in der workers.properties ein anderer Wert eingetragen wird, dann muss die Portnummer in der server.xml vom Tomcat angepasst werden.
Der Name “mainworker” muss in die server.xml noch eingetragen werden.

<Engine name="Catalina"  defaultHost="localhost"  jvmRoute="mainworker"  >

Die eben gezeigte Zeile ist in der server.xml schon vorhanden. Sie muss lediglich um das Attribut jmvRoute=”mainworker” ergaenzt werden. Als naechstes oeffnen wir mit folgendem Befehl die apache2 Konfigurationsdatei:

debian:~# emacs /etc/apache2/apache2.conf

An letzter Stelle muss folgende Zeile hinzugefuegt werden

Include /etc/apache2/sites-enabled/

In dem Verzeichnis /etc/apache2/sites-enables/ befindet sich eine Datei mit dem Namen “000-default”. Die Datei ist in wirklichkeit ein Symlink auf “/etc/apache2/sites-available/default”, aber das soll uns nicht weiter stoeren. Die Datei kann mit jedem beliebigen Texteditor geoeffnet werden und muss um die folgenden Zeilen an erster Stelle ergaenzt werden.

<IfModule !mod_jk.c>
LoadModule jk_module "/usr/lib/apache2/modules/mod_jk.so"
</IfModule>

JkWorkersFile "/etc/apache2/workers.properties"
JkLogFile "/opt/tomcat/logs/mod_jk.log"
JkLogLevel info

In der gleichen Datei befindet sich ein Abschnitt mit VirtualHost, der um die JkMount Eintraege ergaenzt werden muss.
NameVirtualHost *

<VirtualHost *>
ServerAdmin webmaster@localhost
ServerAlias ploin.de www.ploin.de

DocumentRoot /var/www/
..
..
..
..

JkMount /robertReiz mainworker
JkMount /robertReiz/* mainworker

</VirtualHost>

Die letzten zwei Zeilen im VirtualHost Abschnitt teilen den Webserver mit das er alle Anfragen die mit robertReiz beginnen an den mainworker weiter leiten soll.

Nun muessen nur noch der Tomcat und der apche2 Webserver neu gestartet werden. Damit ist die Konfiguration abgeschlossen.