Por qué y cómo construimos un controlador de admisión para hacer más seguros los drenajes de nodos al ejecutar aplicaciones con estado en Kubernetes.


La ejecución de aplicaciones con estado en Kubernetes es cada vez más común y a menudo se gestionan mediante recursos y operadores personalizados. Sin embargo, la naturaleza dinámica de Kubernetes implica que los pods, especialmente los de cargas de trabajo con estado, no tienen garantizada una larga vida útil. Eventos como el mantenimiento o la presión sobre los recursos pueden desencadenar desalojos de pods, que corren el riesgo de interrumpir los servicios si no se gestionan con cuidado.

Eviction Reschedule Hook es un proyecto de código abierto que pretende resolver este problema utilizando los controladores de admisión de Kubernetes para interceptar y rechazar las solicitudes de desalojo de pods gestionados por operadores, al tiempo que notifica al operador que un pod necesita ser trasladado. Su objetivo es ayudar a preservar la disponibilidad del servicio y reducir las interrupciones durante eventos como el mantenimiento de nodos.

Siga leyendo:

    • El problema que abordamos
    • Por qué las opciones existentes no son suficientes
    • Introducción a la reprogramación de proyectos gancho

Aunque cierta familiaridad con Kubernetes operadoresNodo vaciadoadmisión control y dinámica webhooks sería útil, intentaré cubrir los conceptos clave a lo largo del camino. En Couchbase También se hará referencia a Autonomous Operator como Operator para abreviar, y la mayoría de los ejemplos y demostraciones incluirán recursos Couchbase de algún tipo.

Aunque originalmente se desarrolló pensando en la plataforma de datos Couchbase, el proyecto de gancho de reprogramación pretende ser flexible y puede configurarse para proteger otras aplicaciones con estado gestionadas por el operador.


Parte 1 - Por qué los desalojos son un reto para las aplicaciones con estado gestionadas por el operador

Comprender los desahucios de las vainas

En Kubernetes, para desalojar un pod es necesario activar el comando PreStop hook y luego enviar un SIGTERM tras su finalización. Si el pod no ha salido después de un período de gracia, esto es seguido por un SIGKILL. Los desalojos se producen tanto voluntaria como involuntariamente por multitud de razones, desde la presión de los nodos hasta el autoescalado, pero este proyecto se centra en los desalojos voluntarios que se desencadenan al vaciar los nodos. Este es el proceso por el cual los pods se eliminan de los nodos utilizando la API de desalojo de Kubernetes, que a menudo es necesaria para despejar el camino para operaciones como el mantenimiento o las actualizaciones.

Ejecutar kubectl drenar es una forma común de preparar un nodo para su mantenimiento en Kubernetes. Marca el nodo como no programable y envía solicitudes de desalojo simultáneamente para todos los pods que se ejecutan en ese nodo.


Por qué las aplicaciones con estado son un reto

Las cargas de trabajo con estado añaden complejidad a la gestión de los desalojos:

Tiempos de cierre impredecibles: Estas cargas de trabajo pueden implicar procesos de larga duración, como consultas en vuelo, que deben finalizar antes de que un pod pueda cerrarse de forma segura.

Coordinación de aplicaciones: A menudo es necesario notificar a la propia aplicación antes de eliminar el pod para garantizar la coherencia de los datos y un reequilibrio adecuado.

Gestión de operadores: Los operadores que gestionan la aplicación necesitan tiempo para coordinar la retirada de vainas con el estado interno del operador

Movimiento de volumen: La migración de volúmenes Kubernetes de un nodo a otro puede llevar un tiempo considerable que supere el tiempo de vida del pod. terminationGracePeriodSeconds

Un Operador automatiza el ciclo de vida de aplicaciones complejas reconciliando continuamente su estado real con el estado deseado definido en recursos personalizados.

Tomemos Couchbase como ejemplo. Un cluster de Couchbase se compone de múltiples pods, cada uno ejecutando una instancia de Couchbase Server, normalmente repartidos en diferentes nodos y zonas de disponibilidad. El Operador se asegura de que el estado del cluster coincide con el estado definido en un CouchbaseCluster recurso. Cuando un pod necesita ser eliminado, el Operador debe notificar al cluster para reequilibrar los datos y manejar cualquier proceso en ejecución con gracia.

Retos organizativos

