Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Verstecktes Cloud Waste in Ihrem Code aufspüren

By Joshua FoxMay 19, 20256 min read

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Ein systematischer Ansatz zur Cloud-Kostenoptimierung

Cloud-Kostenoptimierung folgt dem Pareto-Prinzip (80/20): Ein kleiner Teil Ihrer Ressourcen verursacht in der Regel den Großteil der Kosten. Beginnen Sie mit Tools wie DoiT Cloud Intelligence™, um Ihre größten Kostenblöcke zu identifizieren.

Schritt 2: Schnelle administrative Erfolge mitnehmen

Bevor Sie sich in Code-Optimierungen vertiefen, sollten Sie zunächst die einfacheren administrativen Anpassungen in der Cloud angehen:

  • Right-Sizing unterausgelasteter Instanzen
  • Entfernen verwaister Ressourcen
  • Einsatz passender Storage-Tiers
  • Anpassen der Autoscaling-Parameter

Das ist deutlich günstiger, als direkt am Code anzusetzen.

Schritt 3: Auf Anzeichen für Ineffizienz auf Code-Ebene achten

Sobald die administrativen Optimierungen abgeschlossen sind, sollten Sie kostenintensive Ressourcen auf mögliche code-bedingte Ineffizienzen untersuchen.

Wegen der "Effizienz-Illusion" sind diese Anzeichen oft subtil. Die Praxisbeispiele weiter unten in diesem Artikel zeigen typische Warnsignale, die sich häufig schon mit den Standard-Monitoring-Tools in den Konsolen von GCP, AWS und Azure erkennen lassen.

Schritt 4: Untersuchen – Profiling und Analyse

Wechseln Sie von der Cloud-Ebene auf die Code-Ebene und nutzen Sie Profiling-Tools, die sich in der Cloud ausführen lassen.

  • Für Datenbanken: Query-Analyzer und Performance Insights in der Cloud-Konsole
  • Für Anwendungen: sprachspezifische Profiler und Memory-Analyzer
  • Für Datenpipelines: Execution Graphs und Distribution Metrics

Flamegraph, from Google Cloud Profiler

Flame Graph aus dem Google Cloud Profiler

Die Umsetzung kann einfach sein – etwa bei SQL, wo entsprechende Tools direkt in der Cloud bereitstehen – oder anspruchsvoll, etwa beim Memory-Profiling von Python in verteilten Anwendungen in Managed Environments.

Schritt 5: Umsetzen, messen, validieren

Beheben Sie die identifizierten Probleme, deployen Sie neu und messen Sie die technischen Verbesserungen über die Cloud-Konsolen von AWS, GCP und Azure. Prüfen Sie anschließend die Kostenberichte in Cloud Intelligence™, um die Einsparungen zu bestätigen.

Praxisszenarien und Lösungen

Szenario 1: Java-Microservice mit Memory Leaks

Was effizient aussah: Ein Java-Lambda-Microservice mit konstant 70–100 % Speicherauslastung – auf den ersten Blick eine optimale Ressourcennutzung.

Die Realität: Die Anwendung litt unter Memory Leaks: Ein globales Objekt hielt Referenzketten und behielt so Objekte über Aufrufe hinweg im Speicher. Vereinzelte Crashes und Instanzaustausche traten so selten auf, dass sie dem SRE-Team entgingen.

Der Hinweis: Das Monitoring zeigte mit der Zeit ein sägezahnförmiges Muster im Speicherverbrauch. Die Analyse der Einbrüche im Sägezahn führte zu CloudWatch-Logs mit periodischen Crashes.

Untersuchung: Der CodeGuru Profiler wurde aktiviert und zeigte einen kontinuierlich steigenden Speicherverbrauch. Eine Offline-Analyse mit einem JVM-Profiler identifizierte unerwartet zurückgehaltene Objekte.

Lösung: Der Code wurde so angepasst, dass Objektreferenzen am Ende jedes Web-Requests freigegeben werden.

Ergebnis: Stabiler Speicherverbrauch, weniger Instanzaustausche und niedrigere Ressourcenkosten.

