Serverless: llevando Cloud y microservicios a la máxima potencia (2/2)

En la anterior parte de nuestro post sobre serverless situamos a esta arquitectura en el roadmap del sector TI como la principal referencia a largo plazo de la industria. También analizamos diversos patrones y soluciones de la misma que ya se están utilizando en las más exitosas arquitecturas actuales.

Además, empezamos a visualizar algunos de los problemas que aún están por solventar, como son los arranques en frío.

En esta segunda parte analizaremos los demás retos que aún quedan por abordar como, por ejemplo, las conexiones de base de datos y veremos los casos de uso típicos para este tipo de arquitecturas. ¡Vamos allá!

Conexiones de base de datos

Al introducir una nueva arquitectura tan innovadora, lógicamente podemos encontrarnos con nuevas problemáticas que se resuelven de forma distinta.

Una de las problemáticas más reconocidas en el mundo serverless es cómo gestionar las conexiones a la base de datos. Esto es debido a que obtener las conexiones para un pool durante el arranque requiere de un tiempo que condiciona brutalmente el rendimiento de la invocación y que por lo tanto lo hace inviable como opción para comunicaciones síncronas.

En este contexto existen diversas soluciones, algunas saliéndose de serverless, otras más conservadoras y otras más modernas:

Externalizar la operativa con bases de datos a microservicios

La primera opción, y que parece más obvia, es extraer la operativa de base de datos de la parte serverless. Es decir, tener uno o varios microservicios ejecutándose en contenedores que mantengan un pool de conexiones y que expongan un API REST a través del cual realizar las operaciones de base de datos.

De esta forma, las funciones solo deben preocuparse de realizar peticiones a dichos servicios. Si bien los tiempos se incrementan debido al incremento de comunicaciones de red, estos probablemente serán menores que iniciar una nueva conexión a la base de datos. Recordemos también que estas comunicaciones de red se realizarán dentro de una red interna.

Con esta solución perdemos cierta potencia de serverless al tener que disponer de una serie de microservicios levantados de forma continua pero seguimos aprovechándola en las funciones que lleven a cabo nuestra lógica de negocio.

De hecho, las arquitecturas de dos niveles en microservicios encajan muy bien con este modelo. La capa inferior, encargada del acceso al dato, se implementa con microservicios desplegados en contenedores, mientras que la capa superior encargada de la lógica de negocio/agregación/coordinación… se puede implementar con serverless.

Esto nos permite, por ejemplo, tener siempre disponible el acceso al dato para la visualización y realización de operaciones sencillas: típicamente microservicios de pequeño tamaño y un bajo consumo de recursos. Y la lógica de negocio, mucho más compleja, pero menos invocada, en serverless.

Pensemos, por ejemplo, en un ecommerce en el ratio entre visualizaciones de contenido y procesamiento de pedidos. Además, la lógica de negocio de la mayoría de compañías tiene muy buen encaje con un modelo basado en eventos con procesamiento asíncrono.

Bases de datos con APIs REST

Otra opción es no utilizar una capa de microservicios que recubra la base de datos para interactuar con ella, sino hacerlo directamente con la base de datos. Hoy en día existen diversas bases de datos que ofrecen APIs REST directamente, de forma que no es necesario mantener conexiones a la misma.

Simplemente realizaremos una petición REST incluyendo los credenciales necesarios. Ejemplos de este tipo de productos son Couchbase, Firebase… Este tipo de modelos es lo que también se conoce como Backend as a Service, donde servicios típicamente de backend como base de datos, autenticación… se ofertan ‘as a Service’. Por ejemplo, MongoDB es el modelo que oferta en su solución Cloud: Atlas.

Comparado con la solución anterior, esto tiene la desventaja de que no podemos hacer validación y sanitización de entradas como podríamos hacer en un microservicio. Por otro lado, nos quitamos una capa de procesamiento muchas veces innecesaria.