Otro reto común es la separación de responsabilidades en las grandes organizaciones:

    • Los administradores de Kubernetes gestionan la infraestructura
    • Los equipos de aplicaciones gestionan las aplicaciones con estado que se ejecutan en esa infraestructura

Esto significa que los administradores de Kubernetes necesitan formas de vaciar nodos de forma segura para realizar tareas de mantenimiento sin tener que coordinarse constantemente con los propietarios de las aplicaciones ni afectar a la disponibilidad o el estado de la aplicación subyacente.


Protecciones existentes de Kubernetes: por qué no son suficientes

Kubernetes proporciona herramientas como Ganchos PreStop y Presupuestos Pod Disruption para ayudar con los cierres graceful y la disponibilidad de las aplicaciones durante los desalojos.

Ganchos PreStop ejecutar scripts dentro del contenedor antes del SIGTERM dando al pod la oportunidad de apagarse. Para aplicaciones sin estado, esto suele ser suficiente. Pero para aplicaciones con estado, los operadores a menudo necesitan coordinar la eliminación del pod antes de la terminación para evitar los problemas descritos anteriormente, lo que es difícil de hacer dentro de los ganchos preStop.

Un enfoque ingenioso que hemos visto consiste en añadir un script preStop a las plantillas de pod utilizadas por el Operador. Este script hace que el pod se añada a sí mismo la anotación de reprogramación (de la que hablaremos más adelante) para notificar al Operador que debe ser movido, y luego hace un bucle hasta que ha sido expulsado de forma segura del clúster.

Esquema básico del guión, no exacto:

Sin embargo, para ello es necesario montar kubectl y couchbase-cli en cada pod, aumentando la complejidad y el tamaño de la imagen. Los problemas de red o de otro tipo también podrían interrumpir la secuencia de comandos y provocar la finalización prematura del pod.

Presupuestos de disrupción de vainas (PDB) aseguran que un número mínimo de pods permanezca disponible durante interrupciones voluntarias. Pero solo limitan el número de módulos que pueden desalojarse simultáneamente, lo que significa que seguimos enfrentándonos a la situación en la que un operador no es capaz de gestionar correctamente la retirada de módulos.


¿Qué ocurre si hoy se desalojan las cápsulas?

El Operador crea PDBs para Couchbase para limitar cuántos pods pueden ser desalojados a la vez. Aunque esto asegura un nivel mínimo de disponibilidad para la aplicación, los pods que sean desalojados serán eliminados de forma insegura del clúster de Couchbase mediante failover.

También podemos ver cómo Operator maneja esto observando lo que ocurre en la interfaz de usuario del administrador de Couchbase.

Incluso cuando los desalojos ocurren un pod a la vez, una vez que un pod se reinicia en un nuevo nodo, Kubernetes puede permitir desalojos en el siguiente pod antes de que el nuevo pod se haya añadido de forma segura a la aplicación con estado. En Couchbase, evitamos esto con una puerta de preparación en los pods.

Para solucionar estos problemas, hemos introducido el cao.couchbase.com/rechedule en CAO v2.7.0. Cuando se añade a un pod, indica al Operador que debe ser recreado. Sin embargo, acordonar manualmente los nodos y añadir esta anotación es tedioso, no se escala bien, y sólo afecta a los pods de Couchbase - otros pods todavía requieren un drenaje normal.


¿Por qué no gestionar todo esto dentro del Operador?

Nos planteamos integrar la lógica de validación del desalojo en el propio Operador, pero:

    • Es un anti-patrón en el diseño de operadores Kubernetes. Los operadores funcionan a través de bucles de reconciliación, no de controladores de admisión
    • Añadir puntos finales de control de admisión a los operadores complica la implantación y el mantenimiento
    • Los webhooks de validación son recursos de ámbito de clúster y, por tanto, requieren permisos de ámbito de clúster. El Operador es de ámbito de espacio de nombres y requeriría una gran expansión de sus permisos requeridos.
    • La construcción de esta lógica en un proyecto propio permite el "open sourcing" e invita a la comunidad a contribuir en casos de uso similares.

Parte 2 - aprovechamiento de Kubernetes Webhooks para desalojos inteligentes de pods

