Architektur des Google App Engine Cloud Service Framework macht Webseiten für Startups skalierbar

Die Architektur des Google App Engine Cloud Service (GAE) ist ein Framework bzw. Server Stack, der es ermöglicht eine Webseite skalierbar mit hoher Performance zu betreiben ohne als Startup bei der Unternehmensgründung selbst eine Infrastruktur aus Clustern aufbauen und betreiben zu müssen. Das Hosting einer Webapp ist in der Free Version sogar kostenlos, sodass eine Homepage gratis für Googles Plattform in Python, Java oder Go entwickelt werden kann. Steigt die Serverlast später an, kann der eigene Cluster mitwachsen ohne dafür die Software anpassen zu müssen.

Der Vorteil der Cloud ist, dass bei einer Plattform als Service nur variable Kosten anfallen. Um die Skalierbarkeit kümmern sich andere und man selbst hat keine Fixkosten von mehrere tausende Euro pro Monat für IT-Spezialisten. Dieser Artikel beschreibt Aufbau und Funktionsweise der App Engine, über die täglich Millionen von Besuchern möglich sind.

Startup / skalierbarer Webserver

Besonders für Startups eignet sich die App Engine, da die Infrastruktur mitwächst und zu Beginn kostenlos ist.

Die traditionelle IT-Architektur im Client-/Serverumfeld bzw. von Webservern ist meist statisch und zentralisiert. Das bedeutet die Anwendung besteht aus einem oder mehreren fest konfigurierten Servern in einem Rechenzentrum und nutzt ein relationales Datenbanksystem. Die Kapazität dieser Ressourcen und damit die maximal mögliche Serverlast ist begrenzt. Übersteigt die Besucherzahl dann zunehmend die Kapazitäten des eigenen Systems ist guter Rat teuer, denn nicht jeder IT’ler beherrscht Skalierung.

Die Infrastruktur der App Engine wird als Platform as a Service (PaaS) genutzt. Bei dieser Plattform sind die Webserver, der Cache, die Datenbank und die Dateiserver weltweit in einer public Cloud verteilte, sodass jeder Request von anderen Servern beantwortet werden kann. Die Anzahl der benötigten Server wird dynamisch an die aktuelle Last angepasst. Ideal für Startups und neue Projekte, in denen sich die IT auf die Umsetzung der Anwendung konzentriert und nicht zusätzlich mit der Infrastruktur sowie deren Betrieb belastet wird.

Komponenten / Server Stack

Mit der Google App Engine kann sofort mit der Umsetzung der Webseite begonnen werden, da sie alle nötigen Komponenten für Entwicklung, Test, Deployment und Betrieb enthält.

Die Komponenten der Google App Engine Cloud können auch für den Aufbau einer eigenen skalierbaren Infrastrukturen adaptiert werden. Für Teile existieren Open Source Lösungen oder Amazon Services, die im eigenen Stack integrierbar sind. Im Vergleich zu einer Apache MySQL PHP Installation unter Linux (LAMP) oder Windows (WAMP) ähnelt die App Engine von der Laufzeitumgebung eher einem Java EE Application Server (JBoss, Websphere, Tomcat), da Programme z.B. auch bei Python im Hauptspeicher gecached werden.

Bestandteile des App Engine Cloud Service sind:

  • Webspace (Application)
  • Webserver (Instanz)
  • Load-Balancer (Scheduler)
  • Laufzeitumgebung (Python, Java, Go)
  • Cache (Memcache)
  • hierarchische Datenbank (Datastore)
  • relationale Datenbank (SQL-DB)
  • Fileserver (Blobstore)
  • Scheduler (Cron)
  • Webhook (Task)
  • Batchserver (Backend)
  • HTTP-Push (Channel)
  • Emailserver (API)
  • Bibliotheken (API’s)
  • Entwicklungsumgebung (Eclipse)
  • Testumgebung (SDK)
  • Deploymentverfahren (SDK)
  • Versionsverwaltung (GAE)
  • Benutzerverwaltung (GAE)
  • Logging (GAE)
  • Monitoring (GAE)
  • Domainverwaltung (Apps)
  • Dokumentation (Web)

