BLOG | NGINX

MRA, Parte 6: Implementación del patrón de disyuntor con NGINX Plus

NGINX - Parte de F5 - horizontal, negro, tipo RGB
Miniatura de Chris Stetson
Chris Stetson
Publicado el 10 de noviembre de 2016

Nota del autor : Esta publicación de blog es la sexta de una serie:

  1. Presentación de la arquitectura de referencia de microservicios de NGINX
  2. MRA, Parte 2: El modelo proxy
  3. MRA, Parte 3: El modelo de malla del enrutador
  4. MRA, Parte 4: El modelo de tela
  5. MRA, Parte 5: Adaptación de la aplicación de doce factores para microservicios
  6. MRA, Parte 6: Implementando el patrón de disyuntor con NGINX Plus (esta publicación)

Los seis blogs, además de un blog sobre interfaces web para aplicações de microservicios <.htmla>, se han recopilado en un libro electrónico gratuito .

Consulte también estos otros recursos de NGINX sobre microservicios:

 

El diseño de aplicação de microservicios ha producido un cambio radical en el modo en que funcionan las aplicações . En una arquitectura de microservicios, una “aplicação” es ahora una colección de servicios que dependen unos de otros para realizar tareas y proporcionar funcionalidad. En aplicações complejas, el gráfico de servicios puede ser bastante profundo y tener múltiples interdependencias entre los distintos servicios.

Por ejemplo, un servicio de usuario puede ser parte integral de muchos otros servicios que dependen de los datos proporcionados por el servicio. En este escenario, una falla del servicio de usuario podría provocar una cascada de fallas en toda la aplicação.

El patrón Circuit Breaker , un término popularizado por Martin Fowler, ha ido ganando popularidad entre los arquitectos de microservicios como patrón de diseño de aplicação para evitar fallas en cascada del servicio. La idea del patrón Circuit Breaker es monitorear los servicios de su aplicação y el tráfico que fluye entre ellos para evitar fallas y, cuando ocurren, minimizar el impacto de esas fallas en sus aplicações.

Para los microservicios, el patrón Circuit Breaker es especialmente valioso, ya que proporciona resiliencia de abajo hacia arriba. Si se implementa correctamente, puede ayudar a evitar fallas en cascada al proporcionar continuidad del servicio incluso cuando los servicios no están disponibles. El patrón Circuit Breaker ha sido adoptado por Netflix como un componente crítico en su filosofía de diseño de aplicação .

No evites el fracaso, acéptalo

Un principio clave del diseño de aplicação modernas es que se producirán fallos. La torta de capas de la que dependen las aplicações modernas (desde máquinas virtuales alojadas en la nube hasta contenedores, bibliotecas de aplicação y redes dinámicas) significa que las partes móviles de cualquier aplicação son legión. Debes asumir que una o más partes de tu aplicação fallarán de alguna manera en algún momento. Esperar que se produzcan fallos y desarrollar mecanismos para mitigar sus efectos contribuye en gran medida a que su aplicação sea más resistente.

Uno de los objetivos más críticos del patrón de disyuntor es intentar prevenir la falla en primer lugar. Para algunos tipos de condiciones de error, como quedarse sin memoria, es posible reconocer que la falla es inminente y tomar medidas para prevenirla. Generalmente, esto se logra cuando el servicio señala que no está en buen estado y el disyuntor le da al servicio la oportunidad de recuperarse reduciendo la cantidad de solicitudes o redirigiéndolas por completo. Una vez que el servicio se haya recuperado, también es prudente que el disyuntor aumente gradualmente las solicitudes al servicio para no sobrecargarlo inmediatamente y correr el riesgo de que vuelva a dejar de funcionar.

En la arquitectura de referencia de microservicios de NGINX , tenemos un servicio llamado resizer . Cuando se carga una foto grande en el sistema, el redimensionador la descomprime, corrige su rotación, la reduce, luego la reduce nuevamente, guardando la imagen original corregida y las dos imágenes redimensionadas en un almacén de objetos. La naturaleza de estos procesos hace que el redimensionador sea la parte de la aplicação que consume más procesador y más memoria.

