La ingeniería del caos es una metodología proactiva que introduce intencionalmente interrupciones y fallas controladas en un sistema para descubrir debilidades, mejorar la resiliencia y garantizar un rendimiento sólido frente a interrupciones imprevistas.
En esta publicación, exploraremos las prácticas de ingeniería del caos y proporcionaremos un código de ejemplo para ejecutar un experimento de caos dentro de una arquitectura sin servidor implementada en AWS.
Además, aprovecharemos AWS FIS (servicio de inyección de fallas) para mejorar la experimentación.
Esta es la segunda parte de una serie de publicaciones que analizan la ingeniería del caos en arquitecturas sin servidor. Aplica los conceptos presentados en la Parte 1 de esta serie.
Introducción del autor invitado
Koby Aharon es arquitecto de software principal en CyberArk . Es un ingeniero profesional práctico al que le encanta usar la tecnología para resolver problemas complejos.
Puedes seguir a Koby en sus cuentas de LinkedIn y Medium .
Tabla de contenido
Definición de ingeniería del caos
Los proveedores de servicios en la nube, como AWS, gestionan arquitecturas sin servidor, pero garantizar que sus aplicaciones sin servidor sean resistentes sigue siendo esencial. Por ejemplo, en caso de una interrupción del servicio en una región, conviene asegurarse de que su arquitectura sea resistente y de que sus clientes en otras regiones no se vean afectados.
La ingeniería del caos puede ayudarlo a simular estas interrupciones y validar la resiliencia de sus aplicaciones sin servidor. Siguiendo sus principios, puede realizar experimentos de caos que simulen una falla en una región y verifiquen que el sistema esté funcionando como se espera.
Antes de sumergirnos en el código, recapitulemos brevemente nuestra arquitectura de muestra y los pasos de un experimento que cubrimos en la Parte 1 .
Presentamos la siguiente arquitectura:
El diagrama de arquitectura presenta una arquitectura clásica sin servidor que se ejecuta sobre AWS. Consiste en los siguientes recursos:
API alojada en la puerta de enlace de API de Amazon . Nuestra API se implementa en dos regiones, us-east-1 y eu-west-1, y expone dos rutas: '/api/health' para la comprobación del estado y '/api/hello' como nuestra API de servicio. Nuestro estado devuelve "La región {región} está en buen estado", mientras que nuestra API de servicio devuelve: "Hola desde la región {región}" (la región marca el punto final que manejó la solicitud, ya sea 'us-east-1' o 'eu-west-1').
Hay dos funciones Lambda en cada región. Nuestra API activa las funciones (tenemos una Lambda dedicada por API). Hay más información sobre el uso de Lambda con AWS API Gateway aquí .
Dominio personalizado registrado en Amazon Route 53. Cada API regional obtendrá un dominio personalizado dedicado que indica la región relevante. Por ejemplo: “ejemplo.{región}.midominio.com” (la región debe ser 'us' o 'eu', p. ej., “ ejemplo.us.midominio.com ”).
El enrutamiento basado en latencia de Route53 apunta a nuestros puntos finales en ambas regiones. La dirección de entrada debe coincidir con “ ejemplo.midominio.com ”.
Control de estado de Route53 para verificar el estado de nuestras API. Cada API regional tendrá un control de estado dedicado que apunta a la dirección de dominio correspondiente: “ https://exmaple .{region}.mydomain.com/api/health”.
Pasos del experimento del caos
Presentamos los cuatro pasos de un experimento de caos:
Formular una hipótesis (plan)
Introducir estrés/fallas (hacer)
Observar (verificar)
Mejorar (actuar)
Y definió la siguiente hipótesis:
Dado: Tenemos una puerta de enlace API multirregional detrás del enrutamiento basado en latencia.
Hipótesis: una falla en una región no afecta a las demás. Además, la conmutación por error de Route 53 debería detectar el problema rápidamente y redirigir el tráfico a la región en buen estado.
En esta publicación, se mostrará cómo experimentar con AWS mediante AWS FIS para probar nuestra hipótesis. Durante nuestro experimento, utilizaremos una extensión Lambda para inyectar fallas en nuestras funciones Lambda (más información sobre esto a continuación). Consulte la primera parte para obtener más información sobre los diferentes enfoques para inyectar fallas.
Después de presentar nuestra hipótesis y explicar cómo inyectaremos fallas en la función Lambda, comenzaremos nuestro experimento entendiendo primero cómo validar nuestra hipótesis durante nuestro experimento de Caos.
Preparándonos para nuestro primer experimento
Antes de ejecutar nuestro experimento, es fundamental validar que la aplicación se comporte como se espera y simular el comportamiento del usuario dentro del sistema. Además, es crucial determinar cómo funciona el sistema desde el punto de vista del cliente.
Más específicamente, si la región de EE. UU. no funciona, debemos poder verificar que no funciona y que nuestros clientes de la UE no se ven afectados.
Usaremos Amazon CloudWatch para validar el comportamiento de la aplicación y crear un panel con una combinación de métricas integradas y personalizadas. Con esas métricas, podemos monitorear nuestra aplicación tanto desde el punto de vista del operador del sistema como del cliente. Las métricas y el panel son fundamentales para el paso de "observación" de nuestro experimento. Hablaremos sobre esas métricas en detalle más adelante en esta publicación.
Definición del experimento
Nuestro experimento consta de dos partes principales: la primera simula el comportamiento del cliente y la segunda lo ejecuta e inyecta fallas.
Simulador de comportamiento del cliente: Simular el comportamiento del cliente es fundamental, ya que primero queremos probar nuestra hipótesis en un entorno controlado sin afectar a los clientes reales. Para simular el comportamiento del cliente, utilizaremos una versión actualizada de esta aplicación llamada "load-gen", extraída del Taller de ingeniería del caos de AWS . Esta aplicación se puede implementar como una función Lambda e invocar para simular la carga en una API determinada. Para obtener más información sobre la aplicación y cómo usarla, consulte el Taller de ingeniería del caos de AWS .
Ejecución de nuestro experimento: utilizaremos AWS FIS (Fault Injection Service) para ejecutar nuestro experimento. AWS FIS es un servicio totalmente administrado para ejecutar experimentos de inyección de fallas (Chaos) en AWS. A diferencia de las herramientas externas, FIS inyecta fallas directamente a través del plano de control de AWS, lo que le permite imitar problemas del mundo real específicos de los servicios de AWS.
Además de AWS FIS, utilizaremos AWS System Manager , un servicio que administra sus recursos y aplicaciones de AWS en las instalaciones o en la nube. Escribiremos un documento de automatización personalizado en AWS System Manager y lo utilizaremos desde FIS. Más adelante en esta publicación, analizaremos más en detalle este tema.
Conceptos de AWS FIS
A continuación, se presenta un desglose de los conceptos clave de AWS FIS y cómo aprovecharlos para la ingeniería del caos en nuestro entorno sin servidor:
Plantilla de experimento: este modelo define nuestro experimento. Especifica los recursos (por ejemplo, funciones Lambda) que queremos utilizar, las fallas que queremos inyectar (latencia, errores) y las condiciones de detención (duración, umbrales de error).
Acciones: son formas específicas de interrumpir nuestros recursos, como limitar el tráfico de la red o generar demoras. FIS proporciona acciones predefinidas o nos permite definir acciones personalizadas. Consulta aquí una lista de acciones admitidas.
Objetivos: Queremos inyectar fallas en estos recursos de AWS, como nuestras funciones Lambda.
Condiciones de detención: define cuándo debe detenerse automáticamente el experimento. Puedes basar la condición de detención en un límite de tiempo, errores específicos encontrados o en la superación de los umbrales de utilización de recursos.
Ahora que entendemos los servicios que usaremos para realizar nuestro experimento, repasemos las etapas del experimento y expliquemos cómo usaremos AWS FIS.
Pasos del experimento
1 - Formular una hipótesis
Recordatorio rápido: aquí está nuestra hipótesis:
Dado: Tenemos una puerta de enlace API multirregional detrás del enrutamiento basado en latencia.
Hipótesis: una falla en una región no afecta a las demás. Además, la conmutación por error de Route 53 debería detectar el problema rápidamente y redirigir el tráfico a la región en buen estado.
2 - Introducir fallas (caos)
Se puede optar por inyectar distintos tipos de fallas en una función Lambda. En esta publicación, se utilizará el tipo de falla Latencia, que es simple e intuitivo. Este tipo de falla introduce demoras artificiales en la ejecución de Lambda al agregar latencia, lo que eventualmente causa fallas debido a tiempos de espera. Más información sobre esto a continuación.
Al momento de escribir este blog, AWS FIS no tiene acciones predefinidas para el servicio Lambda, por lo que debemos recurrir a otros enfoques. Usaremos una extensión Lambda para inyectar una falla de latencia en nuestro Lambda. Una vez conectado a nuestro Lambda, podemos habilitarlo configurando nuestras variables de entorno Lambda y configurándolo para agregar demoras artificiales durante la invocación de Lambda. Puede leer más sobre la extensión en el archivo README oficial.
Para conectar la extensión a nuestro Lambda, utilizaremos la acción aws:ssm:start-automation-execution de FIS, que nos permite invocar un documento de automatización de AWS System Manager. Para nuestros fines, desarrollaremos un documento de automatización que:
Obtenga un Lambda y un ARN de extensión Lambda como parámetros.
Adjunte la extensión al Lambda.
Habilite la inyección de fallas (Caos) configurando las variables de entorno de Lambda.
(Opcional) Actualice un alias de Lambda para apuntar a nuestra nueva versión (más sobre esto a continuación).
Consulte aquí para obtener más información sobre la automatización de AWS System Manager y aquí para obtener más información sobre cómo escribir documentos de automatización personalizados.
Puede escribir documentos de automatización en formato JSON o YAML. Consulte el siguiente fragmento de YAML para ver la lista de parámetros que utilizaremos en nuestro documento:
Como puedes ver en el fragmento, tenemos los siguientes parámetros:
FunctionName: el nombre de la función lambda para agregar la capa.
LayerArn: el ARN de la capa que se agregará a la función lambda.
AutomationAssumeRole: el ARN del rol que permite que Automation realice las acciones en su nombre.
ChaosMode — (Opcional) Si queremos habilitar o deshabilitar Chaos (predeterminado: habilitar).
AliasName: (opcional) el nombre del alias de Lambda que se debe actualizar. Un alias de Lambda es un puntero a una versión de función y, por lo general, lo usamos al conectar una función de Lambda a una API Gateway. Si no usa un alias de Lambda, puede ignorar este parámetro y dejarlo vacío.
El documento toma los parámetros y ejecuta un script de Python. Puedes ver el siguiente fragmento que contiene la función “handler”, que es la parte principal de nuestro script:
Esta función Lambda conecta la extensión y agrega variables de entorno para habilitar Chaos (inyectar falla de latencia).
Nuestro script también permite deshabilitar Chaos (deteniendo la inyección de fallas) separando la extensión y eliminando las variables de entorno agregadas.
Puede ver la versión completa del documento SSM aquí .
Definición del experimento FIS
Después de revisar nuestro documento de automatización, revisemos nuestra plantilla de experimento FIS, que es un modelo que define nuestro experimento. Puede ver el siguiente fragmento de ejemplo de la plantilla FIS que usaremos (parcial):
En el fragmento anterior, podemos ver que nuestro experimento contiene las siguientes acciones (líneas 4 a 35):
“S00_AttachExtensionToLambda” (líneas 5 a 13) : habilite Chaos en nuestro Lambda invocando nuestro documento de automatización. Usaremos la extensión mencionada anteriormente mientras habilitamos el tipo de respuesta de latencia configurado con 60 segundos. Habilitar el tipo de respuesta de latencia provocará uno de los siguientes eventos:
Tiempo de espera de la función: esto sucederá si configuramos nuestro Lambda para que se ejecute durante menos de 60 segundos.
Tiempo de espera de API de API Gateway: API Gateway tiene un tiempo de espera de integración máximo de 30 segundos (límite estricto).
“S01_Wait” (líneas 14 a 22) : esperar 10 minutos. Esta acción es esencial, ya que debemos esperar a que haya suficiente tráfico para validar nuestra hipótesis más adelante.
“S02_DetachExtensionFromLambda” (líneas 23 a 34) : deshabilite Chaos invocando nuestro documento de automatización con el parámetro “ChaosMode” establecido en “DISABLED”. Esto revertirá nuestro Lambda a su estado original y completará el experimento.
Es importante tener en cuenta que el fragmento anterior se centra en la actualización de un solo Lambda. Sin embargo, en nuestro caso, deberíamos ampliarlo para inyectar fallas en ambas funciones Lambda en la región de EE. UU.: verificación de estado y servicio. De lo contrario, no simularemos una falla regional en el servicio Lambda.
Para obtener más información sobre las plantillas de experimentos de AWS FIS, consulte aquí .
3 - Observar
Como se mencionó anteriormente, para verificar nuestra hipótesis durante el experimento, crearemos un panel de CloudWatch que muestre una combinación de métricas integradas y personalizadas que contengan una dimensión. Puede leer más sobre métricas y dimensiones personalizadas aquí . El panel debe brindarnos visibilidad de ambas regiones y presentar el código de respuesta que recibe nuestro cliente. Contendrá las siguientes métricas:
Route53 HealthCheckStatus : con esta métrica integrada de AWS, podemos verificar que nuestro punto de conexión se considera en mal estado desde el punto de vista de un operador del sistema. Monitorearemos las métricas de nuestra API en ambas regiones.
“region_{region}”: una métrica personalizada para marcar la cantidad de solicitudes que maneja una región en particular. Podemos calcular este valor analizando la carga útil de la respuesta, ya que nuestra API devuelve “Hola desde la región {region}” en caso de un código de respuesta HTTP 200.
Código de retorno de invocación de API: varias métricas personalizadas para marcar el código de respuesta HTTP devuelto al llamar a nuestra API. Tendremos las siguientes métricas:
status_2xx - número de respuestas 2xx.
status_4xx - número de respuestas 4xx.
status_5xx - número de respuestas 5xx.
Nuestro simulador de comportamiento personalizado mencionado anteriormente (la aplicación “load-gen”) publicará nuestras métricas personalizadas y contendrá la API invocada como una dimensión. Al utilizar nuestras métricas personalizadas, podemos comprender si un cliente experimenta una falla al llamar a nuestra API y qué punto final de la API manejó la solicitud (región 'us-east-1' o 'eu-west-1').
Por ejemplo, si la URL de nuestra API es “ https://example.mydomain.com” y obtenemos un código de respuesta HTTP 200 al llamar a la API al llegar a la región 'us-east-1', tendremos las siguientes métricas:
region_us-east-1: API - “ https://example.mydomain.com” , valor - 1
status_2xx: API - “ https://ejemplo.midominio.com” , valor - 1
status_4xx: API - “ https://ejemplo.midominio.com” , valor - 0
status_5xx: API - “ https://ejemplo.midominio.com” , valor - 0
4 - Mejorar
Una vez que el experimento termine de ejecutarse, revisaremos nuestro panel y validaremos nuestra hipótesis. Debemos corregir cualquier problema que descubramos y volver a ejecutar nuestro experimento.
Libera el caos en nuestra arquitectura
Ahora que hemos revisado todos los pasos necesarios para realizar nuestro experimento, repasaremos los pasos exactos que tomaremos para desatar el Caos:
1. Implemente nuestra arquitectura de muestra presentada arriba.
2. Comience a ejecutar nuestro simulador de clientes (la aplicación load-gen descrita anteriormente). Podemos ejecutarlo como una función Lambda en las regiones de la UE y EE. UU. y, al mismo tiempo, asegurarnos de que ambas instancias llamen a la URL basada en latencia: " https://example.mydomain.com/api/hello".
3. Abra nuestro panel de CloudWatch y verifique que todo esté funcionando correctamente. Esperamos que ambos puntos de conexión de Route53 estén en buen estado y que los clientes de ambas regiones obtengan un código de respuesta HTTP 200.
4. Comenzamos nuestro experimento FIS.
5. Una vez finalizado, observamos nuestro panel de control y validamos nuestra hipótesis. Si no vemos el comportamiento esperado en el panel de control, debemos comprobar por qué, solucionar el problema y volver a ejecutar el experimento volviendo al paso 2.
Después de revisar los pasos que debemos seguir, veamos el siguiente tablero que monitoreó un experimento que realizamos:
Repasemos los widgets en el tablero de izquierda a derecha:
Estado de salud “us-east-1”: muestra el estado de salud de nuestro punto final en la región de EE. UU. (1: en buen estado, 0: en mal estado). Este widget utiliza Route53 HealthCheckStatus .
“us-east-1” #requests: muestra la cantidad de solicitudes que llegaron a la región de EE. UU. Este widget utiliza el valor de nuestra región en la métrica personalizada que se muestra arriba.
“eu-west-1” #solicitudes — Igual que el widget 2 para la región de la UE.
Este panel muestra un problema oculto que tenemos. Detengámonos un momento y pensemos: ¿cuál podría ser el problema en nuestro caso?
Tenemos alrededor de 44 solicitudes en nuestras regiones de EE. UU. y alrededor de 29 en nuestras regiones de la UE. Una vez que comenzamos nuestro experimento (marcado con la flecha roja), vemos una disminución en las solicitudes que llegan a la región de EE. UU., mientras que la UE permanece constante. No vemos un aumento en las solicitudes que llegan a la región de la UE, ya que esperamos que la conmutación por error de Route 53 se active y dirija el tráfico desde nuestra región en mal estado.
La clave del problema es que nuestro punto final de EE. UU. se considera en buen estado durante todo nuestro experimento (widget 1); sin embargo, esperamos que no sea así. Como ambos puntos finales se consideran en buen estado, Route53 sigue dirigiendo a los clientes de EE. UU. al punto final de EE. UU., lo que hace que fallen en lugar de moverlos a la región de la UE.
¿Cuál es el problema? Hemos configurado por error nuestra comprobación de estado de EE. UU. para que apunte a “ ejemplo.eu.midominio.com ” en lugar de “ ejemplo.us.midominio.com ”.
¡Es genial! ¡Encontramos un problema y el experimento resultó beneficioso!
Nuestros clientes de EE. UU. habrían experimentado una interrupción durante una interrupción regional real en lugar de ser trasladados a la región europea.
Vamos a solucionarlo y volver a ejecutar el experimento.
Validando la corrección
Después de solucionar el problema, veamos el siguiente panel:
En este panel, agregamos otro widget (el último de la primera fila) que presenta los códigos de estado que se devuelven al invocar nuestras API. Utiliza nuestra métrica personalizada de invocación de API mencionada anteriormente.
Vemos que todo está funcionando como se esperaba: una vez que iniciamos nuestro experimento (flecha roja), vemos una disminución en las solicitudes us-east-1 con un aumento correspondiente en la cantidad de códigos de estado HTTP 5xx (el último widget en la primera fila). Después de unos minutos, se activa la conmutación por error de Route53, marcando nuestro punto final de EE. UU. como no saludable (flecha verde en el primer widget en la primera fila) y enrutando todo el tráfico a la región de la UE. Podemos validarlo al ver un aumento en las solicitudes que llegan a la región de la UE (el último widget en la segunda fila) y una disminución correspondiente en los códigos de respuesta HTTP 5xx (componente de códigos de estado en la primera fila).
Resumen
Eso es todo por ahora. En esta publicación, tomamos la arquitectura de ejemplo y los conceptos presentados en la primera parte y los pusimos en práctica. Realizamos un experimento con AWS FIS y un documento SSM personalizado, encontramos una configuración incorrecta y la solucionamos. Ahora estamos listos para un período de inactividad regional.
Espero que este artículo (y el anterior) te resulte útil y práctico y te animes a probar tus arquitecturas sin servidor. Puede que te sorprendas con lo que veas :)
Agradecimientos especiales
Me gustaría agradecer a Ran Isenberg y Maxim Drobachevsky por tomarse el tiempo de revisar esta publicación y brindar sus valiosos comentarios.
Comentarios