Nube e infraestructura

Cómo mecanismos válidos de Kubernetes pueden provocar cortes en el plano de control

Los planos de control de Kubernetes enfrentan rutas de fallo ocultas donde mecanismos válidos como los informers, la agregación de esquemas y las sondas de liveness amplifican defectos locales hasta convertirlos en cortes a nivel del sistema.

Un cubo de vidrio con capas de circuitos brillantes que representan la complejidad del plano de control de Kubernetes.
Ilustración generada para este artículo

Traducido automáticamente del original en inglés.

Los planos de control de Kubernetes están diseñados para aislar fallos individuales de componentes, pero su disponibilidad general sigue siendo vulnerable al comportamiento de los componentes críticos bajo estrés. Un análisis reciente revela que, a escala de flota, un componente no necesita fallar para causar una interrupción; en cambio, los mecanismos estándar pueden amplificar defectos locales a través de metadatos compartidos o trabajos de inicio sincronizados.

Qué ocurrió

Tres casos específicos ilustran cómo funciones estándar de Kubernetes pueden impulsar inadvertidamente la inestabilidad del plano de control. En el primer caso, un servidor de API de extensión publicó un esquema inválido tras una actualización de dependencias. Este error bloqueó a clientes que nunca habían interactuado con la API de extensión. El fallo se propagó a través de metadatos compartidos, específicamente un documento de esquema que combinaba definiciones de todas las APIs registradas en el clúster. Los usuarios que intentaban despliegues con Helm para charts que solo contenían recursos estándar de Kubernetes encontraron errores de validación antes de que sus solicitudes llegaran incluso al servidor de API, a pesar de no tener ninguna relación con la extensión defectuosa.

El segundo caso involucró a un agente DaemonSet reiniciándose en una flota grande. Aunque estaba previsto como una medida de recuperación, el reinicio progresivo convirtió el trabajo rutinario de inicio en una tormenta de solicitudes que el servidor de API no pudo absorber. Cada nueva instancia del agente reconstruía su vista local del estado del clúster listando todos los Pods. Como muchos agentes iniciaban simultáneamente, generaron una oleada sincronizada de operaciones de listado. En clústeres grandes, estas solicitudes devolvían decenas de miles de objetos, agotando la memoria y la CPU del servidor de API mientras construía respuestas masivas para miles de clientes concurrentes.

En el tercer escenario, una comprobación profunda de liveness en un componente dentro de la ruta de solicitud de la API trató una caída de una dependencia externa como un fallo del proceso. La sonda reinició repetidamente el componente mientras la dependencia externa seguía indisponible. Este ciclo provocó reinicios constantes sin resolver el problema subyacente, interrumpiendo el tráfico de la API y descartando el estado en caché que podría haber mantenido una capacidad de servicio parcial durante la degradación.

Cómo funciona

Estos fallos derivan de cómo Kubernetes expone los metadatos de la API y gestiona la sincronización del estado. Kubernetes utiliza servidores de API agregados para extender la funcionalidad sin modificar el núcleo. Para los clientes, estas extensiones parecen nativas. El sistema expone contratos de esquema en dos formas: OpenAPI v2 y v3. OpenAPI v2 proporciona un único documento combinado que contiene definiciones tanto del núcleo como de todas las APIs de extensión. Esto crea un dominio de fallo compartido donde una definición inválida de una extensión puede impedir que los clientes analicen todo el documento, bloqueando el acceso a recursos no relacionados. OpenAPI v3 separa los esquemas por grupo de API, reduciendo el alcance del impacto.

Los informers proporcionan a los controladores una vista local del estado del clúster, reduciendo las consultas repetidas al servidor de API. Sin embargo, cuando muchos controladores se reinician simultáneamente, la reconstrucción de estas vistas ejerce una presión significativa sobre el plano de control. Los informers suelen usar resourceVersion=0 para leer desde la caché de watch del servidor de API en lugar de etcd. Históricamente, esta ruta ignoraba los límites de paginación, devolviendo colecciones completas de objetos. Cuando miles de agentes solicitan estas listas completas concurrentemente durante un evento de recuperación, el servidor de API lucha por serializar y enviar cientos de megabytes de datos por solicitud.

