Alexander Kaserbacher
15.05.2023
Performance ist für viele Systeme wichtig. Es gibt einige Stellschrauben, an denen Sie drehen können, um Performance zu optimieren. Darüber hinaus ist ein guter Prozess nützlich, der Sie bei der Optimierung dieser Eigenschaft unterstützt. Die "Performance Efficiency"-Pillars der drei großen Architektur-Frameworks von Amazon Web Services (AWS), Microsoft Azure und Google Cloud beschäftigen sich mit diesem Thema.
In diesem Blogpost lernen wir wichtige Designprinzipien und Best Practices zum Thema Performance kennen. Außerdem werden wir einen Prozess zur Performance-Optimierung illustrieren. Falls Sie weiter in die Tiefe gehen möchten, finden Sie an den entsprechenden Stellen Referenzen. Die Namenskürzel der Verweise haben eine Bedeutung: Kürzel, die mit A beginnen, beziehen sich auf AWS, M auf Microsoft Azure und G auf Google Cloud.
Wenn Sie Performance optimieren möchten, dann müssen Sie Ihr System auf die passenden Ressourcen und Services aufbauen. Dabei sind unterschiedliche Kriterien wichtig.
Auswahl von Rechenressourcen
Zuerst müssen Sie entscheiden, auf welcher Plattform Sie Ihr System ausführen. Daraus ergeben sich weitere Entscheidungen oder Maßnahmen zur Performance-Optimierung. Ich habe einige Aspekte herausgegriffen und in Abbildung 2 eingeordnet.
Server/virtuelle Maschinen
(Monolithische) Systeme, die Unternehmen frisch in die Cloud migriert haben, laufen oft direkt auf virtuellen Maschinen, die sie als Server nutzen. In diesem Modell haben Sie eine sehr hohe Kontrolle über die Konfiguration Ihrer Umgebung.
Ist Ihr System technisch horizontal skalierbar, dann können Sie Autoskalierung einrichten. Dieser Mechanismus kann je nach Auslastung (z.B. CPU-Last der bestehenden Server) neue virtuelle Maschinen zuschalten oder herunterfahren. Ihrer Performance kommt diese Skalierung zugute, da die einzelnen Instanzen weniger Last abbekommen und dadurch weniger Anfragen gleichzeitig abarbeiten müssen.
Darüber hinaus können Sie aus verschiedenen Instanztypen wählen. Diese Instanztypen definieren wie viele Ressourcen Ihre virtuelle Maschine verbrauchen kann. Die Cloud-Anbieter gruppieren diese Instanzen in solche, die für rechenintensive Zwecke optimiert sind oder Instanzen, die besonderen Bedarf an (Arbeits-)Speicher haben. Es gibt auch eine Gruppe der “General Purpose Instances”, die eine möglichst breite und diverse Gruppe an Systemarten unterstützt.
Container
Wenn Sie Ihr System basierend auf Containern betreiben wollen (bspw. in einer Microservice- oder servicebasierten Architektur), dann bieten Ihnen die Cloud-Provider verschiedene Möglichkeiten. Einerseits können Sie selbst konfigurieren, welche virtuelle Maschinen (und damit welche Instanztypen) im Cluster laufen. Hierbei können Sie Regeln definieren, wann Ihre virtuellen Maschinen (= Nodes) oder Container skalieren.
Andererseits können Sie den Cluster auch “serverless” betreiben. In diesem Fall müssen Sie sich keine Gedanken über die zugrunde liegende Infrastruktur machen – Skalierung übernimmt der Cloud-Anbieter.
Durch eine gute Skalierungskonfiguration erhöhen Sie Ihre Performance, da Lastspitzen von zusätzlichen Containern und Nodes abgefangen werden. Denken Sie zudem die Performance-Anforderungen einzelner Container mit und konfigurieren Sie die benötigten Ressourcen entsprechend. Diese Konfigurationen sind sehr Service- und Technologie-spezifisch – einige Ansätze finden Sie für Amazon ECS [ATS], Azure Container Apps [MCC] oder für Kubernetes [KAM] [KAC].
Function as a Service (FaaS)
Vertikale Architekturstile, die ihre Funktionalität auf separate Services aufteilen, laufen oft auf Container-Infrastrukturen. Ist die Funktionalität Ihrer Services sehr klein und bauen Sie zudem auf eine event-getriebene Architektur, dann bietet sich ein “Function as a Service”-Modell an.
Dabei deployen Sie ausführbaren Code auf einer Plattform Ihres Cloud-Anbieters und definieren zudem die Events, die diesen Code anstoßen sollen. Tritt nun eines dieser Events ein, wird Ihr Cloud-Anbieter den Code in Containern starten und ausführen. Sie müssen sich um nichts kümmern – keine Container, keine virtuellen Maschinen.
Auch hier können Sie einiges an Performance herausholen. Da diese Funktionen (vor allem bei Lastspitzen) oft gestartet werden müssen, lohnt es sich ihre Startzeit zu optimieren [ACC] [GSC]. Seien Sie beispielsweise achtsam, wenn Ihr Code viele Abhängigkeiten hat. Wenn diese nicht durch “lazy loading” bei Bedarf geladen werden, sondern beim Hochfahren der Instanz, dann tragen sie zu einer längeren Startzeit bei [GDW]. Darüber hinaus können Sie auch eine Mindestzahl von Funktionsinstanzen definieren, die immer laufen und somit bereit zur Ausführung sind [APC] [GMI] [MRI].
Bei Serverless-Funktionen ist es zudem eine Good Practice, wenn die einzelnen Instanzen aus einer Queue lesen. So können sich bei Lastspitzen eingehende Anfragen in dieser Queue “aufstauen” und belasten nicht die laufenden Instanzen. Ihre Cloud-Umgebung wird bei entsprechender Last in der Queue automatisch die Funktionsinstanzen hochskalieren [MQU].
Auswahl von Datenbanksystemen
Ihr System kann eine oder mehrere verschiedene Datenbanken benutzen. Stellen Sie sicher, dass Sie bei der Wahl Ihrer Datenbank Datencharakteristika, Zugriffsmuster und Skalierungsanforderungen einbeziehen. Alle drei Frameworks geben Ihnen Hinweise und Heuristiken zur Auswahl der passenden Datenbanktechnologie [ADS] [GDS] [MDC]. Abbildung 3 zeigt eine Auswahl von Technologien für jede Kategorie.
Relationale Datenbanken
Nutzen Sie relationale Datenbanken, wenn Ihnen ACID-Eigenschaften [WAP] und referentielle Integrität [WRI] sehr wichtig sind. Zudem eignen sich relationale Datenbanken für strukturierte Daten und geben Ihnen ein klares Schema vor.
Relationale Datenbanken sind schwer horizontal zu skalieren. Meistens müssen Sie die Daten auf verschiedene Instanzen aufteilen, oder replizierte Instanzen nur für Lese-Zugriff freigeben.
Key-Value-Stores und dokumentenorientierte Datenbanken
Im Gegensatz zu relationalen Datenbanken sind solche Datenbanktypen auf Skalierbarkeit ausgelegt. Dadurch, dass Daten sich im Schema nicht direkt referenzieren, können diese Datenbanken deutlich flexibler skalieren und erreichen höhere Performance.
Anders als relationale Datenbanken setzen sie auf “eventual consistency”, sodass neue oder geänderte Daten nicht alle Knoten eines Datenbankclusters unmittelbar erreichen. Auch dieser Faktor erhöht die Flexibilität, da keine aufwendigen Mechanismen zur Konsistenzerhaltung ausgeführt werden müssen. Die folgenden Datenbanken halten es mit der Konsistenz ähnlich.
In-Memory Datenbanken
Diese Datenbanksysteme speichern ihre Daten im Arbeitsspeicher und erreichen somit eine sehr hohe Performance. Insbesondere wenn Sie Caching implementieren wollen und die Daten somit auch potenziell verloren gehen dürfen, sollten Sie an diese Datenbankkategorie denken.
Andere Datenbanksysteme
Wenn Ihre Daten stark vernetzt und in einem Graphen abbildbar sind (z.B. Daten eines sozialen Netzwerkes), dann nutzen Sie Graph-Datenbanken (ein populärer Verteter ist Neo4j). Sind Ihre Daten hingegen zeitlich verteilt und wollen Sie bspw. Daten über bestimmte Zeiträume abfragen, dann ergeben Time-Series-Datenbanken Sinn. Wenn Sie in einem Umfeld sind, in denen sich die beteiligten Knoten untereinander nicht vertrauen, dann stellen Cloud-Anbieter auch Distributed Ledger-Services [WDL] zur Verfügung.
Das Google Cloud Architecture Framework definiert einen Prozess zur kontinuierlichen Evaluation und Optimierung von Performance [GPP], den ich in diesem Abschnitt als Grundlage verwenden werde und in Abbildung 4 skizziert habe. Eine entsprechende Best Practice finden Sie auch im AWS Well-Architected Framework (diese führt allerdings keinen Prozess explizit auf) [APP].
Anforderungen und KPIs definieren
Im ersten Schritt müssen wir unsere Anforderungen an Performance definieren [AKP] [MBG] [GPR]. Abgeleitet von den Anforderungen Ihrer Nutzer, Stakeholder und Industrie-Standards können wir Key Performance Indicators (KPIs) definieren, gegen die wir unser System später testen. Denken Sie hier an extern messbare Eigenschaften wie die Ladezeit der Startseite, die Verarbeitungszeit von eingegebenen Nutzerdaten oder die Wartezeit auf Ergebnisse.
Zudem sollten Sie interne Metriken definieren, die den Zustand eines (Teil-)Systems widerspiegeln. Dies können beispielsweise Anforderungen an CPU oder RAM Ihrer virtuellen Maschinen sein oder der Durchsatz Ihres Datenbanksystems.
System (weiter)entwickeln und deployen
Starten Sie mit Annahmen zu Performance und deployen Sie Ihr System auf einer Infrastruktur, die in ersten Tests Ihre KPIs erfüllt. Ihre Annahmen und Ihre Architektur werden sich wahrscheinlich später ändern und Sie müssen nachjustieren.
Performance monitoren und analysieren
Überwachen Sie laufend Performance-Aspekte (KPIs und technische Metriken wie CPU-Auslastung, I/O und Netzwerk-Latenz, Distributed Tracing etc.) Ihrer Anwendung und identifizieren Sie Flaschenhälse [APM] [GMP] [MMP]. Überwachen Sie Ihr System in Ihrer Produktionsumgebung, um realistische Daten zu erhalten. Darüber hinaus sollten Sie in Ihrer Entwicklungs- bzw. Testumgebung gezielte Performance-Tests durchführen – gute Anregungen zu diesem Thema finden Sie im Azure Well-Architected Framework [MPT].
Performance-Probleme können verschiedene Ursachen haben. Einerseits können sie durch suboptimale Architekturentscheidungen oder ineffiziente Programmierung auftreten – andererseits kann Performance über die Zeit degradieren. Letzteres tritt ein, wenn Sie und Ihr Team im Laufe der Zeit (viele) Architekturentscheidungen getroffen haben, die negativ auf die Performance einzahlen oder sich technische Schulden angesammelt haben.
Wenn Sie unzureichende Performance feststellen, dann haben Sie die Möglichkeit, Performance zu optimieren oder müssen transparent mit Product Ownern über Anforderungen und KPIs sprechen und diese gegebenenfalls nachschärfen, insbesondere wenn Performance-Änderungen teuer und aufwändig wären.
Performance optimieren
Sie können verschiedene Maßnahmen zur Performance-Optimierung ergreifen. Einerseits können Sie Ressourcen optimieren (z.B. schnellere virtuelle Maschinen ausprobieren) oder Service-Konfigurationen auf Performance optimieren – hierzu suchen Sie am besten nach Hinweisen in der Dokumentation des jeweiligen Services. Andererseits können Sie Architekturentscheidungen bewerten und hinterfragen. Im Abschnitt “Auswahl von Ressourcen und Services” haben wir beispielsweise kurz die Auswahl von Datenbanksystemen diskutiert.
Das war der fünfte Beitrag der Blogreihe “Well Architected Cloud”. Im nächsten Artikel werden wir die Pillar “Cost Optimization” und Sustainability beleuchten.
Sie möchten sich zum Thema Performance oder den Architektur-Frameworks der Cloud-Anbieter austauschen? Melden Sie sich gerne, meine Kontaktdaten finden Sie hier.
“Well Architected Cloud: ein Überblick über drei Architektur-Ratgeber”
‘Well Architected Cloud: die “Operational Excellence”-Pillar’
“Well Architected Cloud: die Security-Pillar”
“Well Architected Cloud: die Reliability-Pillar”
“Well Architected Cloud: die Pillars “Cost Optimization” und Sustainability”
“Well Architected Cloud: die Architektur-Frameworks effektiv und effizient einsetzen”
“Die Pillar im AWS Well-Architected Framework”
“Die Pillar im Azure Well-Architected Framework”
“Die Pillar im Google Cloud Architecture Framework”
[ACC] “Understand the available compute configuration options”
[ADS] “Database architecture selection”
[AKP] “stablish key performance indicators (KPIs) to measure workload performance”
[APC] “Configuring reserved concurrency”
[APM] “Record performance-related metrics”
[APP] “Define a process to improve workload performance”
[ATS] “Determining task size”
[GDS] “Data storage”
[GDW] “Use dependencies wisely”
[GMP] “Monitor and analyze performance”
[GMI] “Reduce cold starts by setting a minimum number of instances”
[GSC] “Start containers quickly”
[GPP] “Performance optimization process”
[GPR] “Define performance requirements
“
[KAM] “Assign Memory Resources to Containers and Pods”
[KAC] “Assign CPU Resources to Containers and Pods
“
[MBG] “Identify baselines and goals for performance”
[MCC] “Container Configuration”
[MDC] “Database considerations”
[MMP] “Monitor the performance of a cloud application”
[MPT] “Checklist - Testing for performance efficiency”
[MRI] “Container configuration”
[MQU] “Process faster by queuing and batching requests”
[WAP] “ACID”
[WDL] “Distributed Ledger”
[WRI] “Referential integrity”