Bei der Entwicklung für Skalierung und Hochverfügbarkeit sorgen

Last reviewed 2023-08-05 UTC

In diesem Dokument des Google Cloud-Architektur-Frameworks werden Designprinzipien erläutert, um Ihre Dienste so zu gestalten, dass sie fehlertolerant sind und entsprechend der Kundennachfrage skaliert werden können. Ein zuverlässiger Dienst kann auch dann Kundenanfragen beantworten, wenn eine hohe Nachfrage nach dem Dienst besteht oder wenn ein Wartungsereignis auftritt. Die folgenden Prinzipien und Best Practices für das Zuverlässigkeitsdesign sollten Teil Ihrer Systemarchitektur und Ihres Bereitstellungsplans sein.

Redundanz für höhere Verfügbarkeit schaffen

Systeme mit hohen Zuverlässigkeitsanforderungen dürfen keine Single Points of Failure haben. Ihre Ressourcen müssen über mehrere fehlerhafte Domains hinweg repliziert werden. Eine fehlerhafte Domain ist ein Pool von Ressourcen, die unabhängig voneinander ausfallen können, z. B. eine VM-Instanz, eine Zone oder eine Region. Wenn Sie über fehlerhafte Domains hinweg replizieren, erhalten Sie eine höhere aggregierte Verfügbarkeit, als es einzelne Instanzen erreichen könnten. Weitere Informationen finden Sie unter Regionen und Zonen.

Zur Isolierung von Fehlern bei der DNS-Registrierung in einzelne Zonen können Sie zonale DNS-Namen für Instanzen im selben Netzwerk verwenden, um aufeinander zuzugreifen. Dies wäre ein spezifisches Beispiel für Redundanz in Ihrer Systemarchitektur.

Mehrzonenarchitektur mit Failover für Hochverfügbarkeit entwerfen

Schützen Sie Ihre Anwendung gegen zonale Ausfälle, indem Sie sie für Ressourcenpools verwenden, die über mehrere Zonen verteilt sind – mit Datenreplikation, Load-Balancing und automatisiertem Failover zwischen den Zonen. Führen Sie zonale Replikate jeder Ebene des Anwendungspakets aus und entfernen Sie alle zonenübergreifenden Abhängigkeiten in der Architektur.

Daten über Regionen hinweg für die Notfallwiederherstellung replizieren

Replizieren oder archivieren Sie Daten in eine entfernte Region, um bei einem regionalen Ausfall oder einem Datenverlust eine Notfallwiederherstellung zu ermöglichen. Wenn die Replikation verwendet wird, ist die Wiederherstellung schneller, da Speichersysteme in der Remote-Region bereits Daten enthalten, die nahezu auf dem neuesten Stand sind. Es besteht weiter die Möglichkeit des Verlusts einer kleinen Datenmenge aufgrund der Replikationsverzögerung. Wenn Sie die regelmäßige Archivierung anstelle der kontinuierlichen Replikation verwenden, müssen Sie bei einer Notfallwiederherstellung Daten aus Sicherungen oder Archiven in einer neuen Region wiederherstellen. Dieses Verfahren führt in der Regel zu längeren Dienstausfällen als bei der Aktivierung eines kontinuierlich aktualisierten Datenbankreplikats und kann aufgrund der Zeitlücke zwischen aufeinanderfolgenden Sicherungsvorgängen zu mehr Datenverlust führen. Unabhängig vom verwendeten Ansatz muss das gesamte Anwendungspaket neu bereitgestellt und in der neuen Region gestartet werden. In diesem Fall ist der Dienst nicht verfügbar.

Ausführliche Informationen zu Konzepten und Techniken zur Notfallwiederherstellung finden Sie unter Architektur der Notfallwiederherstellung bei Ausfällen der Cloud-Infrastruktur.

Multiregionale Architektur für Sicherheit gegen regionale Ausfälle entwerfen

