En mi artículo anterior me adentré un poco a hablar sobre layers en serverless en python, hoy continuaremos esa misma línea y trataremos de solucionar uno de los errores más frecuentes que ocurre al crear un layer propio e intentar usar layers de terceros, esto nos pondrá en una tarea de solucionar un error, pero generar 10 más, como se puede apreciar en la Figura 1
Layers en serverless
Las layers en AWS ofrecen una gran ventaja, esto porque permiten crear código compartido y separarlo del despliegue del código de la lambda, lo cual permite quitar peso al archivo que se construye a la hora de realizar un despliegue, si bien en serverless para nosotros es transparente, por debajo serverless en cada despliegue genera estos archivos .zip con todas las dependencias, otro beneficio de los layers es la capacidad de versionar librerías, haciendo actualización al código del layer sin dejar que el lambda que ya funcionaba tenga que actualizar de inmediato a la nueva versión, una estructuración de los layers se aprecia en la siguiente Figura
Como se puede ver, los layers se agrupan dentro del mismo entorno de ejecución, por lo tanto, comparte con la lambda recursos como variables de entorno, configuración, memoria, etc. Esto es bueno porque dentro de estos layers se podría obtener información cargada por la lambda y realizar diferentes operaciones, sin embargo, estos layers de terceros se ubican dentro de una carpeta llamada /opt/python, pero cuando es un layer propio se ubica solamente en la carpeta /opt, con esto tenemos un problema porque Python puede cargar los módulos solamente desde una ubicación, la cual busca en el PYTHONPATH
Layers compartidos y PYTHONPATH
Entendida la teoría del porqué sucede, pasemos a lo importante, la solución, AWS la plantea muy sencillo, donde básicamente se plantea que existen unos PATH para cada runtime (python, javascript, java) donde el entorno de ejecución buscara los módulos, esta clasificación se aprecia en la Figura 3
En la Figura se ve que:
- Para Node.js buscará las librerías en el path nodejs/node{#version}/node_modules
- Para Python buscará las librerías en el path
python/lib/python#version/site-packages
con esto ya sabemos donde debemos guardar nuestro módulo compartido en nuestro layer, ahora como hacemos para guardarlo en ese folder en específico?, básicamente debemos empaquetar nuestro módulo en una carpeta llamada python y para Node.js en una carpeta llamada nodejs/node_modules, probemos esto mismo en código, para ello vamos a utilizar la misma estructura del post anterior con algunos cambios
los cambios aquí son pocos, básicamente cambié la carpeta que contiene el código a src para que poetry lo agregue de forma automática, ahora voy a agregar la siguiente configuración adicional a la lambda que usara el layer
Ami lambda de user, agregue algunas configuraciones en el serverless.yml, agregue un layer público que agrega a pandas una librería en python, cambie el import porque ahora está dentro del folder src, agregue también al archivo pyproject.toml pandas (debe estar en dev-dependencies) y la importe en el archivo register.py, ahora despliego y pruebo como resultado se ve en la siguiente Figura
efectivamente, nos da error, este error les aseguro que puede tomar horas encontrarlo, porque hay muchos componentes que pueden estar generando la excepción, al final en este caso el problema es a nivel conceptual, pues se debe entender como funciona Python, cambiamos el nombre de la carpeta en nuestro módulo llamado src en shared/libs a python y en el serverless.yml de la lambda user quitamos la variable PYTHONPATH
nos quedaría el serverlees.yml del user de la siguiente forma, sin el PYTHONPATH
service: mylambdaprojectv2
frameworkVersion: '2'
provider:
layers:
- ${cf:libs-dev.SharedLibsExport}
- arn:aws:lambda:us-east-1:770693421928:layer:Klayers-p38-pandas:1
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
region: us-east-1
functions:
register:
handler: service.register.register
timeout: 10
events:
- http:
path: register
method: post
cors: true
package:
exclude:
- node_modules/**
- venv/**
plugins:
- serverless-python-requirements
probamos el servicio desplegado
y todo funciona perfecto, pero ahora para seguir trabajando en local, debo cambiar de nuevo el nombre de la carpeta python a src, esto lo vamos a solucionar con un script
Escribiendo un script
Ya hablé hace un tiempo de hooks, estos hooks son código que ejecutan en cierto momento del ciclo de vida del despliegue de serverless, esto es lo que vamos a hacer antes del empaquetado, con el script vamos a cambiar el nombre de src a python y después del empaquetado lo cambiaremos o lo dejaremos con el nombre de src, crearé una carpeta scripts con dos archivos en python
service: libs
frameworkVersion: '2'
custom:
scripts:
hooks:
'before:package:initialize': python3 scripts/rename_folder_before.py
'before:package:finalize': python3 scripts/rename_folder_after.py
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
layers:
sharedLibs:
path: .
compatibleRuntimes:
- python3.8
compatibleArchitectures:
- x86_64
resources:
Outputs:
SharedLibsExport:
Value:
Ref: SharedLibsLambdaLayer
Export:
Name: SharedLibsLambdaLayer-${opt:stage, 'dev'}
plugins:
- serverless-plugin-scripts
para agregar el serverless plugin deben de instarlo con el siguiente comando
npm install --save serverless-plugin-scripts
Los archivos scripts lo que hacen es renombrar la carpeta src a python cada vez que se ejecute sls deploy, facilitando así el despliegue y el trabajo de estar renombrado la carpeta, con esto ya se puede tener los layers propios junto con layers compartidos sin tener que cambiar la variable PYTHONPATH
Conclusiones
Los layers son muy importantes en serverless, estos errores son errores muy difíciles de encontrar, porque hay muchas herramientas de por medio y cualquiera puede ser la responsable, en estos casos de errores en módulos es de vital importancia entender como funcionan en Python, para descartar errores generados en alguna configuración, como sucedía en el caso de modificar la variable PYTHONPATH, que un principio fue funcional, pero después ocasiono inconvenientes. El ciclo de vida de serverless brinda un apoyo importante para estas transformaciones sencillas, y siempre son un buen aliado para implementar el continuos deployment