Jede Komponente der App Engine Architektur ist skalierbar! Um die Kapazität der Cloud insgesamt zu erweitern müssen nur weitere Server hinzugefügt werden. Zudem kann ein Server seine Aufgabe ändern indem er mit einem anderen Image ausgeführt wird. Das gesamte System ist weniger anfällig gegen Ausfälle einzelner Server. Um das Management der Cloud kümmert sich Google, zu jeder Uhrzeit und auch am Wochenende.

Application / Webspace

Mit einem kleinen Webspace und wenigen Dateien skaliert die Application besser, da ein Warmup nur Millisekunden benötigt.

Jeder Application steht aktuell ein Webspace von bis zu 150 MB für statische Inhalte zur Verfügung. Pro Verzeichnis können maximal 1.000 und je Anwendung insgesamt 10.000 Dateien beim Deployment hochgeladen werden. Die maximale Größe einer Datei ist auf 32 MB begrenzt.

Im Webspace werden Konfigurationsdateien, statische Webseiten, Templates, Stylesheets, Javascripts, Grafiken, Applets sowie Programme und Bibliotheken gespeichert. Es können auch Datenbankinhalte in Form von komprimierten ZIP-Dateien in den Webspace ausgelagert werden, um kostenpflichtige Datastore-Zugriffe für rein lesende Operationen zu sparen. Die Anzahl der Dateien kann reduziert werden, indem Libraries als unkomprimiertes ZIP-Archiv gespeichert werden.

Der Webspace muss bei jedem Warmup einer neuen Instanz erst auf die Webserver repliziert werden, erkennbar an den wechselnden ID’s im Verzeichnisnamen. Ein sehr großer Webspace und sehr viele Dateien erhöhen den Zeitbedarf des Warmups von wenigen Millisekunden auf deutlich spürbare Sekunden!

Bei selten besuchten Webseiten mit großem Webspace sollte zumindest eine Instanz dauerhaft am Leben gehalten werden (AlwaysOn / Cron-Ping), da nach 15 Minuten Inaktivität der Webspace auf dem Webserver beim Cooldown wieder gelöscht wird und dann später der erste Seitenaufruf eine störende Latenz vom mehreren Sekunden durch den erneuten Warmup hat.

Instanz / virtueller Server

Das Webapp-Framework spart erheblich Instanzen, da es konkurrierende Requests in parallelen Threads verarbeitet.

Die Application Frontend Instanz einer Google Appengine ist ein eigener Prozess auf einem Webserver, dem exklusiv bis zu 128 MB RAM und ca. 600 MHz CPU zur Verfügung stehen. Eine Instanz ist vergleichbar mit einem virtuellen Server, wobei der Overhead für die Virtualisierung entfällt, da lediglich ein neuer Prozess zum
Start einer Instanz gestartet werden
muss.

Eine Instanz bzw. ein Prozess kann gleichzeitig nur einen einzigen Request verarbeiten, wenn CGI direkt genutzt wird! CGI liesst zuerst den HTTP-Request aus der Standardeingabe des Prozesses und schreibt dann den HTTP-Response in dessen Standardausgabe. Die maximale Anzahl Requests pro Sekunde hängt direkt mit der Programmlaufzeit zur Erzeugung des Response zusammen. Bei einer Latenz von 200 ms pro Request können maximal 5 Requests pro Sekunde verarbeitet werden. Smartphones mit langsamer Verbindung erhöhen die Latenz je Request deutlich. Ein Denial-of-Service muss zudem nur den Response nicht abholen, um eine Instanz zu blockieren.

Es werden Threads benötigt, um konkurrierende Anfragen parallel mit einer Instanz verarbeiten zu können. Dies ist mit der Laufzeitumgebung Java sowie ab Python 2.7 mit dem Framework Google Webapp möglich, da dieses threadable ist.

