Desarrollo de Arquitecturas serverless en AWS

Ian Alon Yon Yon
Entel Ocean
Published in
9 min readJul 1, 2019

Este post es un reflejo de la charla que dimos en el AWS Cloud Experience Chile el 13 de junio de 2019

En este post contaremos la historia de nuestro proceso de desarrollo de la plataforma de IoT, las decisiones que tomamos en el camino y las razones para estas, siguiendo la siguiente estructura:

  • Explicación que la plataforma IoT Reef
  • Arquitectura V1
  • Flujo de desarrollo
  • Evoluciones de la arquitectura
  • Arquitectura V2
  • Roadmap

La plataforma Reef y solución E2E

Para entender la arquitectura que mostraremos más adelante primero explicaremos las funcionalidades de la plataforma y un caso de uso que desarrollamos en paralelo con esta.

Reef es una plataforma tecnológica que permite a desarrolladores y organizaciones que quieran crear soluciones de IoT, acelerar su proceso de desarrollo, mediante la disponibilización de diferentes funcionalidades tanto creadas por nosotros, por partners o por los usuarios de la plataforma.

Para que esto sea útil para los desarrolladores, Reef debe cumplir ciertos requerimientos:

  • Masividad: la existencia de diferentes actores en la plataforma da una rica fuente de funcionalidades que ayudan a otros desarrolladores y ayuda a aprovechar las economías de escala.
  • Costos: todas las soluciones necesitan mantener los costos a raya, por lo que la plataforma de desarrollo no puede ser un impedimento para crear soluciones competitivas en el mercado
  • Seguridad: la creación de soluciones de IoT representa grandes desafíos en seguridad puesto que no solo hay que proteger la plataforma y aplicaciones, sino que también los dispositivos y su interacción con la plataforma.
  • Innovación: los diferentes usuarios de la plataforma tienen necesidades completamente diferentes y por tanto la plataforma debe soportar estas diferencias y permitirles diferenciarse, y en caso de no soportarlo debe evolucionar rápidamente para lograrlo
  • Elasticidad: los patrones de uso y conectividad de los diferentes dispositivos y casos de uso son impredecibles y diversos, por lo que la plataforma debe responder transparentemente a esto.

El responder a esto con una arquitectura de servidores tradicional requiere un gran esfuerzo para mantener el dinamismo funcional de la plataforma y configurar el autoescalado de los servidores. Esto a su vez hace difícil alcanzar los costos esperados de la plataforma y tener una velocidad de desarrollo competitiva, por lo que vuelve natural elegir servicios de la nube de AWS y una arquitectura serverless basada en eventos para la construcción de esta plataforma. Esto nos permite tener escalabilidad prácticamente infinita sin grandes esfuerzos para que los servicios escalen y nos permiten mantener los costos apegados al consumo, evitando tener que provisionar para el peak de consumo reduciendo los costos potencialmente de cientos de miles de dólares a miles de dólares.

Un caso de uso que estamos acelerando con Reef es una plataforma interna de gestión de flotas Codename: Seahorse.

Funcionalidades

Algunas de las funcionalidades que hemos estado abordando como parte del inicio de Reef y que nos ayudan a construir la aplicación Seahorse son las siguientes:

  • Detección de anomalías
  • Notificación de datos en tiempo real
  • Análisis de datos para BI
  • API de datos transaccional
  • API de datos históricos
  • Gestión de sensores y dispositivos
  • Anonimización de datos
  • Notificaciones y alertas por correo, sms, etc.
  • Visualizaciones de datos

Gestión de flotas: Seahorse

Esta aplicación sirve para gestionar y optimizar las rutas de flotas de vehículos que entregan pedidos, para lo que dispone de aplicaciones Android, iOS y Web. Esto ayuda a reducir costos de despacho debido a que los recorridos serán más cortos y se puede estimar con mayor certeza el horario de entrega real de los pedidos, lo que ayuda a la planificación; también facilita la gestión liberando tiempo de tareas repetitivas y ayuda a mejorar los indicadores de calidad del proceso incorporando puntos de control y ayudas a operadores y administradores.