Esto puede suponer un gran ahorro en costes de infraestructura: imaginad quitar todos los microservicios de acceso a datos, probablemente escalados para estar en HA y en todos los entornos.

Estos servicios de bajo nivel muchas veces nos sirven para hacer segmentación de dominios, pero lo mismo se puede conseguir utilizando diversos esquemas con credenciales diferentes para cada uno, garantizando así que cada función interactúa con el dominio correspondiente de forma aislada.

Otro de los argumentos que se podrían esgrimir contra este modelo es que los microservicios escalan mientras que la base de datos no. Esto realmente no es así.

Tus microservicios escalan en número de instancias y en número de conexiones a la base de datos, pero su escalado siempre ha estado limitado por la cantidad de conexiones que tu base de datos puede proporcionar.

Me explicaré mejor: tu base de datos puede soportar 100 conexiones, a lo mejor tu microservicio crea un pool de 10, por lo que cada instancia que escales consumirá 10 conexiones más. La consecuencia es que a partir de 10 instancias no podrás escalar tu microservicio.

¿Por qué? Porque la instancia 11 no será capaz de arrancar al no poder obtener la conexión a base de datos. Tus microservicios han limitado cuantas conexiones de base de datos usas, pero tu capacidad máxima de escalado siempre ha estado marcada por la base de datos, hayas utilizado microservicios o no.

De hecho, conozco a un equipo que hace un tiempo se vio en esta situación: durante un pico de carga sus instancias empezaron a escalar, pero a partir de un punto no pudieron seguir, porque ya no había conexiones a base de datos disponibles. Esto viene a demostrar una vez más la importancia de las pruebas de estrés.

En resumen, el hecho de no tener microservicios sobre la base de datos no te impide alcanzar la capacidad máxima que tenías con los mismos, es más, te reducirá costes y probablemente también complejidad.

Puede que a mucha gente le sorprenda esta situación, pero es como el dicho de la cadena y el eslabón más débil: tu sistema escala tanto como el componente que menos escale de la arquitectura.

¿Puede tu base de datos escalar? Realmente el proceso de escalado de una base de datos nunca es tan sencillo como levantar una instancia más, aunque hoy en día ya existen productos como Amazon Aurora Serverless, que lleva el concepto de serverles a una base de datos SQL, que escalará según tus necesidades de la misma, incluyendo el número de conexiones.

Reutilizar pools de conexiones en los contenedores

Como ya hemos dicho previamente, la mayoría de soluciones serverless reutilizan contenedores para la ejecución de las funciones. Esto nos da la posibilidad de seguir el mismo principio: reutilizar pools de conexiones.

En esta solución, el pool de conexiones es obtenido la primera vez que se arranca el contenedor con la función. Dicho pool permanece vivo de forma independiente al ciclo de vida de la función, de forma que posteriores invocaciones pueden reutilizar la misma conexión. En general esta parece ser la aproximación principal en el mercado.

“Lambda functions are stateless and asynchronous which is great, except that it would be wonderful to share a few things like connection pools, that are expensive to setup. Connection pooling isn’t properly supported. Setting up and tearing down database connections for each request increases latency and affect performance.” Best Practices For Serverless: Connection Pooling Your Database

Es importante que tengamos en cuenta el orden de magnitud del que estamos hablando. Por ejemplo, en este artículo del blog de MongoDB hablan de que los tiempos de ejecución para una query sencilla pueden requerir entre 4 y 5 segundos si es necesario obtener la conexión para cada invocación.

En AWS, en lo que respecta a su solución Lambda, se habla de tiempos inferiores, pero siguen siendo valores por encima de 200ms para obtener una conexión.

No obstante, aunque no son malos tiempos, sigue habiendo una diferencia muy relevante entre reutilizar o no las conexiones:

“It appears that “warm” functions now average anywhere between 4 and 20ms to connect to RDS instances in the same VPC. Cold starts still average greater than 100ms. Lambda does handle setting up DB connections really well under heavy load, but I still favor connection reuse as it cuts several milliseconds off your execution time.” How To: Reuse Database Connections in AWS Lambda

