Istio Circuit Breaker: desactivando contenedores defectuosos

Las vacaciones han terminado y regresamos con nuestra segunda publicación de la serie sobre Istio Service Mesh.

Istio Circuit Breaker: desactivando contenedores defectuosos

El tema de hoy es Circuit Breaker, que en términos eléctricos se traduce como «disyuntor» o, coloquialmente, «automático de protección». En Istio, este disyuntor no interrumpe un circuito que se ha cortocircuitado o sobrecargado, sino que apaga contenedores defectuosos.

Cómo debería funcionar en ideal

Cuando los microservicios son gestionados por Kubernetes, por ejemplo, en la plataforma OpenShift, se escalan automáticamente hacia arriba y hacia abajo según la carga. Dado que los microservicios funcionan en pods, puede haber varias instancias de un microservicio containerizado en un mismo punto final, y Kubernetes enruta las solicitudes y balancea la carga entre ellas. Y, en ideal, todo esto debería funcionar perfectamente.

Recordemos que los microservicios son pequeños y efímeros. La efimeridad, que aquí se refiere a la facilidad de aparición y desaparición, a menudo se subestima. El nacimiento y la muerte de cada instancia de un microservicio en un pod son cosas bastante esperadas; OpenShift y Kubernetes manejan esto bien y todo funciona de maravilla, pero de nuevo, esto es solo en teoría.

Cómo funciona realmente

Ahora imagina que una instancia específica de un microservicio, es decir, un contenedor, ha fallado: o no responde (error 503) o, lo que es peor, responde, pero demasiado lento. En otras palabras, presenta fallos intermitentes o no responde a las solicitudes, pero no se elimina automáticamente del pool. ¿Qué se debe hacer en este caso? ¿Repetir la solicitud? ¿Eliminarlo del esquema de enrutamiento? ¿Y qué significa «demasiado lento»? ¿Cuánto es eso en números y quién los establece? ¿Quizás simplemente darle un respiro y probar más tarde? Si es así, ¿cuánto más tarde?

Qué es Pool Ejection en Istio

Aquí es donde Istio entra en acción con sus disyuntores automáticos Circuit Breaker, que eliminan temporalmente los contenedores defectuosos del pool de recursos de enrutamiento y balanceo de carga, implementando el procedimiento de Pool Ejection.

Utilizando una estrategia de detección de anomalías (outlier detection), Istio detecta pods erráticos que se desvían de la norma y los retira del pool de recursos durante un tiempo determinado, conocido como «ventana de sueño» (sleep window).

Para mostrar cómo funciona esto en Kubernetes en la plataforma OpenShift, comencemos con una captura de pantalla de microservicios operativos del ejemplo en el repositorio. Demos de Red Hat Developer. Aquí tenemos dos pods, v1 y v2, cada uno corriendo un contenedor. Cuando no se utilizan las reglas de enrutamiento de Istio, Kubernetes aplica por defecto un enrutamiento cíclico equilibrado:

Istio Circuit Breaker: desactivando contenedores defectuosos

Preparándonos para la falla

Antes de realizar la Eliminación de Pool, debemos crear una regla de enrutamiento de Istio. Supongamos que queremos distribuir las solicitudes entre los pods en una proporción de 50/50. Además, aumentaremos el número de contenedores de v2 de uno a dos, así:

oc scale deployment recommendation-v2 --replicas=2 -n tutorial

Ahora establecemos una regla de enrutamiento para que el tráfico se distribuya entre los pods en una proporción de 50/50.

Istio Circuit Breaker: desactivando contenedores defectuosos
Y así es como se ve el resultado de esta regla:

Istio Circuit Breaker: desactivando contenedores defectuosos
Se podría argumentar que en esta pantalla no es 50/50, sino 14:9, pero con el tiempo la situación se ajustará.

Provocando la falla

Ahora dejaremos fuera de operación uno de los dos contenedores v2, de modo que tengamos un contenedor v1 funcionando, un contenedor v2 funcionando y un contenedor v2 fallido:

Istio Circuit Breaker: desactivando contenedores defectuosos

Reparando la falla

Así que tenemos un contenedor fallido, y ha llegado el momento de la Eliminación de Pool. Con una configuración muy simple, excluiremos este contenedor fallido de cualquier esquema de enrutamiento durante 15 segundos, a la espera de que se recupere a un estado operativo (ya sea reiniciándose o restaurando su rendimiento). Así es como se ve esta configuración y los resultados de su ejecución:

Istio Circuit Breaker: desactivando contenedores defectuosos
Istio Circuit Breaker: desactivando contenedores defectuosos
Como se puede ver, el contenedor v2 fallido ya no se utiliza para el enrutamiento de solicitudes, ya que fue excluido del pool. Sin embargo, después de 15 segundos, volverá automáticamente al pool. En realidad, acabamos de mostrar cómo funciona la Eliminación de Pool.

Comenzando a construir la arquitectura

