Desafíos implementando una arquitectura sin servidor (serverless)
Al enfrentar un problema, lo primero que hacemos es comprenderlo a fondo para encontrar una solución adecuada. Este mismo enfoque lo aplicamos a cada proyecto, creandosoluciones que se adaptan a las necesidades de nuestros clientes y les permiten alcanzar a sus metas. A este enfoque le llamamos esquema de la solución.
Como parte de este esquema, uno de los primeros procesos que implementamos es seleccionar una arquitectura para el sistema donde establecemos como trabajaran todos los componentes de este en conjunto. Dentro de las arquitecturas que solemos utilizar, se encuentra la arquitectura sin servidor (serverless), la cual, en este último año, hemos empleado frecuentemente. No obstante, seleccionar una arquitectura no es una tarea simple. Una vez elegida, esta trae consigo los retos que implican materializarla, sumando además la curva de aprendizaje del equipo que estará trabajando en el proyecto.
En este artículo, compartiremos los retos que enfrentamos al implementar una arquitectura serverless así como sus fases.
Fase I – Manejo y despliegue de recursos
La característica principal de esta arquitectura es que todo debe ejecutarse con funciones independientes y descentralizadas, es decir, al tener un grupo de funciones que no usan los mismos recursos, es importante planear el manejo de estas. Para proyectos pequeños, el manejo manual puede ser suficiente. Sin embargo, para proyectos más grandes con equipos numerosos y recursos extensos,se requiere una estrategia adecuada.
Para esto existen varias alternativas, pero la selección de cada una de ellas depende del proyecto, necesidades y conocimiento que tenga el equipo. A continuación, presentamos un conjunto de herramientas que permiten la creación, eliminación, conteo de recursos, documentación (el código utilizado como tal) de la infraestructura y despliegue de cambios de manera efectiva.
En nuestro caso, utilizamos Serverless Application Model (SAM) de AWS, que permite compartir documentación con CloudFormation, otra tecnología propietaria de AWS. SAM tiene una amplia trayectoria en el mercado, por ende, cuenta con una gran comunidad y soporte por parte del mismo proveedor.
Fase II – Determinar el peso del artefacto
En otros proyectos con código de servidor, el tamaño del artefacto no suele ser un factor importante, ya que la mayoría de las arquitecturas de servidor conllevan a tener un ejecutable corriendo, el cual nos maneje las peticiones de informaciones. En el caso de Serverless esto no funciona así, pues no tenemos un servidor activo las 24 horas del día, sino que tenemos un código que cuando lo invocamos tiene un ciclo de vida que crea el ejecutable. Luego maneja la petición de información y por último sale de memoria, provocando entonces que si nuestro artefacto tiene un alto peso, la creación de los ejecutables tarde más y nos impacte en tiempo de respuesta
Esto está definido dentro del ciclo de vida de las Lambdas, el cual cuenta con las siguientes fases:
-
- Init: Inicialización de ejecutable.
- Invoke: Ejecución de nuestro código.
- Shutdown: Liberación de infraestructura.
Para cumplir con esto, AWS nos limita el peso de nuestro artefacto con las siguientes restricciones:
-
- 50 MB (comprimido, para carga directa).
- 250 MB (descomprimido, incluidas las capas).
- 3 MB (editor de consola), 512 KB máximo para un archivo individual.
Además, para poder lograrlo, llevamos a cabo la siguiente lista de puntos a verificar:
-
- Eliminar dependencias no utilizadas: Identifica dependencias de desarrollo, dependencias no utilizadas y dependencias solo para hacer scripts. Estas dependencias no forman parte del runtime.
- Identificar código muerto: Eliminar todo el código que no se está ejecutando en tu aplicación.
- Limitarte al desarrollo necesario: Realizar solo las implementaciones imprescindibles para el funcionamiento de la solución. Evitar agregar elementos con el argumento de que podrian ser útiles en el futuro.
- Limitar cada función a la dependencia necesaria: Cada función (o endpoint) debe limitarse a utilizar la dependencia necesaria para su funcionamiento.
Fase III – Límites de cuota de servicio
Para poder asegurar el funcionamiento correcto, Serverless cuenta con varias limitaciones de servicios los cuales a veces nos obligan a hacer nuestro código de cierta manera. Nos enfrentamos a limitantes como las siguientes:
- Tiempo máximo de ejecución de una función:
- Funciones Lambdas: 15 minutos.
- API Gateways: 30 segundos.
En este caso, el tiempo máximo de ejecución de una función es más que suficiente. Sin embargo, al crear un endpoint para que las aplicaciones y páginas web consuman información, nos encontramos con la limitación de 30 segundos de API Gateways.
Para cumplir con estos tiempos, existen diversas alternativas para las funciones que realizan trabajos muy pesados:
-
- Endpoints asincrónicos: Estos endpoints no devuelven la información de inmediato, sino que inician el proceso de ejecución y permiten consultar la información cuando esté lista mediante otro endpoint. Esto asegura que nuestro tiempo de procesamiento se realice en una función sin la limitación de tiempo de API Gateways.
- Usar cola de mensajes: Enviar la información al código del servidor para que la ingrese en una cola para su procesamiento previo.
- Usar caché: Preprocesar información en una caché para garantizar un tiempo de respuesta rápido.
- Hacer uso de webhooks: Si tenemos una integración con un servicio externo, verificar si dispone de webhooks para evitar consultas constantes. En caso contrario, crear un almacenamiento previo (similar a la técnica de caché) para eliminar este tiempo de respuesta.
- Tamaño máximo de variables de entorno: 4 Kilobytes
Dentro de las limitaciones de recursos, tenemos el tamaño de las variables de entorno, pues solo tenemos disponibles 4 Kilobytes. Con el objetivo de hacer nuestro ambiente inyectable en la aplicación solemos utilizar muchas variables de entorno, así poder ejecutar la misma base de código en varios ambientes, pero en el caso de Lambda nos limita el tamaño de variables que podíamos tener. En este caso no encontramos ningún tipo de solución elaborada, procedimos a solo utilizar las variables estrictamente necesarias.
- Tamaño del Payload: 10 Megabytes
Esta restricción nos obligó a cambiar parte de nuestra solución en proyectos donde necesitábamos manejar multimedias de alta calidad. Para subsanar esta restricción utilizamos una alternativa que nos ofrece el proveedor, la cual consiste en subir archivos directamente a su servicio de almacenamiento (S3) utilizando URL previamente autorizadas.
Dado que nuestro código es desacoplado y cada Lambda es un artefacto independiente, la forma de compartir código y dependencias de manera efectiva fue un desafío. Para esto, contamos con varias alternativas que deben seleccionarse según las necesidades específicas de nuestro proyecto.
-
- Mono repositorio: La idea es mantener todo el código en un repositorio y realizar una compilación utilizando herramientas como Webpack o similar para crear artefactos para cada Lambda.
- Lambda Layer: Esta solución propietaria de AWS consiste en crear una dependencia compartida entre todas las Lambdas (o varias), la cual podemos usar en nuestra función. Esta nos permite también mejorar el tiempo de ejecución porque está previamente procesada.
- Múltiple repositorio: Crear diferentes repositorios y usarlos como dependencia en la Lambda que necesitemos.
En nuestro caso, Lambda Layer nos permitió mejorar el tiempo de respuesta y compartir fácilmente la dependencia y el código general. Sin embargo, esto incrementó el tamaño de las Lambdas durante la ejecución.
Como ven, existen ciertas variables (literalmente) que pudiesen afectar todo un esquema. Es importante que al momento de elegir una arquitectura serverless conozcamos las limitaciones de la plataforma en la que la vamos a construir, de esta forma podamos tomarlas en cuenta y diseñar la solución viable en base a estas.
Bibliografías:
Lambda quotas:
https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html
Env size:
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-environment-variable-size/
Ciclo de vida de los lambda:
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html#runtimes-lifecycle-ib
Lambda layer:
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html
SAM:
https://aws.amazon.com/serverless/sam/
Serverless:
https://www.serverless.com/
Presigned URL:
https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html
CloudFormation:
https://aws.amazon.com/cloudformation/