Wenn Ihr Dienst auch im seltenen Fall, wenn eine ganze Region ausfällt, kontinuierlich ausgeführt werden muss, sollten Sie ihn so gestalten, dass er Pools von Rechenressourcen verwendet, die über verschiedene Regionen verteilt sind. Führen Sie regionale Replikate jeder Ebene des Anwendungspakets aus.

Verwenden Sie Datenreplikation über Regionen und automatisches Failover, wenn eine Region ausfällt. Einige Google Cloud-Dienste haben multiregionale Varianten wie Spanner. Verwenden Sie nach Möglichkeit diese multiregionalen Dienste in Ihrem Design, um es vor regionalen Ausfällen zu schützen. Weitere Informationen zu Regionen und Dienstverfügbarkeit finden Sie unter Google Cloud-Standorte.

Sorgen Sie dafür, dass keine regionenübergreifenden Abhängigkeiten vorhanden sind, damit die Auswirkungen eines Ausfalls auf Regionsebene auf diese Region beschränkt sind.

Beseitigen Sie regionale Single Points of Failure, z. B. eine primäre Datenbank in einer Region, die bei Nichterreichbarkeit einen globalen Ausfall verursachen kann. Multiregionale Architekturen kosten oft mehr. Berücksichtigen Sie daher die geschäftlichen Anforderungen und die Kosten, bevor Sie diesen Ansatz anwenden.

Weitere Informationen zur Implementierung der Redundanz in allen fehlerhaften domains finden Sie im Dokument Deployment-Archetypen für Cloudanwendungen (PDF).

Engpässe bei der Skalierbarkeit beseitigen

Ermitteln Sie Systemkomponenten, die nicht über die Ressourcenlimits einer einzelnen VM oder einer einzelnen Zone hinauswachsen können. Einige Anwendungen skalieren vertikal. Hierbei fügen Sie einer einzelnen VM-Instanz weitere CPU-Kerne, Arbeitsspeicher oder Netzwerkbandbreite hinzu, um den Anstieg der Last zu bewältigen. Die Skalierbarkeit dieser Anwendungen ist stark eingeschränkt. Sie müssen sie häufig manuell konfigurieren, um das Wachstum zu bewältigen.

Gestalten Sie diese Komponenten nach Möglichkeit neu, damit sie horizontal skaliert werden können, beispielsweise mit einer Fragmentierung oder Partitionierung über VMs oder Zonen. Fügen Sie weitere Shards hinzu, um das Wachstum des Traffics oder der Nutzung zu bewältigen. Verwenden Sie Standard-VM-Typen, die automatisch hinzugefügt werden können, um erhöhte Shard-Lasten zu bewältigen. Weitere Informationen finden Sie unter Muster für skalierbare und robuste Anwendungen.

Wenn Sie die Anwendung nicht neu entwerfen können, können Sie von Ihnen verwaltete Komponenten durch vollständig verwaltete Cloud-Dienste ersetzen, die für eine horizontale Skalierung ohne Nutzeraktion konzipiert sind.

Service-Levels bei Überlastung reibungslos heruntersetzen

Entwerfen Sie Ihre Dienste so, dass sie eine Überlastung tolerieren können. Dienste sollten eine Überlastung erkennen und Antworten mit geringerer Qualität an den Nutzer zurückgeben oder den Traffic teilweise unterbrechen, damit die Daten nicht vollständig überlastet werden.

So kann ein Dienst beispielsweise auf Nutzeranfragen mit statischen Webseiten reagieren und dynamisches Verhalten, das in der Verarbeitung teurer ist, vorübergehend deaktivieren. Dieses Verhalten ist im Warm-Failover-Muster von Compute Engine zu Cloud Storage ausführlich beschrieben. Alternativ kann der Dienst schreibgeschützte Vorgänge zulassen und Datenaktualisierungen vorübergehend deaktivieren.

Operatoren sollten benachrichtigt werden, um die Fehlerbedingung zu korrigieren, wenn ein Dienst beeinträchtigt wird.

Trafficspitzen verhindern und minimieren