Das besondere an der Appengine Cloud ist, dass Google die von einer Application benötigten Serverinstanzen dynamisch an die tatsächliche Last anpasst.

Scheduler / Load-Balancer

Ein höherer IDLE Timeout und längeres Pending spart Instanzen, da der Webserver langsam sein darf.

Die zentrale Hauptkomponente des Google Appengine Cloud Framework ist der Scheduler. Er steuert die Skalierung indem er bei Bedarf zusätzliche Instanzen auf weiteren Servern startet und als Load-Balancer die Requests im Cluster verteilt.

Der Scheduler wird lediglich über zwei Parameter im Administrationsbereich der Appengine konfiguriert, was unmittelbar Einfluss auf die Performance bzw. Größe des Server Clusters und somit die Kosten hat. In der Standardeinstellung von Google ist der Cluster auf maximale Performance konfiguriert, darf beliebig schnell wachsen und die Kosten können explodieren!

Mit dem Parameter „Idle Instances“ wird die Anzahl der Serverprozesse – also die Größe des Clusters – definiert. Mit „Max Idle Instances“ kann die Anzahl der Instanzen von 1 bis 100 beschränkt werden, die der Scheduler parallel ausführen darf (Standard: unendlich). Dadurch können die Kosten reduziert werden, jedoch bei starken Lastspitzen eventuell auch keine Anfragen mehr beantwortet werden. Bei bezahlten Apps und Premium Accounts gibt es zusätzlich die Option „Min Idle Instances“, um die Anzahl der ständig verfügbaren AlwaysOn Instanzen zu definieren (Warmup Latenz entfällt).

Wenn alle aktuell laufenden Instanzen belegt sind, dann schiebt der Scheduler die eingehenden Requests zunächst in eine Warteschleife, die Pending Queue. Mit der „Pending Latency“ wird gesteuert, wie lange der Scheduler warten muss, bevor er eine weitere Instanz starten darf (Standard: sofort). Mit „Min Pending Latency“ kann die minimale Wartedauer von 10 Millisekunden bis 15 Sekunden definiert werden, bevor eine weitere Instanz gestartet werden darf. Sofern kein Request länger warten muss wird auch nie eine weitere Instanz gestartet. Bei bezahlten Apps und Premium Accounts gibt es zusätzlich die Option „Max Pending Latency“, um die maximale Wartedauer zu definieren, nach der eine weitere Instanz gestartet werden muss. Je länger die Besucher notfalls warten, desto weniger Instanzen sind notwendig.

In der Free Version kann mit Max Idle Instances = 1 die Application durchgehend auf einem Webserver betrieben werden, ist dann aber nicht in einem Cluster skalierbar.

Warmup / Loading Request

Eine dauerhaft verfügbare Instanz hat für den Besucher keine spürbare Latenz, weil der Warmup entfällt.

Aufgrund der Architektur mit verteilten Webservern und Instanzen gibt es zwei Arten von Initialisierungen, die im Zusammenhang mit dem Scheduler relevant sind. Nach einem Deployment gibt es zunächst keine aktive Instanz auf einem Webserver. Kommt nun die erste Anfrage muss der Webspace zunächst auf einen Webserver kopiert werden und die Application im Cluster für diesen Webserver registriert werden. Dieser Warmup genannte Vorgang kann mehrere Sekunden dauern, weshalb Google den Webspace und die Application temporär auf dem Webserver eingerichtet lässt. Nun wird ein neuer Prozess für die Application auf dem Webserver gestartet, was normalerweise nur wenige Millisekunden dauert. Die erste Anfrage – die das Starten eines neuen Prozesses notwendig macht – wird Loading Request genannt. Dabei werden die benötigten Programme und Bibliotheken in den Hauptspeicher geladen. Der Prozess wartet nach jeder Bearbeitung von Anfragen auf neue Requests.

