Lambda es uno de los mejores servicios que existen en AWS (si excluimos mi servicio favorito S3). Es un servicio que define a la perfección las bondades de AWS y, probablemente, del mundo Cloud.

Y a qué se debe mi amor por este servicio, qué es lo que me parece diferencial y por qué pienso que si utilizas AWS y no utilizas Lambda estás cometiendo un error. Empezamos con esta bonita carta de amor a este servicio de AWS.

¿Qué es AWS Lambda?

Lambda es un servicio puramente Serverless que permite la ejecución de código sin necesidad de provisionar un servidor.

AWS Lambda fue presentado re:Invent de 2014, durante la keynote de Werner Vogles.

La gran ventaja de este servicio es que permite ejecutar código en microVM preconfiguradas durante un tiempo limitado (15 minutos actualmente) y que puede escalar exponencialmente permitiendo hasta miles de ejecuciones concurrentes (1000 es el límite por defecto, pero es ampliable y no existe un límite máximo definido).

Tenemos una serie entornos de Lambda y que soporta múltiples lenguajes como Node.js, Python, Ruby, Java, Go, C# y PowerShell.

Adicionalmente, Lambda permite generar runtimes custom y, por otro lado, ejecutar un contenedor dentro de una Lambda lo que puede llegar a dar mayor flexibilidad.

AWS Lambda no es el primer servicio que fue Serverless, ni siquiera en AWS (SQS tiene ese honor y muchos otros porque es el primer servicio lanzado en AWS), pero seguramente es el servicio que mejor representa que significa Serverless.

Esto incluye todas las ventajas de un servicio serverless:

¿Cuándo debo utilizar Lambda?

La respuesta corta es simple, es un servicio muy potente y muy flexible que tiene muchísimos casos de uso, yo diría que realmente es válido para todo lo que se nos ocurra. La respuesta larga es un poco más compleja. Realmente hay que entender cómo funciona Lambda y cambiar la filosofía de muchos desarrollos. Esto provoca que muchas veces no se opte por Lambda.

Si tenemos en cuenta como funciona Lambda, la mayoría de casos de uso son válidos para utilizar Lambda en alguna parte de la solución.

Dentro de Lambda tenemos dos tipos de invocaciones:

Ahora lo importante es cómo funciona Lambda y cómo cambia la filosofía respecto a otros modelos.

Lo primero es que en Lambda no solo se tiene a la microsegmentación, algo habitual en el mundo de contenedores, sino más bien a la nanosegmentacion. Esto es debido a cómo funciona Lambda.

Imaginemos que desarrollamos una API y subimos el código a una función Lambda con esta API. Mientras no invoquemos la API esta función Lambda no se ejecutará.

La primera vez que ejecutemos la API, se desplegará nuestro código en el entorno de ejecución de esta lambda y se inicializará. Dependiendo del lenguaje elegido esta inicialización será más o menos rápida, en algunos casos como Java esta inicialización puede llegar a ser muy lenta y en otros como Python suele ser más rápida.

Cada invocación desemboca en una llamada a la función Lambda y la ejecución de ese entorno de ejecución que ya está desplegado. El entorno de ejecución seguirá desplegado mientras ejecutemos llamadas a él y se utilizará las veces que sea necesario.

Si requerimos más capacidad por ejecuciones concurrentes, si hay problemas en ese entorno de ejecución o si se despliega una versión nueva del código, se levanta un nuevo entorno de ejecución siendo requerido una nueva inicialización de un nuevo entorno de ejecución.

Este tipo de ejecución más larga se llama Cold Start, ya que tiene más duración y es algo habitual en Lambda. Después hablaremos de una nueva funcionalidad que lo mitiga al máximo.

Si la función lambda no es invocada durante un periodo largo de tiempo el entorno de ejecución se apaga.

Función no invocada

Existe la opción de reservar concurrencia provisionada (aunque tiene coste adicional), de forma que nuestros entornos de ejecución se mantienen levantados para asumir la carga y eliminar los Cold Start.

Teniendo este método de funcionamiento en cuenta es cuando tenemos que cambiar nuestra filosofía de desarrollo.

Puede parecernos una buena idea utilizar una sola Lambda para diferentes invocaciones y aunque es posible y funciona, hay que tener en cuenta que cuanto mayor sea nuestra función, más tiempo tardará en inicializarse.

De ahí que en muchos casos de uso sea muy interesante microsegmentar e, incluso, nanosegmentar nuestras funciones para ganar eficiencia. Al tener funciones más pequeñas se inicializan más rápido y, por otro lado, su ejecución requiere mucho menos tiempo.