Synchronisieren Sie keine Anfragen zwischen Clients. Wenn zu viele Clients gleichzeitig Traffic senden, verursacht dies Trafficspitzen, die zu kaskadierenden Fehlern führen können.

Implementieren Sie Strategien zur Minderung von Spitzen auf der Serverseite, wie z. B. Drosselung, Warteschlangen, Entlastung oder Schutzschaltung, graduelle Fehlertoleranz und Priorisierung kritischer Anfragen.

Strategien zur Risikominderung auf dem Client umfassen clientseitige Drosselung und exponentiellen Backoff mit Jitter.

Eingaben bereinigen und validieren

Um fehlerhafte, zufällige oder böswillige Eingaben zu verhindern, die Serviceausfälle oder Sicherheitsverstöße verursachen, sollten Sie die Eingabeparameter für APIs und operative Tools bereinigen und validieren. Apigee und Google Cloud Armor können beispielsweise vor Injektionsangriffen schützen.

Verwenden Sie regelmäßig Fuzzing-Tests, bei denen ein Test-Harnisch vorsätzlich APIs mit zufälligen, leeren oder zu großen Eingaben aufruft. Führen Sie diese Tests in einer isolierten Testumgebung durch:

Operative Tools sollten Konfigurationsänderungen automatisch validieren, bevor die Änderungen eingeführt werden, und Änderungen ablehnen, wenn die Validierung fehlschlägt.

Ausfallsicherheit unter Beibehaltung der Funktion

Wenn ein Problem auftritt, sollten die Systemkomponenten so ausfallen, dass das Gesamtsystem weiter funktioniert. Zu diesen Problemen zählen möglicherweise ein Softwarefehler, eine fehlerhafte Eingabe oder Konfiguration, ein ungeplanter Ausfall einer Instanz oder ein menschliches Versagen. Anhand des Prozesses, den Ihre Dienste durchlaufen, lässt sich feststellen, ob Sie übermäßig moderat oder übermäßig vereinfachend sein sollten, anstatt übermäßig restriktiv.

Betrachten Sie die folgenden Beispielszenarien und die Reaktion darauf, wie auf einen Fehler reagiert wird:

  • In der Regel ist es besser, wenn eine Firewallkomponente mit einer fehlerhaften oder leeren Konfiguration "offen" ausfällt und nicht autorisierten Netzwerktraffic für kurze Zeit durchlässt, bis der Zuständige den Fehler behoben hat. Durch dieses Verhalten bleibt der Dienst verfügbar und fällt nicht geschlossen aus und blockiert den gesamten Traffic. Der Dienst muss sich dann auf Authentifizierungs- und Autorisierungsprüfungen im Anwendungs-Stack verlassen, um vertrauliche Bereiche zu schützen, während der gesamte Traffic weitergeleitet wird.
  • Jedoch ist es besser, wenn eine Berechtigungsserverkomponente, die den Zugriff auf Nutzerdaten steuert, geschlossen ausfällt und jeden Zugriff blockiert. Dieses Verhalten führt zu einem Ausfall des Dienstes, wenn die Konfiguration beschädigt ist, vermeidet aber das Risiko eines Lecks vertraulicher Nutzerdaten, wenn sie offen ausfällt.

In beiden Fällen sollte der Fehler eine Benachrichtigung mit hoher Priorität auslösen, damit ein Operator die Fehlerbedingung beheben kann. Dienstkomponenten sollten dem Fehler standhalten, es sei denn, es birgt große Risiken für das Unternehmen.

API-Aufrufe und operative Befehle wiederholbar entwickeln

APIs und Tools müssen Aufrufe so weit wie möglich wiederholungssicher machen. Bei vielen Fehlerbedingungen wird oft versucht, die vorherige Aktion zu wiederholen, aber Sie wissen möglicherweise nicht, ob der erste Versuch erfolgreich war.