Cuando se redimensionan muchas imágenes simultáneamente, el redimensionador puede quedarse sin memoria y, en algunos casos, fallar por completo. Para evitar problemas, colocamos un disyuntor entre las instancias del servicio de cambio de tamaño y el servicio de carga que les envía las imágenes. El cargador consulta periódicamente el estado de salud de las instancias de cambio de tamaño. La consulta activa el redimensionador para evaluar si ha utilizado más del 80% de la memoria disponible, entre otras comprobaciones de estado, y responde al cargador con su estado de estado.

Si una instancia de cambio de tamaño indica que no está en buen estado, el cargador dirige las solicitudes a otras instancias (como se muestra en la Figura 1), pero sigue verificando para ver si esa instancia de cambio de tamaño se ha recuperado. Cuando la instancia de cambio de tamaño indica que está nuevamente en buen estado, se la vuelve a colocar en el grupo de equilibrio de carga y el cargador aumenta lentamente el tráfico hasta la capacidad total de la instancia. Este diseño evita que las instancias del redimensionador fallen por completo, evita que se inicie un trabajo pero no se complete, evita esperas excesivas para usuarios cuyos procesos de otra manera habrían fallado y ayuda al sistema a gestionar de manera más efectiva el flujo de solicitudes que se le envía.

El patrón del disyuntor corta el tráfico hacia instancias no saludables. Un disyuntor y NGINX funcionan bien juntos.
Figura 1. Los controles de estado activos evitan llamadas a una instancia de microservicio en mal estado

El patrón del disyuntor mejora la consistencia

Uno de los beneficios de implementar el disyuntor en el nivel NGINX es que crea una capa universal, consistente y altamente flexible para administrar disyuntores en toda su aplicação de microservicios. Esta universalidad y consistencia significa que no es necesario administrar ni desarrollar en torno a los matices e inconsistencias de las bibliotecas de disyuntores para cada lenguaje.

Obtendrás muchas ventajas si mantienes la mayor parte de la funcionalidad del disyuntor fuera del código de cada servicio y la implementas en NGINX Plus:

  • El disyuntor para un servicio escrito, por ejemplo, en Java, es el mismo que para un servicio escrito en PHP, y el disyuntor en sí puede escribirse en otro lenguaje, según sea necesario.
  • Evita tener que volver a implementar la funcionalidad del disyuntor en la combinación de idiomas y bibliotecas de soporte utilizadas por cada uno de sus servicios.
  • De esta manera, cada servicio que no necesita incluir el código del disyuntor se simplifica: se ejecuta más rápido y es más fácil de escribir, depurar, ejecutar y mantener.
  • El código de soporte para cada servicio se simplifica; la combinación de bibliotecas y sistemas utilizados puede reflejar solo la funcionalidad principal del servicio.
  • El código del disyuntor se simplifica; al existir en un solo lugar, se puede reducir a lo esencial, sin necesidad de adaptarse a los contextos locales.
  • El código del disyuntor puede aprovechar las capacidades de NGINX Plus, como el almacenamiento en caché, lo que lo hace mucho más potente.
  • Puede ajustar su código de disyuntor de nivel NGINX Plus y luego reutilizarlo en otras aplicações y en diferentes plataformas de implementación, como en las instalaciones locales, en diferentes plataformas de nube y en entornos combinados.

Es importante tener en cuenta, sin embargo, que los disyuntores no se pueden implementar solo en NGINX Plus. Un verdadero disyuntor requiere que el servicio proporcione una verificación de estado activa e introspectiva en una URI designada (normalmente /health ). El chequeo de salud debe ser adecuado a las necesidades de ese servicio específico.

Al desarrollar la comprobación del estado, debe comprender el perfil de fallas del servicio y los tipos de condiciones que pueden provocar fallas, como una falla en la conexión de la base de datos, una condición de falta de memoria, quedarse sin espacio en disco o una CPU sobrecargada. Estas condiciones se evalúan en el proceso de control de salud, que luego proporciona un estado binario de saludable o no saludable.

