Arquitectura Serverless con Lambdas sobre AWS

3
5814
server room 3d illustration with node base programming data design element.concept of big data storage and cloud computing technology.

Índice de contenidos


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.

3 COMENTARIOS

  1. 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.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad