Skalierbare und robuste Webanwendungen auf der Google Cloud Platform erstellen

Die Entwicklung von Apps, die sowohl robust als auch skalierbar sind, ist ein wesentlicher Bestandteil jeder Anwendungsarchitektur. Eine optimal aufgebaute Anwendung sollte in der Lage sein, sich durch nahtloses Skalieren an eine höhere oder niedrigere Nachfrage anzupassen, und robust genug sein, um den Verlust einer oder mehrerer Rechenressourcen auszugleichen.

Dieses Dokument richtet sich an Systems Operations-Profis, die mit Compute Engine vertraut sind. In diesem Dokument erfahren Sie, wie Sie mit der Google Cloud Platform skalierbare und robuste Anwendungsarchitekturen mithilfe von Mustern und Verfahren erstellen, die für fast jede Webanwendung verwendet werden können. Eine beispielhafte Bereitstellung des beliebten Open-Source-Projektmanagement-Tools Redmine, einer Ruby on Rails-basierten Anwendung, zeigt Ihnen, wie diese Prinzipien in der Praxis anzuwenden sind. Im Abschnitt Bereitstellung der Beispiellösung haben Sie die Möglichkeit, die Anwendung selbst bereitzustellen und alle Quellcodes zum Nachlesen herunterzuladen.

Mit der GCP können Sie skalierbare und robuste Webanwendungen auf einfache Weise und kostengünstig erstellen sowie bereitstellen. Mit Diensten wie Compute Engine und Autoscaling ist es ganz einfach, Ihre Anwendungsressourcen an den Bedarf anzupassen. Gemäß dem Compute Engine-Preismodell zahlen Sie außerdem pro Sekunde und erhalten mit Rabatten für kontinuierliche Nutzung automatisch den besten Preis ohne komplizierte Kapazitäts- oder Reservierungsplanung.

Eine allgemeine Übersicht über Ihre Optionen für das Webhosting auf der GCP finden Sie unter Websites bereitstellen.

Vorbereitung

Skalierbarkeit und Robustheit definieren

Vor der Beschreibung einer beispielhaften Anwendungsarchitektur sollen zuerst die Begriffe Skalierbarkeit und Robustheit definiert werden.

Skalierbarkeit: Kapazitäten an Nachfrage anpassen

Eine skalierbare Webanwendung funktioniert mit einem Nutzer genauso gut wie mit einer Million Nutzer. Sie passt sich automatisch den Schwankungen des Traffics an. Durch die Möglichkeit, virtuelle Maschinen je nach Bedarf hinzuzufügen und zu entfernen, nutzen skalierbare Anwendungen immer nur die Ressourcen, die für den jeweiligen Bedarf erforderlich sind.

Das folgende Diagramm zeigt, wie eine skalierbare Anwendung auf Anstieg oder Rückgang des Bedarfs reagiert.

Ein Diagramm, das zeigt, wie Ressourcen nach Bedarf skaliert werden.

Beachten Sie, dass die Kapazität sich dynamisch an die Veränderungen der Nachfrage anpasst. Mit dieser Konfiguration, die auch als "Designelastizität" bezeichnet wird, bezahlen Sie immer nur die Rechenressourcen, die Ihre Anwendung zu einer bestimmten Zeit auch wirklich benötigt.

Robustheit: immer auf das Unerwartete vorbereitet

Eine hoch verfügbare oder robuste Webanwendung funktioniert auch dann weiter, wenn bei Systemkomponenten erwartete oder unerwartete Fehler auftreten. Sollte eine einzelne Instanz ausfallen oder ein Fehler in einer gesamten Zone auftreten, bleibt eine robuste Anwendung fehlertolerant – sie funktioniert weiterhin und repariert sich automatisch selbst, falls notwendig. Da zustandsorientierte Informationen nicht in einer einzelnen Instanz gespeichert sind, wird sich so der Ausfall einer einzelnen Instanz oder sogar einer ganzen Zone nicht auf die Leistung der Anwendung auswirken.

Eine wirklich robuste Anwendung erfordert sowohl in der Softwareentwicklung als auch auf Ebene der Anwendungsarchitektur viel Planung. Dieses Dokument bezieht sich größtenteils auf die Ebene der Anwendungsarchitektur.

Folgendes gehört in der Regel zum Entwurf einer Anwendungsarchitektur für eine robuste Anwendung:

  • Lastenausgleichsmodule, um Server zu überwachen und den Traffic auf die Server zu verteilen, die Anfragen am besten verarbeiten können
  • Hosting von virtuellen Maschinen in mehreren Regionen
  • Konfiguration einer robusten Speicherlösung

Google Cloud Platform: flexibel und kostengünstig

Für herkömmliche Architekturen, die Skalierbarkeit und Robustheit unterstützen, sind oft hohe Investitionen in Ressourcen erforderlich. Bei lokalen Lösungen führt die Skalierbarkeit oft dazu, dass die Entscheidung zwischen zwei Optionen fallen muss: entweder zu hohe Ausgaben für Serverkapazitäten, die an die maximale Nutzungsrate angepasst sind, oder an durchschnittliche Nutzungswerte orientierte Ausgaben, die das Risiko schlechterer Anwendungsleistungen und Nutzererfahrungen zu Traffic-Spitzenzeiten mit sich bringen. Die Robustheit hängt jedoch von mehr als nur der Serverkapazität ab – auch die Zone spielt eine wichtige Rolle. Zur Minimierung der Auswirkungen physischer Ereignisse wie Stürme oder Erdbeben sollten Server bei einer solchen Konstellation an verschiedenen physischen Standorten betrieben werden. Dies kann zu hohen Kosten führen.

Die GCP bietet eine Alternative: eine Reihe von Clouddiensten als flexible Möglichkeit, Ihre Architektur skalierbar und robust zu machen. Diese Dienste stellt die GCP mit einer einfach zu steuernden Preisstruktur bereit.