La Eliminación de Pool combinada con las capacidades de monitoreo de Istio permite comenzar a construir un marco para la sustitución automática de contenedores fallidos, con el fin de reducir, e incluso eliminar, los tiempos de inactividad y fallas.
 
NASA tiene un lema muy conocido: Failure Is Not an Option, cuyo autor es el director de vuelos Gene Kranz. En español se podría traducir como «El fracaso no es una opción», y aquí el significado es que todo se puede hacer funcionar con suficiente determinación. Sin embargo, en la vida real, las fallas no solo ocurren, son inevitables, en todas partes y en todo. ¿Y cómo lidiar con ellas en el caso de los microservicios? En nuestra opinión, es mejor confiar no en la fuerza de voluntad, sino en las capacidades de los contenedores. Kubernetes, Red Hat OpenShiftcomo Istio.

Istio, como ya hemos mencionado antes, implementa la probada en el mundo físico idea de los interruptores automáticos. Así como un disyuntor eléctrico desconecta la parte problemática de un circuito, el Circuit Breaker en Istio interrumpe la conexión entre el flujo de solicitudes y el contenedor problemático cuando hay algo mal con el punto final, por ejemplo, cuando el servidor se ha caído o ha comenzado a ralentizarse.

Además, en el segundo caso hay aún más problemas, ya que la lentitud de un contenedor no solo causa una cascada de retrasos en los servicios que acceden a él y, como consecuencia, disminuye el rendimiento del sistema en general, sino que también genera solicitudes repetidas hacia un servicio que ya está funcionando lentamente, lo que solo agrava la situación.

Circuit Breaker en teoría

Circuit Breaker es un proxy que controla el flujo de solicitudes hacia un punto final. Cuando este punto deja de funcionar o, dependiendo de la configuración establecida, comienza a ralentizarse, el proxy rompe la conexión con el contenedor. El tráfico se redirige a otros contenedores, simplemente por razones de balanceo de carga. La conexión permanece abierta (open) durante un período de tiempo de espera establecido, digamos, dos minutos, y luego se considera semiabierta (half-open). El intento de enviar la siguiente solicitud determina el estado de la conexión. Si todo está bien con el servicio, la conexión vuelve a su estado operativo y se cierra (closed) nuevamente. Si el servicio sigue teniendo problemas, la conexión se interrumpe y se vuelve a iniciar el período de espera. Así es como se ve un diagrama simplificado de los estados del Circuit Breaker:

Istio Circuit Breaker: desactivando contenedores defectuosos
Es importante señalar que todo esto ocurre a nivel de arquitectura del sistema, por lo que en algún momento tendrás que enseñar a tus aplicaciones a trabajar con Circuit Breaker, por ejemplo, proporcionando un valor por defecto en respuesta o, si es posible, ignorando la existencia del servicio. Para ello se utiliza el patrón bulkhead, aunque esto está fuera del alcance de este artículo.

Circuit Breaker en la práctica

Para ilustrar, ejecutaremos en OpenShift dos versiones de nuestro microservicio de recomendaciones. La versión 1 funcionará normalmente, mientras que en v2 incorporaremos una demora para simular una ralentización en el servidor. Para ver los resultados, se utiliza la herramienta siege:

siege -r 2 -c 20 -v customer-tutorial.$(minishift ip).nip.io

Istio Circuit Breaker: desactivando contenedores defectuosos
Todo parece funcionar, ¿pero a qué precio? A primera vista, tenemos un 100 % de disponibilidad, pero observa: la duración máxima de la transacción es de 12 segundos. Esto es claramente un cuello de botella y debe resolverse.

Para ello, utilizaremos Istio para excluir las solicitudes a los contenedores lentos. Así es como se ve la configuración correspondiente utilizando Circuit Breaker:

Istio Circuit Breaker: desactivando contenedores defectuosos
La última línea con el parámetro httpMaxRequestsPerConnection indica que la conexión debe interrumpirse al intentar crear una nueva – segunda – conexión además de la que ya existe. Dado que nuestro contenedor simula un servicio lento, estas situaciones surgirán periódicamente, y entonces Istio devolverá un error 503, y esto es lo que mostrará siege:

Istio Circuit Breaker: desactivando contenedores defectuosos

Bien, tenemos Circuit Breaker, ¿y ahora qué?

Así que hemos implementado la desconexión automática sin tocar el código fuente de los propios servicios. Usando Circuit Breaker y el procedimiento de Ejectación de Pool descrito anteriormente, podemos eliminar de la agrupación de recursos los contenedores que están fallando hasta que se recuperen, y verificar su estado con una frecuencia definida – en nuestro ejemplo, cada dos minutos (parámetro sleepWindow).

Ten en cuenta que la capacidad de la aplicación para reaccionar a un error 503 sigue estando determinada a nivel de su código fuente. Existen múltiples estrategias de trabajo con Circuit Breaker que se aplican según la situación.

En la próxima publicación: hablaremos sobre el trazado y monitoreo que ya están integrados o se pueden agregar fácilmente en Istio, así como sobre cómo introducir errores en el sistema intencionadamente.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster