E-Commerce unter Last

Magento in der Cloud
Kommentare

Magento ist eine großartige Plattform. Die zahlreichen Features lassen sich leicht ergänzen und auf den eigenen Business Case anpassen. Will man Magento im Enterprise-Umfeld betreiben, kommen schnell neue Anforderungen an Prozesse, Stabilität und Performance ins Spiel. Um einen Shop zu betreiben, der problemlos in der Lage ist, mehr als eine Millionen Besucher pro Tag, 5000 davon zur gleichen Zeit und bis zu zehn Bestellungen pro Sekunde zu verarbeiten, muss man über den Tellerrand einzelner Server hinweg schauen und einiges optimieren. Mit dem Angry-Birds-Shop, einem der derzeit Traffic-stärksten Magento Stores, wurde bewiesen, dass dies möglich ist. Was hinter den Kulissen stattfindet, wird in diesem Artikel gezeigt.

Die Anforderungen an einen professionellen Shop unterscheiden sich deutlich von einem kleineren Shop. Mit manuellen Anpassungen am Magento-Core und dem Kopieren einzelner Dateien kommt man nicht weit. Auch das synchrone Deployen neuer Features nimmt andere Dimensionen an, wenn mehr als ein Server im Spiel ist. Dazu muss der Shop hochverfügbar und ausfallsicher sein. Auch spontane, unvorhergesehene Lastspitzen dürfen den Shop nicht in die Knie zwingen. Und dass die blitzschnelle Auslieferung von Seiten nicht nur für die Suchmaschinen gut ist, sondern sich auch positiv auf die Verkaufszahlen auswirkt, ist schon lange kein Geheimnis mehr.

Beim Betreiben eines Shops auf mehreren Servern kommen noch weitere Wünsche und Anforderungen hinzu: Die Server für den Backend-Zugang sollten unabhängig von den Frontend-Instanzen sein. Auch Hintergrund-Tasks und die Kommunikation mit externen Services sollten weder das Arbeiten im Backend noch die Auslieferung des Frontends beeinträchtigen.

Während eines Deployments und beim laufenden Betrieb sollte der Besucher nichts von neu hinzukommenden oder wegfallenden Serverinstanzen mitbekommen. Warenkörbe und Logins dürfen nicht verloren gehen und der Checkout-Prozess darf nicht unterbrochen werden.

Herausforderungen

All diese Anforderungen sind eine Herausforderung beim Konzipieren und Implementieren des Shops und des Deployment-Vorgangs. Wäre ein Magento-Server eine „Share Nothing“-Architektur [1], also vollkommen unabhängig von anderen Knoten, dann wären das Skalieren und das Deployment relativ einfach. Allerdings gibt es viele gemeinsame Ressourcen, die unter den Knoten geteilt oder synchronisiert werden müssen. Darunter die Datenbank, der Cache, die Sessions, die Produktbilder und Assets für das Theming, wobei die gebündelten, minifizierten und komprimierten JavaScript- und CSS-Dateien hier nochmal eine besondere Herausforderung darstellen.

Lösungsbausteine

Der Angry-Birds-Webshop [2] ist in der Amazon Cloud gehostet. Für die Verwaltung von Deployments, EC2-Serverinstanzen und andere Amazon-Services wird RightScale verwendet. Neben RightScripts kann auch Chef und Puppet verwendet werden, um die Serverinstanzen je nach Einsatzzweck zu provisionieren. Das zentrale Monitoring und Logging, eine flexible Verwaltung von allen relevanten Parametern, ein ausgereiftes Webinterface mit vielen Funktionen sowie das RightScale-API ermöglichen dabei ein sauberes und effizientes Cloud-Setup und die automatisierte Durchführung von Deployments. Daneben wird Varnish als Reverse Proxy eingesetzt, um „cachebare“ Inhalte effizient auszuliefern und die Last von den Webservern zu nehmen. Abbildung 1 zeigt Amazon-AWS und RightScale als Bausteine für das Cloud Hosting.