Robuste und skalierbare Architekturen mit der Google Cloud Platform erstellen

Die folgende Tabelle zeigt Ihnen, wie sich verschiedene GCP-Dienste den wichtigsten Voraussetzungen zuordnen lassen, die für das Erstellen skalierbarer und robuster Anwendungen gegeben sein müssen.

Architekturvoraussetzung Google Cloud Platform-Dienst
Lastenausgleich HTTP-Lastenausgleich
Serverhosting Compute Engine, Regionen und Zonen
Servermanagement Instanzvorlagen, verwaltete Instanzgruppen, Autoscaling
Datenspeicherung Cloud SQL, Cloud Storage

Die folgende Grafik zeigt, wie diese GCP-Komponenten durch ihr Zusammenspiel eine skalierbare, robuste Webanwendung bilden. Die Bedeutung der einzelnen Komponenten wird im nächsten Abschnitt genauer beschrieben.

Ein Diagramm, das eine skalierbare und stabile Anwendung zeigt.

Komponenten

Jede Komponente in der Beispiel-Anwendungsarchitektur trägt dazu bei, dass die Anwendung sowohl skalierbar als auch robust ist. In diesem Abschnitt werden diese Dienste zuerst einzeln beschrieben. In späteren Abschnitten wird ihr Zusammenwirken erläutert.

HTTP-Lastenausgleichsmodul

Das HTTP-Lastenausgleichsmodul enthält eine einzige öffentliche IP-Adresse, über die Kunden auf die Anwendung zugreifen können. Diese IP-Adresse kann mit einem DNS-Datensatz A (z. B. example.com) oder mit CNAME (z. B. www.example.com) verknüpft werden. Eingehende Anfragen werden in den Zonen je nach Kapazität der einzelnen Gruppen auf die Instanzgruppen verteilt. Innerhalb einer Zone werden Anfragen gleichmäßig den Instanzen innerhalb der Gruppe zugewiesen. Das HTTP-Lastenausgleichsmodul kann den Traffic auch zwischen mehreren Regionen ausgleichen. In dieser Beispiellösung wird es aber in einer einzelnen Region mit mehreren Zonen verwendet.

Zone

Eine Zone ist ein isolierter Ort innerhalb einer Region. Zonen haben Netzwerkverbindungen mit hoher Bandbreite und niedriger Latenz zu anderen Zonen in derselben Region. Google empfiehlt, Anwendungen in jeweils mehreren Zonen einer Region bereitzustellen.

Instanz

Eine Instanz ist eine virtuelle Maschine, die in der Infrastruktur von Google gehostet wird. Sie können Instanzen wie physische Server installieren und konfigurieren. In der Beispielbereitstellung verwenden Sie Startskripts und das Tool "Chef" zur Konfiguration von Instanzen mit dem Anwendungsserver und -code für die Webanwendung.

Instanzvorlage

Eine Instanzvorlage definiert den Maschinentyp sowie Image, Zone, Labels und andere Instanz-Properties. Mit dieser Vorlage können Sie eine verwaltete Instanzgruppe erstellen.

Verwaltete Instanzgruppe

Eine verwaltete Instanzgruppe enthält einheitliche Instanzen, die auf einer bestimmten Instanzvorlage basieren. Sie können für eine verwaltete Instanzgruppe ein HTTP-Lastenausgleichsmodul einrichten, mit dem die Arbeitslast auf die Instanzen in der Gruppe verteilt wird. Eine verwaltete Instanzgruppe hat eine entsprechende Instanzgruppenmanager-Ressource für das Hinzufügen und Entfernen von Instanzen in der Gruppe.

Autoscaling

Mit dem Compute Engine-Autoscaling werden einer verwalteten Instanzgruppe durch Verknüpfung mit dem Gruppenmanager Compute Engine-Instanzen hinzugefügt oder sie werden daraus entfernt. Dies erfolgt in Abhängigkeit von Schwankungen des Traffics und der CPU-Auslastung sowie aufgrund anderer Signale. In unserer Beispiellösung reagiert das Autoscaling auf den Messwert "Anfrage pro Sekunde" (RPS, Request per Second) des HTTP-Lastenausgleichsmoduls. Autoscaling ist für jede Instanzgruppe erforderlich, die automatisch skaliert werden soll.

Cloud SQL

Cloud SQL ist ein vollständig verwalteter Datenbankdienst, der sowohl MySQL als auch PostgreSQL unterstützt. Replikation, Verschlüsselung, Patches und Sicherungen werden von Google verwaltet. Eine Cloud SQL-Instanz wird in einer einzelnen Zone bereitgestellt und die Daten werden automatisch in andere Zonen repliziert. Die Anwendung Redmine in diesem Beispiel ist mit MySQL kompatibel und funktioniert problemlos mit Cloud SQL.

Cloud Storage

Mit Cloud Storage können Objekte (in der Regel Dateien) über eine einfache und skalierbare Oberfläche gespeichert und abgerufen werden. Für diese Lösung werden mit einem Cloud Storage-Bucket private SSL-Schlüssel an die skalierbaren Compute Engine-Instanzen verteilt. Darüber hinaus werden damit alle in die Redmine-Anwendung hochgeladenen Dateien gespeichert, d. h. es werden keine zustandsorientierten Informationen auf den Laufwerken einer Instanz gespeichert.

Robustheit

Damit diese Beispielarchitektur robust ist, muss sie in der Lage sein, Instanzen automatisch zu ersetzen, die ausgefallen oder nicht mehr verfügbar sind. Wenn eine Instanz online geht, sollte sie:

  • Informationen über ihre Rolle im System besitzen
  • sich automatisch selbst konfigurieren
  • mögliche Abhängigkeiten erkennen
  • Anfragen automatisch verarbeiten

Für das automatische Ersetzen einer fehlerhaften Instanz können Sie mehrere Compute Engine-Komponenten kombiniert verwenden:

Ein Startskript wird ausgeführt, wenn Ihre Instanz gestartet oder neu gestartet wird. Sie können diese Skripts für die Installation von Software und Updates verwenden, um sicherzustellen, dass Dienste innerhalb der virtuellen Maschine ausgeführt werden, oder auch um ein Konfigurationsmanagementtool wie Chef, Puppet, Ansible oder Salt zu installieren.

In diesem Szenario wird mit einem Startskript Chef Solo installiert. Das Tool konfiguriert wiederum Instanzen, damit sie innerhalb der Anwendung angewendet werden können. Am Ende dieses Themas erfahren Sie unter Anhang: Neue Instanz hinzufügen, wie Sie mit Startskripts und Chef Solo Instanzen automatisch konfigurieren.

Zusätzlich zu einem Startskript müssen Sie noch weitere Elemente definieren, bevor Sie eine Compute Engine-Instanz starten. So müssen Sie zum Beispiel den Maschinentyp sowie das zu verwendende Betriebssystem-Image und alle Laufwerke, die Sie hinzufügen möchten, angeben. Diese Optionen legen Sie mithilfe einer Instanzvorlage fest.

Gemeinsam definieren eine Instanzvorlage und ein Startskript, wie eine Compute Engine-Instanz gestartet und wie die Software für diese Instanz konfiguriert wird, damit sie eine bestimmte Funktion in Ihrer Anwendungsarchitektur erfüllen kann.

Ein Diagramm, das das Zusammenwirken von Startskripts, Instanzvorlagen und Instanzen zeigt.

Dabei bleibt eine Instanzvorlage natürlich nur eine Vorlage. Damit diese Vorlage funktioniert, müssen Sie einen Weg finden, die Vorlage auf neue Compute Engine-Instanzen anzuwenden, die online gehen. Dazu erstellen Sie eine verwaltete Instanzgruppe. Sie legen die Anzahl der Instanzen fest, die zu einem bestimmten Zeitpunkt ausgeführt werden sollen, und geben an, welche Instanzvorlage auf diese Instanzen angewendet werden soll. Ein Instanzgruppenmanager ist dann für das Starten und Konfigurieren dieser Instanzen nach Bedarf zuständig.

Die folgende Grafik zeigt, wie diese Komponenten zusammenspielen:

  • Startskript
  • Instanzvorlage
  • Instanzgruppenmanager
  • Verwaltete Instanzgruppe
Ein Diagramm, das das Zusammenwirken von Startskripts, Instanzvorlagen, Instanzgruppenmanagern und verwalteten Instanzgruppen zeigt.

Bei einer verwalteten Instanzgruppe und beim zugehörigen Instanzgruppenmanager kann es sich um zonenspezifische oder regionale Ressourcen handeln. Eine Instanzvorlage ist eine Ressource auf Projektebene, die für mehrere verwaltete Instanzgruppen in einer beliebigen Zone und Region verwendet werden kann. Sie haben aber auch die Möglichkeit, in einer Instanzvorlage zonale Ressourcen anzugeben, was die Verwendung der Vorlage dann auf die Zone beschränkt, in der sich die zonalen Ressourcen befinden.

Mit Startskripts, Instanzvorlagen und verwalteten Instanzgruppen haben Sie nun ein System, mit dem fehlerhafte Instanzen durch neue ersetzt werden können. Im nächsten Abschnitt wird eine Möglichkeit beschrieben, eine fehlerhafte Instanz zu definieren und zu erkennen.

Systemdiagnosen

Die Beispielanwendung verfügt nun über fast alle notwendigen Tools für einen robusten Zustand. Eine Sache fehlt jedoch noch: Sie muss in der Lage sein, fehlerhafte Instanzen zu erkennen, damit sie diese ersetzen kann.

Diese Anwendung ist so konzipiert, dass Nutzer über ein HTTP-Lastenausgleichsmodul auf eine geeignete, fehlerfreie Instanz zugreifen können. Mit dieser Architektur können Sie zwei Dienste zum Ermitteln von Instanzen nutzen, die in der Lage sind, Anfragen zu übernehmen:

  • Systemdiagnosen. Bei einer HTTP-Systemdiagnose werden der Port und der Pfad festgelegt, für die die Systemdiagnose für jede Instanz ausgeführt werden soll. Die Systemdiagnose erwartet von einer fehlerfreien Instanz die Antwort "200 OK".
  • Back-End-Dienste. Bei einem Back-End-Dienst werden eine oder mehrere Instanzgruppen bestimmt, die Traffic von einem Lastenausgleichsmodul erhalten sollen. Dabei werden der Port und das Protokoll festgelegt, die von den Instanzen bereitgestellt werden (z. B. HTTP-Port 80), sowie die HTTP-Systemdiagnose, die auf die Instanzen in der Instanzgruppe angewendet werden soll.

Die folgende Grafik stellt die Anwendungsarchitektur dar und zeigt, wie ein Back-End-Dienst und eine HTTP-Systemdiagnose mit dem Lastenausgleichsmodul und der Instanzgruppe zusammenhängen.

Ein Diagramm, das Systemdiagnose- und Back-End-Services zeigt.

Datenrobustheit mit Cloud SQL

Die drei Hauptbereiche jeder Anwendungsarchitektur sind Netzwerk, Computing und Speicherung. Die hier beschriebene Anwendungsarchitektur enthält bereits die Netzwerk- und Computingkomponenten, es fehlt aber noch die Speicherungskomponente.

Diese Beispiellösung verwendet Cloud SQL-Instanzen der ersten Generation für die Bereitstellung einer vollständig verwalteten MySQL-Datenbank. Mit Cloud SQL verwaltet Google automatisch Replikation, Verschlüsselung, Patchmanagement und Sicherungen.

Cloud SQL-Datenbanken sind flächendeckend, d. h. Daten werden innerhalb einer Region zonenübergreifend repliziert. Das ist in etwa so, als würde bei jeder Aktualisierung Ihrer Daten eine Sicherung erstellt werden. Auch im unwahrscheinlichen Fall eines kompletten Ausfalls einer Zone bleiben so alle Daten erhalten.

Mit Cloud SQL können Sie zwischen zwei Replikationstypen wählen:

  • Synchrone Replikation. Bei einer synchronen Replikation werden Aktualisierungen in mehrere Zonen kopiert, bevor sie zum Client zurückkehren. Das macht die Anwendung natürlich auch im Notfall zuverlässig und verfügbar, dafür sind die Schreibvorgänge langsamer.
  • Asynchrone Replikation. Die asynchrone Replikation erhöht den Durchsatz für Schreibvorgänge, da Schreibvorgänge bestätigt werden, sobald sie lokal im Cache gespeichert aber noch bevor die Daten in andere Zonen kopiert werden. Eine asynchrone Replikation führt zu schnelleren Schreibvorgängen in der Datenbank, da nicht auf den Abschluss der Replikation gewartet werden muss. Allerdings ist es möglich, dass die letzten Aktualisierungen im unwahrscheinlichen Fall, dass ein Systemfehler im Rechenzentrum in den ersten Sekunden nach dem Update der Datenbank auftritt, verloren gehen.

Die Anwendung Redmine aus diesem Beispiel verwendet die synchrone Replikation, da die Arbeitslast nicht sehr schreibintensiv ist. Sie sollten bei der Wahl zwischen synchroner und asynchroner Replikation die spezifische Schreibleistungs- und Datenhaltbarkeitsvorgaben Ihrer Anwendung berücksichtigen.

Skalierbarkeit

In den vorherigen Abschnitten wurde gezeigt, wie die Beispielanwendung mithilfe der GCP eine robuste Anwendung erstellt. Robustheit allein reicht jedoch nicht aus – auch die Skalierbarkeit ist wichtig. Die Anwendung sollte sowohl für einen als auch für 1.000.000 Nutzer gut funktionieren und ihre Ressourcen sollten je nach Anzahl der Nutzer zu- oder abnehmen, um kostengünstig zu bleiben.

Damit die Ressourcen zu- oder abnehmen können, benötigt die Anwendung:

  • Eine Möglichkeit, um Instanzen dem Dienst hinzuzufügen oder diese wieder daraus zu entfernen. Es muss auch entschieden werden, wann eine Instanz hinzugefügt und wann eine Instanz entfernt werden muss. Das Autoscaling von GCP löst dieses Problem.
  • Eine Möglichkeit, zustandsorientierte Daten zu speichern. Da Instanzen kommen und gehen, ist es nicht empfehlenswert, zustandsorientierte Daten in diesen Instanzen zu speichern. Die Anwendungsarchitektur löst dieses Problem für relationale Daten durch Speichern der Daten in einer separaten Cloud SQL-Instanz. Allerdings muss es auch eine Lösung für Dateien geben, die von Nutzern hochgeladen werden. Cloud Storage erfüllt diese Anforderung.

In den folgenden Abschnitten wird beschrieben, wie mithilfe von Autoscaling die Infrastruktur der Redmine-Anwendung skaliert und Cloud Storage für hochgeladene Dateien genutzt werden kann.

Mit Autoscaling skalieren

Bei schwankender Nutzung der Anwendung muss diese in der Lage sein, die notwendigen Ressourcen dynamisch anzupassen. Diese Anforderung können Sie mit dem Compute Engine-Autoscaling lösen.

Nehmen Traffic oder Arbeitslasten zu, fügt das Autoscaling Ressourcen hinzu, damit die zusätzliche Aktivität bewältigt werden kann, und es entfernt zwecks Kosteneinsparung Ressourcen, wenn Traffic oder Arbeitslasten abnehmen. Autoscaling führt diese Aktionen automatisch im Rahmen der von Ihnen definierten Skalierungsregeln aus, ohne dass zusätzliche Eingriffe Ihrerseits notwendig sind.

Autoscaling wirkt sich in doppelter Hinsicht aus:

  1. Ihre Nutzer machen mit Ihrer Anwendung eine positive Erfahrung, da immer ausreichend Ressourcen für ihre Anforderungen vorhanden sind.
  2. Sie haben als Kunde mehr Kontrolle über Ihre Kosten, da das Autoscaling Instanzen entfernt, wenn der Bedarf unter einen bestimmten Schwellenwert fällt.

Autoscaling skaliert die Anzahl virtueller Maschinen je nach CPU-Auslastung, Bereitstellungskapazität oder einem Stackdriver-Monitoringmesswert. Diese Lösung greift auf den Bereitstellungskapazitätsmesswert zurück, um Compute Engine-Instanzen je nach Anzahl der Anfragen pro Sekunde (RPS), die die Instanzen vom Lastenausgleichsmodul erhalten, hinzuzufügen oder zu entfernen. Weitere Informationen finden Sie unter Batch-Verarbeitung mit Compute Engine Autoscaling.

Anfragen pro Sekunde (RPS)

In den vorherigen Abschnitten wurde ein einzelner Back-End-Dienst beschrieben, der die Instanzgruppen bestimmt, die den Traffic vom Lastenausgleichsmodul erhalten sollen. Für jede mit dem Back-End-Dienst verknüpfte Instanzgruppe legt die Beispiellösung balancingMode=RATE fest. Diese Property erteilt dem Lastenausgleichsmodul die Anweisung, den Lastenausgleich jeweils anhand des RPS-Werts vorzunehmen, der in der Property maxRatePerInstance festgelegt ist. In diesem Beispiel ist es der Wert 100. Bei dieser Konfiguration versucht das Lastenausgleichsmodul, jede Instanz bei oder unter 100 RPS zu halten. Im Abschnitt Dokumentation für Back-End-Dienste erhalten Sie weitere Informationen zu den Konfigurations-Properties eines Back-End-Dienstes.

Für eine Skalierung nach RPS-Wert müssen Sie für jede Instanzgruppe, die automatisch skaliert werden soll, ein Autoscaling erstellen. In diesem Beispiel ist die Instanzgruppe eine zonenspezifische Ressource. Daher müssen Sie für jede Zone ein Autoscaling erstellen.

Ein Diagramm, das zeigt, wie Autoscaling in die Architektur einer Anwendung pass.

Jedes Autoscaling hat eine Property utilizationTarget, die den Anteil an der maximalen Bereitstellungskapazität des Lastenausgleichsmoduls bestimmt, den das Autoscaling garantieren soll. In diesem Beispiel wird der Wert von utilizationTarget des Autoscaling auf 80 % der maximalen Rate des Back-End-Dienstes von 100 RPS für jede Instanz festgelegt. Das bedeutet, dass das Autoscaling skaliert, sobald der RPS-Wert 80 % der maximalen Rate pro Instanz, also 80 RPS, übersteigt. Fällt der RPS-Wert unter diesen Schwellenwert, wird vom Autoscaling herunterskaliert.

Ein Flussdiagramm, dass zeigt, wie Autoscaling bestimmt, ob eine Instanz hinzugefügt oder entfernt werden soll.

Bei jedem Autoscaling wird auch eine minimale und eine maximale Anzahl von Instanzen definiert, die vom Autoscaling nicht unter- bzw. überschritten werden soll.

Beachten Sie, dass Autoscaling-Funktionen nur für verwaltete Instanzgruppen verwendet werden können. Weitere Informationen finden Sie unter Instanzgruppen und Autoscaling von Instanzgruppen.

Dateiuploads verarbeiten

Zur Funktionalität der Redmine-Anwendung gehört, dass Nutzer Dateien hochladen und speichern können, wenn sie angemeldet sind. Redmine und viele vergleichbare Webanwendungen speichern diese Dateien standardmäßig direkt auf lokalen Laufwerken. Diese Lösung ist in Ordnung, wenn Sie nur einen Server mit einem klar definierten Sicherungsmechanismus haben. Allerdings ist dies nicht der ideale Ansatz, wenn Sie mit mehreren automatisch skalierten Compute Engine-Instanzen hinter einem Lastenausgleichsmodul arbeiten. Wenn ein Nutzer eine Datei hochlädt, gibt es keine Garantie dafür, dass die nächste Anfrage bei der Maschine ankommt, auf der die Dateien gespeichert sind. Es ist ebenfalls nicht gewährleistet, dass eine überflüssige Instanz, auf der Dateien gespeichert sind, nicht vom Autoscaling entfernt wird.

Eine bessere Lösung stellt Cloud Storage dar. Es bietet einen zentralen Ort, an dem Dateiuploads durch eine automatisch skalierte Gruppe von Webservern gespeichert werden und von dem aus auf die Uploads zugegriffen werden kann. Cloud Storage enthält außerdem eine API, die mit Amazon S3-Clients interoperabel ist. Damit ist es ohne Änderungen mit vorhandenen Anwendungs-Plug-ins für S3, einschließlich des Redmine S3-Plug-in, kompatibel. Viele Drittanbieter- und Open-Source-Anwendungen haben Plug-ins, die Objektspeicher wie Google Cloud Storage unterstützen. Wenn Sie Ihre eigene Anwendung erstellen, können Sie die Cloud Storage API direkt für das Speichern von Dateien verwenden.

Hier sehen Sie den Ablauf für den Upload (blaue Pfeile) und das Abrufen (grüne Pfeile) von Dateien mit Redmine und Google Cloud Storage:

Ein Diagramm, das den Lauf von Anfragen durch die Anwendung Redmine zeigt.

Folgender Prozess wird in der Grafik dargestellt:

  1. Der Nutzer POSTet die Datei aus einem Webbrowser.
  2. Das Lastenausgleichsmodul wählt eine Instanz aus, um die Anfrage zu verarbeiten.
  3. Die Instanz speichert die Datei in Cloud Storage.
  4. Die Instanz speichert Dateimetadaten (z. B. Name, Eigentümer und Standort in Cloud Storage) in der Cloud SQL-Datenbank.
  5. Wenn ein Nutzer eine Datei anfordert, wird die Datei von Cloud Storage in eine Instanz gestreamt.
  6. Die Instanz sendet den Stream durch das Lastenausgleichsmodul.
  7. Die Datei wird an den Nutzer gesendet.

Speicherkapazität

Neben der Möglichkeit, zustandsorientierte Dateiuploads aus Compute Engine-Instanzen zu entfernen, und der dynamischen Skalierbarkeit bietet Cloud Storage redundanten und langlebigen Speicher für eine praktisch unendliche Anzahl von Dateiuploads. Diese Speicherlösung ist robust, skalierbar und kostengünstig. Sie bezahlen nur für den Speicher, den Sie nutzen, ohne sich um die Kapazitätsplanung kümmern zu müssen. Außerdem werden Daten automatisch zonenübergreifend redundant gespeichert.

Kosten

Bisher wurde mit der in diesem Dokument beschriebenen Anwendungsarchitektur gezeigt, wie mithilfe der GCP eine robuste und skalierbare Anwendung erstellt werden kann. Die Frage ist nun, wie die Kosten dafür kontrolliert werden können.

