Enunciado y alcance
Esta es una pregunta de diseño de sistemas sobre el límite entre el plano de control del nodo y el runtime. Las llamadas CRI estándar ListContainers, ListPodSandbox y ListImages son RPCs unarias que devuelven todos los resultados en una sola respuesta. En un nodo denso, el resultado serializado puede superar el límite predeterminado de 16 MiB por mensaje de gRPC, lo que impide la reconciliación de kubelet. Kubernetes v1.36 introduce el feature gate alpha CRIListStreaming para que kubelet pueda recibir resultados a través de RPCs de streaming del lado del servidor. El diseño debe abordar conjuntamente el throughput, la memoria, el fallback de compatibilidad y el riesgo de despliegue.
Qué evalúa el entrevistador
- Si conecta el tamaño del mensaje, la asignación de serialización y la falla de reconciliación en lugar de decir únicamente que el nodo es grande.
- Si distingue el transporte de listas en streaming de un watch o flujo de eventos mientras preserva la consistencia de lista más eventos.
- Si el consumidor cuenta con contrapresión, cancelación, plazos límite (deadlines) y semántica explícita para resultados parciales.
- Si comprende un gate en estado alpha deshabilitado por defecto, la capacidad del runtime y el fallback automático.
- Si puede definir condiciones para canary, métricas, alertas y rollback.
Preguntas clarificadoras para hacer
- ¿Cuántos objetos en ejecución, detenidos y sandboxes existen en el punto máximo y qué tan rápido crece ese número?
- ¿Implementa el runtime de contenedores las tres RPCs de streaming y qué versiones están en la ventana de actualización?
- ¿Es el síntoma un error de límite de mensaje, presión de memoria en kubelet o una construcción lenta de listas dentro del runtime?
- ¿Son aceptables reintentos cortos de reconciliación y qué fallas deben fallar de forma cerrada (fail closed)?
- ¿Se puede habilitar el gate alpha en un grupo de nodos aislado y qué tan rápido se puede revertir?
Una respuesta de 30 segundos
“Primero verificaría que las listas CRI unarias colocan cada objeto en un único mensaje gRPC, por lo que aproximadamente diez mil objetos pueden alcanzar el límite predeterminado de 16 MiB y generar un pico de asignación. El gate CRIListStreaming de Kubernetes v1.36 es alpha y está deshabilitado por defecto; al habilitarlo, kubelet utiliza tres RPCs de streaming del lado del servidor y el runtime envía fragmentos que el consumidor fusiona incrementalmente. Realizaría un canary solo en runtimes que admitan las RPCs, limitaría los búferes, propagaría la cancelación y monitorearía la duración de la lista, los bytes de los mensajes, la memoria y los errores de reconciliación. Los runtimes no compatibles retroceden automáticamente a llamadas unarias, pero los nodos densos aún requieren una alerta porque el modo de falla anterior persiste.”
Análisis detallado paso a paso
Paso 1: Aislar la falla de mensaje único
La respuesta unaria contiene la lista completa, por lo que el cliente experimenta picos por la decodificación de protobuf, la asignación de objetos y la fusión de estados. El límite predeterminado de mensajes gRPC es de aproximadamente 16 MiB; la cantidad de objetos, la longitud de los campos y los nombres de las imágenes determinan si se supera. Aproximadamente diez mil contenedores es una señal de escala basada en la experiencia, no un umbral del protocolo; confírmelo con tamaños de objetos, registros del runtime y métricas de kubelet.
Paso 2: Definir el contrato de streaming
Con CRIListStreaming habilitado, kubelet utiliza StreamContainers, StreamPodSandboxes y StreamImages. El runtime, como servidor, envía lotes; el cliente decodifica y fusiona cada lote en lugar de ubicar toda la lista en un solo mensaje. La lista final todavía necesita una instantánea consistente antes de que continúe la lógica de reconciliación existente. Una lista en streaming no es un watch y no crea una suscripción continua a eventos.
Paso 3: Controlar contrapresión, cancelación y fallas
Establezca un deadline y delimite los lotes no procesados y los búferes de objetos. Si el procesamiento se retrasa, pause las lecturas o permita que el runtime observe el control de flujo. Cierre el stream cuando el nodo desaparezca, la versión de sincronización expire o el llamador cancele la operación, evitando fugas de goroutines y conexiones. Una desconexión a mitad del stream no debe reportarse como un éxito completo: descarte la instantánea temporal y reintente, o use un punto de control versionado explícitamente con recuperación idempotente antes de consolidar un estado completo. Los reintentos no deben contar objetos dos veces.
Paso 4: Desplegar con compatibilidad
Haga un inventario de si el runtime implementa las tres RPCs de streaming y luego habilite el feature gate en un grupo de nodos aislado. Un runtime no compatible recurre automáticamente al modo unario para mantener compatibilidad hacia atrás; ese fallback no es una solución de capacidad. Etiquete la capacidad del nodo y genere alertas en nodos de alta densidad que permanezcan en unario. Durante el canary, compare la duración de la lista, el RSS pico, la cola de decodificación, los reintentos de stream y la tasa de fallas de reconciliación entre los nodos con streaming y los nodos con fallback.
Paso 5: Agregar observabilidad y protección de capacidad
Registre el recuento de objetos, el recuento de lotes, los bytes por lote, la duración total, el tiempo hasta el primer lote, las cancelaciones de stream y los motivos de reintento para cada lista. Correlaciónelos con el working set de kubelet, la CPU del runtime, el recuento de conexiones y la presión del nodo para ver si los picos de memoria se transformaron en un mayor tiempo de procesamiento. Establezca límites de objetos, deadlines y control de admisión; devuelva un error diagnosticable cuando se superen los límites en lugar de truncar silenciosamente la lista. El éxito significa un estado de reconciliación completo, no simplemente un primer lote rápido.
Paso 6: Definir límites de rollback y actualización
El gate alpha está deshabilitado por defecto y su alcance debe ser reversible mediante la configuración de kubelet. Si el runtime falla inesperadamente, las desconexiones aumentan, la consistencia de la lista falla o la memoria no mejora, deshabilite el gate y reinicie los kubelets afectados; luego inspeccione los registros del runtime y de kubelet. Preserve la detección de capacidad y el fallback durante las actualizaciones del runtime; expanda el grupo de nodos solo después de que se valide una matriz multiversión.
Respuesta de ejemplo de alta calidad
“Atribuiría el incidente a los picos de asignación de objetos y de mensaje único de la lista CRI unaria, en lugar de simplemente aumentar el límite de gRPC. CRIListStreaming de Kubernetes v1.36 es una funcionalidad alpha deshabilitada por defecto; una vez habilitada, kubelet llama a tres RPCs de streaming del lado del servidor y el runtime envía resultados de contenedores, sandboxes e imágenes en lotes. El cliente fusiona los lotes en una instantánea temporal con un deadline, búferes acotados y cancelación. Una desconexión descarta la instantánea incompleta y reintenta de forma idempotente antes de la reconciliación. El canary primero verifica el soporte de RPCs en el runtime y luego compara el recuento de lotes, la duración inicial y total, el RSS de kubelet, los reintentos y los errores de estado. Los runtimes no compatibles recurren a RPCs unarias, por lo que los nodos densos con fallback aún necesitan alertas ya que persisten los riesgos de 16 MiB y picos de memoria. Cualquier error de consistencia desencadena el rollback del gate.”
Errores comunes
- Tratar una lista en streaming como un watch y perder el límite entre la instantánea y los eventos.
- Aumentar únicamente el límite de gRPC ignorando la serialización y los picos de decodificación de kubelet.
- Confirmar la reconciliación antes de que lleguen todos los lotes, dejando un estado parcial tras una desconexión.
- Asumir que el fallback automático resolvió el problema sin identificar runtimes no compatibles.
- Medir únicamente la duración promedio en lugar del RSS, tamaño de lote, reintentos y completitud del estado.
- Presentar aproximadamente diez mil contenedores como un disparador fijo en lugar de verificar el tamaño de los objetos y las métricas.
Preguntas y respuestas de seguimiento
¿Se pueden conservar los objetos ya recibidos tras una desconexión a mitad del stream?
No como una lista completa por defecto. Escriba los lotes en una instantánea temporal y descártela tras la desconexión, o recupere de forma idempotente desde un punto de control versionado. Confirme solo después de que el marcador de completitud del protocolo y la validación sean exitosos.
¿Qué sucede si el runtime implementa solo una RPC de streaming?
Sondee la capacidad por método y mantenga los métodos no compatibles en RPCs unarias. Registre una etiqueta de capacidad por nodo y genere alertas en nodos densos; la habilitación parcial no elimina todos los modos de falla de listas.
¿Por qué no simplemente aumentar el límite de mensajes?
Un límite mayor solo pospone la falla de mensaje único mientras incrementa los picos de asignación, copia y GC. También puede amplificar los picos de latencia de kubelet/runtime. Es preferible el streaming fragmentado y utilizar métricas para demostrar un estado completo y una mejora de capacidad.