AWS Lambda es un servicio de Amazon que nos permite ejecutar código sin la necesidad de tener que provisionar ningún servidor. Tiene la ventaja que que no necesitamos administrar ningún servidor por nuestra parte y no tenemos que preocuparnos de escalar nuestros servidores para tener alta disponibilidad, ya que todo eso lo hace AWS por nosotros.
En esta serie de entradas, vamos a ver cómo crear una función AWS Lambda, llamarla a través de API Gateway y autenticarnos a través de Cognito para poder tener permisos para realizar la llamada a nuestra función Lambda.
Antes de comenzar, mencionar que no en todas las regiones vamos a poder realizar lo que pretendemos, ya que no están disponibles todos los servicios que usaremos aquí en todas las Regiones de AWS, por lo que en mi caso he elegido la Región Norte de Virginia para realizar las pruebas.
Una vez dicho esto, vamos a comenzar a ver cómo implantar esta pequeña arquitectura.
Lo primero que vamos a hacer es crearnos una simple función AWS Lambda en Python para ver cómo funciona. Para ello, nos iremos a la consola de AWS y buscaremos el servicio Lambda. Una vez ahí, pinchamos sobre el botón Crear Función.
En la siguiente pantalla, vamos a seleccionar Crear desde cero, indicaremos el nombre que le queremos dar a la función y el lenguaje que queremos usar para escribir la función, y pincharemos sobre Crear función.
Una vez hecho esto, se nos creará nuestra función. Tendremos 3 pestañas: Configuración (donde podemos configurar el diseño, código de la función en sí, varibles de entorno, etc.), Permisos (roles que tienen permiso para ejecutar nuestra función) y Monitorización (donde podremos visualizar las ejecuciones de nuestra función Lambda). Por ahora nos vamos a centrar solamente en el código de la función. Veremos que AWS nos ha creado un código de ejemplo básico para poder probar nuestra función Lambda.
Podemos probar nuestro código pinchando sobre el botón Probar. Si no tenemos creado ningún evento de prueba, nos pedirá crear uno.
Si ejecutamos ahora nuestro código con el evento de prueba que hemos creado, veremos el resultado, que en este caso no es muy sorprendente. Veremos que nos ha devuelto un JSON con un status y un body con un Hola Mundo!
El parámetro context contiene información acerca de la invocación, la función y el entorno de ejecución (ver https://docs.aws.amazon.com/lambda/latest/dg/python-context.html para más detalle)
El parámetro event contiene la información de nuestra llamada. Generalmente suele ser un diccionario de Python (nosotros hemos pasado un JSON en el ejemplo, que viene a ser un diccionario), aunque también puede ser una lista, un string, un entero, un float o un NoneType. Vamos a modificar un poco nuestra función para que nos devuelva el mismo JSON de entrada y así ver de dónde podemos después coger estos valores. Para ello, vamos a reemplazar el código, devolviendo en este caso en lugar de un Hello World! el event de entrada.
import json
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps(event)
}
Si ejecutamos ahora la prueba, veremos que en el body nos devuelve el JSON que habíamos creado en el evento de prueba.
A partir de aquí, podemos ir modificando la función para recuperar los datos que pasamos en la llamada y tratarlos. Vamos a realizar otra pequeña modificación, tanto en el JSON de entrada como en la función para tratarla.
Lo primero, vamos a modificar el JSON. Para ello, nos iremos al desplegable donde está nuestro evento de prueba y le daremos a Configurar eventos de prueba.
Una vez ahí, modificaremos el JSON de entrada por el que nosotros queramos. En mi caso, he puesto el siguiente:
{
"name": "Samir",
"type": "test",
"message": "This is my test"
}
Y ahora modificaremos la función Lambda para devolver un mensaje en el body, que se componga de los diferentes valores de nuestra llamada.
import json
def lambda_handler(event, context):
# TODO implement
str = "Hello {}, you are runing a {} with the message '{}'".format(event['name'], event['type'], event['message'])
return {
'statusCode': 200,
'body': str
}
Vemos que vamos a devolver una cadena, la cual se ha formado a partir de los distintos valores del JSON de entrada. Para ello, hemos ido recuperando los values a partir de las diferentes keys que venían en el diccionario en el parámetro event.
El resultado es el siguiente:
Una vez hemos visto estos ejemplos sencillos, podemos complicar mucho más nuestra función Python para que se ejecute dentro del servicio AWS Lambda.
Sin embargo, no vamos a poder utilizar cualquier librería de una manera tan sencilla, ya que AWS Lambda no dispone de todas las librerías Python existentes. Un ejemplo es si queremos trabajar con Numpy. Aunque hagamos un import en nuestra función, nos dará un error puesto.
Para mostrar esto, veamos el siguiente ejemplo:
import json
import numpy as np
def lambda_handler(event, context):
# TODO implement
a = np.array([1, 2, 3])
return {
'statusCode': 200,
'body': json.dumps({'result':a.tolist()})
}
Si ejecutamos ahora este código, veremos el siguiente error.
¿Qué podemos hacer entonces si queremos ejecutar una función en AWS Lambda que utilice módulos que no están disponibles?
Vamos a ver cómo hacerlo con el ejemplo del módulo de Numpy. Lo primero que vamos a hacer es crearnos una carpeta donde crearemos un fichero lambda_function.py, la cual contendrá nuestro código como en el ejemplo anterior.
Dentro de esa carpeta, instalaremos el módulo numpy con pip, ejecutando el siguiente comando (yo he utilizado pip3 para ello):
$ pip3 install -t . numpy
Una vez instalado, veremos que tenemos numpy dentro de nuestra carpeta, junto con el fichero lambda_function.py creado previamente.
Borramos la carpeta numpy-1.19.1.dist-info (y __pycache__ si existe).
$ rm -fr numpy-1.19.1.dist-info __pychache__
Y comprimiremos todo en un fichero .zip.
$ zip -r zip.zip .
Una vez tenemos generado el zip, volvemos a nuestra consola de AWS, en la sección de Código indicaremos que queremos Cargar un archivo zip (para archivos mayores de 10M nos aconseja cargarlos desde S3).
Una vez subido, probamos nuestra función.
En la siguiente entrada, veremos cómo usar API Gateway para llamar a nuestra función desde cualquier lugar.