Este tipo de soluciones, si bien son válidas y suponen una mejora relevante, también requieren por nuestra parte de una gestión asociada a otro tipo de situaciones.

Por ejemplo, si se tira el contenedor tras un tiempo sin uso ¿se libera la conexión de base de datos? Si empezamos a recibir peticiones y las funciones empiezan a escalar podríamos llegar a agotar todas las conexiones.

Respecto a la primera situación, una posible solución es establecer un tiempo máximo de vida a las conexiones, de forma que caduquen. Simplemente deberemos gestionar esta situación para que dentro del contenedor se compruebe el estado de la conexión antes de utilizarla y si esta se ha perdido al obtener una nueva.

Con esto simplemente nos exponemos a una latencia mayor en la primera invocación cada x tiempo, según el tiempo de vida que hayamos definido.

Para resolver la situación en la que el escalado de funciones agote las conexiones de base de datos, una posible solución es tan sencilla como limitar el número de instancias de funciones que permitimos ejecutarse.

Por otro lado, como ya hemos comentado, podemos utilizar Amazon Aurora Serverless. Aurora es Serverless en el sentido que la base de datos se inicia, escala y se apaga según el uso que necesitemos de ella. Serverless en el sentido que no requiere administración de capacidad e instancias.

Actualmente el mercado carece de una variedad de soluciones de base de datos pensadas para su utilización con serverless, lo cual es una interesante oportunidad de negocio para muchas empresas para poder lanzar un producto.

En general, disponemos de diversas soluciones para optimizar los tiempos de las funciones. En lo referente a las bases de datos por ejemplo, uno de las más efectivas viene ya de las arquitecturas monolíticas, y es utilizar una caché.

Las funciones más utilizadas accederán a un subconjunto potencialmente pequeño de datos, por lo que la utilización de una caché nos puede introducir una gran mejora de rendimiento.

Monitorización de recursos

La mayoría de soluciones de monitorización actuales no están enfocadas a serverless: al carácter unitario de las funciones y efímero de sus ejecuciones.

Así, aunque se pueden capturar las métricas de los contenedores que ejecutan las funciones, no podemos agrupar ejecuciones de la misma función para poder compararlas, ya que para la mayoría de herramientas la unidad a monitorizar es el contenedor/aplicación, no una ejecución.

Así, en serverless puede darnos más igual en qué contenedor se ha ejecutado la función. Más bien debemos centrarnos en lo que son las diversas ejecuciones de la misma.

De forma ya más general, estas nuevas arquitecturas requieren también un nuevo enfoque en campos como la monitorización y subsiguientes donde a lo mejor el enfoque no debe ir tanto ya a la virtualización (contenedor), aunque este siempre será necesario, sino que ahora el esfuerzo debería ir dirigido a la unidad de ejecución como es la función.

Depuración y reproducción de bugs

En las arquitecturas no serverless, si queremos depurar una petición, no tenemos más que realizar dicha petición a la instancia que consideremos y utilizando la herramienta que corresponda depurarla.

Sin embargo, en serverless esto no es tan sencillo, ya que no somos nosotros los que dirigimos la petición a la instancia de la función: una petición podría levantar una nueva instancia del contenedor si las existentes ya están ocupadas, es más, la instancia en la que se produjo el error que queremos reproducir podría haber desaparecido ya.

Por tanto, si quisiéramos realizar este proceso, debería existir una forma de marcar una instancia de ejecución de función ‘para revisión’.

Este servicio debería ser proporcionado por la propia plataforma serverless e idealmente debería no remitir más peticiones a dicha instancia, además de no permitir morir al contenedor.

Así podríamos realizar la evaluación de la misma y determinar la existencia o no de algún problema.

En todo este proceso estamos suponiendo que la solución serverless que escojamos, en cloud pública o on-premise, nos permita depurar los contenedores, lo cual por motivos de seguridad, en entornos productivos podría no permitirse.

