15. November 2024
Release-Zyklus Tage → Stunden + SLO-getriebener BetriebMicroservices-Modernisierung: Vom Monolithen zur beobachtbaren Plattform
Release-Zyklen verkürzt und Incident-Auswirkungen reduziert durch Microservices-Migration und SLO-getriebene Zuverlässigkeit
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.