Ihre Systemarchitektur sollte dafür sorgen, dass Aktionen idempotent sind – wenn Sie die gleiche Aktion an einem Objekt zwei oder mehr Mal hintereinander ausführen, sollte dies zu den gleichen Ergebnissen führen wie ein einziger Aufruf. Nicht-idempotente Aktionen erfordern komplexeren Code, um eine Verfälschung des Systemzustands zu vermeiden.

Dienstabhängigkeiten erkennen und verwalten

Dienstdesigner und -inhaber müssen eine vollständige Liste von Abhängigkeiten von anderen Systemkomponenten verwalten. Das Dienstdesign muss auch die Wiederherstellung nach Abhängigkeitsfehlern oder eine graduelle Fehlertoleranz umfassen, wenn keine vollständige Wiederherstellung möglich ist. Berücksichtigen Sie Abhängigkeiten von Cloud-Diensten, die von Ihrem System und externen Abhängigkeiten verwendet werden, z. B. Dienst-APIs von Drittanbietern, wissend, dass jede Systemabhängigkeit eine Fehlerquote ungleich null hat.

Beachten Sie beim Festlegen von Zuverlässigkeitszielen, dass das SLO für einen Dienst mathematisch durch die SLOs aller kritischen Abhängigkeiten beschränkt ist. Sie können nicht zuverlässiger als das niedrigste SLO einer der Abhängigkeiten sein. Weitere Informationen finden Sie im Bereich der Dienstverfügbarkeit.

Startabhängigkeiten

Dienste verhalten sich im Vergleich zu ihrem stabilen Zustand-Verhalten beim Start anders. Startabhängigkeiten können sich erheblich von stabilen Zustand-Laufzeitabhängigkeiten unterscheiden.

So kann es beispielsweise sein, dass ein Service beim Start Nutzer- oder Kontoinformationen aus einem Nutzer-Metadatendienst laden muss, den er nur selten wieder aufruft. Wenn viele Dienstreplikate nach einem Absturz oder einer routinemäßigen Wartung neu gestartet werden, können die Replikate die Last für Startabhängigkeiten stark erhöhen, insbesondere wenn Caches leer sind und neu gefüllt werden müssen.

Testen Sie den Starts des Services unter Last und stellen Sie die Startabhängigkeiten entsprechend bereit. Ziehen Sie ein Design in Erwägung, das eine schrittweise Herabsetzung durch Speichern einer Kopie der Daten vorsieht, die es von kritischen Startabhängigkeiten abruft. Durch dieses Verhalten kann Ihr Service mit möglicherweise veralteten Daten neu gestartet werden, anstatt bei einem Ausfall einer kritischen Abhängigkeit nicht mehr starten zu können. Ihr Service kann später aktuelle Daten laden, wenn dies möglich ist, um zum normalen Betrieb zurückzukehren.

Startabhängigkeiten sind auch wichtig, wenn Sie einen Dienst in einer neuen Umgebung booten. Entwerfen Sie Ihren Anwendungs-Stack mit einer mehrschichtigen Architektur, ohne zyklische Abhängigkeiten zwischen den Schichten. Zyklische Abhängigkeiten scheinen vielleicht tolerierbar zu sein, da sie inkrementelle Änderungen an einer einzelnen Anwendung nicht blockieren. Durch zyklische Abhängigkeiten kann es jedoch schwierig oder unmöglich sein, einen Neustart auszuführen, wenn ein schwerwiegendes Ereignis den gesamten Dienst-Stack deaktiviert hat.

Kritische Abhängigkeiten minimieren

Minimieren Sie die Anzahl der kritischen Abhängigkeiten für Ihren Dienst, d. h. andere Komponenten, deren Fehler zu Ausfällen bei Ihrem Dienst führen. werden. Wenn Sie Ihren Dienst gegenüber Fehlern oder Verzögerungen bei anderen Komponenten, von denen er abhängig ist, stabiler machen möchten, sollten Sie die folgenden Methoden und Prinzipien des Beispieldesigns beachten, um kritische Abhängigkeiten in nicht kritische Abhängigkeiten umzuwandeln:

  • Erhöhen Sie die Redundanzstufe in kritischen Abhängigkeiten. Weitere Replikate verringern die Wahrscheinlichkeit, dass eine ganze Komponente nicht mehr verfügbar ist.
  • Verwenden Sie asynchrone Anfragen an andere Dienste, anstatt eine Antwort zu blockieren, oder veröffentlichen/abonnieren Sie Nachrichten, um Anfragen von Antworten zu entkoppeln.
  • Speichern Sie Antworten von anderen Diensten im Cache, um eine Wiederherstellung nach kurzfristiger Nichtverfügbarkeit von Abhängigkeiten durchzuführen.

