Blog de Amazon Web Services (AWS)

Creación de cargas de trabajo resilientes usando arquitecturas basadas en “células”

Por: Camilo García Villabona, Arquitecto de Soluciones para Sector Público en Amazon Web Services (AWS)

Problema

La informática en la nube es la distribución de recursos de Tecnologías de Información (TI) bajo demanda a través de Internet mediante un modelo de pago por uso. En lugar de comprar, poseer y mantener servidores y centros de datos físicos, usted puede acceder a servicios tecnológicos, como computo, almacenamiento y bases de datos, en función de sus necesidades. La oferta de servicios, plataformas, patrones y diseños es amplia. Por ende, las alternativas de solución a un requerimiento son numerosas, cada una con sus puntos a favor y en contra.

Una estrategia común es utilizar la nube para dejar atrás el tradicional escalamiento vertical de recursos. Este traía consigo desafíos como limitaciones de tamaño máximo, factores de escalamiento no-lineares e infraestructuras muy grandes para hacer pruebas eficientes. Adicionalmente, existe otro problema que será uno de los puntos de discusión de este blog, conocido como tener un “destino compartido para todo un sistema”, es decir, que una falla en un componente lleve inevitablemente a una falla en todo el sistema.

Al pasar a un modelo de escalamiento horizontal (potenciado por la agilidad y elasticidad de la nube) se deberían solucionar estos problemas. En teoría, se debería lograr aislamiento de cargas de trabajo otorgando tolerancia ante fallos comunes como fallas de despliegue, “píldoras venenosas”, usuarios con comportamiento errático, corrupción de datos, errores operacionales, etc.

Sin embargo, no siempre agregando más instancias de un recurso en vez de hacer el recurso más grande se logra un escalamiento horizontal. Pensemos, por ejemplo, en una base de datos conformada por diferentes instancias (clustering multi-servidor). Aunque el hardware está escalando horizontalmente, sigue siendo una única base de datos.

Lo mismo se puede decir de algunos diseños basados en flotas de instancias de cómputo para sostener un servicio. Aunque se escalan de forma horizontal a nivel de hardware, desde un punto de vista lógico tenemos una unidad y estaríamos escalando verticalmente. Por ende, existe la posibilidad de volver a encontrar los problemas mencionados anteriormente, entre los cuales resalta el destino compartido para todo el sistema.

Ahora, analizando los problemas listados, es posible ver cómo eventualmente podemos llegar a un escenario en donde la capacidad de ofrecer el servicio como es esperado se ve impactada.

Aún más, si tenemos en cuenta que muchas estrategias de resiliencia están basadas en la replicación de arquitectura, estos problemas podrían repetirse rápidamente tras una recuperación. Como consecuencia, podría haber una interrupción del servicio, aun cuando exista un plan de contingencia.

Arquitectura basada en células, escalamiento y resiliencia

La arquitectura basada en células es una alternativa para lograr este crecimiento horizontal real, que permita aprovechar al máximo las capacidades de escalamiento y fiabilidad de la nube de AWS y simultáneamente evitar tener un destino compartido para todo el sistema.

Una célula bajo este contexto se debe entender como una colección de componentes agrupados que forman una unidad funcional completa de una aplicación. Esto significa que cada célula debe ser capaz de llevar a cabo el trabajo necesario para satisfacer las necesidades de negocio asignadas a esa carga de trabajo en su totalidad. Asimismo, se debe poder desplegar, administrar y monitorear de forma independiente.

El propósito de este blog es centrarnos en cómo podemos aprovechar la arquitectura basada en células para reducir el “área de impacto” de una eventualidad, creando cargas de trabajo con un nivel mayor de tolerancia a fallos y resiliencia que eviten el escenario del destino compartido para todo el sistema.

Fundamentos, basados en servicios de AWS

Para empezar a entender que son las arquitecturas basadas en células, vale la pena recordar la diferencia entre los planos de control y de datos. En pocas palabras, mientras el plano de datos se encarga de ejecutar las peticiones de los usuarios (es decir, prestar el servicio en si), en el plano de control se da toda la administración y distribución de la configuración de los recursos para lograr esto.

Pensemos en AWS Lambda. Lo que soluciona como tal las peticiones de los usuarios (plano de datos) es la llamada de invocación “Invoke API”. No obstante, este no es el único proceso que permite las ejecuciones Serverless de este servicio. Detrás de cámara se tienen una serie de llamadas extra que se hacen por parte de AWS, como CreateFunction, UpdateFunctionCode, PublishLayerVersion, etc… que nos permite en primer lugar tener los recursos (plano de control) sobre los cuales nuestros usuarios van a poder solucionar sus peticiones.

Asimismo, a través de estas funciones inherentes al plano de control, se llevan a cabo procedimientos importantes para la preservación funcional del servicio. Un buen ejemplo de estas funciones es la administración de los ambientes de ejecución de Lambda en base a la concurrencia de esa función, manejo de ciclo de vida, procesos de balanceo internos, entre otros.

De servicios a sistemas

Ahora bien, habiendo repasado los beneficios de la separación en planos de datos y de control, debe ser un poco más claro los beneficios que traería imitar esta separación en un sistema o una carga de trabajo completa.

Para iniciar, recordemos la definición de célula expuesta anteriormente: “Una célula bajo este contexto se debe entender como una colección de componentes agrupados que forman una instancia completa de una aplicación.” Esto quiere decir que cada célula sería capaz de brindar el servicio que se desea al usuario final, lo que la haría parte del plano de datos de una arquitectura basada en células.

Por lo tanto, intuitivamente, necesitaríamos un plano de control, que en este caso se llamaría el router de células. Este estaría encargado de todas las políticas de enrutamiento, aislamiento y distribución de carga para mantener un buen nivel de servicio.

Como nota importante, este router debe estar muy bien construido y debe contar con un nivel de disponibilidad alto por diseño. Esto implica que debe ser un componente altamente probado, resiliente y confiable, pues de su correcto funcionamiento depende la orquestación y administración de la flota de células.

Tradicionalmente este componente se conoce como “la capa más fina”, representando su relevancia al ser uno de los únicos componentes compartidos en las arquitecturas basadas en células. Por lo tanto, un problema sobre este componente llevará a una afectación mayor a comparación de un problema sobre células individuales.

Por otro lado, está la flota de células, que serviría como el plano de datos en nuestra analogía. El propósito de esta flota es que cada una de las células pueda atender de forma independiente la carga de trabajo completa. Esto significa que, entonces, a diferencia de arquitecturas tradicionales, donde encontramos capas (balanceo, web, persistencia) encontraremos células, cada una con las correspondientes capas.

Ilustración 1. Arquitectura por capas vs Arquitectura basada en células.

Para ver los beneficios de esta arquitectura, pensemos en un fallo de software o una mala petición. Si llegara a suceder esto, en la arquitectura tradicional podríamos tener una afectación total del servicio, independiente de las consideraciones en cuanto a zonas de disponibilidad y regiones. Esto se da ya que, aunque separemos las responsabilidades que componen la carga de trabajo físicamente, en un plano lógico hay un único frente. Por lo tanto, un fallo de software o mala petición hecha varias veces puede dejar inutilizable la capa web y consecuentemente, la aplicación, aun cuando la capa de persistencia no se haya visto afectada.

En la arquitectura basada en células, contrariamente, hay una separación de responsabilidades más estricta. La separación no es solo al nivel de la infraestructura que soporta la carga de trabajo, sino también a un nivel lógico de la aplicación. Por lo tanto, en el momento que se haga un ataque o una mala petición, podemos reducir la afectación a una única célula, por lo que las demás van a poder seguir prestando el servicio completo aun cuando una haya sido incapacitada.

Adicionalmente, si se logra incluir en el controlador un mecanismo de gobernanza, que permita identificar peticiones conflictivas y denegarlas después de un par de intentos, podemos reducir la afectación aún más. Para demostrar la diferencia, tenemos el siguiente gráfico.

Ilustración 2. Problemas de radio de explosión con arquitectura por capas.

Como se puede ver, una sola mala petición, por el motivo que sea, es capaz de causar indisponibilidad en el sistema pues no se contiene su “área de impacto” al no haber ningún tipo de separación lógica.

Ilustración 3. Alternativa que contiene rango de explosión con arquitectura basada en células.

Contrariamente, en la arquitectura basada en células si hay una separación lógica, pues el router de células define que clientes (y sus peticiones) van ligados a que células. Por lo tanto, si llega a haber una petición mal formada en una célula, en ningún momento afectará el funcionamiento de las otras, pues no se redirecciona a ellas. Esto está basado siempre en el hecho de que cada célula es una unidad capaz de resolver el objetivo de negocio detrás del sistema, por lo que no se necesitan las unas a las otras para continuar funcionando.

En consecuencia, el área de impacto de esta solución es de clientes/número de células.

Idealmente, debemos también ir agregando células a medida que sea necesario, de tal forma que para aquellos clientes que sean atendidos por la célula afectada, eventualmente podrán seguir utilizando el servicio.

Shuffle sharding para mejorar el servicio

Ahora, con el anterior ejemplo es claro un inconveniente que nos podríamos encontrar con la arquitectura de células propuesta. Aunque el servicio en general tendrá un nivel mayor de resiliencia y tolerancia a fallos, va a existir un cliente o conjunto de clientes que igual se verán afectados por la mala petición o ataque que lleve a esta célula a la indisponibilidad, por lo menos hasta que se cree una célula nueva y se bloquee el problema de fondo.

Pensando en esto, existe una técnica para la repartición de clientes entre células, conocida como “shuffle sharding”. Haciendo esto, asignaremos a los usuarios finales a más de una célula, de tal forma que si una célula falla, se hace un tipo de failover a la siguiente célula (siempre y cuando se vuelva a hacer la petición). La idea es entonces, que eventualmente si la petición mala afecta más de una célula, se acabarán las células donde esto puede ocurrir, mientras que los clientes que funcionan correctamente seguirán teniendo acceso al servicio.

Mediante la implementación de esta técnica, reducimos el “área de impacto” a “# clientes / # de combinaciones posibles”, una mejora significativa frente al escenario inicial con una arquitectura por capas o al escenario que solo incluía células, sin llevar a cabo el “shuffle sharding”.

Siguientes pasos

La arquitectura basada en células a nivel de servicios es una alternativa interesante a las arquitecturas basadas en capas que se conocen tradicionalmente. Aunque durante este blog no revisamos las alternativas multi zona de disponibilidad y multi región, se aplicarían las células de la misma forma.

A pesar de que hay consideraciones a tener en cuenta, como lograr crear células que funcionen de forma totalmente independientes (y su respectivo costo) este patrón podría revisarse para la creación de arquitecturas resilientes.

Por ende, valdría la pena hacer una revisión de las cargas de trabajo actuales, así como de las planeadas para el futuro. Una identificación temprana de cargas que puedan adaptarse a este tipo de arquitecturas permitiría la creación de sistemas más resilientes y confiables para cumplir con los objetivos de su organización.

Para aprender más sobre arquitecturas basadas en células, como se utilizan para lograr resiliencia y conocer algunos casos de éxito, puede visitar el siguiente enlace (contenido en inglés).


Sobre el autor:

Camilo García es un Arquitecto de Soluciones en Amazon Web Services para el sector público en la región Andina y el Caribe. Camilo ha profundizado en temas de resiliencia, con énfasis en recuperación de desastres. Le apasionan los temas de TI en las organizaciones, manejo de redes y diseño de sistemas resilientes.

 

 

Revisores técnicos:

Ray Zaman Ray Zaman es Arquitecto de Soluciones en AWS con más de 30 años de experiencia soportando clientes en el sector financiero, de salud, seguros, fabricación, medios de comunicación, petroquímicos, farmacéutico, utilidad pública, ventas, semiconductores y telecomunicaciones a avanzar sus objetivos de negocio.

 

 

 

Agustín Calatayud es Arquitecto de Soluciones en AWS con sede en Buenos Aires, Argentina. Actualmente trabaja para el segmento Small and Medium Business (SMB) de Argentina, Paraguay y Uruguay. Ayuda a los clientes diseñando arquitecturas altamente disponibles, resilientes y costo-eficientes para satisfacer sus necesidades.

 

 

 

Gaston Perez Gaston Perez es Arquitecto de Soluciones Principal en AWS con sede en San Pablo, Brasil. Actualmente trabaja para el segmento Salud y Ciencias de la vida. Ayuda a los clientes diseñando arquitecturas altamente disponibles, resilientes y costo-eficientes para satisfacer sus necesidades.