En lo que respecta a la depuración local existen ya algunos servicios como AWS Cloud9 para la depuración de Lambdas escritas en Python y librerías como Lambda-local para Node.js.

De forma más general, la propia documentación de AWS Lambda incluye ejemplos de configuración para depuración local así como la posibilidad de arrancar un API Gateway local.

De todas formas este CLI todavía se encuentra en versión Beta aunque parece que puede venir a arrojar luz a un área todavía muy oscura.

Trazabilidad entre peticiones

La trazabilidad entre peticiones en sistemas de microservicios consiste, desde el punto de vista técnico, básicamente en incluir una serie de cabeceras en las peticiones para que así se relacionen las peticiones entrantes y las salientes y así poder establecer ‘el camino’ a través de todos los microservicios que ha seguido una petición externa y poder hacer análisis de latencias.

Con este sistema es el propio microservicio el encargado de incluir esas cabeceras en las peticiones salientes.

Esto debe ser así porque un microservicio puede recibir varias peticiones concurrentes y no es posible para nadie más que para él mismo saber qué peticiones salientes se corresponden con las entrantes. Pensemos, por ejemplo, lo que ocurre en patrones del tipo sidecar-proxy.

Por contra, una instancia de una función no puede recibir peticiones concurrentes (en dicha situación se levantarían más instancias) y tiene un ‘timestamp’ de arranque de ejecución y de fin de ejecución. Por lo tanto, es sencillo saber qué peticiones salientes están asociadas con las entrantes.

En este contexto ya no es necesario que sea la aplicación la que realice la trazabilidad, sino que debería ser la plataforma serverless la responsable de esto para, una vez más, abstraer a los desarrolladores de estas complejidades.

Simplemente puede interceptar las peticiones entrantes y las salientes del contenedor para incluir las cabeceras correspondientes. Como además conoce los logs correspondientes a dicha ejecución (al conocer la franja temporal de ejecución), puede asociar fácilmente las cabeceras correspondientes con los logs.

Existen ya soluciones como Istio que utilizan el patrón proxy-sidecar y que podrían en el futuro incorporar dicha funcionalidad para arquitecturas serverless.

Seguridad

Ya desde el cambio de arquitecturas monolíticas a microservicios nos encontramos con muchas situaciones sensibles en lo que a seguridad se refiere. Muchas comunicaciones que antes tenían lugar dentro de la aplicación ahora ocurren a través de la red (aunque sea una red interna). Con serverless estas situaciones se van a multiplicar.

Dichas comunicaciones muchas veces implican datos sensibles que no deberían viajar ‘en claro’. Así, por ejemplo, las comunicaciones entre funciones pasándose datos deberían estar cifradas. De la misma forma si utilizamos un bus de eventos o mensajes estos deberán estar cifrados en el mismo.

Otro punto importante es certificar que estamos hablando con la entidad que dice ser. Así será necesario incluir certificados para validar la identidad de las funciones, rotar estos certificados cada cierto tiempo, poder definir qué funciones pueden invocar a cuales… Una buena solución serverless debería permitirnos configurar todo esto de forma transparente.

Finalmente, otro punto a considerar en materia de seguridad son los ataques DDoS. En una aplicación de microservicios si uno de tus microservicios recibe un ataque de este estilo escalará debido al número de peticiones hasta el número máximo de instancias configurado (digamos 6 por ejemplo) probablemente debido a la carga todas ellas se estén cayendo y levantando continuamente, provocando efectivamente una ‘denegación de servicio’.

Eso sí, este ataque estará restringido a dichos microservicios y, como mucho, a los que éste invoque (aunque en la práctica no será capaz de trasladar toda la carga a los que invoque por lo que el daño será menor).

Si has implementado fallback, como deberías hacer, en los demás que lo invoquen, estos podrán seguir funcionando, con lo que el daño, aun existiendo, habrá sido contenido.

