Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Lambdas
- 4. Serverless framework
- 5. Ejemplo
- 6. Despliegue
- 7. Conclusiones
1. Introducción
¿Estás ya cansado de los microservicios? Da el siguiente paso con una arquitectura serverless.
Serverless es una forma de construir aplicaciones en la que nos centramos en el código, abstrayendonos aún más de los sistemas, máquinas, disponibilidad, escalado, etc.
Para ello haremos uso de «lambdas». Vamos a explicar en qué consiste esto y verás como ahora el «micro» de microservicio si tendrá sentido. Implementaremos un ejemplo sencillito de API y lo pondremos en producción. Utilizaremos un framework llamado «Serverless» que nos facilitará el despliegue, y tendremos en un momento nuestra API desplegada sobre la plataforma cloud de Amazon.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2,2 GHz Intel Core i7, 16GB DDR3).
- Sistema Operativo: Mac OS High Sierra
- Entorno de desarrollo: Visual Studio Code
- Software: serverless framwork, aws client, nodejs
3. Lambdas
Las «lambdas» o «functions» son pequeñas unidades funcionales de código que se ejecutarán bajo los eventos que se configuren: llamadas a un endpoint, eventos en una cola, cada cierto tiempo, cuando se crea/elimina un fichero de un directorio…
Cada proveedor de cloud ofrece su solución. Todas son parecidas, suelen cobrar por tiempo de ejecución y RAM consumida. No por ciclos de reloj, si no por el tiempo entre el inicio y fin de ejecución de la función aunque esté ociosa.
Los proveedores más famosos son:
4. Serverless framework
El framework Serverless es una herramienta para facilitar el despliegue de nuestro código y la configuración de eventos, bases de datos y de más piezas de la arquitectura.
Soporta varios proveedores de cloud y hace que poner en funcionamiento nuestro código sea realmente sencillo y la configuración quede muy organizada y centralizada en el propio código de nuestro programa.
Más información en: https://serverless.com/framework/
5. Ejemplo
Para demostrar el funcionamiento vamos a implementar un CRUD con un simple API REST que permita crear, actualizar, leer y borrar libros de una base de datos.
De todos los lenguajes de programación que podríamos elegir para implementarlo hemos elegido JavaScript por su sencillez y para intentar que cualquiera pueda seguir el tutorial. Pero se podría implementar para AWS Lambdas en los siguientes lenguajes: Node.js (JavaScript), Python, Java (compatible con Java 8), C# (.NET Core) y Go.
Crearemos un servicio con las siguientes funciones:
- createBook – para crear un registro
- updateBook – para actualizar un registro concreto
- getBooks – para obtener todos los registros
- getBook – para obtener un registro concreto
- deleteBooks – para borrar un registro concreto
Puedes ver el código completo en este repositorio de GitHub: https://github.com/miyoda/serverless-example
Configuración de Serverless
Dentro de nuestro proyecto creamos un directorio «books» que es un proyecto NodeJS independiente que será nuestro servicio «books» con las 5 funciones arriba indicadas.
Con un fichero «serverless.yml» definimos toda la configuración de la arquitectura y despliegue de nuestro servicio.
En el inicio del fichero marcamos el nombre del servicio el cual concatenará al nombre de todas las lambdas junto con el nombre de entorno/stage.
service: serverless-example-books
En el apartado «provider» marcamos donde se desplegará, para qué entorno, en qué región y lenguaje de programación. También podemos configurar variables de entorno como el nombre de la base de datos que puede depender de variables más globales como el nombre del servicio y el entorno.
provider: name: aws stage: dev region: eu-west-3 runtime: nodejs4.3 environment: DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
En el apartado «functions» definimos nuestras «lambdas» indicando qué manejador contiene el código y bajo qué eventos se lanza. Hemos configurado eventos http con los verbos y rutas típicas de una API REST.
functions: createBook: handler: handler.createBook events: - http: 'POST /books' updateBook: handler: handler.updateBook events: - http: 'PUT /books/{id}' getBooks: handler: handler.getBooks events: - http: 'GET /books' getBook: handler: handler.getBook events: - http: 'GET /books/{id}' deleteBook: handler: handler.deleteBook events: - http: 'DELETE /books/{id}'
Y por último definimos la base de datos. En este caso un DynamoDB que es la base de datos NoSQL de AWS
resources: Resources: BooksDynamoDBTable: Type: 'AWS::DynamoDB::Table' Properties: TableName: ${self:provider.environment.DYNAMODB_TABLE} AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
Código NodeJS
Primero configuramos en el fichero ‘dynamodb.js’. Este incluirá la configuración para la conexión a la base de datos dependiendo de si ejecutamos en local o en cloud.
En el fichero ‘handler.js’ creamos las diferentes funciones. Por ejemplo la función de creación de la entidad «book» sería:
module.exports.createBook = (event, context, callback) => { const timestamp = new Date().getTime(); const data = JSON.parse(event.body); if (typeof data.title !== 'string') { callbackError(400, 'Validation error', callback); return; } const params = { TableName: process.env.DYNAMODB_TABLE, Item: { id: uuid.v1(), title: data.title, author: data.author, createdAt: timestamp, updatedAt: timestamp, }, }; // write the todo to the database dynamodb.put(params, (error) => { // handle potential errors if (error) { callbackError(error.statusCode || 501, error, callback); return; } // create a response const response = { statusCode: 200, body: JSON.stringify(params.Item), }; callback(null, response); }); };
La función recibe tres parámetros: event, context y callback.
El parámetro ‘event’ contiene toda la información del evento que lanzo la petición. Por ejemplo en caso de ser un evento de un endpoint llevará los parámetros y los headers del request.
El parámetro ‘context’ contiene información sobre el entorno de ejecución actual de la función. Puedes encontrar más información de este parámetro aquí.
Y por último el parámetro ‘callback’ es la función que debemos llamar para retornar el resultado de la función. Su primer parámetro es el error (si lo hay) y el segundo un string con el resultado (ignorado si el error no era null). Para una respuesta JSON hay que parsearla con «JSON.stringify(…)». Más información aquí.
Podéis ver el resto del fichero ‘handler.js’ aquí; pues no es el objetivo del tutorial aprender NodeJS
6. Despliegue
Vamos a proceder a desplegar nuestras funciones en local y en AWS. Para ello primero tenemos que tener instalado «serverless»:
npm i serverless -g
Instalamos también las dependencias declaradas en el fichero «books/package.json» con:
cd books npm install
Despliegue en local
Para poder desplegar en local primero debemos instalar dynamoDB en local para poder conectar a la base de datos. Para ello ejecutamos dentro del directorio «books» el comando siguiente:
serverless dynamodb install
Y ahora procedemos a arrancar todo en local con el siguiente comando:
serverless offline start
En el fichero «curls.sh» tenéis ejemplos de peticiones a nuestra API REST en el que se lanzan peticiones «curl» a los endpoints de las 5 funciones.
Despliegue en AWS
Vamos a proceder a desplegar en AWS nuestras funciones. Tenemos que tener instalado y configurado el cliente de AWS. Si no tienes una cuenta puedes crearla y usar la
capa gratuita de AWS que ofrece más que de sobra para todas las pruebas que queráis.
Podéis ver cómo instalar el cliente aquí y como configurarlo aquí.
Serverless basándose en la configuración que hemos definido en el fichero «books/serverless.yml» nos creará todas las piezas necesarias dentro del ecosistema de AWS con solo ejecutar:
serverless deploy
Esto nos mostrará una salida por consola donde veremos que componentes/piezas se están desplegando en AWS y nos mostrará el endpoint que ha creado para cada una de nuestras funciones para que podamos consultarlo. Podemos ver toda esta información reflejada en la consola de AWS.
Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress... ..... Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (35.69 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... ................................................................................................ Serverless: Stack update finished... Service Information service: serverless-example-books stage: dev region: eu-west-3 stack: serverless-example-books-dev api keys: None endpoints: POST - https://xxxxxxxx.execute-api.eu-west-3.amazonaws.com/dev/books PUT - https://xxxxxxxx.execute-api.eu-west-3.amazonaws.com/dev/books/{id} GET - https://xxxxxxxx.execute-api.eu-west-3.amazonaws.com/dev/books GET - https://xxxxxxxx.execute-api.eu-west-3.amazonaws.com/dev/books/{id} DELETE - https://xxxxxxxx.execute-api.eu-west-3.amazonaws.com/dev/books/{id} functions: createBook: serverless-example-books-dev-createBook updateBook: serverless-example-books-dev-updateBook getBooks: serverless-example-books-dev-getBooks getBook: serverless-example-books-dev-getBook deleteBook: serverless-example-books-dev-deleteBook
En este caso se ha desplegado simplemente algo así:
La primera pieza es un «API Gateway». Este se encarga de gestionar los endpoints de la API, los conecta con las funciones y ofrece monitorización, escalado, autorización y versionado.
Las siguientes piezas son las propias funciones que nos conectará a un sistema de recopilación de logs con «Amazon CloudWatch Logs» que podremos consultar con:
serverless logs -f NOMBRE_FUNCION
La última pieza es la base de datos NoSQL llamada DynamoDB que podremos administrar online desde la propia consola de AWS.
Podéis modificar el fichero «curls.sh» para que apunte a vuestra API en AWS y probarla. Pero ya debería estar operativa y totalmente preparada para escalar y aguantar una buena biblioteca!
Con el siguiente comando podemos borrar todos los elementos que serverless ha dado de alta en AWS automáticamente:
serverless remove
7. Conclusiones
Como veis con AWS Lambdas y el framework Serverless es muy sencillo y rápido crear una API totalmente operativa y altamente escalable en cuestión de un rato.
Es ideal para crear pequeñas aplicaciones, pilotos, o incluso con un poco más de arquitectura aplicaciones complejas altamente eficientes.
En próximos tutoriales veremos cómo el framework Serverless también facilita la utilización de otras piezas de AWS como SNS para poder interconectar funciones entré si con bajo acoplamiento mediante eventos. Esto nos permitirá diseñar arquitecturas orientadas a eventos como CQRS. También podemos configurar sistemas de autorización personalizados para que se ejecuten automáticamente antes de invocar a nuestras funciones por ejemplo para validar y decodificar la información de un JWT.
Gracias Alfonso por tus palabras!!
[…] utilizando eventos para la sincronización entre las distintas partes del sistema. En el tutorial “Arquitectura Serverless con Lambdas sobre AWS” vimos como funcionan las arquitecturas “serverless” y como nos pueden facilitar el […]
Hola! muchas gracias por tu post, y me quedó una duda. Entiendo que el lambda recibe las variables de entorno, de ahí identifico el ambiente en que trabajará. Pero ¿Cómo es el tema de las definiciones de las tablas en DynamoDB según ambiente? por ejemplo, en tu caso la tabla «books» estando creada para tu ejemplo ¿Podría ser dev_books, test_books o prod_books? No he logrado encontrar donde se refieren a eso en AWS.
Saludos y gracias nuevamente.