El Eviction Reschedule Hook es un controlador de admisión de Kubernetes de código abierto diseñado para mejorar la forma en que se gestionan los desalojos durante los vaciados de nodos para evitar los problemas descritos en la primera parte. Trabajando en tándem con un Operador, automatiza la reprogramación del pod interceptando las peticiones de desalojo antes de que lleguen al pod. Estas solicitudes se rechazan de forma selectiva, de manera que se sigue permitiendo la reprogramación estándar de los nodos. kubectl drenar funcione como es debido.

En lugar de terminar inmediatamente los pods o probar los límites del presupuesto de interrupción de pods (PDB), el controlador de admisión notifica al operador que es necesario mover un pod mediante el comando cao.couchbase.com/rechedule anotación.


Comprender el control de admisión

El control de admisión es una etapa clave en el ciclo de vida de las solicitudes de la API de Kubernetes que se produce después de que una solicitud haya sido autenticada y autorizada, pero antes de que persista en el clúster.

Los controladores de admisión pueden validar o mutar estas solicitudes basándose en una lógica personalizada.

Cuando se vacía un nodo en Kubernetes, cada intento de desalojo de un pod desencadena un CREAR solicitud de vainas/desahucios subrecurso. Aquí es donde entra en juego nuestro controlador de admisión. Intercepta estas solicitudes de desalojo antes de que lleguen a los pods y determina si deben permitirse. En nuestro caso, la lógica de admisión implica señalar al Operador que un pod debe ser reprogramado de forma segura añadiendo la anotación de reprogramación.

Estamos utilizando un webhook de validación en lugar de un webhook de mutación porque nuestro objetivo principal es evaluar si la solicitud de desalojo está permitida. Aunque podemos anotar el pod como parte del proceso de decisión, el webhook en sí está realizando la validación, no la mutación, de la solicitud de desalojo.


Gestión de drenajes de nodos

Como el título de este artículo deja claro, el objetivo principal de este proyecto es permitir un manejo elegante de los drenajes de nodos. Con el Eviction Reschedule Hook en su lugar, el flujo estándar de drenaje de nodos se modifica para soportar una migración de pods más segura.

Las solicitudes de desalojo se interceptan antes de que el PreStop se ejecuta. En lugar de terminar el pod, se rechaza la solicitud de desalojo y se anota el pod. El Operator comprueba si aparece la anotación de reprogramación durante cada bucle de reconciliación. Si aparece la anotación, se encargará de volver a crear el pod de forma segura.

Como drenar un nodo lo mancha con node.kubernetes.io/unschedulableSuponiendo que exista otro nodo en el clúster Kubernetes que cumpla los requisitos de programación del pod, cuando el Operador cree el nuevo pod, éste existirá en otro nodo.

Es importante destacar que el controlador de admisión está diseñado para filtrar selectivamente sólo los pods relevantes. Todas las demás cargas de trabajo en el nodo continúan siguiendo el proceso de desalojo por defecto.


Trabajar con Kubectl

Para integrarse sin problemas con kubectles importante entender lo que ocurre bajo el capó cuando se ejecuta kubectl drain. Mirando el vaciar.ir fuenteUna vez acordonado el nodo, se genera una goroutine independiente para cada pod de ese nodo para gestionar su desalojo.

Si kubectl continúa intentando desalojar un pod depende de la respuesta del controlador de admisión. Si la solicitud de desalojo tiene éxito (es decir, no se devuelve ningún error), se sale del bucle en el que se envían las solicitudes de desalojo. A continuación, se realiza una comprobación de seguimiento en la que kubectl espera a que se elimine el pod. Sin embargo, para los pods gestionados por Operator, queremos kubectl para seguir reintentando hasta que se hayan reprogramado de forma segura, momento en el que deberíamos finalizar la goroutina antes de realizar más comprobaciones.

Por diseño, kubectl trata a 429 DemasiadasPeticiones a la solicitud de desalojo en la goroutine como señal para hacer una pausa de 5 segundos antes de volver a intentarlo. Podemos aprovechar este comportamiento en nuestro controlador de admisión: después de la lógica de selección de pods, devolvemos un 429 si el pod está siendo anotado para reprogramación o si ya está anotado y esperando a ser movido.

Después de que el pod haya sido reprogramado con éxito, tendrá un nombre diferente y, por lo tanto, el controlador de admisión ya no podrá localizarlo por nombre y espacio de nombres. En ese momento, podemos devolver un 404 No encontrado para salir de la goroutine.


Reprogramación de pods in situ

En algunos casos, el Operador puede eliminar y volver a crear un pod utilizando el mismo nombre.

En Couchbase, el upgradeProcess en un campo CouchbaseCluster puede cambiarse a control la forma en que el operador sustituye las cápsulas. El valor predeterminado es SwapRebalanceque indica al operador que debe crear un nuevo pod con un nombre diferente, reequilibrarlo en el clúster y, a continuación, eliminar el pod antiguo. InPlaceUpgrade es una alternativa más rápida pero menos flexible, por la que el Operador borrará el pod y lo volverá a crear con el mismo nombre, reutilizando los volúmenes existentes. Esto supone un reto para el controlador de admisión.

Las solicitudes de desalojo enviadas por la goroutine en kubectl sólo incluyen el nombre y el espacio de nombres del pod. Debido a este contexto limitado, el controlador de admisión no puede asumir que un pod que carece de una anotación de reprogramación necesita ser movido - puede ser simplemente una instancia recién recreada de un pod que ya fue reprogramado.

Para ello, el controlador de admisión mantiene un mecanismo de seguimiento ligero que se utiliza cuando se reprograman pods con el mismo nombre. Cuando se anota un pod para su reprogramación, también se anota otro recurso, denominado recurso de seguimiento, con el atributo reprogramar.hook/. clave.

Si se intercepta una solicitud de desalojo posterior para un pod sin la anotación de reprogramación, la presencia de esta anotación en el recurso de seguimiento indica que el pod ya ha sido reprogramado. Cuando esto ocurra, el webhook limpiará la anotación de seguimiento para ese pod antes de devolver un 404 No encontrado para salir de la goroutine.

Mientras que el tipo de recurso de seguimiento será por defecto CouchbaseClusterel webhook también permite utilizar el pod Espacio de nombres. Si los pods nunca se reprogramarán con el mismo nombre, esta función puede desactivarse.


Ejecutar el gancho de reprogramación de desahucios

Desplegar el gancho de reprogramación de desalojos y sus componentes de apoyo es sencillo. La dirección LÉAME ofrece instrucciones detalladas para crear la imagen y desplegar la pila completa.

Los principales componentes del gancho de reprogramación son:

ValidatingWebhookConfiguration: Indica al servidor API de Kubernetes que reenvíe CREAR solicitudes de vainas/desahucios a nuestro servicio webhook

Servicio: Enruta las peticiones entrantes de webhook al pod del controlador de admisión.

Controlador de admisiones: Ejecuta el gancho de reprogramación dentro de un contenedor, normalmente desplegado como un despliegue de Kubernetes.

ServiceAccount, ClusterRole y ClusterRoleBinding: Conceden al controlador de admisión los permisos necesarios. Como mínimo, consiga y parche se necesitan permisos para los pods. Si se utiliza un recurso de seguimiento, consiga y parche también deben añadirse permisos para el recurso de seguimiento

Una vez desplegado, el vaciado de un nodo en el que residen pods de Couchbase demuestra cómo el Reschedule Hook intercepta y rechaza las peticiones de desalojo hasta que el Operator recrea y equilibra de forma segura los pods de nuevo en el clúster:

El manejo elegante de las solicitudes de desalojo también se puede ver en la interfaz de usuario del administrador de Couchbase. Los pods ya no fallan y son reemplazados con cero tiempo de inactividad:


Pruébalo, participa

El gancho de reprogramación de desalojos hace que los vaciados de nodos sean más seguros y predecibles para las aplicaciones con estado que se ejecutan en Kubernetes. Al coordinar la gestión de desalojos con un operador, permite una reprogramación elegante sin interrumpir la aplicación subyacente.

El proyecto es de código abierto. Visite la página repositorio en GitHub para empezar y echa un vistazo al Contribución si estás interesado en ayudar. Comentarios, problemas y pull requests son bienvenidos.

Autor

Publicado por Ben Mottershead, Ingeniero de software

Ben es un ingeniero de software que trabaja en la oficina de Couchbase en Manchester. Forma parte del equipo de operadores autónomos desde agosto, lo que le introdujo en el ecosistema de datos en Kubernetes. Ben ha estado desarrollando aplicaciones para la nube desde que terminó la universidad en 2021 y ha trabajado con varios lenguajes y tecnologías, centrándose más recientemente en Go, K8s y Couchbase Server.

Dejar una respuesta