Las sondas de liveness están diseñadas para reiniciar procesos poco saludables. Sin embargo, si una sonda verifica dependencias externas en lugar de la capacidad local, puede reiniciar un proceso que aún es funcional utilizando estado en caché. Por ejemplo, un proveedor KMS podría perder conectividad con un servicio externo de gestión de claves pero seguir atendiendo solicitudes de cifrado usando claves en caché. Una sonda que prueba la accesibilidad externa reiniciará innecesariamente el proveedor, forzándolo a restablecer conexiones y descartando estado útil en caché.

Detalles clave

  • OpenAPI v2 combina todos los esquemas de API en un solo documento, creando un dominio de fallo compartido donde un esquema de extensión inválido puede bloquear a todos los clientes.
  • OpenAPI v3 separa los esquemas por grupo de API, limitando el radio de explosión de las definiciones inválidas a usuarios específicos de la API.
  • Los informers que usan resourceVersion=0 leen desde la caché de watch, que históricamente ignoraba los límites de paginación, lo que llevaba a tamaños de respuesta masivos.
  • Los reinicios sincronizados de agentes DaemonSet pueden generar una tormenta de solicitudes, abrumando al servidor de API con operaciones de listado concurrentes.
  • Las sondas de liveness que verifican dependencias externas pueden reiniciar procesos que aún son capaces de atender solicitudes usando estado en caché.
  • La corrección a nivel de componente es necesaria pero insuficiente para la disponibilidad del sistema; los mecanismos deben evaluarse por su comportamiento cuando se violan las suposiciones.

Por qué importa

Para los equipos que construyen y operan plataformas Kubernetes, estos hallazgos destacan que los patrones operativos estándar pueden convertirse en amplificadores de fallos a gran escala. Los ingenieros a menudo asumen que las comprobaciones de salud y las actualizaciones progresivas son comportamientos predeterminados seguros. Sin embargo, cuando los componentes comparten metadatos o sincronizan tareas de inicio, estos mecanismos pueden interactuar de maneras inesperadas. Comprender la diferencia entre probar dependencias y probar capacidades es crucial para diseñar sistemas resilientes. Una sonda que informa fielmente sobre una caída aguas abajo aún puede violar el contrato de disponibilidad del sistema al provocar reinicios innecesarios.

Esto es particularmente importante para equipos que ejecutan APIs de extensión, controladores que observan grandes colecciones de objetos o componentes en la ruta de solicitud de la API. El costo de reconstruir el estado local o validar esquemas puede exceder la capacidad del plano de control durante picos de carga o eventos de recuperación. Los líderes de plataforma deben revisar los procedimientos de actualización, los ejercicios de recuperación y los diseños de comprobación de salud para asegurar que no descarten inadvertidamente el estado en caché ni creen demanda sincronizada sobre dependencias degradadas.

Qué puedes hacer

  • Valida los esquemas OpenAPI después de las actualizaciones obteniendo tanto documentos v2 como v3 y analizándolos con librerías cliente para detectar definiciones inválidas tempranamente.
  • Restringe los alcances de los informers a la colección de recursos más pequeña necesaria, como namespaces específicos o vistas solo de metadatos, para reducir la carga de inicio.
  • Utiliza WatchList o las funciones actualizadas de Prioridad y Equidad de API (API Priority and Fairness) en Kubernetes 1.31+ para mitigar el costo de las operaciones de listado iniciales.
  • Diseña las sondas de liveness para probar la capacidad local y la validez del estado en caché en lugar de la accesibilidad de dependencias externas.
  • Separa las comprobaciones de dependencias de las sondas de liveness para alertar a los operadores sin provocar reinicios automáticos durante caídas transitorias aguas arriba.
  • Revisa las estrategias de actualización de DaemonSet y la configuración de maxUnavailable para evitar que los reinicios sincronizados abrumen al servidor de API durante la recuperación.
  • #kubernetes
  • #control plane
  • #reliability
  • #openapi
  • #infrastructure

Herramientas de la Tienda de Bytechap

Seguir leyendo

Todos los artículos