En Serverless, por contra, una función no puede recibir más de una invocación concurrente, por lo que nuevas llamadas levantarán nuevas instancias de funciones.

En esta situación un ataque DDoS podría provocar que se levantaran miles de instancias de una función agotando así todos los recursos de la infraestructura, pudiendo afectar incluso a otras funciones que no se puedan levantar debido a esto.

Es necesario, por tanto, que nuestra solución serverless nos permita definir el número máximo de peticiones concurrentes (o instancias en este caso) tanto de toda la plataforma como de cada una de las funciones.

Coregrafiador vs Orquestador

Cuando necesitamos abordar funcionalidad de un mayor nivel de complejidad no será suficiente con una única función, sino que tendremos que componer varias para así obtener el resultado deseado. La cuestión es cómo componemos dichas funciones y aquí tenemos en esencia dos posibilidades:

  • Patrón Orquestador: en este patrón un elemento central es el responsable de coordinar todo el proceso, realizando las llamadas a los diversos servicios.
  • Patrón Coreografía: no existe un elemento coordinador central, sino que cada servicio procesa lo que le corresponde y posteriormente publica un evento, que es recogido por el siguiente servicio para continuar con el procesamiento.

Normalmente, implementar el patrón orquestador suele ser más sencillo, ya que es más parecido a lo que haríamos en un monolito y todo es coordinado a nivel de código.

Por contra, el patrón Coreografía requiere de algún tipo de servicio de mensajería donde publicar los eventos y que las partes implicadas puedan ser notificadas. En el primero, la lógica de coordinación está en un punto centralizado; mientras que en el segundo está distribuida por todo el sistema.

En general, el modelo orquestador suele utilizarse principalmente en soluciones síncronas (aunque también puede utilizarse de forma asíncrona donde todos los eventos son recogidos por el punto de coordinación central) y el modelo coreografía encaja mejor en soluciones asíncronas.

La ventaja del modelo coreografía es que es más escalable a nivel de desarrollo: es más sencillo añadir nuevos suscriptores a los eventos. Añadir un nuevo suscriptor no es más que enganchar un nuevo elemento que se suscriba a un topic por ejemplo, mientras que en el modelo orquestador requiere añadir código.

Además muchas veces el orquestador suele ser un cuello de botella (cuando no un punto único de fallo), también debido a que utilizándolo en el modelo síncrono el throughput es más bajo, por no hablar de que muchas veces terminan aglutinando tanta lógica que se convierten en auténticos microlitos.

En general el modelo coreografía parece encajar más con sistemas distribuidos (y sobre todo con EDA) donde el control se reparte entre todas las piezas del sistema. Por tanto, siendo esta el tipo de arquitectura más habitual en serverless parece que sería el que tendría más sentido.

No obstante existen soluciones serverless como Fission Workflow que para la composición de funciones utilizan un patrón orquestador. Para los que no conozcáis esta solución:

“Fission Workflows is an open source framework that allows you to orchestrate a set of serverless functions without directly dealing with networking, message queues, etc.”

En este caso utilizan internamente un modelo asíncrono, donde una vez definido el flujo, la ejecución de cada función notifica a una cola un estado asociado a su ejecución y el orquestador recoge estos estados para coordinar la invocación a las siguientes funciones.

No obstante, a pesar de ser asíncrono, como las ejecuciones de flujos se producen de forma secuencial y muy rápido, da la impresión de ser síncrono, que es el modelo de comunicación que utiliza con el cliente.

Control de concurrencia

En la sección de seguridad ya comentamos que un ataque DDoS podría hacernos levantar instancias hasta agotar los recursos disponibles y ‘tirar’ así todo nuestro ecosistema de funciones si no disponemos de los controles adecuados en nuestra plataforma serverless.

Fuera del ámbito de seguridad estas circunstancias podrían producirse como consecuencia de situaciones naturales. Pongamos por ejemplo el caso de uso de una plataforma IoT.