Da jeder aktive Prozess aber Hauptspeicher auf dem Webserver belegt, werden Prozesse zu ungenutzten Instanzen (ohne erneute Requests) bereits nach wenigen Sekunden wieder beendet. Es ist auch nicht notwendig die Prozesse ständig auszuführen, da die Latenz des Loading Requests kaum spürbar, Hauptspeicher aber kostbar ist. Wenn innerhalb von 15 Minuten kein Prozess zu einer Instanz gestartet wird, dann löscht Google den Webspace wieder vom Webserver. Abgerechnet wird der gesamte Zeitraum, indem der Webspace repliziert war – also Nutzungsdauer plus 15 Minuten.

Laufzeitumgebung / Python, Java, Go

Über Wrapper-Klassen kann die Implementierung unabhängig von Google gemacht werden, um später zu einem anderen Provider wechseln zu können.

Googles Appengine ermöglich es dynamische Webseiten inklusive Template-Engine in den Programmiersprachen Python, Java oder Go zu entwickeln. Es können unterschiedliche Laufzeitumgebungen auch parallel über verschiedene Subdomains in mehreren Instanzen ausgeführt werden.

Ein großer Nachteil kann die Abhängigkeit zur Infrastruktur von Google werden, wenn sie später z.B. die Preise erhöhen oder das Produkt Appengine einstellen sollten. Der lokale Testserver aus dem SDK eignet sich auf keinen Fall für den produktiven Betrieb, da hier sämtliche Komponenten der Skalierung fehlen und Sicherheitslücken möglich sind.

Die einfachste Lösung ist, wenn das API von Google über Wrapper-Klassen gekapselt wird. Das hat den Vorteil, dass die entwickelte Software auch als Inhouse-Lösung in einer privaten Cloud betrieben werden kann und so optional auch als Kaufsoftware vertrieben werden kann.

Cache (Memcache)

Das aggressive Caching von Daten im Hauptspeicher erhöht die Performance verteilter Webanwendungen extrem.

Ein Datensatz im Memcache darf maximal 1 MB groß sein. Pro Request können maximal ca. 200 MB Daten übertragen werden. Der maximale Timeout beträgt ca. einen Monat.

Eine Architektur aus statischen Webservern bildet ein Cluster oft über mehrere Subdomains ab (www1.domain.tld, www2.domain.tld, …) und verteilt einfach die Anfrage durch Redirects im HTTP-Header. Hier kann jeder Server lokal im Hauptspeicher die Daten cachen. Der Einsatz von Subdomains kann sich negativ auf das Ranking in Suchmaschinen auswirken (doppelter Content) und zu Problemen bei Bookmarks führen. Diese Implementierung skaliert auch nicht gleichmäßig über alle eingesetzen Server.

Bei einem Cluster mit internem Load Balancer muss das Caching im RAM oder über ein sehr schnelles RAID an spezielle Server ausgelagert werden, auf die jeder interne Webserver zugreifen kann. Eine Implementierung dafür ist der Memcache (www.memcached.org).

Der Memcache ist ein Client/Server-System, dass Daten über eine Hash-Tabelle temporär im Netzwerk verteilt. Auf den Servern werden alte jeweils durch neue Daten verdrängt, wenn der Speicher voll ist. Die Anwendung kann daher nie sicher sein, dass ein gespeicherter Memcache-Datensatz existiert. Das Protokoll und der Zugriff auf den Memcache ist aber wesentlich schneller als auf ein Datenbanksystem.

Die Appengine löscht Daten zu einer Application nicht aus dem Memcache, selbst wenn keine Instanz mehr auf einem Webserver läuft oder die Anwendung komplett neu deployed wird! Bei einer neuen Version muss der Memcache explizit gelöscht werden, wenn sich die Datenstrukturen im Memcache ändern oder die Keys enthalten alternativ immer eine Releasenummer.

Hierarchische Datenbank (Datastore)

Redundanz in den Daten erhöht die Performance extrem, wenn dadurch Lesezugriffe gesparrt werden.

Ein Datensatz im Datastore darf maximal 1 MB groß sein. Als Free Account können bis zu 1 GB gespeichert werden. Pro Request dürfen maximal 1.000 Datensätze gelesen werden. Es sind maximal 200 Indezies erlaubt, wobei ein Index maximal 5.000 Keys enthalten darf.