Abb. 1: Amazon-AWS und RightScale als Bausteine für das Cloud Hosting

Serverinfrastruktur

Beim Hosting in der Cloud geht es nun nicht mehr um einzelne Serverinstanzen, sondern um Gruppen von Servern, die eine bestimmte Funktion erfüllen. Die so genannten „Server-Arrays“ kümmern sich selbstständig darum, dass immer eine Mindestanzahl von Servern in der jeweiligen Gruppe vorhanden ist, und dass eine ausreichende Anzahl von Servern gebootet und in das Deployment eingebunden wird, um die aktuelle Last verarbeiten zu können.

Anhand eines Voting-Mechanismus, der beispielsweise von der CPU-Last oder auch von businessspezifischen Metriken, wie zum Beispiel Bestellungen pro Minute, abhängen kann, können weitere Server automatisch gebootet werden oder bestehende, nicht mehr benötigte Server heruntergefahren werden. Voraussetzung für das automatische Booten neuer Serverinstanzen ist, dass die Server vollautomatisch provisioniert werden können. Sobald der Server einsatzbereit ist, wird er automatisch von den Load Balancern berücksichtigt und bekommt von nun an Traffic ab.

Für den Betrieb eines Magento-Shops werden vier Server-Arrays benötigt: Die Server im Frontend-Array bedienen alle dynamische Requests der Besucher. Das Backend-Array ist für die Shopadministratoren und -redakteure da. Das Varnish-Server-Array cacht so viel wie möglich und verhindert, dass zu viele Requests beim Frontend-Server-Array auftreffen. Das Worker-Server-Array verarbeitet alle Magento-Crontasks, API-Requests und jede Kommunikation mit anderen externen Systemen.

Jede Komponente in der vorliegenden Architektur muss redundant ausgelegt sein, um die Ausfallsicherheit zu gewährleisten. Daher sind die Server-Arrays so konfiguriert, dass sie immer mindestens zwei Serverinstanzen vorhalten, auch wenn diese sich (wie im Beispiel der sehr effizienten und ressourcensparenden Varnish-Server) langweilen. Fällt ein Server aus, wird automatisch eine neue EC2-Instanz gebootet. Hier ist kein manuelles Eingreifen notwendig.

Die Server der Backend- und Frontend-Server-Arrays werden mithilfe des RightScale-API in der Varnish-Konfiguration als Backends hinterlegt und bei Änderungen automatisch aktualisiert. Die Varnish-Server sind bei einem Load Balancer registriert. Die vier Server-Arrays und der Load Balancer bilden zusammen das „Deployment“.

Als Datenbank wird Amazon RDS verwenden. Dieser Dienst wird als Service angeboten, der sich intern um die Verfügbarkeit und Ausfallsicherheit kümmert und zudem mit automatischen Backups, Snapshots und Read-Replicas viele wichtige Features eines Setups im großen Stil zur Verfügung stellt.

Als persistenter Speicher für Produkt-, Kategorie- und CMS-Bilder wird ein S3-Bucket verwendet. Auch die skalierten Versionen der Bilder werden in den S3-Bucket zurück geschrieben und müssen dadurch nicht mehrmals berechnet werden. Die Bilder werden direkt aus dem S3-Bucket über eine Cloudfront-Domain ausgeliefert. Dadurch ist kein gemeinsames Dateisystem notwendig, auf das alle Server zugreifen müssen.

Ein weiterer Cloudfront-Service, der hier ebenfalls als Reverse Proxy agiert und sich die Dateien über Varnish von den Frontend-Servern zieht, ist dafür zuständig, alle notwendigen Skin-Dateien auszuliefern. Die Anzahl der Zugriffe auf die Webserver wird damit auf die tatsächlichen dynamischen Requests minimiert. Abbildung 2 zeigt die Serverarchitektur im Überblick.

Abb. 2: Die Serverarchitektur im Überblick

A/B-Deployment im großen Stil

