15. November 2024

Release-Zyklus Tage → Stunden + SLO-getriebener Betrieb

Microservices-Modernisierung: Vom Monolithen zur beobachtbaren Plattform

Release-Zyklen verkürzt und Incident-Auswirkungen reduziert durch Microservices-Migration und SLO-getriebene Zuverlässigkeit

Platform EngineeringMicroservicesSREBeobachtbarkeit

Umfang & Rahmenbedingungen

Rolle
Systemarchitekt
Umfang
Plattformmodernisierung + Beobachtbarkeit + Zuverlässigkeit

Rolle & Umfang

  • Team: ~15 Engineers in produktorientierten Squads
  • Verantwortung: Zielarchitektur, Migrationsstrategie, Zuverlässigkeitsstandards und Delivery-Fundament
  • Stakeholder: Produktleitung, Betrieb und Business Owner für kritische Flows
  • Rahmenbedingungen: hohes Traffic-Volumen, Risikomanagement und inkrementelle Migration ohne Downtime

Outcome-Metriken

  • Lieferung: Release-Zyklus von Wochen auf Tage/Stunden (repräsentativ)
  • Zuverlässigkeit: SLOs + Error Budgets fokussierten Arbeit auf Nutzerwirkung
  • Betrieb: schnellere Diagnose via Metriken/Logs/Traces (repräsentative MTTR-Reduktion)

TL;DR

  • Liefergeschwindigkeit: Teams konnten unabhängiger shippen; Release-Zyklen gingen von Wochen auf Tage/Stunden (repräsentativ).
  • Zuverlässigkeit & Betrieb: SLOs und Error Budgets fokussierten Zuverlässigkeitsarbeit auf Nutzerwirkung. Beobachtbarkeit (Metriken/Logs/Traces) reduzierte Incident-Auswirkungen und verbesserte die Zeit bis zur Diagnose.
  • Effizienz & Autonomie: Services skalierten unabhängig nach Nachfrage. Teams hatten End-to-End Ownership mit klarerer Accountability.

Kontext

Ich stieg in ein Insurtech-Produkt ein, das als monolithische PHP-Anwendung betrieben wurde. Das System bediente hohen Traffic und geschäftskritische Nutzerflüsse (Angebotserstellung, Onboarding, Policy-Änderungen, Schadenmeldungen), aber Deployment-Zyklen waren langsam (2–3‑wöchige Releases) und kaskadierende Ausfälle wurden häufiger, je mehr Traffic und Funktionsumfang wuchsen.

Das Engineering-Team (~15 Personen) war funktional organisiert, was schnelle Lieferung erschwerte. Änderungen erforderten Koordination über mehrere Teams hinweg, und die Blast Radius war groß: Ein fehlerhaftes Deployment konnte die gesamte Plattform beeinträchtigen.

Problem

Die Kernprobleme:

Deployment-Engpässe: Der Monolith erforderte koordinierte Releases mit langen QA-Zyklen. Teams konnten nicht unabhängig shippen, was zu gebündelten Releases und verzögerten Features führte.

Zuverlässigkeit: Kaskadierende Ausfälle waren häufig. Ein Speicherleck in einem Subsystem konnte die gesamte Anwendung beeinträchtigen. Uns fehlte Sichtbarkeit, welche Komponenten unter Last ausfielen.

Skalierungsgrenzen: Der Monolith skalierte als eine Einheit. Wir konnten High-Traffic-Services (z. B. Quote/Pricing) nicht unabhängig skalieren, ohne Ressourcen für Low-Traffic-Funktionen zu überprovisionieren.

Zu geringe Beobachtbarkeit: Wir hatten Basis‑Überwachung (CPU, Speicher, Antwortzeiten), aber keine ausreichend granularen Signale, um Verhalten unter Last zu verstehen oder Incidents schnell zu diagnostizieren.

Vorgehen

Wir modernisierten inkrementell: High-Value-Services zuerst, Zuverlässigkeitspraktiken parallel zur Migration.

Strategie zur Service-Zerlegung:

  • Start mit abgegrenzten Kontexten mit klaren Interfaces (Angebotserstellung, Identität/Profil, Policy‑Lebenszyklus, Schadenaufnahme)
  • Strangler-Fig-Pattern: Traffic schrittweise zu neuen Services routen, Monolith als Fallback
  • Service Contracts via OpenAPI + Contract Testing
  • Split-Stack dort, wo sinnvoll: Go für performancekritische Services, NestJS für Produkt-APIs/BFF-Orchestrierung, Next.js für das Frontend

