Loadbalancer, die keine sind

Viele Web- und Proxyserver bringen eine Loadbalancerfunktion mit. Mit Session-Stickyness. „Hach wie praktisch“, könnte man denken. Leider funktioniert das nicht immer so wie es im Handbuch steht.

Ein Loadbalancer (Lastverteiler) ist dazu da, viele Anfragen auf mehrere Server zu verteilen, um den Durchsatz und die Ausfallsicherheit zu erhöhen. Knifflig wird es, wenn auf einem Server benutzerspezifische Informationen gespeichert werden, zum Beispiel ein Warenkorb – dann muss der Loadbalancer nämlich alle Anfragen dieses Benutzers immer auf den gleichen Server weiterleiten, auf dem der Warenkorb gespeichert ist, damit der erhalten bleibt.
Bei einem bekannten Onlineshop hatten wir kürzlich das Problem, dass bei einigen Bestellungen plötzlich der Warenkorb leer ist, sobald der Benutzer versucht, sich einzuloggen. Beim Login wechselt die Seite auf eine SSL-Verbindung – kann also gut sein, dass der Loadbalancer die Verbindung auf einen anderen Server leitet und die Session mit dem Warenkorb verloren geht. Ich probiere das gleich aus.

„Bei mir lokal läufts.“

Klick auf Login. Der Browser wechselt auf https. Es sind noch alle Waren im Korb. Auch bei Tests von anderen Rechnern aus. Das Problem lässt sich sehr schwer nachstellen. Offenbar tritt das Phänomen nur zu gewissen Uhrzeiten und meistens bei Benutzern aus Singapur auf.

Wie sieht denn die Konfiguration aus? Vor zwei Tomcat Hybris Nodes steht ein Nginx, der sich um die Terminierung der SSL-Verbindungen und um die Lastverteilung kümmert:

Wenn jetzt also ein Benutzer Artikel zum Warenkorb hinzufügt, werden die in einer Session gespeichert, z.B. auf Node 1. Falls der Loadbalancer die Verbindung bei einem Wechsel nach SSL auf den anderen Node leitet, ist die Session dort nicht vorhanden und wird neu erzeugt, der Warenkorb ist leer.

Nginx ist so konfiguriert, dass eine Benutzer-IP immer auf dem gleichen Server landet.
Was passiert, wenn sich die IP-Adresse eines Benutzers ändert? Das ist sehr unwahrscheinlich, und wir glauben auch nicht so recht dran. Aber Singapur hat Proxies und Zensurinfrastrukturen, und so entschliessen wir uns, auf ein Cookie-basiertes Loadbalancing umzustellen.

Gar nicht mal so einfach. Nginx bringt das von Haus aus nicht mit. Man muss ein externes Modul mit einkompilieren.