Deployments müssen weitestgehend automatisiert ablaufen und dürfen keine Risiken mit sich bringen. Ein neues Paket zu deployen, heißt nicht automatisch, dass neue Features zur Verfügung stehen. Idealerweise sind Features mithilfe von Feature-Flags zunächst deaktiviert und können dann unabhängig vom Deployment-Prozess gezielt ein- und ausgeschaltet werden. Sollte beim Deployment etwas schief gegangen sein oder sollten sich doch Fehler in die neue Version eingeschlichen haben, muss ein Rollback zur vorhergehenden Version möglich sein. Alle Deployment-Vorgänge müssen synchron und atomar passieren und dürfen nicht von DNS-Updates abhängig sein. Außerdem müssen alle Dateien, die gegebenenfalls gecacht worden sind, jetzt den richtigen Inhalt ausliefern oder unter neuen URLs erreichbar sein.

Während eines Deployments darf der laufende Betrieb nicht beeinträchtigt werden. Der Besucher der Webseite sollte nichts davon mitbekommen. Das bedeutet auch, dass alle Caches vorgewärmt werden müssen, bevor der tatsächlich Switch stattfinden. Um all diesen Anforderungen gerecht zu werden, machen wir ein A/B-Deployment im großen Stil. Anstatt das neue Paket auf den existierenden Servern zu installieren und dann einen Symlink umzubiegen, bauen wir ein komplettes Deployment mit neuen Server-Arrays aus. Alle Server, die in diesen neuen Deployments gebootet werden, installieren automatisch das in der Konfiguration hinterlegte neue Paket. Das neue Deployment erhält auch einen eigenen Load Balancer und einen Deployment-spezifischen Cache-Prefix, sodass das parallele Hochfahren einer separaten Infrastruktur keinen Einfluss auf die aktuelle Produktivumgebung hat.

Das Cachewarming für die Magento-internen Caches und die Varnish-Server kann stattfinden, obwohl das Deployment von außen noch nicht erreichbar ist. Die Hauptdomain shop.angrybirds.com zeigt nicht direkt auf den aktiven Load Balancer sondern auf den Amazon-Route-53-Service. Dieser erlaubt in einer atomaren Operation, ohne dass die DNS-Einstellungen der Domain selbst geändert werden müssen, das Deployment umzuschalten, indem man die Konfiguration auf den Load Balancer des neuen Deployments umbiegt, sobald alle Server hochgefahren sind und alle Caches vorgewärmt sind. Dieser Prozess dauert üblicherweise nur wenige Minuten. Ganz ohne Downtime fangen die neue Server-Arrays an, alle Requests zu bearbeiten sobald das Route-53-Konfigurationsupdate geschehen ist.

Sollten unerwartete Probleme auftreten, kann das alte Deployment wieder aktiviert werden indem der Route-53-Service upgedatet wird. Ansonsten können die Server des alten Deployments heruntergefahren und das Deployment archiviert werden. Abbildung 3 zeigt das A/B-Deployment mit ganzen Server-Arrays inklusive Load Balance.

Abb. 3: A/B-Deployment mit ganzen Server-Arrays inklusive Load Balance

Varnish verleiht dem Shop Flügel

Ein zentrales Element der Cloud-Infrastruktur ist der Open Source Reverse Proxy „Varnish“. Eine Analyse hat ergeben, dass bis zu 95 Prozent aller angeforderten Seiten weitestgehend cachebar sind. Logininformation und Angaben zum Warenkorbinhalt können dabei lokal im Browser zwischengespeichert werden und per JavaScript nach dem Laden der Seite eingebettet werden. Auf diese Art und Weise können sowohl alle Produkt- und Kategorieseiten als auch CMS-Seiten und die Startseite komplett von Varnish aus bedient werden. Alle dynamischen Requests (z. B. die Warenkorbaktionen, der Checkout-Prozess und das Kundenkonto) werden weiterhin zu den Frontend-Servern durchgereicht.