SLI/SLO-Framework:

  • Mit Produkt- und Business-Stakeholdern sinnvolle SLIs (Availability, Latency, Error Rate) definiert
  • SLOs an User Expectations und Business Requirements ausgerichtet (z. B. 99,5% Availability für den Quote-Flow)
  • Error Budgets eingeführt, um Feature Velocity und Zuverlässigkeitsarbeit zu balancieren

Plattform- und Delivery-Fundament:

  • Services mit Docker containerisiert, um Dev/Test/Deploy zu standardisieren
  • Laufzeitumgebung auf AWS gebracht, um reproduzierbare Environments und sicherere Rollouts zu ermöglichen
  • CI/CD-Pipelines implementiert, um Build/Test/Deploy zu automatisieren (weniger manueller Release-Aufwand und Risiko)

Beobachtbarkeits-Stack:

  • Prometheus + Grafana für Metrics und Dashboards
  • Zentrales Logging mit strukturierten Logs (JSON) via ELK
  • Distributed Tracing mit Jaeger, um Request-Flows über Services hinweg zu verstehen
  • Runbooks und Alerts an SLO-Verletzungen gekoppelt

Organisatorische Änderungen:

  • Umstellung auf produktorientierte Teams mit End-to-End Ownership
  • On-Call-Rotationen mit klaren Eskalationspfaden etabliert
  • Game Days durchgeführt, um Incident Response zu üben und Beobachtbarkeit zu validieren

Ergebnisse

Über 18 Monate migrierten wir 8 Kern-Services auf eine Microservices-Architektur:

Planbare Lieferung: Teams shippten unabhängiger und häufiger; Release-Zyklen gingen von Wochen auf Tage/Stunden (repräsentativ).

Zuverlässigkeit und Betrieb: SLOs + Error Budgets fokussierten Zuverlässigkeitsarbeit. Beobachtbarkeit (Metriken/Logs/Traces) reduzierte Incident-Auswirkungen und verbesserte die Zeit bis zur Diagnose.

Effizienz und Autonomie: Services skalierten unabhängig nach Nachfrage, Teams hatten End-to-End Ownership mit klarerer Accountability.

Stack / Rahmenbedingungen

Stack: PHP-Monolith migriert zu Microservices mit Go und NestJS, mit einem Next.js Frontend; Docker für Containerisierung; AWS für Hosting; CI/CD für automatisierten Build/Test/Deploy; OpenAPI für Contracts; Prometheus/Grafana für Metrics; ELK für Logs; Jaeger für Distributed Tracing.

Rahmenbedingungen: Hohes Traffic-Volumen, Risikomanagement-Anforderungen und inkrementelle Migration ohne Downtime.

Entscheidungen & Tradeoffs

  • Inkrementelle Migration vs. Rewrite: Strangler-Pattern, um Risiko zu reduzieren und Business Continuity zu sichern.
  • Service-Grenzen vs. Geschwindigkeit: Domain Modeling investiert, um „chatty“ Services zu vermeiden; langsamere Anfangsphase zugunsten besserer Operability.
  • Rollout der Beobachtbarkeit: Erst Metrics/Alerting an SLOs gekoppelt, bevor tiefes Tracing ausgerollt wurde, um Adoption nachhaltig zu halten.

Was ich anders machen würde

Früher in Contract Testing investieren: Mehr Contract Tests von Tag 1 hätten mehrere Integrationsprobleme früher sichtbar gemacht.

Beobachtbarkeit schrittweiser ausrollen: Metrics, Logs und Tracing gleichzeitig einzuführen war für Teams überfordernd. Ein phasenweises Vorgehen (Metrics → Logs → Tracing) wäre nachhaltiger gewesen.

Service-Grenzen früher schärfen: Einige initiale Service-Grenzen waren nicht optimal, was zu chatty inter-service Kommunikation führte. Mehr Zeit für Domain Modeling und Event Storming vor dem ersten Schnitt hätte geholfen.

Bessere Dokumentation von Entscheidungen: Wir dokumentierten nicht immer konsequent, warum wir bestimmte Tradeoffs gewählt haben. Das erschwerte Onboarding neuer Teammitglieder.

Trotz dieser Learnings war die Migration erfolgreich: bessere Reliability, schnellere Deployments und mehr Team-Autonomie. Der SLO-getriebene Reliability-Ansatz steuert Engineering-Prioritäten bis heute.