En estas plataformas es habitual que un dispositivo pierda la conectividad de red y que mientras no la recupere almacene sus eventos/datos de forma local. Una vez recuperada la conexión vuelca estos eventos acumulados en la cola de la forma que habitualmente hace y de repente te ves con 1000 mensajes en la cola.

Sin ningún tipo de control, esto potencialmente te puede hacer levantar 1000 instancias de la función que procesa dichos mensajes. Haciéndote así a ti mismo un ataque DoS.

En general habrá casos donde quieras procesar los mensajes/peticiones tan pronto como lleguen (con un modelo más síncrono), pero habrá otros donde preferirás que se encolen e irlos procesando poco a poco con un número fijo de instancias (con un modelo asíncrono).

Es necesario que la plataforma serverless te proporcione las posibilidades de configuración para trabajar con ambos modelos.

Virtualización ligera

Esta sección he decidido añadirla cuando el grueso del artículo ya estaba escrito y a pesar de que ya existe otra sección que habla de un tema relacionado, pero ha habido cambios relevantes recientes en lo que respecta al mundo serverless y creo que también es interesante separar el estado del arte actual de lo que está por venir.

Con ‘cambios relevantes recientes’ hablo en concreto de la publicación de Firecracker en el último Amazon Re:Invent de Noviembre de 2018.

Como comentábamos en la sección ‘Arranques en frío’, uno de los escollos actuales en serverless es la tecnología subyacente, los contenedores (o VM en el caso de AWS), que requieren de unos tiempos de arranque demasiado altos como para iniciar un contenedor ante cada invocación de una función.

En ese sentido AWS ha trabajado en una virtualización más ligera que reduzca el que parece ser el mayor inconveniente de serverless.

De todas formas no debemos perder el foco del verdadero objetivo detrás de esta línea de desarrollo, que no es otro que poder seguir utilizando VM debido a su mayor seguridad respecto a contenedores.

Opción que, a priori, puede parecer una apuesta perdedora debido al mayor peso de virtualización y mayor consumo de recursos de una VM en comparación con un contenedor, pero ahí es donde hace su aparición Firecracker.

Yendo a números concretos hablan detiempos de arranque de 125 ms y de overhead de memoria de 5MB por cada instancia de lo que ellos llaman microVM.

Si tenéis tiempo os recomendaría ver el detalle de su compromiso de rendimiento, el cual es realmente alucinante. Pero lo mejor de todo esto es que han liberado el desarrollo como Open Source, por lo que otras plataformas serverless o las soluciones pensadas para on-premise podrán aprovecharse de ello dando un muy fuerte empujón al mundo serverless.

¿Qué más?

Hasta este punto hemos hablado de un montón de temas sobre serverless, pero la realidad es que aún quedan mucho más, por no hablar de que en algunos solo hemos rascado la superficie.

Es inviable en un solo artículo abordar todo lo que serverless representa, pero solo para darnos una idea de temas relevantes que no hemos tratado:

  • Integración (o entrega o despliegue) continuo: como podemos automatizar la integración, empaquetado, prueba y despliegue de las funciones. Sobre todo cuando estamos hablando de un orden de magnitud mayor que en el caso de los microservicios
  • Pruebas: muy relacionado con el punto anterior tenemos un tema como son los tests unitarios y de integración. Quizá los primeros no supongan tanto cambio si bien los segundos suponen un cambio de enfoque: al tener únicamente una función no haremos tests de integración entre capas como solíamos hacer tradicionalmente en las aplicaciones Java con patrón MVC.
  • API Gateway: cuando AWS publicó Lambda lo que terminó de darle el golpe de efecto necesario fue la posterior publicación de su API Gateway. Existe una gran relación entre en mundo serverless y el mundo de las APIs y sus diversas herramientas.
  • Migración: no siempre es posible desarrollar una aplicación de cero desechando la solución anterior, sino que en muchas ocasiones nos vemos obligados a realizar migraciones, siendo necesario la coexistencia de diversas arquitecturas. ¿Qué dificultades nos encontraremos? ¿Cuáles son los pasos a llevar a cabo? ¿Son compatibles las arquitecturas serverless con las monolíticas? Desde luego un tema muy amplio y muy interesante a tratar.
  • Versionado de funciones: las arquitecturas de microservicios hicieron explotar la problemática del versionado y retrocompatibilidad. ¿Qué versiones de un microservicio son compatibles con que versiones de otros? Un problema que también existe en el mundo de las APIs (al final los microservicios no dejan de publicar APIs). Este mismo problema lo encontraremos con nuestras funciones, las integraciones entre ellas, ya sea a través de APIs o con sistemas de mensajería.