Szenario 2: Java-Datenverarbeitungspipeline mit ineffizienten Datenstrukturen

Was effizient aussah: Eine Dataflow-Pipeline mit eigenen Java-Containern, die täglich Millionen Datensätze verarbeitete – bei durchgehend hoher CPU-Auslastung.

Die Realität: Der Code verwendete ineffiziente Datenstrukturen, darunter Maps mit unnötiger String-Konkatenation pro Objekt in engen Schleifen. Das erzeugte massiven Garbage-Collection-Overhead.

Der Hinweis: Der hohe Ressourcenverbrauch deutete auf weiteren Untersuchungsbedarf hin.

Untersuchung: Der GCP Cloud Profiler wurde dem Container hinzugefügt. Er zeigte ein überlineares Wachstum von Laufzeit und Speicherverbrauch bei größeren Datensätzen.

Lösung:

  • Maps wurden durch eigene Objekte ersetzt, die nur die wirklich benötigten Informationen enthalten.
  • Statt wiederholter Konkatenation kam sauberes String-Joining zum Einsatz.

Ergebnis: 50 % weniger Speicherverbrauch und 70 % kürzere Verarbeitungszeit – das ermöglichte kleinere Worker-Maschinen und weniger Instanzen.

Szenario 3: In-Memory-Datenstrukturen führen zu überdimensionierten VMs

Was effizient aussah: Speicherstarke VMs wirkten kostengünstiger als horizontal skalierte Lösungen, und In-Memory-Datenstrukturen in Python boten algorithmische Geschwindigkeitsvorteile gegenüber Datenbankabfragen.

Die Realität: Dieser Ansatz erzeugte gleich mehrere Ineffizienzen:

  • Cloud-Anbieter erzwingen ein Mindestverhältnis von CPU zu Speicher – das führt zu teurer, ungenutzter CPU-Kapazität.
  • Speicher wird in vordefinierten Schritten zugewiesen, sodass für ungenutzten Pufferplatz mitbezahlt wird.
  • Lange Initialisierungszeiten machten es nötig, mehrere teure Instanzen gleichzeitig laufen zu lassen, um die Ausfallsicherheit zu gewährleisten.

Der Hinweis: DoiT Cloud Intelligence™ zeigte, dass ein großer Teil der Gesamtkosten auf extrem große VMs entfiel – typischerweise ein Indikator für problematische Statefulness in Cloud-Architekturen.

Untersuchung: Eine eingehende Analyse der Algorithmen offenbarte Möglichkeiten zum Refactoring, um Daten außerhalb des Anwendungsspeichers abzulegen.

Lösung:

  • Refactoring der Algorithmen, sodass sie mit Teilmengen aus der Datenbank arbeiten, die bei Bedarf abgefragt werden
  • Einführung einer NoSQL-Datenbank mit Redis als In-Memory-Cache
  • Wo das vollständige Vorladen wichtiger Referenzdaten nötig war, ermöglichten optimierte Datenstrukturen in Redis einen kleineren Speicher-Footprint als Objekte im Anwendungsspeicher

Ergebnis: Diese architektonische Änderung verkleinerte die VMs deutlich, ermöglichte horizontale Skalierung und senkte die Kosten radikal – allerdings mit erheblichem Engineering-Aufwand.

Rückblick auf frühere Fälle

Werfen wir noch einen Blick auf zwei weitere Beispiele aus dem eingangs erwähnten Artikel "Stop Chasing Idle Servers" und ordnen sie in dieses Framework ein.

Szenario 4: Datenbank bei 85 % IOPS

Was effizient aussah: Die RDS-Instanz wirkte voll ausgelastet – ein scheinbares Indiz für optimale Ressourcennutzung.

Die Realität: Jede Abfrage führte einen Full-Table-Scan durch, weil zwei kritische Indizes fehlten. Das trieb den Ressourcenbedarf drastisch in die Höhe.

Der Hinweis: Da die meisten SQL-Abfragen keinen hohen Ressourcenverbrauch verursachen sollten (außer in stark optimierten Batch-Prozessen), deutete eine dauerhaft hohe Auslastung auf Optimierungspotenzial hin.