El patrón del disyuntor proporciona flexibilidad

Cuando se implementa el patrón de disyuntor en el nivel NGINX, como se describe aquí, depende de NGINX Plus lidiar con la situación cuando una instancia de servicio comunica que no está en buen estado. Hay varias opciones.

La primera opción es redirigir las solicitudes a otras instancias en buen estado y seguir consultando la instancia en mal estado para ver si se recupera. La segunda opción es proporcionar respuestas en caché a los clientes que solicitan el servicio, manteniendo la estabilidad incluso si el servicio no está disponible. Esta solución funciona bien con servicios orientados a la lectura, como un servicio de contenido.

Otra opción es proporcionar fuentes de datos alternativas. Por ejemplo, uno de nuestros clientes tiene un servidor de anuncios personalizado que utiliza datos de perfil para ofrecer anuncios específicos a sus usuarios. Si el servidor de anuncios personalizado no funciona, la solicitud del usuario se redirige a un servidor de respaldo que proporciona un conjunto genérico de anuncios apropiados para todos. Este enfoque de fuente de datos alternativa puede ser bastante poderoso.

Por último, si tiene una comprensión muy clara del perfil de falla de un servicio, puede mitigar la falla agregando limitación de velocidad al disyuntor. Las solicitudes se permiten al servicio solo según la velocidad que éste puede gestionar. Esto crea un amortiguador dentro del disyuntor para que pueda absorber picos de tráfico.

La limitación de velocidad puede ser particularmente poderosa en un escenario de equilibrio de carga centralizado como el modelo de malla de enrutador , donde el tráfico de la aplicação se enruta a través de un número limitado de balanceadores de carga que pueden tener una buena comprensión del uso total del tráfico en el sitio.

Implementación del patrón de disyuntor en NGINX Plus

Como hemos descrito anteriormente, el patrón de disyuntor puede prevenir fallas antes de que ocurran al reducir el tráfico hacia un servicio en mal estado o enrutar solicitudes fuera de él. Esto requiere un control de salud activo conectado a un monitor de salud introspectivo en cada servicio. Lamentablemente, un control de salud pasivo no funciona, ya que solo verifica si hay fallas, momento en el cual ya es demasiado tarde para tomar medidas preventivas. Es por este motivo que NGINX Open Source no puede implementar el patrón de disyuntor: solo admite controles de estado pasivos.

Sin embargo, NGINX Plus tiene un sólido sistema de control de salud activo con muchas opciones para verificar y responder a problemas de salud. Analizar la implementación de algunos de los tipos de servicios para la arquitectura de referencia de microservicios proporciona buenos ejemplos de las opciones y los casos de uso para implementar el disyuntor.

Comencemos con el servicio de carga que se conecta al redimensionador. El cargador coloca las imágenes en un almacén de objetos y luego le dice al redimensionador que abra una imagen, la corrija y la redimensione. Esta es una operación que consume muchos recursos de procesamiento y memoria. El cargador debe monitorear el estado del redimensionador y evitar sobrecargarlo, ya que este puede literalmente matar al host en el que se está ejecutando.

