Problema y alcance
Diseña un sistema interno de descubrimiento de servicios para 2,000 servicios lógicos y 100,000 instancias dinámicas distribuidas en 3 regiones. La reprogramación de contenedores, el escalado automático y los despliegues continuos cambian las direcciones constantemente. Un despliegue grande puede reemplazar 10,000 endpoints en 2 minutos. Los emisores de llamadas utilizan varios lenguajes, por lo que no se puede esperar que cada equipo mantenga un SDK de descubrimiento sofisticado.
El problema separa dos plazos. Después de que el plano de control observa la transición deliberada de una instancia fuera de servicio, el p99 desde esa observación hasta detener el nuevo tráfico es de como máximo 3 segundos. Si un proceso o nodo desaparece sin previo aviso, el p99 para la detección de fallas críticas es de como máximo 15 segundos. Las llamadas existentes deben continuar desde los últimos endpoints conocidos durante una interrupción de 10 minutos en el plano de control de descubrimiento. Esto no implica que las nuevas instancias se vuelvan visibles o que las instancias en caché permanezcan vivas durante la interrupción.
La cantidad de servicios, la cantidad de instancias, la cantidad de regiones, el tamaño del despliegue y los SLO son suposiciones de la entrevista. El alcance incluye registro, concesiones (leases), estado de salud, consultas de endpoints y entrega incremental, almacenamiento en caché, drenado (draining), comportamiento multirregión y verificación. El balanceo de solicitudes se cubre solo donde el descubrimiento lo requiere; las API de negocio, un plano de datos completo de service-mesh y el DNS público quedan fuera de alcance. Esto es system-design porque la tarea central abarca un plano de control, proxies, señales de salud y el comportamiento del tráfico de extremo a extremo.
Qué evalúan los entrevistadores
La primera señal es separar el registro, el descubrimiento, las decisiones de salud y el enrutamiento. Un registro guarda quién afirma estar ejecutándose y dónde. La lógica de salud decide si una instancia debe recibir tráfico ahora. El descubrimiento entrega candidatos al lado que realiza la llamada. Un proxy o cliente selecciona un candidato. Dibujar los cuatro como una sola base de datos ignora la latencia de propagación y los límites de falla.
La segunda señal es la separación del plano de control y el plano de datos. Si cada solicitud de negocio consulta sincrónicamente el registro, una ralentización del registro se convierte en una interrupción de todo el sitio. Un diseño sólido mantiene instantáneas (snapshots) versionadas en los proxies y recibe los cambios en segundo plano. Durante una falla del plano de control, el plano de datos utiliza su último conjunto conocido y gestiona los endpoints obsoletos con tiempos de espera de conexión cortos, reintentos acotados y expulsión local pasiva.
La tercera señal es reconocer que la información de descubrimiento siempre puede estar obsoleta. Los TTL de DNS, las cachés de proxy, el retraso de observación (watch delay), la detección de fallas y el apagado continuo crean ventanas de tiempo. Una respuesta sólida define qué evento inicia cada temporizador, asigna SLO separados a la retirada deliberada y a las fallas críticas, y presupuesta la cadencia de sondeo, el umbral y la propagación. "La consistencia fuerte evita llamadas a instancias muertas" ignora las particiones y el hecho de que una instancia falle inmediatamente después de una lectura.
Finalmente, el entrevistador busca escala y criterio operativo. Si 100,000 instancias renuevan un lease directamente cada 10 segundos, el registro recibe 10,000 renovaciones por segundo antes de los cambios de rollout y la dispersión de watch (fanout). El diseño debe contener la amplificación de escritura, las tormentas de reconexión, las instantáneas completas, los dominios de falla regionales y las verificaciones de salud defectuosas en lugar de limitarse a nombrar Consul, etcd o Kubernetes.
Preguntas para aclarar antes de responder
- ¿Cuál es la fuente de verdad del registro? Si un orquestador es dueño del ciclo de vida de los Pods, un controlador debería producir los registros. Un agente local con identidad de carga de trabajo puede registrar máquinas virtuales o procesos externos. Permitir el autorregistro sin autenticación contamina el catálogo.
- ¿Cuándo comienza el reloj de 3 segundos? Aquí comienza cuando el plano de control acepta el cambio de READY a DRAINING o no preparado. Si una aplicación se congela antes de reportarlo, la detección de fallas críticas se hace cargo.
- ¿Cuántas eliminaciones en falso puede tolerar el objetivo de 15 segundos? Tres fallas consecutivas reducen los errores por pérdida transitoria de paquetes, pero un intervalo de 5 segundos más los tiempos de espera consume casi todo el presupuesto. Las tasas de error pasivas, la capacidad de reserva regional y las ventanas de confirmación modifican la elección.
- ¿Los emisores de llamadas necesitan direcciones de instancia o una dirección de servicio estable? DNS más balanceo de carga de plataforma es lo más simple para una VIP estable. Un descubrimiento por proxy o cliente es más adecuado cuando los emisores necesitan metadatos de versión, región o partición (shard).
- ¿Una interrupción del plano de control falla abierta (fail open) o cerrada (fail closed)? Los servicios internos ordinarios pueden continuar a partir de la última instantánea conocida. La revocación de seguridad y el aislamiento estricto no pueden depender de una caché de descubrimiento obsoleta; una capa independiente de identidad y autorización debe denegarlos.
- ¿Se permite la conmutación por error (failover) automática entre regiones? Las lecturas sin estado pueden conmutar por política. La residencia de datos, el estado de escritor único o el alto costo interregional deben restringir explícitamente el conjunto de destino.
Respuesta de 30 segundos
"Construiría un plano de control regional y un plano de datos local. Un orquestador o agente autenticado escribe instancias en un catálogo particionado con estados STARTING, READY, DRAINING, UNHEALTHY y EXPIRED. Solo READY es enrutable. El apagado elegante pasa a DRAINING antes de que se drenen las conexiones. Un líder regional ordena los cambios con revisiones monotónicas. Un nivel de distribución envía deltas a 5,000 proxies de nodo, y un proxy obtiene una instantánea completa cuando detecta una brecha de revisión.
Las solicitudes de negocio utilizan el proxy local o una VIP estable y nunca consultan el registro de forma sincrónica. Durante una interrupción, los proxies conservan la última instantánea y expulsan temporalmente los endpoints defectuosos mediante tiempos de espera de conexión, reintentos acotados y errores pasivos. La salud separa el inicio, la preparación, la vitalidad y las señales pasivas. Sondas activas de cinco segundos con tres fallas apuntan a una detección de caídas de aproximadamente 15 segundos. Termino con inyección de fallas para rollouts, particiones, watches rotos, tormentas de reconexión y sondas defectuosas, midiendo la latencia de eliminación, las solicitudes obsoletas y la convergencia".
Análisis paso a paso a profundidad
Paso 1: Definir el modelo de datos y la máquina de estados
La clave del catálogo incluye al menos espacio de nombres (namespace), nombre del servicio y nombre del puerto para que los entornos y protocolos no colisionen. Un endpoint contiene un ID de instancia estable, dirección, región, zona, versión, peso, etiquetas de capacidad, estado, expiración de lease y revisión. Solo se permiten dimensiones de etiquetas predefinidas; los datos arbitrarios de alta cardinalidad no pertenecen al plano de descubrimiento.
Una máquina de estados aporta más significado que un solo booleano healthy. STARTING no recibe tráfico. READY acepta tráfico nuevo. DRAINING detiene nuevas solicitudes mientras finalizan las conexiones existentes. UNHEALTHY refleja una decisión de falla activa o pasiva. EXPIRED significa que un lease no se renovó. Cada transición registra su motivo, origen y revisión monotónica para que las actualizaciones fuera de orden sean auditables.
Las actualizaciones de un endpoint se deduplican por ID de instancia y generación de inicio. Un lease retrasado de un proceso antiguo no puede resucitar una dirección ya reemplazada. Si el orquestador es autoritativo, un controlador observa el ciclo de vida deseado y la preparación real. Con el autorregistro, el escritor se autentica como una carga de trabajo y solo puede modificar su propio registro de servicio e instancia.
Paso 2: Elegir un patrón de descubrimiento sin duplicar complejidad en cada lenguaje
DNS más una VIP estable funciona cuando los emisores de llamadas solo necesitan un nombre de servicio y la plataforma ya gestiona los endpoints y la salud. Devolver direcciones de instancias directamente a través de DNS es simple, pero el TTL crea un dilema entre la carga de consultas y el tiempo de obsolescencia. Los clientes que ignoran un TTL corto amplían el riesgo.
El descubrimiento del lado del cliente puede seleccionar por versión, zona y carga, pero cada lenguaje debe implementar la gestión de watches, el almacenamiento en caché, el balanceo, los reintentos y las actualizaciones seguras. Dado que el problema tiene emisores en múltiples lenguajes, es preferible el descubrimiento del lado del servidor a través de un proxy local de nodo o de plataforma existente. La aplicación llama a una dirección local estable y el proxy es dueño del conjunto de endpoints y la elección del backend. Un salto local adicional proporciona semántica uniforme y actualizaciones rápidas.
Si todas las cargas de trabajo ya se ejecutan en Kubernetes, Service, DNS y EndpointSlice generalmente cubren el descubrimiento básico. Construir otro registro duplicaría la plataforma. Introduce un plano de control independiente solo ante un requisito real de comunicación entre máquinas virtuales, entre clústeres, enrutamiento avanzado o políticas, y prioriza el consumo de la verdad de endpoints del orquestador.
Paso 3: Ordenar escrituras permitiendo lecturas obsoletas
Despliega 3 o 5 réplicas del catálogo por región, utilizando un líder de consenso para los registros y las transiciones de estado. Particiona (shard) por clave de servicio o inquilino para que un solo líder global no sea dueño de las 100,000 instancias. No repliques sincrónicamente cada escritura entre regiones; una partición remota no debería bloquear regiones saludables. La capa global sincroniza la política del servicio y los destinos permitidos para failover.
Una escritura exitosa significa que el catálogo regional aceptó una revisión. No significa que todos los proxies la hayan visto. El distribuidor envía deltas ordenados. Un proxy persiste su última instantánea completa y revisión. Aplica una revisión contigua, pero obtiene una instantánea completa para ese servicio si detecta una brecha, falla la validación o se reconecta después de que la retención de deltas haya expirado. El reemplazo de instantáneas es atómico para que las mitades viejas y nuevas nunca se mezclen.
La ruta de lectura intercambia obsolescencia acotada por disponibilidad. Un proxy registra la antigüedad de la instantánea, el último contacto con el plano de control y el retraso de watch. Alerta cuando supera el presupuesto de antigüedad normal, pero sigue utilizando el último conjunto durante la interrupción estipulada de 10 minutos. Si todos los endpoints conocidos fallan, devuelve un resultado explícito de "sin backend" en lugar de evadir silenciosamente la política hacia cualquier región.
Paso 4: Separar la retirada deliberada de la detección de fallas críticas
Para un apagado elegante, la aplicación primero retira su estado de preparación. El catálogo pasa a DRAINING y los proxies dejan de elegir ese endpoint tras la propagación. El proceso espera durante un período máximo de drenado de conexiones antes de salir. La aplicación también deja de aceptar nuevo trabajo porque la propagación del descubrimiento no es instantánea; los proxies antiguos o las conexiones de larga duración aún pueden retener su dirección.
Una falla crítica no envía ninguna señal. Con sondeos activos cada 5 segundos y eliminación tras 3 fallas consecutivas, una falla justo después de un sondeo exitoso puede consumir casi 15 segundos solo en el muestreo, antes del tiempo de espera del sondeo y la propagación. Cumplir con el p99 de 15 segundos requiere presupuestar juntos el tiempo de espera, el jitter del programador y la entrega, o acortar el intervalo. Un proxy puede expulsar temporalmente un endpoint tras un rechazo de conexión, tiempo de espera o una alta tasa de error local, pero el problema de red de un solo emisor no debe desregistrarlo globalmente.
El inicio, la preparación (readiness) y la vitalidad (liveness) también son distintos. El inicio protege una inicialización lenta. La falla de preparación detiene el tráfico. La falla de vitalidad desencadena un reinicio. Colocar una base de datos compartida en la prueba de vitalidad de cada instancia puede reiniciar todo el servicio durante una interrupción de la base de datos y amplificar la presión. Las dependencias críticas pueden afectar la preparación, pero las verificaciones necesitan tiempos de espera cortos, jitter y protección de capacidad para evitar una tormenta de sondeos.
Paso 5: Calcular la carga de escritura, cambios y distribución (fanout)
Si 100,000 instancias renuevan directamente cada 10 segundos, el estado estable es de 10,000 renovaciones por segundo. Es preferible un watch del orquestador sobre los latidos (heartbeats) por instancia, o agregar renovaciones mediante agentes de nodo con jitter aleatorio. La expiración de concesiones sigue siendo una red de seguridad para registros abandonados, no la ruta normal de retirada.
Reemplazar 10,000 endpoints en 2 minutos promedia aproximadamente 83 adiciones y 83 eliminaciones por segundo, o alrededor de 167 cambios de membresía. Enviar cada cambio de forma independiente a los 5,000 proxies de nodo crearía hasta cerca de 835,000 entregas por segundo. Las suscripciones reales se filtran hacia los servicios que cada proxy necesita, fusionan (coalesce) cambios del mismo servicio en ventanas cortas y utilizan distribución jerárquica. La fusión no debe convertir el SLO de retirada de 3 segundos en un lote de un minuto.
Las instantáneas completas también necesitan un presupuesto. Con un tamaño serializado ilustrativo de 256 bytes por endpoint, una instantánea global de 100,000 endpoints es de aproximadamente 24.4 MiB. Un proxy normal obtiene solo los servicios suscritos, no el catálogo global. La reconexión utiliza retroceso exponencial con jitter, y los distribuidores retienen un registro corto de deltas para que 5,000 proxies no soliciten instantáneas completas simultáneamente tras la recuperación.
Paso 6: Definir límites regionales y de seguridad
Una instancia se registra en su región local por defecto, y las llamadas prefieren endpoints READY en la misma región y zona. Si un catálogo regional pierde cuórum, rechaza nuevas escrituras mientras los proxies locales siguen leyendo sus cachés. Otra región no debe marcar esos endpoints como saludables ni sobrescribir la verdad local basándose en un sondeo remoto.
La política de servicio declara si se permite el enrutamiento interregional, el comportamiento de solo lectura o escritura, el orden de destinos, los límites de capacidad y los límites de datos. Una política explícita desencadena el failover global. Las lecturas sin estado pueden conmutar rápidamente; un proxy para una base de datos de escritor único debe establecer primero la transferencia de propiedad. El descubrimiento devuelve direcciones candidatas y no puede reemplazar un protocolo de consistencia de negocio.
El registro, la desregistración y los watches requieren identidad de carga de trabajo y privilegio mínimo, con cambios de catálogo en un registro de auditoría. Los proxies autentican el plano de control y los servicios sensibles pueden combinar el descubrimiento con TLS mutuo. Las etiquetas de los endpoints no son declaraciones de autorización confiables; los emisores de llamadas aún validan la identidad del servicio y los permisos en el momento de la conexión o la solicitud.
Paso 7: Verificar con líneas de tiempo e inyección de fallas
Primero prueba un reemplazo continuo. La instancia antigua retira su preparación. Registra cuándo el catálogo acepta la revisión, el proxy la aplica, llega la última nueva solicitud y el proceso finaliza. La nueva instancia ingresa al conjunto solo después del inicio y la preparación. Verifica que la propagación de la retirada activa tenga un p99 inferior a 3 segundos, que se completen los trabajos en curso y que haya cero tráfico hacia instancias no preparadas.
Luego termina un proceso forzosamente sin desregistración y verifica que el sondeo de 5 segundos, el umbral de fallas y la distribución cumplan juntos el p99 de 15 segundos. Inyecta una falla de red en un proxy, una falla de nodo completo, seguidores del catálogo retrasados, cambio de líder, pérdida de cuórum regional, una brecha de deltas, una instantánea corrupta y una interrupción de 10 minutos del plano de control. La recuperación se reconecta con jitter y no causa una estampida de instantáneas completas.
Las métricas de producción incluyen tasas de registro y renovación, latencia de escritura del catálogo, recuento de READY por servicio, oscilación de salud (flapping), latencia de sondeo, retraso de watch, antigüedad de instantáneas, brechas de revisión, caídas a instantáneas completas, reconexiones de proxies, nuevas solicitudes a endpoints eliminados, fallas de conexión a endpoints obsoletos y resultados de "sin backend". Una línea de tiempo de tráfico demuestra la propagación; un panel en verde del plano de control por sí solo no lo hace.
Ejemplo de respuesta sólida
"Separo la verdad del registro, las decisiones de salud, la entrega del descubrimiento y el enrutamiento de solicitudes. Un grupo de consenso regional acepta cambios de instancias autenticadas. Cada registro tiene una clave de servicio, ID de instancia, generación de inicio, dirección, región, versión, estado, lease y revisión. Solo READY entra en el conjunto enrutable. El apagado pasa a DRAINING, detiene nuevo tráfico, drena y luego finaliza. Las fallas críticas usan sondeos y expiración de leases. Los errores pasivos se expulsan localmente y no se convierten de inmediato en verdad global.
Dado que los emisores de llamadas son políglotas, no haría que cada proceso observe el registro. Cinco mil proxies de nodo se suscriben solo a los servicios necesarios, persisten una instantánea completa y una revisión monotónica, aplican deltas contiguos y vuelven a obtener un servicio ante una brecha. Las solicitudes usan el proxy local y nunca consultan de forma sincrónica el plano de control. Durante una interrupción de 10 minutos, los proxies usan los últimos endpoints conocidos con tiempos de espera de conexión cortos, reintentos acotados y expulsión local. Las nuevas instancias son invisibles y las direcciones antiguas pueden estar obsoletas, por lo que esos costos son métricas explícitas.
Cien mil instancias renovando cada 10 segundos generarían 10,000 escrituras por segundo, por lo que prefiero el watch del orquestador o la agregación por nodo. Reemplazar 10,000 endpoints en 2 minutos crea aproximadamente 167 cambios de adición y eliminación por segundo. Se filtra por suscripción, se agrupa brevemente y se distribuye jerárquicamente sin consumir el presupuesto de retirada de 3 segundos. Sondear cada 5 segundos y requerir 3 fallas ya se aproxima a 15 segundos, por lo que el tiempo de espera y la propagación forman parte del presupuesto.
Verifico a través de la línea de tiempo del tráfico: ninguna solicitud nueva dentro de los 3 segundos de una retirada aceptada, y un endpoint caído abruptamente sale del conjunto de candidatos en 15 segundos. El tráfico existente continúa durante cambios de líder, una partición regional y una detención de 10 minutos del plano de control. Finalmente, 5,000 proxies se reconectan con jitter, llenan las brechas de revisión y convergen sin sobrecargar el catálogo".
Errores comunes
- Consultar el registro antes de cada solicitud → la demora o falla del plano de control ingresa en la ruta de negocio → almacenar en caché endpoints versionados localmente y recibir cambios en segundo plano.
- Usar un solo booleano
healthy→ las semánticas de inicio, admisión de tráfico, drenado y reinicio colapsan juntas → usar una máquina de estados explícita con orígenes de transición. - Reiniciar ante una falla de preparación (readiness) → una interrupción aguas abajo puede reiniciar toda una flota de servicios → la preparación retira el tráfico; la vitalidad (liveness) cubre solo fallas locales irrecuperables.
- Asumir que un TTL corto de DNS elimina datos obsoletos → las cachés de clientes, la propagación y la detección aún crean una ventana mientras la carga de consultas aumenta → medir el balance del TTL y conservar resiliencia en el plano de datos.
- Permitir el autorregistro sin autenticación → endpoints erróneos u hostiles pueden recibir tráfico interno → usar la verdad del orquestador o identidades de carga de trabajo restringidas.
- Hacer que cada proxy se suscriba al catálogo global → los despliegues y reconexiones crean tormentas de distribución e instantáneas → filtrar por servicio, distribuir jerárquicamente, versionar deltas y aplicar jitter a las reconexiones.
- Replicar fuertemente cada latido (heartbeat) entre regiones → la latencia remota o las particiones bloquean regiones saludables → ordenar escrituras regionalmente y sincronizar solo políticas y metadatos de failover permitidos.
- Tratar el éxito del descubrimiento como éxito de la solicitud → un endpoint puede fallar inmediatamente después de la búsqueda → el protocolo de llamada aún necesita tiempos de espera de conexión, reintentos acotados, disyuntores (circuit breakers) e idempotencia.
- Verificar la salud de cada dependencia → una interrupción compartida elimina todas las instancias a la vez → verificar solo condiciones críticas de admisión de tráfico con tiempos de espera cortos y jitter.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Por qué no permitir que las 100,000 instancias usen descubrimiento del lado del cliente directamente?
El descubrimiento del lado del cliente elimina un salto y permite un enrutamiento detallado, pero duplica la gestión de watches, cachés, reparación de revisiones, balanceo, expulsión y actualizaciones en cada lenguaje y proceso. Con equipos políglotas, los proxies de nodo reducen 100,000 conexiones de clientes a unos 5,000 instancias en el plano de datos. Un único entorno de ejecución maduro con un presupuesto de latencia extremadamente ajustado puede justificar un SDK obligatorio, pero aún requiere pruebas de conformidad de protocolo y actualizaciones forzadas.
Pregunta de seguimiento 2: ¿Por qué es seguro usar endpoints antiguos durante una interrupción del plano de control?
La última instantánea permite que las instancias sobrevivientes continúen, a costa de perder adiciones, retiradas y cambios de failover. Los proxies exponen la antigüedad de la instantánea, usan tiempos de espera cortos y errores pasivos para reducir las llamadas a direcciones muertas, y limitan los reintentos. La revocación de seguridad no debe esperar la propagación de la caché de descubrimiento; una capa independiente de identidad y autorización falla cerrada (fail closed). Una vez que expira la antigüedad máxima de instantánea específica de un servicio, su política puede degradar o rechazar en lugar de aplicar una única regla global.
Pregunta de seguimiento 3: ¿Los sondeos de 5 segundos y 3 fallas realmente cumplen con los 15 segundos?
No necesariamente. Una falla inmediatamente posterior a una verificación exitosa puede usar casi 15 segundos para tres muestras fallidas, y el tiempo de espera, el jitter de programación y la propagación de endpoints agregan más. Para cumplir con el p99, acorta el intervalo, mantén el tiempo de espera por debajo del intervalo y usa el rechazo de conexión u otras señales pasivas para una expulsión local más rápida. La inyección de fallas debe medir la distribución desde la última solicitud exitosa hasta la última ruta nueva; multiplicar los valores de configuración no constituye una prueba.
Pregunta de seguimiento 4: ¿Por qué un despliegue continuo necesita DRAINING en lugar de una eliminación inmediata?
La eliminación solo detiene a los emisores que han visto la nueva lista. No gestiona cachés antiguas, conexiones keep-alive, RPC largas o trabajo en cola. DRAINING elimina el endpoint de los candidatos a nuevas solicitudes mientras el proceso permanece durante un período de drenado acotado. La aplicación también deja de aceptar nuevo trabajo y los tiempos de espera de las llamadas encajan dentro del período de gracia de terminación de la plataforma. La verificación rastrea la última solicitud nueva, la finalización de solicitudes en curso y la terminación forzada.
Pregunta de seguimiento 5: ¿Debería otra región aceptar registros cuando una región pierde el cuórum?
No debería asumir automáticamente la propiedad de la verdad de las instancias de esa región. Una vista interregional puede confundir una partición con una falla total de instancias, y dos planos de control pueden aceptar estados conflictivos. La región sin cuórum detiene las escrituras en el catálogo mientras los proxies leen la caché. La capa global enruta nuevas llamadas únicamente de acuerdo con la política declarada de failover del servicio. Tras la recuperación, las revisiones y las generaciones de inicio concilian el estado para que un lease expirado no pueda sobrescribir una instancia más nueva.