Dieser Abschnitt zeigt, dass die in diesem Dokument beschriebene Anwendungsarchitektur nicht nur robust und skalierbar, sondern auch sehr kostengünstig ist. Als Erstes werden einige allgemeine Annahmen getroffen, wie viel und wie oft die Anwendung genutzt wird. Anschließend werden diese Annahmen in eine einfache Kostenschätzung umgewandelt. Beachten Sie, dass es sich dabei ausschließlich um Annahmen handelt. Sie können diese Zahlen an die erwartete Nutzung Ihrer eigenen Anwendungen anpassen.

Berechnung

Ein wichtiger Aspekt jeder Anwendungsarchitektur ist die Höhe der Betriebskosten der Server. Dieser Kostenanalyse liegen folgende Annahmen zugrunde:

Messwert Wert
Durchschnittliche Seitenaufrufe pro Monat 20.000.000
Durchschnittliche HTTP-Anfragen pro Monat 120.000.000
Spitzenzugriffszeiten (90 % oder höher) Montags bis freitags zwischen 7:00 Uhr und 18:00 Uhr
Datenübertragung pro Seitenaufruf 100 KB
Spitzenzeiten pro Monat (Stunden) 220
Anfragerate zu Spitzenzeiten 127 Anfragen/Sekunde (RPS)
Anfragerate außerhalb der Spitzenzeiten 6 Anfragen/Sekunde (RPS)

Von diesen Annahmen ausgehend können Sie ausrechnen, wie viele Seitenaufrufe die Anwendung während der Spitzenzeiten von Montag bis Freitag zwischen 7:00 Uhr und 18:00 Uhr jeden Monat erhält:

20.000.000 (Aufrufe/Monat) * 6 (Anfragen/Aufruf) * 90 % (zu Spitzenzeiten eingehend) = 108.000.000

Jeden Monat gibt es durchschnittlich 22 Arbeitstage. Wenn jeder Arbeitstag elf Spitzenstunden enthält, müssen Sie ausreichend Rechenressourcen bereitstellen, um 242 Spitzenstunden im Monat bewältigen zu können.

Als Nächstes müssen Sie festlegen, welche Art von Compute Engine-Instanz diese Art von Traffic verarbeiten kann. Diese Anwendungsarchitektur wurde mithilfe von gatling.io einfachen Belastungstests unterzogen. Die Ergebnisse dieser Tests ergaben, dass vier Compute Engine-Instanzen des Typs n1-standard-1 ausreichen.

Außerhalb der Spitzenzeiten werden bei dieser Lösung mindestens zwei Instanzen vom Typ n1-standard-1 ausgeführt.

Die aktuellen Preisschätzungen des Google Cloud Platform-Preisrechners geben Ihnen Auskunft darüber, wie viel die Ausführung der Instanzen kosten wird. Beachten Sie dabei, dass für diese Instanzen in beiden Fällen automatisch ein Rabatt für kontinuierliche Nutzung angesetzt wird.

Lastenausgleich und Datenübertragung

Diese Anwendung stellt ein Lastenausgleichsmodul mit einer einzelnen Weiterleitungsregel bereit. Dies ist die öffentliche IP-Adresse, mit der Nutzer eine Verbindung herstellen. Diese Weiterleitungsregel wird pro Stunde abgerechnet.

Für Datenübertragungsschätzungen sollten Sie zuerst vom schlimmsten Fall ausgehen. Beim Lastenausgleichsmodul fallen Kosten für verarbeitete eingehende Daten an und normale Raten für vom Lastenausgleichsmodul ausgehenden Traffic. Dabei wird angenommen, dass 99,5 % der 120.000.000 HTTP-Anfragen von Nutzern eingehen, die eine Redmine-Projektseite laden. Wenn eine Seite geladen wird, zählt das als eine HTTP GET-Anfrage, die dazu führt, dass fünf weitere HTTP GET-Anfragen andere Inhalte laden (CSS, Bilder und jQuery). Für das Laden einer ganzen Seite sind also sechs HTTP-Anfragen notwendig. Das Ergebnis:

  • Ca. 20.000.000 komplette Seiten geladen pro Monat
  • Rund 10 KB an verarbeiteten eingehenden Daten und 450 KB an Datenübertragungen pro Seite
  • Gesamtmenge von ca. 214 GB Daten, die vom Lastenausgleichsmodul pro Monat verarbeitet werden, und 9.091 GB ausgehenden Traffic

Die restlichen 0,5 % der 20.000.000 HTTP-Anfragen sind HTTP POST-Anfragen, um eine Datei durchschnittlicher Größe (ca. 0,5 MB) für zusätzliche 500 GB verarbeitete Daten pro Monat hochzuladen.

Diese Schätzung des Google Cloud Platform-Preisrechners zeigt die erwarteten Kosten für die 714 GB an Datenübertragungen an, die das Lastenausgleichsmodul verarbeiten würde, plus 9.091 GB ausgehenden Traffic für dieses Szenario.

Diese Schätzung der Datenübertragung betrifft den ungünstigsten Fall, weil dabei der gesamte Inhalt – einschließlich statischer Ressourcen – für jede Anfrage einer Compute Engine-Instanz und über ein Lastenausgleichsmodul ohne Caching oder Content Delivery Network (CDN) bereitgestellt wird. Von den ca. 450 KB Nutzlast für jeden Seitenaufruf werden 333 KB dieser Nutzlast benötigt, um jQuery zu laden – wobei diese Lösung von mehr als 20 Millionen Seitenaufrufen pro Monat ausgeht. Wenn Sie nur eine Zeile der Anwendung ändern, um jQuery aus von Google gehosteten Bibliotheken zu laden, wird die Datenübertragung um 73 % reduziert.

Die aktualisierte Preisschätzung zeigt, wie viel an Datenübertragung eingespart werden kann, wenn Sie zu von Google gehosteten Bibliotheken wechseln.

Speicherung