Lo primero que hay que hacer es crear un bloque de ubicación específicamente para la comprobación del estado del redimensionador. Este bloque es una ubicación interna , lo que significa que no se puede acceder a él con una solicitud a la URL estándar del servidor ( http://example.com/health-check-resizer ). En cambio, actúa como un marcador de posición para la información de verificación de estado. La directiva health_check envía una comprobación de estado a la URI /health cada tres segundos y utiliza las pruebas definidas en el bloque de coincidencia llamado condiciones para comprobar el estado de la instancia de servicio. Una instancia de servicio se marca como no saludable cuando no supera una sola verificación. Las directivas proxy_* envían la comprobación de estado al grupo ascendente de cambio de tamaño , mediante TLS 1.2 sobre HTTP 1.1 con los encabezados HTTP indicados configurados como nulos.

Ubicación /resizer-check-health { interno;
uri_check_health=/health coincidencia=condiciones falla=1 intervalo=3s;

contraseña_proxy https://resizer;
reutilización_sesión_proxy_ssl activada;
protocolos_proxy_ssl TLSv1.2;
versión_proxy_http 1.1;
encabezado_proxy_set Conexión "";
encabezado_proxy_set Codificación_de_aceptación "";
}

El siguiente paso es crear el bloque de coincidencia de condiciones para especificar las respuestas que representan condiciones saludables y no saludables. La primera comprobación es del código de estado de respuesta: si está en el rango de200 a través de399 , la prueba pasa a la siguiente declaración de evaluación. La segunda comprobación es que el tipo de contenido sea aplicação/json . Finalmente, la tercera comprobación es una coincidencia de expresión regular con el valor de las métricas deadlocks , Disk y Memory . Si todos están sanos, entonces se determina que el servicio es saludable.

condiciones del partido { estado 200-399;
encabezado Content-Type ~ "aplicação/json";
cuerpo ~ '{
"bloqueos muertos":{"saludable":verdadero},
"Disco":{"saludable":verdadero},
"Memoria":{"saludable":verdadero}
}';
}

El sistema de control de estado y disyuntor de NGINX Plus también tiene una función de inicio lento. El parámetro slow_start de la directiva del servidor para el servicio de cambio de tamaño en el bloque ascendente le dice a NGINX Plus que modere el flujo de tráfico cuando una instancia de cambio de tamaño regresa por primera vez de un estado no saludable. En lugar de simplemente colapsar el servicio con la misma cantidad de solicitudes enviadas a servicios saludables, el tráfico al servicio en recuperación aumenta lentamente hasta la tasa normal durante el período indicado por el parámetro slow_start : en este caso, 30 segundos. El inicio lento mejora las posibilidades de que el servicio vuelva a su capacidad plena y reduce el impacto si eso no sucede.

redimensionador ascendente { redimensionador del servidor inicio_lento=30s;
zona backend 64k;
tiempo_mínimo último_byte;
mantener_activo 300;
}

La limitación de solicitudes administra y modera el flujo de solicitudes al servicio. Si entiende el perfil de falla de la aplicação lo suficientemente bien como para saber la cantidad de solicitudes que puede manejar en un momento determinado, entonces implementar la limitación de solicitudes puede ser una verdadera ventaja para el proceso. Sin embargo, esta característica solo funciona si NGINX Plus tiene pleno conocimiento del número total de conexiones que se pasan al servicio. Debido a esto, es más útil implementar el disyuntor limitador de solicitudes en una instancia de NGINX Plus que se ejecuta en un contenedor con el servicio en sí, como en el modelo Fabric, o en un balanceador de carga centralizado que se encarga de administrar todo el tráfico en un clúster.

El siguiente fragmento de código de configuración define un límite de velocidad en las solicitudes que se aplicarán a las instancias del servicio de cambio de tamaño en sus contenedores. La directiva limit_req_zone define el límite de velocidad en 100 solicitudes por segundo. La variable $server_addr se utiliza como clave, lo que significa que todas las solicitudes al contenedor de redimensionamiento se cuentan dentro del límite. El nombre de la zona es moderateReqs y el período de tiempo para mantener el recuento de solicitudes es de 1 minuto. La directiva limit_req permite que NGINX Plus almacene en búfer ráfagas de hasta 150 solicitudes. Cuando se supera ese número, los clientes reciben el503 Código de error según lo especificado por la directiva limit_req_status , que indica que el servicio no está disponible.

http { # Entrega moderada
limit_req_zone $server_addr zone=moderateReqs:1m rate=100r/s;
# ...
servidor {
# ...
limit_req_zone=moderateReqs burst=150;
limit_req_status 503;
# ...
}
}

Otro beneficio poderoso de ejecutar el disyuntor dentro de NGINX Plus es la capacidad de incorporar almacenamiento en caché y mantener datos almacenados en caché de forma centralizada para su uso en todo el sistema. Esto es particularmente valioso para servicios orientados a la lectura, como servidores de contenido donde los datos que se leen desde el backend no cambian con frecuencia.