Besonders im Angry-Birds-Webshop, wo virale Twitter- und Facebook-Kampagnen in kürzester Zeit Millionen von Besuchern in den Shop locken, die sich dann die Produkte anschauen (und dann allerdings nur ein Bruchteil der Besucher Produkte kaufen) ist ein ausgereiftes Caching-Konzept besonders wichtig.

Das freie Magento-Modul Aoe_Static [3] kümmert sich hierbei um die Kommunikation zwischen Magento und Varnish und sendet die richtigen Anweisungen, um gezielte Inhalte zu cachen (Abb. 4) oder auch automatisch aus dem Cache zu entfernen. Letzteres ist zum Beispiel notwendig, wenn Produkte bearbeitet werden oder sich der Warenbestand ändert.

Abb. 4: Varnish schützt Magento vor der Masse an Requests und liefert gecachte Inhalte direkt aus

Magento-Caching

Auch Magento-seitig sind einige Optimierungen und Erweiterungen notwendig. Zunächst einmal muss ein geeignetes Cache-Konzept gefunden werden. Während jede Serverinstanz einen eigenen lokalen Cache verwenden könnte, ist es einerseits sehr viel effizienter und andererseits für einige Features auch notwendig, dass alle beteiligen Knoten auf den gleichen Cache-Inhalt lesend und schreibend zugreifen können. Damit ist man in der Auswahl der Cache-Backends eingeschränkt. Ein Two-Level-Setup kann die Cache-Performance erhöhen, bringt aber auch einige Probleme und erhöhte Komplexität mit sich [4]. Eine sehr gute Übersicht über die verschiedenen Alternativen ist unter den Präsentationen der Magento Imagine 2012 zu finden [5].

Beim Angry-Birds-Shop haben wir uns für das Datenbank-Cache-Backend entschieden. Auch hier sind einige Bugfixes notwendig [6] und durch die Auslagerung der Datenbank-Handles auf eine eigene weitere RDS-Instanz werden die Datenbankzugriffe nicht zum Bottleneck.

Eine interessante Alternative ist das Redis Cache-Backend [7], das direkten Tag-Support hat und daher als einziger Cache eingesetzt werden kann. Während diese Lösung sehr schnell und elegant ist, erfordert sie jedoch in unserem redundanten Cloud-Setup ein weiteres Server-Array und erhöht damit die Komplexität des Setups.

Magento-Optimierungen

Magento kommt mit einer Vielzahl an Features. In den meisten Shops kommt allerdings nur ein kleiner Bruchteil davon tatsächlich zum Einsatz. Trotzdem fressen diese Features meistens einen erheblichen Teil der Performance. Daher ist es empfehlenswert den Shop genau unter die Lupe zu nehmen und nicht benötigte Features zu entfernen. Dabei sollten alle Seiten nach Blöcken und Event-Observern untersucht werden, die eventuell keinen sichtbaren Effekt haben, aber trotzdem vorhanden sind.

Einige Features sind fest im Magento-Core integriert und können nur durch Observer und Rewrites entfernt werden. Ein gutes Beispiel hierbei ist das Logging. Ein erheblicher Teil der Rendering-Zeit wird üblicherweise für das Loggen unterschiedlichster Informationen verwendet. Das belastet nicht nur die Performance, sondern führt auch dazu, dass diverse Datenbanktabellen und Log-Dateien sehr schnell riesig werden können. Gerade wenn diese Informationen nicht für Reports oder andere Auswertungen benötigt werden, sollte darauf verzichtet werden.

Durch die sorgfältige Analyse und wiederholtes Profiling mit unterschiedlichen Tools (xdebug, New Relic, Aoe_Profiler [8]) kann Magento sehr viel leichtgewichtiger und auf die funktionalen Anforderungen des Shops zugeschnitten werden.