Casos de uso

Serverless puede tener una gama muy amplia de usos, pero desde luego ya tiene identificados unos casos ‘clásicos’ de aplicación.

Como hemos comentado a lo largo de todo el artículo, serverless encaja muy bien con EDA (arquitecturas orientadas a eventos), caracterizadas éstas por un procesamiento asíncrono como reacción a diversos eventos.

Esto es debido, entre otras cosas, a que, como también hemos comentado, debido a los tiempos de arranque puede no ser la mejor opción para comunicaciones síncronas.

Este es uno de los grandes retos que aún existe en serverless y uno en el que hay puesto bastante foco y que intuimos que probablemente en el corto-medio plazo se pueda solventar.

Una vez hemos entendido el lugar de serverless en el mundo TI, podemos pasar a ver los principales casos de uso:

  • POCs y pilotos: el hecho de no tener que provisionar, mantener ni configurar ningún tipo de infraestructura, simplemente escribir el código de la aplicación, hace de serverless un muy buena opción para poder realizar rápidas pruebas de concepto y crear pequeñas aplicaciones para su despliegue en producción.

Esto nos permite, entre otras cosas, centrarnos en la rápida entrega de valor así como poder evaluar hipótesis de negocio de forma rápida.

  • Procesamiento multimedia: en la mayoría de plataformas, ante la subida de una imagen o vídeo, es necesario realizar algún procesamiento, como por ejemplo validar su contenido, formatos, tamaños, crear thumbnails asociados, generar marcas de agua…

Todo este proceso suele realizarse de forma asíncrona, por lo que podríamos utilizar funciones para realizarlo siendo el evento la subida de un nuevo recurso multimedia.

  • Ejecución de tareas batch: las tareas batch se caracterizan por ejecutarse con cierta periodicidad para realizar algún procesamiento o comprobación generando unos resultados o comunicaciones de salida.

Es precisamente el carácter efímero de estas tareas el que las hace un buen caso de uso para serverless, pudiendo pagar solo por el tiempo necesario para su ejecución

  • Procesamiento de streams: la gran ventaja de serverless en el procesamiento de streams es la habilidad para adaptarse al flujo de datos, pudiendo aumentar o decrementar la provisión de capacidad acorde al flujo.
  • Change Data Capture (CDC): es un patrón utilizado por ejemplo para la replicación de cambios de un sistema a otros.

En este caso, serverless nos permite escalar fácilmente en función del número de cambios para poder procesar de forma rápida cualquiera de ellos, consiguiendo así, por ejemplo, una replicación casi instantánea entre diversas soluciones de bases de datos, el cual es uno de los casos típicos de CDC.

  • Procesamiento de pedidos: en muchos ecommerce una vez has realizado tu pedido y te lo han confirmado, comienza su procesamiento de forma asíncrona.

Este procesamiento puede incluir comprobaciones de pago, de disponibilidad de stock, comunicación con proveedores, con sistemas contables, con proveedores de mensajería…

Todo esto puede ser diseñado con el patrón coreografía y utilizando funciones para el procesamiento de cada uno de los pasos del flujo.

  • Pipelines de CI/CD: típicamente en la construcción de los pipelines estos reaccionan a un evento (un push o un merge-request) y realizan un proceso de CI o CD sobre el cambio que puede incluir compilación de la aplicación, ejecución de diversos tipos de tests (unitarios, integración, regresión, carga…) empaquetado, despliegue…