ruta_caché_proxy /app/cache niveles=1:2 zona_claves=oauth_cache:10m tamaño_máximo=10m inactivo=15s ruta_temp_uso=desactivado;
administrador_usuario_upstream {
administrador_usuario_servidor;
zona_backend 64k;
tiempo_mínimo último_byte;
keepalive 300;
}

servidor {
escucha 443 ssl;
ubicación /v1/usuarios {
contraseña_proxy http://administrador_usuario;
caché_proxy caché_oauth;
caché_proxy_válida 200 30s;
uso_caché_proxy_obsoleto error tiempo_de_espera encabezado_inválido actualizando
http_500 http_502 http_503 http_504;
}
}

Como se muestra en la Figura 2, el almacenamiento en caché de datos significa que muchas solicitudes de datos de clientes nunca llegan a las instancias de microservicio, lo que libera capacidad para solicitudes que no se han recibido anteriormente.

NGINX Plus, que actúa como un disyuntor de microservicios, también admite el almacenamiento en caché.
Figura 2. Si bien el almacenamiento en caché se utiliza generalmente para acelerar el rendimiento al evitar llamadas a instancias de microservicios, también sirve para proporcionar continuidad del servicio en caso de falla completa del servicio.

Sin embargo, con un servicio en el que los datos pueden cambiar, por ejemplo un servicio de administración de usuarios, es necesario gestionar el caché con cuidado. De lo contrario, puede terminar con un escenario en el que un usuario realiza un cambio en su perfil, pero ve datos antiguos en algunos contextos porque los datos están almacenados en caché. Un tiempo de espera razonable y la aceptación del principio de alta disponibilidad con consistencia final pueden resolver este enigma.

Una de las características interesantes del caché NGINX es que puede continuar sirviendo datos almacenados en caché incluso si el servicio no está disponible en absoluto: en el fragmento anterior, si el servicio responde con una de las cuatro respuestas más comunes,500 -códigos de error de la serie.

El almacenamiento en caché no es la única opción para responder a los clientes incluso cuando un servidor no funciona. Como mencionamos en El patrón de disyuntor proporciona flexibilidad , uno de nuestros clientes necesitaba una solución resistente en caso de que su servidor de anuncios personalizado dejara de funcionar y las respuestas en caché no fueran una buena solución. En lugar de ello, querían un servidor de anuncios genérico que proporcionara anuncios generalizados hasta que el servidor personalizado volviera a estar en línea. Esto se logra fácilmente utilizando el parámetro de respaldo en la directiva del servidor . El siguiente fragmento especifica que cuando todos los servidores definidos para el dominio del servidor de anuncios personal no están disponibles, se utilizan en su lugar los servidores definidos para el dominio del servidor de anuncios genérico .

servidor de anuncios personales upstream { servidor servidor de anuncios personales;
servidor servidor de anuncios genérico backup;
zona backend 64k;
tiempo mínimo último byte;
mantener activo 300;
}

Y finalmente, es posible que NGINX evalúe los códigos de respuesta de un servicio y los gestione individualmente. En el siguiente fragmento, si un servicio devuelve un503 error, NGINX Plus envía la solicitud a un servicio alternativo. Por ejemplo, si el redimensionador tiene esta función y la instancia local se sobrecarga o deja de funcionar, las solicitudes se envían a otra instancia del redimensionador.

ubicación / { error_page 503 = @fallback;
}

ubicación @fallback {
contraseña_proxy http://backend-alternativo;
}

CONCLUSIÓN

El patrón de disyuntor es una herramienta poderosa para proporcionar resiliencia y control en su aplicação de microservicios. NGINX Plus ofrece muchas funciones y opciones para implementar el disyuntor en su entorno. La clave para implementar el patrón de disyuntor es comprender el perfil de falla del servicio que está protegiendo y luego elegir las opciones que mejor eviten la falla, cuando sea posible, y que mitiguen mejor los efectos de la falla cuando ocurre.

Para probar NGINX Plus, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso.


"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.