Se tomaron filosofías Mobile First y Event driven en el diseño de la aplicación para devolverle a los usuarios el tiempo pidiendo que solo interactúan con la aplicación cuando es estrictamente necesario. También se mantuvo al usuario como centro del proceso de diseño manteniendo un feedback constante y priorizando con metodologías ágiles aquellas cosas que le agregan más valor.

Primeras ideas de arquitectura + Primeros Flujos + Definiciones de negocio + Primeras maquetas

Arquitectura V1

En función de los requerimientos mencionados y el approach tomado optamos por una arquitectura 100% serverless basada en microservicios. Esto nos permite ser elásticos a los eventos generados por los dispositivos, mantener los costos a raya, innovar rápidamente modificando los microservicios por separado, entre otros.

Los datos llegan desde los dispositivos al AWS IoT Core, quien los direcciona a diferentes microservicios según función.

Ingesta de datos

La arquitectura soporta MQTT y HTTPS para ingestar datos de dispositivos, y además tenemos un módulo para balancear ingesta de datos en UDP, para los dispositivos que lo requieren, para lo que usamos ECS Fargate que tiene un comportamiento serverless.

Microservicios Reef

  • Analítica: mantiene los datos en S3 como Data Lake y usa Athena para consumirlos
  • Alertas: permite programar notificaciones a correo o SMS según ciertas condiciones
  • Datos: permite acceder transaccionalmente a los datos de los dispositivos.

Microservicios Seahorse

El backend de la aplicación está basado en una serie de microservicios en DynamoDB y Lambda, a los que se accede desde Cognito en la aplicación. Para conectarse a los datos corporativos tenemos una serie de conectores y guardamos un caché de los datos que correspondan.

Flujo de desarrollo

Infrastructure as Code

Esta filosofía nos permite versionar la infraestructura que utilizamos, y gracias a que usamos el framework Serverless podemos también separar lógicamente los recursos de infraestructura y funciones lambda que forman parte de un microservicio funcional. Esto nos permite deployar fácilmente e incorporar diferentes plugins del framework serverless para ayudarnos en el proceso.

Por ejemplo, un microservicio puede estar compuesto de una serie de funciones (python 3.6 con diferentes dependencias), que consume un par de tablas DynamoDB con 1 indice cada una; serverless nos permite deployar todo esto como un conjunto, empaquetando las dependencias de las funciones correctamente.

Uno de los puntos a considerar con este tipo de arquitecturas es el hacer eficiente el proceso de desarrollo, puesto que deployar a la nube toma tiempo, y si se están haciendo cambios o debuggeando un problema se requerirían varios despliegues para probar todo, por lo que para facilitar el proceso usamos plugins como WSGI y DynamoDB_local que permiten probar funciones y llamadas a DynamoDB en local.

Integración continua

Mantenemos un flujo de integración continua que nos ayuda a subir la calidad del código. Una diferencia fundamental con herramientas tradicionales de CI como Jenkins es que este requiere de servidores donde correr, por lo que un setup tradicional puede tener una máquina Jenkins de desarrollo más un cluster Jenkins administrado por un equipo de operación que corre cuando el código pasa a un ambiente de UAT o Pre-producción, sin embargo las herramientas de AWS que estamos usando (CodePipeline y CodeBuild) funcionan en modalidad Serverless completamente administrado, lo que nos evita el esfuerzo de mantener estas máquinas

Todo lo anterior nos permitió tener la primera versión de la plataforma y aplicación, sin embargo veíamos algunos puntos que nos impedían tener la velocidad y proceso que buscábamos. Por ejemplo al avanzar las aplicaciones requeríamos diferentes funcionalidades del backend que nos tomaba tiempo desarrollar, siendo que era la mayoría vistas ligeramente diferentes de una fuente de datos común, o necesitábamos una forma de reutilizar código eficientemente entre diferentes servicios y mantener un proceso automatizado y repetible de despliegue de los mismos, lo que nos llevó a los siguientes cambios.

Fase 2 evoluciones

Para mejorar el diseño inicial realizamos varios cambios en la primera versión de la arquitectura.