Untersuchung: Problematische Queries und fehlende Indizes wurden mit dem AWS RDS Performance Optimizer identifiziert, der out of the box in der AWS-Konsole verfügbar ist. (GCP bietet mit Cloud SQL Query Insights ein vergleichbares Werkzeug.)

Lösung: Die fehlenden Indizes wurden ergänzt.

Ergebnis: Eine 10-fach geringere Query-Latenz – und die Möglichkeit, die Datenbank um zwei Tiers zu verkleinern.

Szenario 5: Spark-Job bei 70 % CPU – jede Nacht vier Stunden lang

Was effizient aussah: Der Cluster wies eine durchgehend hohe CPU-Auslastung auf, was auf eine angemessene Ressourcenzuteilung hindeutete.

Die Realität: 80 % der Daten konzentrierten sich auf einen einzigen, verzerrten Schlüssel. Das führte zu Straggler-Tasks, die die Verarbeitungszeit erheblich verlängerten.

Der Hinweis: Das Problem trat ab einem bestimmten Zeitpunkt ohne andere erkennbare Ursache auf. (Wie sich später zeigte, fiel dies mit dem Eintreffen neuer Daten mit dem "Hot Key" zusammen.)

Untersuchung: Spark-Code läuft in einer stark verteilten Umgebung – klassische Profiler, wie man sie für Anwendungen nutzt, lassen sich daher kaum einsetzen. Das ist ein guter Grund, die Logik auf einfache Transformationen zu beschränken und keine komplexe Geschäftslogik darin abzubilden. Über die Spark-UI ließ sich jedoch die Task-Verteilung über die Stages analysieren – so wurden die Straggler identifiziert. Das BigTable-Monitoring offenbarte "Hot Keys" in der verarbeiteten Datenbank.

Lösung: Der problematische Schlüssel wurde repartitioniert und gesalzen, um die Workloads gleichmäßiger zu verteilen.

Ergebnis: Die Laufzeit des Jobs sank von 4 Stunden auf 45 Minuten, und die nötige Clustergröße konnte um zwei Drittel reduziert werden.

Echte Cloud-Effizienz erfordert manchmal mehr als nur die richtige Cloud-Konfiguration – nämlich auch das Beheben von Ineffizienzen auf Code-Ebene. Wenn der Code die eigentliche Ursache überhöhter Cloud-Kosten ist, lösen Infrastruktur-Anpassungen allein das Problem nicht.

Wenn Ihr Entwicklungsteam eng mit FinOps und SRE zusammenarbeitet, lassen sich diese versteckten Ineffizienzen mit einem systematischen Vorgehen aufdecken und beseitigen:

  1. Beginnen Sie mit den größten Kostenblöcken aus den Kostenanalysen.
  2. Setzen Sie zuerst Quick Wins auf Ebene der Cloud-Konfiguration um.
  3. Achten Sie in den Cloud-Konsolen auf verräterische Hinweise, die eine tiefere Analyse nahelegen.
  4. Nutzen Sie passende Profiling-Tools – am besten in der Cloud, bei Bedarf offline – um Ineffizienzen punktgenau zu lokalisieren.
  5. Beheben Sie den Code, deployen Sie neu und validieren Sie die Kosteneinsparungen.

Dieser kollaborative Ansatz senkt nicht nur die Kosten, sondern verbessert häufig auch Performance und Zuverlässigkeit Ihrer Anwendung – ein Gewinn für Ihr Budget und für Ihre Nutzer.

Im Customer Reliability Engineering Team von DoiT begleite ich Unternehmen über den gesamten Optimierungsweg hinweg. Mit DoiT Cloud Intelligence™ und jahrzehntelanger Erfahrung helfen wir, Einsparpotenziale zu erkennen, Maßnahmen auf Cloud-Ebene zu beschreiben, Effizienz-Illusionen aufzudecken und die Wirkung von Code-Verbesserungen zu validieren. Sprechen Sie uns an unter doit.com/services.