Gesagt, getan. Die Cookie-basierte Stickyness ist installiert. Und was macht Nginx?
Ich teste.
Jetzt kann ich das Problem auch bei mir Nachstellen; der Warenkorb wird ab und zu geleert! Das funktioniert überhaupt nicht. Ausserdem erzeugt Nginx jetzt pro URL-Pfad ein Cookie. Tolle Session-Stickyness :-(

So langsam gehen uns die Ideen aus. Wir haben auf den Hybris Nodes ein erweitertes Logging eingerichtet, welches die Sessions überwacht. Kein Fehler von dieser Seite. Der Hoster weiss auch nicht weiter. Es wird langsam eng. Was haben wir noch für Möglichkeiten?

  • Mit dem Memcached Session Manager die JSESSION-IDs zentral speichern
  • Varnish kann auch Session-Stickyness
  • HAProxy ausprobieren

Ich teste den Memcached Session Manager bei mir und stelle das Szenario nach. Es funktioniert nicht immer; das ganze dünkt mich sehr wackelig.

Noch zwei Varianten.

Varnish? Varnish. Der kann zwar keine SSL-Verbindungen terminieren, aber wenn ich den zwischen Nginx und die Hybris-Nodes stelle, dann macht Nginx weiterhin SSL, und Varnish kümmert sich um die korrekte Verteilung der Sessions.

Eigentlich wäre es ganz einfach, sagt das Handbuch:

set client.identity = req.http.cookie;

Von wegen, nix is! Bunt durchmischt landen die Anfragen mal auf Node 1, mal auf Node 2. Das scheint überhaupt nicht zu funktionieren. Ich bin enttäuscht. Vermutlich generiert Varnish eine Prüfsumme auf den Cookie-Header, und wenn unsere Anal-ytics-Software auf dem Browser ihre Kekse schmeisst, wirbelt es diese Prüfsummen durcheinander. Grossartig.

Gopferdammi, das kann doch nicht so schwer sein?! Langsam zweifle ich an meinen Fähigkeiten. Ich komme mir langsam richtig dumm vor. Sessionbasiertes Loadbalancing; andere machen das doch auch, warum funktioniert das bei uns nicht? Ich google bis zur fünfundreissigsten Suchresultatsseite. Loadbalancing Nginx. Loadbalancing Varnish. Webforen, Mailinglisten. Alle behaupten steif und fest, dass das mit client.identity funktioniert. Nein, liebes Internet, das tut es nicht.

Noch eine Variante.

HAProxy. Ich habe bislang nur davon gelesen, „dass es das gibt“. Kann ja mal einen Versuch wagen.

Und ich wurde positiv überrascht. Er ist schnell installiert, die Dokumentation ist allererste Sahne, und eine Zugfahrt später habe ich eine Konfiguration ausprobiert und stelle nach intensiven Tests fest: Die Warenkörbe bleiben erhalten!

HAProxy ist zwar „nur“ ein Loadbalancer – aber DAS macht er richtig gut! Der bietet nicht nur Cookie-basiertes Loadbalancing an; ich kann gleich zwischen verschiedenen Cookie-Strategien auswählen. Will ich intern eine Tabelle mit allen JSESSION-IDs führen? Soll ein Loadbalancer-Cookie an den Client geschickt werden, damit er erkennt, auf welchem Node der Client seine Session hat? Gewichtetes Loadbalancing, ausgefeilte Failover-Strategien, alles kein Problem. Eine Statusseite ist auch mit dabei. Die HTTP-Header werden normalisiert, damit sie einheitlich daherkommen. Ein sehr durchdachtes Stück Software.

Die neue Konfiguration sieht wie folgt aus:

Nginx nimmt wie gehabt alle Anfragen inkl. SSL-Verbindungen entgegen. Leitet aber nur noch an den HAProxy weiter. Der leitet dann die Verbindungen korrekt an die Nodes weiter und sorgt dafür, dass die Warenkörbe voll bleiben. Voilà: Die Kunden aus Singapur können bestellen.

Merke:

  • Use the right tool for the job. Es gibt Werkzeuge, die sind genau für einen Zweck geschaffen. Und den erfüllen sie richtig gut. Nginx für SSL-Terminierung. Varnish für Caching. Und HAProxy für Loadbalancing.
  • Mitgelieferte Beilagen und Nebenfunktionen funktionieren nicht immer. Vorallem nicht, wenn man sie als Hauptfunktion einsetzen will. Das wird auf Dauer zu unflexibel.
  • Vertrau‘ keinem Handbuch. Ja, dort drin steht, das müsste so funktionieren. TUT ES ABER NICHT.

9 Gedanken zu “Loadbalancer, die keine sind

  1. Der gute Apache2 kanns auch (und zwar nicht nur auf dem Papier sondern live) und egal ob Websever oder Hybris dahinter hängt:

    ProxyPreserveHost On
    # Nicht für die ganze Welt Proxy spielen!
    ProxyRequests Off

    # Header mit Server ID setzen:
    Header add Set-Cookie „ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/“ env=BALANCER_ROUTE_CHANGED
    # Proxy Blanacer einfuehren:

    BalancerMember http://webserver01.dmz.local:80 route=1 loadfactor=50
    BalancerMember http://webserver02.dmz.local:80 route=2 loadfactor=50
    ProxySet stickysession=ROUTEID
    Order deny,allow
    Allow from all

    # Proxy balancer aktivieren:
    ProxyPass / balancer://webserver/

    # Reverse Proxy aufloesung aktivieren:
    ProxyPassReverse / http://webserver01.dmz.local:80/
    ProxyPassReverse / http://webserver02.dmz.local:80/
    ProxyPassReverse / balancer://webserver/

    # Management Interface aktivieren:

    SetHandler balancer-manager
    Order deny,allow
    Deny from all
    Allow from 192.168.x.x

  2. Hast Du’s getestet? :)
    Ich trau dem nicht. Ausserdem skaliert der Apache nicht so toll. HAProxy ist schlank, schnell und darauf zugeschnitten.

    @Windows Network Load Balancing:
    Nein. Das Betriebssystem ist gesetzt (Linux)…

    • Der Apache Proxy ist getestet und Live.
      Wenn man eh Apache als Webserver einsetzt kann man das mit dem Apache Loadbalancer Proxy schon machen. Ist Sinnvoll wenn man beim Kunden das Knowhow für Apache eh schon hat.

      • Nee, der Webserver ist in diesem Fall Nginx, der als SSL-Terminator und ursprünglich als Loadbalancer fungiert hat. Neu macht ja jetzt der HAProxy das Loadbalancing, aus den erwähnten Gründen…
        Apache ist nicht installiert.

  3. Hallo Claude,

    hast Du zwischenzeitlich immer noch diese kombinierte Lösung im Einsatz, oder hast Du auch mal die aktuelle ‚session persistence‘-Methode von Nginx probiert?

  4. Pingback: Loadbalancer, die keine sind – Namics Weblog

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>