Diese Lösung verwendet Cloud Storage für alle Dateien, die mit der Redmine-Anwendung hochgeladen werden. Wie im vorherigen Abschnitt beschrieben, machen Dateiuploads ca. 0,5 % dieser Nutzung aus, bei einer durchschnittlichen Größe von ca. 0,5 MB pro Datei. Das bedeutet, dass 1.000.000 neue Dateiuploads jeden Monat zu erwarten sind, was zu zusätzlichen 500 GB Speicher pro Monat führt. Diese Lösung geht außerdem von 1.000.000 HTTP PUT-Vorgängen pro Monat aus, um neue Dateien zu speichern. Dies wird als Vorgang der Klasse A abgerechnet.

Diese Preisschätzung des Google Cloud Platform-Preisrechners zeigt die erwarteten Kosten für die Verwendung von Cloud Storage an.

Diese Architektur verwendet Cloud SQL, um alle relationalen Daten für die Anwendung zu speichern. Von den oben beschriebenen Beispielmesswerten ausgehend, sollte der Datenbanktyp D2 mit 1.024 MB RAM ausreichend Kapazität für die Arbeitslast der Anwendung bieten. Die Datenbank soll rund um die Uhr zur Verfügung stehen. Da diese Datenbank höchstwahrscheinlich sehr viel genutzt wird, ist die Option "Hoch" bei E/A-Vorgängen im Preisrechner zu empfehlen. Für diese Beispielarchitektur wurde ein Test erstellt, bei dem 100.000 Dokumente eingefügt wurden, mit dem Ergebnis, dass ein 50-GB-Laufwerk mehr als 100.000.000 Dokumente unterstützt. Damit kann die Datenbank zur genannten Rate mehr als acht Jahre genutzt werden.

Hier ist eine Schätzung des Google Cloud Platform-Preisrechners, die die erwarteten Kosten für diesen Teil der Architekturkosten zeigt.

Beispiellösung bereitstellen

Informationen zum Bereitstellen der Beispielanwendung aus dieser Lösung finden Sie im GitHub-Repository Skalierbare und robuste Webanwendungen auf der Google Cloud Platform.

Anhang: Neue Instanz hinzufügen

Wenn Sie eine robuste und skalierbare Anwendungsarchitektur erstellen möchten, müssen Sie festlegen, wie neue Instanzen hinzugefügt werden sollen. Insbesondere müssen Sie angeben, wie neue Instanzen automatisch konfiguriert werden sollen, wenn sie online gehen.

In diesem Abschnitt werden einige verfügbare Optionen beschrieben.

Bootstrapping von Softwareinstallationen

Zum Verarbeiten der Webanfrage eines Nutzers benötigt jede Instanz zusätzlich zum Basisbetriebssystem weitere Software sowie Konfigurationsdaten, u. a. mit den Informationen zur Datenbankverbindung und dem Namen des Cloud Storage-Buckets, in dem Dateien gespeichert werden. Wenn Sie sich diese Komponenten als Schichten vorstellen, können Sie den gesamten Stapel visualisieren, der auf jeder Instanz ausgeführt wird.

Ein Diagramm, dass den Softwarestapel einer Instanz zeigt.

Diese Lösung verwendet eine Instanzvorlage, die das Compute Engine-Image festlegt, das Instanzen beim Start nutzen. Dabei wird insbesondere das von Canonical entwickelte und unterstützte Ubuntu 14.10-Image verwendet. Da es sich um das Basisbetriebssystem-Image handelt, enthält es keine spezifische Software und keine von der Anwendung benötigte Konfiguration.

Damit der Rest des Stapels, wenn neue Instanzen hinzugefügt werden, automatisch installiert wird, können Sie eine Kombination aus Google Compute Engine-Startskripts und Chef Solo für ein Bootstrapping zur Startzeit verwenden. Sie haben die Möglichkeit, ein Startskript festzulegen. Fügen Sie hierzu ein Metadaten-Attributelement startup-script zu einer Instanzvorlage hinzu. Ein Startskript wird ausgeführt, wenn die Instanz gestartet wird.

Dieses Startskript leistet Folgendes:

  1. Es installiert den Chef-Client.
  2. Es lädt die besondere Chef-Datei node.json herunter. Diese Datei weist Chef an, welche bestimmte Konfiguration für diese Instanz ausgeführt werden soll.
  3. Es führt Chef aus und sorgt dafür, dass es sich um die detaillierte Konfiguration kümmert.

Hier ist das gesamte Startskript:

#! /bin/bash

# Install Chef
curl -L https://www.opscode.com/chef/install.sh | bash

# Download node.json (runlist)
curl -L https://github.com/googlecloudplatform/... > /tmp/node.json

# Run Chef
chef-solo -j /tmp/node.json -r https://github.com/googlecloudplatform/...

Anmerkung: Eine Beschreibung der Funktionsweise von Chef übersteigt den Umfang dieser Lösung. Alle Anleitungen sind Open-Source-Dokumente und verfügbar unter Skalierbare und robuste Webanwendungen auf der Google Cloud Platform.

Anwendungskonfigurationen bereitstellen

Wenn eine neue Instanz gestartet ist und sich selbst mithilfe des Startskripts und Chef konfiguriert hat, benötigt sie noch einige Informationen, um Anfragen verarbeiten zu können. In diesem Beispiel muss jede Instanz Informationen zur Datenbankverbindung (z. B. Hostname, Nutzername und Passwort) haben sowie den Namen des zu verwendenden Cloud Storage-Buckets und die Anmeldedaten für die Verbindung kennen.

Jede Compute Engine-Instanz hat Metadatenattribute, die Sie definieren können. Sie haben bereits erfahren, wie das spezielle startup-script-Metadatenattribut hinzugefügt wird. Sie können aber auch beliebige Schlüssel/Wert-Paare hinzufügen. Hier haben Sie die Möglichkeit, Attribute in der Instanzvorlage anzugeben und damit Konfigurationsdaten festzulegen, die die Instanzen für die Herstellung der Verbindung zur Datenbank und zum Cloud Storage-Bucket benötigen.