Reemplazamos el backend en Dynamo y lambda por Appsync. Esto nos permite flexibilizar las consultas en el frontend y que este se suscriba a nuevos datos de los dispositivos. En las aplicaciones aplicaciones aprovechamos la librería de AWS Amplify que facilita la llamada de diferentes servicios de AWS y usamos el Framework React Native para las aplicaciones móviles.

Algoritmo de optimización

Ilustración sobre algoritmo de optimización

Incorporamos un algoritmo de optimización de las rutas que mejora el desempeño y previsibilidad de las entregas de los vehículos. Para esto trabajamos con el algoritmo de un partner en la plataforma y en paralelo con uno propio, ambos disponibles para ser usados mediante una API.

Squid Framework

Para el desarrollo de microservicios en el framework Serverless, construimos un framework propio que nos facilita el proceso de construcción, configuración y despliegue de los microservicios, implementando algunas falencias de serverless, y cerrando gaps para que todo el proceso sea automatizado. Algunas de estas falencias:

  1. Extensibilidad del template de servicio
  2. Configuración de los plugins centralizadamente
  3. Utilización de código común entre servicios
  4. Dependencias entre servicios
  5. Importación de configuraciones externas

En nuestro framework, la primera capa considera las dependencias de configuración entre los servicios, y usa el gestor de configuraciones para inyectar estas en las aplicaciones, de forma que cuando se construyan estas ya tengan la configuración apropiada. En paralelo también se guardan los valores dinámicos en AWS Systems Manager Parameter Store, lo que permite modificarlos dinámicamente sin reconstruir las aplicaciones.

La segunda capa del Squid framework permite armar un árbol de dependencias de los servicios y obtener las configuraciones globales, dependencias, plugins y otros necesarios para construir el servicio. Esto es importante pues algunos requieren referencias a otros para cumplir su propósito E2E, tenemos librerías propias que se reutilizan en todos los servicios, hay configuraciones de plugins y esquemas de nombrado que queremos enforzar en todos los servicios, etc.

Finalmente se deployean los servicios y se cargan los datos base o de prueba necesarios para su funcionamiento.

Arquitectura V2

Con estos cambios tenemos la segunda versión de la arquitectura. Un punto interesante de usar AWS es que para poder conectar dispositivos a la plataforma es imperativo usar un certificado de seguridad en el dispositivo, o en el caso de aplicaciones móviles se utiliza el servicio Identity Pool de Cognito, que permite usar la sesión de un usuario loggeado en la aplicación para darle momentáneamente acceso a enviar datos a IoT Core.

Y el resumen de la plataforma de desarrollo

Roadmap

Cómo trabajamos iterativamente, a pesar de las modificaciones incorporadas aún quedan cosas por mejorar

Arquitectura

  • Incorporaremos un API Management que nos facilite la gestión de las API Keys, el control del billing por cada funcionalidad y la gestión de la seguridad.
  • Para mejorar la tolerancia a fallas y desacoplamiento de la arquitectura incorporaremos Simple Queue Service SQS, con lo que podemos manejar colas de eventos erróneos, y no sobrecargar las integraciones hacia los datacenters.
  • Para manejar la conectividad de los dispositivos que usan SIM Cards, incorporaremos el Connectivity Manager: Jasper, que nos permite cambiar, activar y desactivar los planes de datos de estos dispositivos.

Continuous Deployment

  • Rollback automatizado: es necesario para poder alcanzar CD, además avanzaremos incrementar nuestro test coverage y manejar despliegues manteniendo backups de los datos y re ingresandolos a los recursos luego del despliegue.
  • Para reducir el radio de impacto de errores implementaremos Sharding Management de los datos de diferentes usuarios.
  • AWS Cloud Development Kit: es una nueva herramienta de AWS que permite definir templates de CloudFormation en un lenguaje popular como Python o Typescript, lo que facilita la escritura de los templates y su mantención además de darles características más dinámicas.

Chaos Engineering

Incorporaremos prácticas de Chaos engineering creando Lambdas que pruebas fallas de diferentes microservicios, diferentes componentes de la aplicación y diferentes zonas de disponibilidad.

¡Eso es todo! Los invitamos a seguir creando tecnología y seguir pasándola bien!

--

--