Betreibt man Magento unter Hochlast wird man schnell feststellen, dass sich große Datenmengen ansammeln. Einige Daten werden gar nicht benötigt (wie zuvor am Beispiel der Logs erwähnt), andere müssen regelmäßig aufgeräumt werden. So müssen beispielsweise abgelaufene Cache-Einträge [9] sowie nicht mehr benötigte Quotes [10] gelöscht werden. Auf Bewegungsdaten (wie zum Beispiel die „recently viewed products“) kann auch komplett verzichtet werden, wenn die entsprechenden Features nicht verwendet werden.

Bei vielen Backend-Operationen und anderen Requests wird der Cache automatisch aktualisiert. Je nach Füllgrad des Caches und verwendetem Cache-Backend können diese Operationen durchaus mehrere Sekunden dauern, was das Arbeiten im Backend nicht besonders angenehm gestaltet. Ist man nicht auf sekundengenaue Cache-Invalidierung angewiesen, kann das Modul Aoe_AsyncCache [11] eingesetzt werden, das alle Cache-Operation abfängt und asynchron im Hintergrund als Crontask verarbeitet. Das Arbeiten im Backend wird dadurch spürbar schneller. Einen visuellen Überblick und volle Kontrolle über alle Crontasks gibt das Modul Aoe_Scheduler [12].

Qualitätskontrolle

Will man den Anforderungen gerecht werden, muss schon in der Entwicklungsphase darauf geachtet werden, dass jede Zeile Code für das angestrebte Szenario geeignet ist. Dies gilt ganz besonders beim Einsatz von Third-Party-Modulen. Diese sind oft nicht mit den hohen Anforderungen an Geschwindigkeit, Masse und Parallelität im Hinterkopf entwickelt worden. Während diese Module auf einem einzelnen Server mit überschaubarem Traffic ohne Probleme laufen, besteht ein hohes Risiko, dass unter einem Hochlastszenario auf verteilten Servern nicht mehr alles glatt läuft. Fremde Module müssen daher sorgfältig ausgewählt und unter die Lupe genommen werden. Auf verschlüsselte Module sollte grundsätzlich verzichtet werden. Aber auch der Einsatz externer Systeme muss gut durchdacht werden. Sind diese nicht auf die gleiche Geschwindigkeit und Verfügbarkeit ausgerichtet, können sie den eigenen Shop ausbremsen oder wesentlich Bestandteile wie zum Beispiel den Checkout-Prozess außer Kraft setzen. Idealerweise werden externe System asynchron im Hintergrund angesprochen, sodass die Kommunikation mit diesen Systemen keinen negativen Einfluss auf die Besucher hat.

Jede Komponente muss kontinuierlich auf Last geprüft werden. Aussagekräftige Lasttests, die reales Besucherverhalten simulieren, sind hier nicht optional und müssen den Entwicklungsprozess von Anfang an begleiten. Idealerweise sind Lasttests mit vergleichbaren Ergebnissen Bestandteil einer Deployment Pipeline.

Continuous Integration und die Deployment Pipeline

Eine Deployment Pipeline ist von Anfang an wichtig. Im laufenden Betrieb kann es zu unvorhergesehenen, kritischen Regressionen kommen. Umso wichtiger ist es, dass man schnell ein Paket bereitstellen kann, dass den allgemeinen Qualitätsanforderungen gerecht wird.

Jenkins ist ein großartiges Tool, um eine Deployment Pipeline zu implementieren. Zunächst erfolgt der eigentliche Build-Prozess, der neben dem Code selbst alle anderen notwendigen Komponenten, wie zum Beispiel umgebungsspezifische Settings, Datenbankinhalte und Media-Dateien, die notwendig sind, um eine komplette Serverinstanz vollautomatisch zu bauen, in einem Paket vereint. Danach finden mehrere Build-Schritte statt, die helfen zu beurteilen, ob das aktuelle Paket den nötigen Anforderungen entspricht, um auf die Produktivumgebung deployt zu werden (Abb. 5). Dazu gehören automatisierte Unit Tests mit PHPUnit, Frontend-Tests mit Selenium2 und Lasttests mit JMeter. Ist das Paket durch all diese Schritte erfolgreich durchgekommen, wird es zunächst auf einem internen Abnahmesystem installiert. Das Paket wird dann in einen S3-Bucket kopiert und kann von dort aus im Rahmen eines neuen Deployments automatisch auf neue Serverinstanzen installiert werden. Das Staging-System ist identisch mit der Produktivumgebung. Dort kann dann noch eine manuelle Abnahme des neuen Pakets durch die interne QA und die des Shop-Betreibers stattfinden. Hat das Paket alle Qualitätsansprüche erfüllt, kann es auf das Produktivsystem deployt werden.Abb. 5: Qualifizierung für ein Production Deployment