Este proceso se caracteriza así mismo por ser efímero. Así podemos realizar un diseño de nuestros pipelines utilizando funciones para cada uno de los pasos y propagando eventos de terminación o fallo como trigger de los mismos.

A su vez el escalado nos permite lanzar tantos pipelines o steps de los mismos de forma concurrente como sean necesarios, no teniendo que enfrentarnos nunca más a la típica situación que muchos hemos tenido que sufrir de esperar a que haya un esclavo de Jenkins libre para que se integre/construya/despliegue nuestro microservicio.

  • IoT: quizás es el caso de uso más conocido de serverless y uno de los primeros en aplicarse. La casuística de IoT es muy similar al procesamiento de streams e incluso al CDC.

En esencia contamos con diversos dispositivos que generarán eventos de forma más o menos contínua. Estos eventos deberán procesarse y en muchos casos al ritmo al que van llegando.

Adicionalmente existirán situaciones en las que por una actividad especial se incremente el número de eventos o, por ejemplo, porque algún dispositivo pierda conexión a la red y una vez la recupere vuelque todos los eventos acumulados, siendo necesario en estos casos escalar para poder mantener el ritmo de procesamiento al ritmo de publicación.

Más allá de estos casos existen también otros como chat bots, backends para aplicaciones móviles… Ya de forma más general cualquier caso de uso de negocio que utilice una arquitectura orientada a eventos es susceptible de utilizar en una parte de la misma serverless, sino en su totalidad.

Conclusión

Como hemos visto, serverless tiene todas las papeletas para convertirse en una de las, si no en ‘la’, arquitectura de referencia en el futuro.

Nacida dentro del mundo cloud y con mejoras sobre arquitecturas que son consideradas referencia hoy en día como la de microservicios, tiene entre sus características factores clave para desbancar a las soluciones actuales.

Factores como la reducción a la mínima expresión de la operativa sobre infraestructura, una velocidad y capacidad de escalado inigualable, una optimización de los costes de procesamiento por debajo del segundo y la posibilidad de que los equipos de desarrollo puedan centrarse exclusivamente en la lógica de negocio.

Todos estas son líneas maestras en las que se lleva años trabajando en la industria del desarrollo del software.

Por otro lado, estamos hablando de una arquitectura que aún no siendo tan joven no ha conseguido una rápida adopción. Como comentábamos al inicio del artículo, esto está directamente relacionado con la inmadurez de los procesos y organización del TI en el grueso de las compañías (véase las dificultades que están teniendo muchas de ellas para adoptar soluciones menos complejas como microservicios).

Esta falta de adopción se traduce en última instancia en la ausencia de un ecosistema pujante de herramientas y soluciones que den soporte a este tipo de arquitecturas, lo que a su vez redunda en complejidad de adopción, siendo una pescadilla que se muerde la cola.

En mi opinión, la mejora en la madurez de las compañías está muy asociada al nuevo boom que está habiendo del TI en organizaciones más tradicionales, así como la madurez en la adopción de arquitecturas de microservicios serán los elementos que pondrán los cimientos para una futura adopción masiva de las arquitecturas serverless.

Otras fuentes

Abraham Rodríguez actualmente desarrolla funciones de ingeniero backend J2EE en Paradigma donde ya ha realizado diversos proyectos enfocados a arquitecturas de microservicios. Especializado en sistemas Cloud, ha trabajado con AWS y Openshift y es Certified Google Cloud Platform Developer. Cuenta con experiencia en diversos sectores como banca, telefonía, puntocom... Y es un gran defensor de las metodologías ágiles y el software libre.

Ver toda la actividad de Abraham Rodríguez

Comentarios

  1. Juan Ignacio Prieto guerrero dice:

    Me gusta mucho su producto

Escribe un comentario