Cuando trabajamos con serverless Ver figura 1 (que su traducción literal no le haría mucha justicia) nos encontraremos con algunos problemas, esto debido a que cambia el paradigma del que venimos desarrollando, aquí se crea una capa de abstracción a nivel de código, lo que implica al desarrollador no tener que pensar en algunas responsabilidades como por el ejemplo: el enrutamiento, lo efímero de las funciones (su tiempo de vida es durante su ejecución), no se tiene escritura y lectura (podría usar algún servicio bucket), y es código que persigue el principio de SOLID donde se habla de la responsabilidad única, todo este paradigma conlleva algunos errores que se cometen cuando se ingresa a trabajar con functions as a service, por ello en este artículo me enfocaré y resaltare algunos
Timeout
Como mencione anteriormente las lambda functions son efímeras, esto quiere decir que tienen un tiempo en el que inicia y finaliza su ejecución, este tiempo por defecto es de 6 segundos, si por alguna razón este tiempo se sobrepasa, te retornará un error 502 "matando la conexión", hacer el debug cuando esto sucede es bastante complejo, por eso el parámetro timeout les evitará este problema, que generalmente sucede cuando se tiene algún servicio que se llama desde la lambda y que es algo lento, la configuración del serverless.yaml se aprecia a continuación
service: mylambdaproject
frameworkVersion: '2'
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
region: us-east-1
functions:
search:
handler: src.service.search.search
timeout: 10
events:
- http:
path: search
method: post
cors: true
Variables de entorno
Seguramente al trabajar con repositorios de código, se necesitará tener las claves y los key ocultos, para ello en las lambdas también se pueden usar variables de entorno, en serverless específicamente se tienen varios tipos de variable, opt, self y de archivo, ami personalmente me pareció más ordenado utilizar variables de entorno con archivos, para el ejemplo se vería de la siguiente forma el serverless.yaml
service: mylambdaproject
frameworkVersion: '2'
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
region: us-east-1
environment:
API: ${file(./config.${opt:stage, 'dev'}.json):API}
functions:
search:
handler: src.service.search.search
timeout: 10
events:
- http:
path: search
method: post
cors: true
en este ejemplo las variables se cargan a todas las funciones, pero podrían cargarse solo por funciones individuales
AWS profiler
Cuando se van a hacer deploy en diferentes proyectos, se pueden tener diferentes cuentas de AWS para ello se puede utilizar aws-profiler que permite crear y usar diferentes credenciales
serverless config credentials --provider aws --key 1234 --secret 5678 --profile personal-count
en el archivo ~/.aws/credentials tendríamos algo similar a esto
[personal-count]
aws_access_key_id=***************
aws_secret_access_key=***************
y así cuando se realice el deploy se agrega el profiler
sls deploy --aws-profile personal-count
ahora, aquí puede darse un dolor de cabeza ya que puede generar el siguiente error
The security token included in the request is expired
para corregirlo se utiliza el siguiente comando
aws configure set aws_session_token your_token
esto sucede ya que al tener varias cuentas de AWS, el token de la cuenta personal, o por defecto puede estar usando el token erróneo, por ende debe setearse
Utilizar un mismo bucket
Cuando se despliega una lambda, el código del proyecto es subido o guardado en un bucket, por defecto serverless te crea el bucket para cada proyecto y guarda el código de las funciones, sin embargo se es muy común el caso de que en los despliegues se deseen guardar todas las lambdas en un mismo bucket, para ello se puede utilizar el siguiente plugin que garantiza el proceso de manera exitosa
serverless plugin install --name serverless-deployment-bucket
y se agrega la siguiente configuración al archivo serverless.yaml
service: mylambdaproject
frameworkVersion: '2'
provider:
deploymentBucket:
name: ejemplo-test
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
region: us-east-1
environment:
API: ${file(./config.${opt:stage, 'dev'}.json):API}
functions:
search:
handler: src.service.search.search
timeout: 10
events:
- http:
path: search
method: post
cors: true
plugins:
- serverless-deployment-bucket
el deployment bucket queda de manera global en el provider, este es llamado ejemplo-test, si este no llegara a existir serverless lo creara como el procedimiento normal
Requirements y el espacio
El cambio de paradigma implica que todo lo que necesite la función debe subirse en el código, es decir no se tienen los entornos virtuales que generalmente se usan, eso no implica que no se puedan instalar dependencias, para ello con el siguiente plugin lo que hace serverless es subir el código de las dependencias por nosotros y nos ahorra un poco de trabajo
serverless plugin install --name serverless-python-requirement
y ahora adicionalmente si usamos un entorno virtual o cualquier carpeta que no se requiera subir en desarrollo, podemos agregar la siguiente configuración
service: mylambdaproject
frameworkVersion: '2'
provider:
deploymentBucket:
name: ejemplo-test
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
region: us-east-1
environment:
API: ${file(./config.${opt:stage, 'dev'}.json):API}
functions:
search:
handler: src.service.search.search
timeout: 10
events:
- http:
path: search
method: post
cors: true
package:
exclude:
- node_modules/**
- venv/**
plugins:
- serverless-deployment-bucket
- serverless-python-requirements
con esta configuración en el serverless.yaml, cada vez que se despliegue buscará el archivo requirements.txt , se instalarán las dependencias locales y se subirán con el código del lambda, pero además la carpeta de entorno virtual, en mi caso llamada .venv no se subirá, esto debido a que el exclude evita que se suban carpetas o documentos que no se desean tener guardadas con la función, recordando que el espacio es un atributo fundamental, pues es bastante limitado en las funciones
Conclusión
Existen muchos errores que pueden resultar al trabajar con lambdas, sobre todo al comienzo debido al cambio de paradigma, he descrito algunos que seguramente ayudarán a ahorrar un poco de tiempo buscando la solución, pero sin duda me han sucedido algunos otros que espero continuar en un siguiente artículo