Lessons learned

In jedem Projekt kann es zu unvorhergesehenen Problemen kommen. Besonders wenn mit einem komplexen Hosting-Setup und zahlreichen Services die Gesamtkomplexität steigt, ist es wichtig, immer den Überblick behalten und schnell reagieren zu können. Erprobte Deployment-Prozesse und eine umfangreiche Qualitätsprüfung sind daher nicht optional. Und dennoch kommt es zu Situation, die vorher nie da gewesen sind. Gelernt haben wir unter anderem, wie teuer 404-Seiten wirklich sein können. Wird zum Beispiel ein beliebtes Produkt, dessen Produktseite bisher immer von Varnish ausgeliefert worden ist, kurzfristig deaktiviert, treffen alle Requests auf Magento ein. Das automatische Hochskalieren führt dazu, dass in wenigen Minuten zahlreiche Server gebootet werden, um die neue Last abzufangen. 404-Seiten müssen also unbedingt auch gecacht werden – wenn auch nur für wenige Minuten.

Aber auch ganz reguläre Magento-Standard-Features können große Probleme bereiten. Versucht man einen Magento-Report im Backend aufzurufen, wird man die Datenbank extrem beanspruchen, was deutlich im Frontend zu spüren ist. Im Extremfall können in dem Zeitraum keine dynamische Requests bedient werden. Amazons RDS bietet die Möglichkeit, Read-Replica der Datenbank zu erstellen. Auf diesen Read-only-Kopien können dann die Reporting-Skripte ausgeführt werden.

Andere Magento-Features sind nicht auf riesige Produktkataloge oder eine große Anzahl an Orders oder Quotes ausgerichtet und verhindern zum Beispiel das Speichern von Produkten.

Und schließlich gibt es dann noch vermeintliche Selbstverständlichkeiten, wie zum Beispiel die Gzip-Komprimierung von JavaScript- und CSS-Dateien bei Cloudfront-Domains, die ihre Dateien von S3-Buckets beziehen. Spontan müssen hier andere Konzepte gefunden werden, um diese Dateien dennoch komprimiert ausliefern zu können [13].

In jedem Fall muss etwas mehr Zeit eingeplant werden, um Probleme dieser Art zu umgehen. Ein tiefgründiges Verständnis der Funktionsweisen von Magento, Varnish und den Amazon-Services ist hier zwingend erforderlich, um einen Shop erfolgreich implementieren und betreiben zu können.

Fazit

Magento ist ein sehr mächtiges und umfangreiches E-Commerce-Framework. Wenn es um Hochlastsysteme geht, kocht es aber auch nur mit Wasser, und es bedarf entsprechender, durchdachter Architekturkonzepte und Best Practices, um das Maximum an Leistung herauszuholen. Viele Bausteine der oben genannten Lösung stehen kostenfrei auf GitHub [13] zur Verfügung. Unter http://www.fabrizio-branca.de sind außerdem noch zahlreiche Blogposts zu finden, die einzelne verwandte Themen (darunter Varnish mit Magento) genauer betrachten. Und wer professionelle Hilfe braucht, um seinen Shop für den nächsten großen Ansturm oder das natürliche Wachstum fit zu machen oder von Grund auf plant eine große Shoplösung zu bauen, sollte mit unseren Magento- und Deployment-Experten [14] sprechen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -