Uber separa la intención de escalado de su ejecución en Kubernetes
La compañía publica el diseño de ServiceScale, un CRD y su controlador que permiten que varios orquestadores gestionen el escalado de la misma carga sin pisarse.

Uber ha publicado cómo resolvió un problema que aparece en cuanto más de un sistema quiere decidir cuántas réplicas tiene un servicio en Kubernetes. La solución es un recurso propio, ServiceScale, y un controlador nuevo que deja que varios orquestadores gestionen el escalado de la misma carga de trabajo sin pisarse.
El equipo de Container Platform mueve más de 100 clústeres de cómputo repartidos entre centros de datos propios y nubes de Oracle y Google, con unos 4.000 servicios sobre 3 millones de cores y 1,5 millones de lanzamientos de pod al día. Todo eso está federado por una plataforma interna llamada Up, que los dueños de servicio usan para desplegar y declarar expectativas de escalado; el Uber Deployment Controller (UDC) traduce esa intención a primitivas de Kubernetes.
Por qué un controlador nuevo
El detonante fue un cambio en el failover regional. Uber opera centros de datos active-active; cuando una región cae, el tráfico se redirige a otra que necesita capacidad ociosa para absorberlo. En lugar de reservar esa capacidad en todas las regiones, la idea fue reutilizar capacidad de cargas de baja prioridad: bajarlas y subir las críticas. Eso añadió una segunda fuente de intención de escalado que no venía ni de Up ni del UDC.
Extender el UDC con lógica de failover era lo obvio, y lo descartaron. Ese controlador ya está en el camino crítico del ciclo de vida de los servicios, y meterle comportamiento específico de failover habría complicado algo que sostiene los flujos más sensibles. Los ingenieros lo dicen sin rodeos: un fallo en el manejo del failover no se habría quedado aislado en el failover.
La alternativa fue ServiceScale, un CRD, más un controlador propio, el Service Scale Controller (SSC). Cada orquestador expresa su deseo de escalado en el CRD y SSC concilia la intención combinada en objetos de Kubernetes. Sin base de datos externa ni servicio de coordinación aparte, porque no querían un plano de control más difícil de depurar durante un incidente. El estado queda materializado en el propio CRD, así que al investigar se ve qué orquestador pedía qué, y el failback no obliga a reconstruir nada desde los logs.
Tres cosas que aprendieron en producción
La primera fueron las cachés de informer desactualizadas: pueden ir unos segundos por detrás de la realidad y el UDC trataba un campo de estado como entrada terminal. Metieron una guarda de read-your-own-write, anotando la generación que escriben y comprobando que lo leído la refleja antes de reportar. Kubernetes v1.36, de abril de 2026, incorporó una mitigación de staleness comparable.
La segunda fue el multi-escritor. Cuando UDC y SSC actualizaban el mismo recurso a la vez, en ciertas condiciones el ReplicaSet quedaba con metadata desincronizada respecto al spec, lo que rompía el escalado proporcional en los rolling updates y dejaba cargas atascadas. Añadieron observabilidad de esa deriva en toda la flota y un healer automático en el UDC.
La tercera es que el problema de fondo no son las APIs. Un artículo en arXiv sobre la arquitectura de failover cifra el resultado: la provisión en régimen estable bajó de 2x a 1,3x y se eliminaron más de un millón de cores. El despliegue duró un año, con entornos de staging, canaries y pruebas de integración con la herramienta kind para reproducir interacciones reales entre controladores, y terminó sin incidentes que afectaran a los clientes.
Merece la pena mirarlo si operáis una flota multi-clúster o con varios orquestadores: buena parte del trabajo no está en decidir quién manda, sino en lo que ocurre entre escritura y escritura.