So sehen die Metadaten einer Instanzvorlage in der GCP Console aus:

Ein Screenshot der Google Cloud Platform Console mit den benutzerdefinierten Metadaten für eine Instanzvorlage.

Chef verwendet ein Tool namens Ohai, um diese Konfigurationsinformationen aus den Metadaten der Instanz zu parsen und um Vorlagen für das Erstellen der für die Anwendung erforderlichen Konfigurationsdateien zu laden. Hier sehen Sie die Vorlage, die die Datei database.yaml mit den Informationen zur Datenbankverbindung erstellt und automatisch auf die relevanten Metadatenelemente zugreift:

production:
    adapter: mysql2
    database: <%= node['gce']['instance']['attributes']['dbname'] %>
    host:     <%= node['gce']['instance']['attributes']['dbhost'] %>
    username: <%= node['gce']['instance']['attributes']['dbuser'] %>
    password: <%= node['gce']['instance']['attributes']['dbpassword'] %>
...

Sie können innerhalb einer Instanz mithilfe des lokalen Metadatendienstes auch manuell auf Metadatenwerte zugreifen. Hier können Sie curl verwenden, um auf das Passwort der Datenbank zuzugreifen:

curl "http:/metadata.google.internal/computeMetadata/v1/instance/attributes/dbpassword" -H "Metadata-Flavor: Google"

Hinweise zu Leistung und Abhängigkeiten

Der Bootstrapping-Ansatz aus dieser Lösung beginnt mit einem Standardbetriebssystem-Image, wobei die Software mit Chef beim Start installiert wird und zur Bereitstellung der Konfigurationsdaten der Anwendung die Metadaten der Instanz verwendet werden.

Ein Diagramm, dass den Softwarestapel einer Instanz zeigt. Dieser Stapel zeigt, wie Software mit dem Image gebündelt ist. Einige Softwareprogramme werden beim Start installiert, andere nach dem Start bereitgestellt.

Ein Vorteil dieses Ansatzes ist, dass die Systemkonfiguration in einem Chef-Cookbook festgelegt ist. Für das Cookbook kann die Version verwaltet und es kann freigegeben werden. Außerdem besteht die Möglichkeit, es zur lokalen Bereitstellung virtueller Maschinen für Testzwecke mithilfe von Vagrant oder Docker oder zur Konfiguration von Servern in Ihrem Rechenzentrum oder mit verschiedenen Cloudanbietern zu verwenden. Die Image-Verwaltung ist ebenfalls einfacher: In dieser Beispielanwendung müssen Sie nur das eine Basisbetriebssystem-Image berücksichtigen, das von der Anwendung verwendet wird.

Zu den Nachteilen gehören mögliche langsame Startzeiten, da die gesamte Software heruntergeladen und installiert sowie in einigen Fällen kompiliert werden muss. Die Abhängigkeiten, die zu dieser Methode gehören, müssen ebenfalls beachtet werden: In diesem Beispiel hat Chef mehrere Pakete von apt, Rubygems und GitHub installiert. Sollte eines dieser Repositories beim Starten einer neuen Instanz nicht verfügbar sein, schlägt die Konfiguration fehl.

Benutzerdefinierte Images und Bootstrapping

Da Sie Ihre eigenen benutzerdefinierten Images mit Compute Engine erstellen können, ist die gesamte Installation beim Start nicht der einzige Ansatz für das Bootstrapping. Sie können zum Beispiel:

  1. Ein Ubuntu 14.10-Image starten.
  2. Alles außer der Redmine-App installieren (Ruby, nginx und so weiter).
  3. Aus dem Ergebnis ein Image erstellen.
  4. Dieses Image in der Instanzvorlage verwenden.

Wenn die neue Instanz nun gestartet wird, muss sie nur noch Redmine installieren. Die Startzeit wird verbessert und die Anzahl der externen Paketabhängigkeiten reduziert.

Ein Diagramm, das einen Instanzstack zeigt, auf dessen Image alles außer Redmine installiert wurde.

Sie können den Ansatz mit dem benutzerdefinierten Image noch weiterführen und alles in einem Image verankern, einschließlich aller Abhängigkeiten, Anwendungsquellcodes und Konfigurationen. Der Vorteil dabei sind die schnellste Startzeit und keine externen Abhängigkeiten. Wenn sich aber irgendetwas in Ihrer Anwendung ändert, müssen Sie ein neues Image erstellen und die Instanzvorlage aktualisieren.

Ein Diagramm, das einen Instanzstack mit gebündelter Software im Image zeigt.

Stellen Sie es sich so vor, dass sich die Ansätze zum Bootstrapping einer Instanz auf einem Kontinuum befinden. Mehr Konfiguration zum Startzeitpunkt bedeutet langsamere Startzeiten, aber dafür weniger Images, die verwaltet werden müssen. Mehr Konfiguration in einem benutzerdefinierten Image bedeutet schnellere Startzeiten und weniger Abhängigkeiten, aber möglicherweise sehr viel mehr Images, die verwaltet werden müssen. Für die meisten Kunden ist der richtige Ansatz ein Kompromiss, der sich irgendwo dazwischen befindet. Wählen Sie den Ansatz, der für Sie und Ihre Anwendung am meisten Sinn ergibt.

Ein Diagramm, das die vielen Möglichkeiten zeigt, wie Software auf einer Instanz installiert wird.Die Spanne reicht von der Installation aller Komponenten nach dem Start bis zu allen Komponenten, die mit dem Image in einem Bundle zusammengefasst werden.
Hat Ihnen diese Seite weitergeholfen? Teilen Sie uns Ihr Feedback mit:

Feedback geben zu...