Berücksichtigen Sie die folgenden Beispieldesigntechniken und -prinzipien, um Fehler oder Langsamkeit in Ihrem Dienst für andere Komponenten, die davon abhängig sind, weniger schädlich zu machen:

  • Verwenden Sie priorisierte Anfragewarteschlangen und weisen Sie Anfragen, bei denen ein Nutzer auf eine Antwort wartet, eine höhere Priorität zu.
  • Stellen Sie Antworten aus einem Cache bereit, um Latenz und Last zu reduzieren.
  • Ausfallsicherheit unter Beibehaltung der Funktion
  • Setzen Sie bei Traffic-Überlastung die Dienstleistung schrittweise herab.

Gewährleisten, dass jede Änderung rückgängig gemacht werden kann

Wenn es keine klar definierte Möglichkeit gibt, bestimmte Arten von Änderungen an einem Dienst rückgängig zu machen, ändern Sie das Design des Dienstes so, dass ein Rollback unterstützt wird. Testen Sie die Rollback-Prozesse regelmäßig. APIs für jede Komponente oder jeden Mikrodienst müssen versionsverwaltet und abwärtskompatibel sein, damit die vorherige Generation von Clients weiterhin korrekt funktioniert, wenn die API weiterentwickelt wird. Dieser Gestaltungsgrundsatz ist unerlässlich, um eine schrittweise Einführung von API-Änderungen und bei Bedarf ein schnelles Rollback zu ermöglichen.

Ein Rollback kann für mobile Anwendungen teuer sein. Firebase Remote Config ist ein Google Cloud-Dienst, der das Rollback von Features vereinfacht.

Änderungen des Datenbankschemas können nicht einfach rückgängig gemacht werden. Führen Sie sie daher in mehreren Phasen aus. Entwerfen Sie jede Phase so, dass Anfragen zum sicheren Lesen und Aktualisieren des Schemas durch die neueste Version Ihrer Anwendung und die vorherige Version zugelassen werden. Dieser Designansatz ermöglicht ein sicheres Rollback, wenn ein Problem mit der neuesten Version auftritt.

Empfehlungen

Befolgen Sie diese Empfehlungen, um die Anleitung im Architektur-Framework auf Ihre eigene Umgebung anzuwenden:

  • Implementieren Sie den exponentiellen Backoff mit zufälliger Anordnung in der Fehlerwiederholungslogik von Clientanwendungen.
  • Implementieren Sie eine multiregionale Architektur mit automatischem Failover für Hochverfügbarkeit.
  • Verwenden Sie Load-Balancing, um Nutzeranfragen auf Shards und Regionen zu verteilen.
  • Entwickeln Sie die Anwendung so, dass sie ihre Leistung bei Überlastung schrittweise herabsetzt. Bieten Sie Teilantworten oder bieten Sie eingeschränkte Funktionen an, anstatt vollständig auszufallen.
  • Richten Sie einen datengetriebenen Prozess für die Kapazitätsplanung ein und legen Sie mithilfe von Lasttests und Traffic-Prognosen fest, wann Ressourcen bereitgestellt werden sollten.
  • Legen Sie Notfallwiederherstellungsverfahren fest und testen Sie sie regelmäßig.

Nächste Schritte

Weitere Kategorien im Architektur-Framework kennenlernen, z. B. Systemdesign, operative Exzellenz sowie Sicherheit, Datenschutz und Compliance