Der Betrieb von Großinfrastrukturen erfordert präzise Kontrollmechanismen, insbesondere wenn heterogene Systeme denselben Satz an Workloads beeinflussen. Ubers Container-Plattform-Team verwaltet eine Flotte von über 100 Kubernetes-Clustern bei verschiedenen Cloud-Anbietern wie Google und Oracle sowie in eigenen Rechenzentren. Mit rund 4.000 aktiven Services auf 3 Millionen CPU-Kernen und circa 1,5 Millionen täglichen Pod-Starts stößt das klassische Paradigma monolithischer Steuerungslogiken an seine Grenzen. Die Einführung der Custom Resource Definition (CRD) ServiceScale adressiert nun die Kernfrage, wie konkurrierende Skalierungsanforderungen deklarativ und konfliktfrei synchronisiert werden können.
Vom Leerlauf-Puffer zum dynamischen Lastabwurf bei regionalen Failovers
Bisher basierte Ubers Ausfallsicherheit auf einem Active-Active-Setup, bei dem im Ernstfall massiver Traffic in überlebende Regionen umgeleitet wird. Um Lastspitzen abzufangen, hielt das Unternehmen historisch hohe Mengen an ungenutzten Rechenkapazitäten vor – ein kostspieliges Modell. Die moderne Architektur setzt stattdessen auf dynamische Ressourcenumverteilung: Bricht eine Region weg, werden unkritische Low-Tier-Workloads gezielt herunterskaliert, um Kapazitäten für unternehmenskritische High-Tier-Dienste freizusetzen.
Dies führte jedoch zu einem fundamentalen Architekturkonflikt: Der reguläre Lifecycle-Controller – der Uber Deployment Controller (UDC), gesteuert über die interne Federation-Plattform Up – verwaltete bereits den kontinuierlichen Soll-Zustand. Ein separater Failover-Orchestrator musste nun temporär eingreifen können. Die Überlegung, Failover-Logik direkt in den UDC einzubauen, verwarfen die Ingenieure bewusst, da der UDC auf dem kritischen Pfad aller Deployments liegt. Regressive Fehler in der Ausfallbehandlung hätten somit unweigerlich das gesamte unternehmensweite Deployment-System gefährdet.
Deklarative Koexistenz via ServiceScale CRD
Die Lösung besteht in einer klaren Trennung zwischen Skalierungsabsicht (Intent) und physischer Umsetzung (Execution). Mit der ServiceScale-CRD und dem dedizierten Service Scale Controller (SSC) wurde eine Abstraktionsschicht geschaffen, über die mehrere Orchestratoren parallel ihre Anforderungen hinterlegen können. Der SSC aggregiert diese konkurrierenden Intentionen und transformiert sie in konkrete Kubernetes-Primitive.
apiVersion: scaling.uber.internal/v1alpha1
kind: ServiceScale
metadata:
name: payment-processing-service
spec:
sources:
steadyState:
replicas: 120
managedBy: UpPlatformController
failoverOverride:
replicas: 350
managedBy: RegionalFailoverOrchestrator
validUntil: "2026-09-30T14:00:00Z"
status:
effectiveReplicas: 350
activeSource: failoverOverrideDieser deklarative Ansatz bringt signifikante Observability-Vorteile: Der aktuelle Systemzustand lässt sich jederzeit direkt im Kubernetes-API-Server inspizieren. Engineers erkennen auf einen Blick, welcher Controller welche Replikazahl anfordert. Zudem erfordert der anschließende Failback keine Rekonstruktion aus Event-Logs, da der ursprüngliche Steady-State im Spec-Objekt intakt bleibt und nach Deaktivierung des Overrides automatisch wieder greift.
Cache-Staleness und Read-Your-Own-Write-Garantien
In der Praxis zeigte sich jedoch eine klassische Hürde ereignisgesteuerter Controller: Informer-Caches hinken dem tatsächlichen etcd-Zustand um Sekundenbruchteile hinterher. Wenn ein Upstream-System wie Up den Status eines Subsystems als Bestätigung für nachgelagerte, irreversible Workflow-Schritte nutzt, führen veraltete Cache-Zustände zu fehlerhaften Zustandsübergängen.
Uber löste dies über eine strikte Read-Your-Own-Write-Konsistenz: Modifiziert ein Controller eine Ressource, annotiert er diese mit seiner aktuellen Generierungsnummer. Nachfolgende Statusmeldungen werden erst dann emittiert, wenn der lokale Cache mindestens diesen Mutationsstand widerspiegelt. Diese Thematik ist industieweit relevant; Kubernetes v1.36 adressierte Cache-Staleness über analoge ResourceVersion-Prüfungen, während parallel an entsprechenden Bibliotheksunterstützungen in controller-runtime gearbeitet wird.
Architekturfazit für Plattform-Teams
Ubers Entwurf verdeutlicht ein universelles Leitprinzip für Plattform-Architekturen: Single-Responsibility-Prinzipien dürfen nicht an Kubernetes-Controller-Grenzen enden. Sobald mehr als eine Quelle denselben Zustand (wie spec.replicas) mutieren will, ist eine dedizierte Vermittlungsschicht zwingend erforderlich. Teams sollten Controller-Pfade für kontinuierliche Deployments radikal von Ausnahme-Orchestrierungen entkoppeln, um Kaskadeneffekte bei Infrastrukturvorfällen strukturell auszuschließen.