Esto puede parecer mala idea porque no reutilizamos código, pero en Lambda podemos utilizar las Layers que permiten reutilizar librerías y dependencias de forma que el código de cada Lambda sea mucho menor y su ejecución más rápida.

Pero si tenemos muchas funciones tenemos un problema, porque hay que orquestar.

Aquí entra Step Functions que es un orquestador de flujos de trabajo que tiene total integración con Lambda y que añade una potencia y funcionalidad extra a Lambda.

Step Functions es una auténtica maravilla que maximiza las posibilidades de AWS Lambda y utilizarlo de forma conjunta es increíble. Como curiosidad, todas las veces que he recomendado a alguien utilizar Step Functions, ha quedado maravillado con el servicio (os animo a todos a probarlo, si no lo habéis hecho ya).

Teniendo en cuenta esto, podemos utilizar Lambda para múltiples usos desde elementos más interactivos con llamadas síncronas o incluso desacoplando gran parte de los eventos con llamadas asíncronas.

Para las llamadas asíncronas podemos apoyarnos en EventBrigde, un bus de eventos que está totalmente integrado con lambda de forma que nuestras Lambdas puedan interaccionar de forma asíncrona con multitud de eventos.

Si queréis algún ejemplo de arquitectura asíncrona os recomiendo revisar Serverlesspresso, una arquitectura que realizaron los Serverless Advocates de AWS y que es una auténtica maravilla para aprender sobre este tipo de arquitecturas.

Además, hay un workshop sobre Serverlesspresso muy interesante para aprender.

Flexibilidad e integración

Pero hemos hablado mucho de un desarrollo normal y quizás la mayor potencia de Lambda es su flexibilidad y su integración con el ecosistema de AWS.

Podemos realizar muchas tareas del día a día utilizando Lambda, ya que permite utilizar vía SDK las APIs de AWS, algunos ejemplos fuera de arquitecturas más habituales serían:

AWS Lambda tiene un coste muy bajo si tenemos en cuenta una utilización habitual de cualquier aplicación.

Lo primero que tenemos que tener en cuenta, es que si analizamos nuestras peticiones, no están todo el rato en ejecución y además no suelen ser procesos que tengan una duración grande.

Por ejemplo, utilizando una Lambda de 512Mb (que para la mayoría de procesos es un tamaño válido, incluso diría que bastante grande), que se ejecutase 10000 veces al día, con una duración por invocación de 1 segundo, tendríamos un coste inferior a unos 3$ mensuales, teniendo en cuenta la alta disponibilidad, la escalabilidad, etc., estamos ante un servicio muy económico.

Otro ejemplo sería un procesamiento de 1.000.000 de archivos diarios con una Lambda de 1Gb que durase unos 10 segundos (sería un caso de un procesamiento bastante extremo de datos) y que tendría un coste aproximado de algo más de 5000 $ al mes. Este tipo de procesamientos en una infraestructura con servidores dedicados sería bastante más cara.

Lambda es una gran solución con un coste muy reducido y además también se puede beneficiar de Saving Plans en caso de que se tenga un consumo muy grande.

¿Cómo funciona a bajo nivel Lambda?

Toca explicar cómo es posible que Lambda funcione tan bien y pueda ofrecer este rendimiento de forma tan económica.

Lo primero que tenemos que saber es que Lambda se basa en una tecnología Open Source desarrollada por AWS llamada Firecracker.

Esta tecnología es un Virtualizador de MicroVM que permite aislar estas en diferentes Tenants, de forma que cada MicroVM está aislada por sí misma y no tiene visibilidad con el resto de MicroVM. Algo similar a cómo funciona una EC2 en AWS, pero con MicroVM en vez de servidores mucho más grandes.

Una las grandes ventajas de Firecracker es que reduce al mínimo el consumo de computación al minimizar muchísimas cosas como la emulación de dispositivos no esenciales (solo emula 5 tipos de dispositivo).

Esto permite que el arranque sea prácticamente instantáneo (menos de 125 ms) y con un consumo mínimo de memoria (solamente 5 MiB por MicroVM) algo muy competitivo.

Inicialmente, Lambda nació sin utilizar Firecracker, Lambda se lanzó en 2014 y empezó a utilizar Firecracker en 2018. En sus inicios se utilizaban instancias t2 dedicadas para cada Tenant (es decir, para cada cuenta) y en estas se ejecutaban los entornos de ejecución de cada tenant, de forma que la separación entre diferentes tenants se realizaba a nivel de hipervisor de AWS, igual que en cualquier EC2.

Esto provocaba ciertos problemas a nivel de recursos y si hemos trabajado con lambda históricamente sabemos que esto generaba bastantes problemas cuando levantábamos una Lambda conectada a nuestra VPC (cada lambda levantaba una Network Interface lo que era extremadamente lento).

A día de hoy, esto ha cambiado sustancialmente, ya que cada entorno de ejecución se ejecuta en una MicroVM de Firecracker, que como hemos comentado anteriormente está aislada a nivel de virtualización, separando los Tenants de diferentes clientes, lo que implica una mejor distribución de recursos.

En vez de utilizar instancias ec2 de tipo t2, se utilizan m5.metal. que son instancias físicas totalmente dedicadas.

Este modelo permite que en una misma instancia se ejecuten diferentes MicroVM, de diferentes tamaños y con diferente propósito, distribuyendo la carga por diferentes instancias m5.metal y por diferentes zonas de disponibilidad.

Diferentes zonas de disponibilidad

Como ya hemos mencionado, cada MicroVM está aislada del resto y contiene el entorno de ejecución, incluyendo el código y las extensiones que añadamos a nuestros entornos.

Cada MicroVM contiene el entorno de ejecución, código y extensiones.

De esta forma cuando necesitamos ejecutar nuestra Lambda por primera vez o en caso de escalado, Lambda provisionará una nueva MicroVM de Firecracker para generar nuestro entorno de ejecución.

Este proceso es muy rápido, una vez generado todas las ejecuciones siguientes utilizarán los entornos de ejecución ya desplegados. Si estos no se usaran durante un periodo largo de tiempo, Lambda desaprovisionaría nuestra MicroVM para utilizar esos recursos para otra MicroVM.

¿Y cómo se solucionan los Cold Start?

Históricamente, los Cold Start han sido un problema que ha traído de cabeza, principalmente, a los usuarios de Java, ya que al ser un lenguaje compilado y, por su propio funcionamiento, tarda bastante tiempo en inicializarse.

Una vez inicializado es probablemente uno de los más eficientes y rápidos, pero ese tiempo de inicialización penaliza mucho en Lambda cuando se ejecutaba por primera vez, en caso de escalado, o si las ejecuciones se dilataban mucho en el tiempo.

Diferencias entre Cold Start y Warm Start

Han existido múltiples soluciones como los Lambda Warmer, que básicamente era una invocación rápida, para que no se desaprovisionara el entorno de ejecución, la provisión de concurrencia para asegurar que los entornos de ejecución siguen funcionando y algún otro Workaround existente.

En el último re:Invent se presentó una funcionalidad nueva llamada SnapStart que se basa en la funcionalidad de Firecracker Snapshotting.

Esta nueva funcionalidad realiza la inicialización de la misma forma, pero una vez que está inicializada, realiza un Snapshot del entorno de ejecución.

Se realiza un Snapshot del entorno de ejecución.

En caso de requerir un escalado o si el entorno de ejecución se ha apagado por no utilizarse, en vez de inicializar el entorno de ejecución, restaura el Snapshot con el entorno ya inicializado.

Se restaura el Snapshot con el entorno ya inicializado.

De esta forma la inicialización se reduce drásticamente, siendo capaz de restaurar el entorno de ejecución en menos de 1 segundo.

Si queréis comparar tiempos de ejecución en diferentes contextos de Lambda os recomiendo esta serie de posts donde compararon diferentes configuraciones con Lambda SnapStart.

En el caso de Java esta funcionalidad es muy interesante porque reduce el tiempo de ejecución, no está implementada en otros runtimes (porque no mejora sustancialmente los ColdStart de otros lenguajes donde el tiempo de inicialización siempre suele ser menor al de SnapStart).

¿Todavía no amas Lambda?

Y con esto termina esta carta de amor a este maravilloso servicio de AWS.

Como último punto, me gustaría añadir que una de las mayores ventajas que tiene Lambda es que a diferencia de otros servicios de AWS que están más orientados a ciertos perfiles, Lambda es un servicio que es muy útil para un amplio espectro de perfiles como desarrolladores, ingenieros/as cloud, ingenieros/as de seguridad, DevOps, FinOps, operaciones Cloud, arquitectos/as, etc.

Y la última pregunta de este artículo es: ¿qué piensas tú de Lambda?

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.