Der Datastore ist kein Datenbanksystem im klassischen Sinn, sondern eine persistente Implementierung des Memcache. Der Vorteil einer Hash-Tabelle ist die lineare Skalierbarkeit! Google nennt das Konzept BigTable, weil die Datenbank beliebig groß werden
kann und durch die Verteilung der Tablespaces auf unterschiedliche Platten sowie Server parallelisierbar ist. Kurz, der Datastore performt extrem schnell bei beliebig großen Datenmengen bei gezielten Zugriffen.

Eine skalierbare Webanwendung benötigt eine beliebig skalierbare Datenbank. Daher empfiehlt Google unbedingt die Verwendung des DataStores. Da dieser kein SQL kennt müssen Relationen wie 1-N / N-M und Aggregationen über andere Pattern gelösst werden, d.h. Google zwingt die Entwickler zu skalierbaren Anwendungen.

Ein Scan über den gesamten Tablespace für theoretisch unendliche Daten skaliert nie, weshalb dieses Szenario als Batch realisiert werden muss. Das Szenario für die Appengine ist keine Volltextsuche, sondern 1 Millionen Seitenaufrufe pro Stunde ausliefern zu können.

Relationale Datenbank (SQL-DB)

Wegen Joins, Indizes und zentralisiertem Tablespace skalieren relationale Datenbanken sehr schlecht.

In IT-Systemen mit kleinen Datenmengen oder wenigen Benutzern werden gern relationale Datenbanken auch Online eingesetzt, um Daten möglichst in dritter Normalform ohne Redundanzen zu speichern. Die Daten müssen mittels Joins über Fremdschlüssel durch den Datenbankserver zusammengelesen werden. Die Server benötigen dafür sehr viel Hauptspeicher, CPU-Zeit und nutzen meist langsame Tempspaces auf der Festplatte. Dadurch wird die Datenbank zum Flaschenhals jeder Skalierung.

Wenn, dann sollte ein relationales Datenbanksystem eher im Batch zur Befüllung des Datastore oder im Online für Scans eines kleineren Tablespaces genutzt werden.

Fileserver (Blobstore)

Große Dateien werden
direkt aus dem Blobstore an den Browser ausgeliefert.

Ein Datensatz im Blobstore darf maximal 32 MB groß sein. Pro Tag dürfen maximal 5 GB in der Free Version gelesen werden.

Anfangs mussten Datei-Uploads über normales CGI realisiert werden, weshalb Dateien maximal 10 MB groß sein durften und dann auf mehrere Datastore-Datensätzen aufgeteilt werden mussten. Beim Download über den Datastore muss zunächst die gesamte Datei in den Hauptspeicher des Webservers geladen
werden und dann nochmals in die Standardausgabe geschrieben werden. Dadurch wurde doppelt Hauptspeicher verbraucht und der Webserver konnte die Datei erst an den Browser ausliefern, wenn das Script beendet war.

Beim Blobstore erfolgt der Upload direkt auf den Dateiserver und der Download wird direkt an den Browser gesendet, ohne die Datei komplett in den Hauptspeicher zu laden.

Scheduler (Cron)

Viele Cronjobs können die Performance negativ beeinflussen.

Die Appengine realisiert automatisierte Cronjobs als normale Requests. Es können bis zu 20 Abläufe automatisiert werden.

Es ist keine gute Idee 20 Cronjobs gleichzeitig zu definieren, die jede Minute laufen, denn jeder Cronjob führt einen HTTP-Request mit einer Latenz aus. Dadurch würde eine Instanz allein durch Cronjobs ausgelastet.

Cronjobs werden wie normale Requests mit einem Timeout von 60
Sekunden beschränkt. Länger laufende Prozesse können als Tasks im Cronjob gestartet werden.

Webhook (Task)

Webhooks können asynchron über Tasks ausgeführt werden ohne die Latenz des Requests zu erhöhen.

Mit Tasks können Programme mit einer Laufzeit von bis zu 10 Minuten asynchron gestartet werden. Dieses Verfahren wird auch als Webhook bezeichnet.

Batchserver (Backend)

Langlaufende Abläufe können regelmäßig außerhalb des Webservers im Hintergrund ausgeführt werden.

Für Programme die mehrere Stunden laufen gibt es spezielle Batchinstanzen mit unterschiedlichen Kapazitäten (B1: 128 MB / 600 MHz, B2: 256 MB / 1.2 GHz, B3: 512 MB / 2.4 GHz, B4: 1.2 GB / 4.8 GHz).

HTTP-Push (Channel)

Über Channels kann der Webserver dauerhaft eine Verbindung mit dem Webbrowser halten, um z.B. für Chats Daten direkt vom Server an den Client zu pushen.

Mit HTTP Push sendet der Server an den Client neue Daten, sobald diese existieren. Das spart unwahrscheinlich Latenz und Requests bei Chatsystemen oder Multiusergames, da vom Client nur eine Verbindung offen gehalten wird. Früher musste mit AJAX z.B. im Sekundentakt ein Request ausgeführt werden, um nachzusehen, ob neue Daten abzuholen sind.

Emailserver (API)

Emails werden statt per IMAP oder POP3 vom Webserver als HTTP-Request empfangen.

Eingehende Emails werden vom Mailserver als HTTP-Request an den Webserver übergeben. Die Appengine ist über die Email @id.appspotmail.com erreichbar, d.h. man muss die Emails an seine @domain.de weiterleiten.

Ausgehende Emails sind nur mit einem Absender möglich, der auch als Benutzer im Adminbereich der Appengine eingetragen ist. Der massenhaft Versand von Emails ist sehr teuer, weshalb hierfür auch auf dem Amazon Service oder ein einfachen Webhosting-Account mit z.B. PHP ausgewichen werden kann.

Bibliotheken (API’s)

Rechenintensive Funktionalitäten können per API auf optimierte Implementierungen oder spezialisierte Server ausgelagert werden.

Bei einer Platform as a Service wird versucht, einerseits die Betriebskosten zu minimieren und andererseits einzelne Services nutzungsabhängig zu verkaufen. Über shared Libraries können alle Anwendungen gemeinsame Funktionen nutzen, ohne dass jede Anwendung den Code dafür nochmals laden zu müssen. Beispiele für solche Bibliotheken sind Bildbearbeitung oder PDF-Erstellung.

Shared Libraries können über eine API entweder direkt auf dem Webserver oder remote über einen speziellen Serverdienst ausgeführt werden.

Entwicklung

Neben SDK und Plugin für Eclipse wird nur die Laufzeitumgebung benötigt. Nach 30 Minuten ist die erste HalloWelt-App online.

Unter appengine.google.com sind alle Downloads (SDK, Runtime, …) verlinkt und in der Dokumentation ist Vorgehen und API unfassend beschrieben.

Über eine Versions-ID können nacheinander mehrere Releases einer Anwendung deployed werden. Im Administrationsbereich der App Engine kann problemlos auf ein altes Release zurückgesprungen werden.

Administration

Das Dashboard liefert alle Informationen zum Status der App Engine. Domains werden über Google Applications verwaltet.

Im Administrationsbereich der App Engine werden Plattform, Benutzer und Versionen usw. verwaltet. Hier finden sich auch die Logs.

Die Zuordnung von Domains (www.meinname.de) zu einer Application (meinname.appspot.com) erfolgt nicht in der App Engine, sondern über Google Apps! Google Apps ist ein eigener Dienst zum verwalten von Domains, der auch kostenlos genutzt werden kann.

Software Market

Eine App Engine Application kann im Software Market von Google verkauft werden.

Google bietet wie Salesforce eine Software as a Service Plattform. Hierüber können auch Webanwendungen verkauft werden, die als App Engine realisiert wurden.

Hinterlasse einen Kommentar