Disclaimer
Esta nota fue escrita previa al cambio de nombre de Twitter y la posterior eliminación de su API. Si bien no vas a poder replicar el ejemplo tal cual como está, la idea se aplica de la misma manera para cualquier otra API. Algunos ejemplos interesantes para que practiques son la de Binance o la de Openweather.
Serverless o “el nombre peor puesto en la historia”
Si estás recién empezando tu carrera en datos, o si recién ahora estás empezando a acercarte a todo lo que es el mundo cloud, seguramente te hayas encontrado en más de una oportunidad con el término serverless. Aunque el nombre podría hacerte pensar que refiere a algo sin servidores, la idea en realidad completamente distinta.
Serverless remite a que la persona que desarrolla no tiene acceso a la infraestructura subyacente, provisto generalmente por un proveedor cloud (AWS, GCP, Azure, etc).
Cabe mencionar que el mundo serverless viene ganando vuelo en los últimos años porque presenta ventajas significativas tanto para developers como para el área de negocio:
- Si bien es un arma de doble filo, la ventaja para data scientists y desarolladores en general está en que se corren bajos riesgos de instalar algo que pueda desarmar un entorno.
- Por el lado del negocio, en la mayoría de los casos, una buena arquitectura serverless dirigida por eventos genera una mayor eficiencia en costos y control sobre lo que se está haciendo a lo largo de una organización.
El mundo serverless puede ser intimidante, así que lo mejor es verlo en acción. Este es el primero de tres artículos en los que vamos a ir progresivamente avanzando hacia una arquitectura cada vez más automatizada, haciendo crecer un desarrollo en el que haremos querys a la API de Twitter y las almacenaremos en S3 para trabajarlas luego.
¡Manos a la obra!
Lo que vamos a necesitar en esta etapa es:
- API Key developer de Twitter (la v2 de la API se consigue instantáneamente y a nuestros efectos nos alcanza).
- Acceso a AWS con los roles que nos permiten interactuar entre Lambda, S3 y Eventbridge.
- Un bucket en S3.
Como un opcional, acceso a una notebook de AWS Sagemaker (y los roles que se requieren, especialmente para conectar con S3).
Paso 1: Armando y probando la scrapera
Veamos el código del scraper:
- Con tweepy podemos interactuar con los servicios de Twitter. La documentación es muy completa y tiene métodos y clases tanto para la v1 como la v2 de la API.
- Nos autenticamos mediante el OAuthHandler, donde vamos a pasarle el bearer token que nos da Twitter al momento de crear esta aplicación. Es muy importante preservar la seguridad de estas claves de accesos, que bajo ningún concepto son públicas. Normalmente lo que hacemos es primero ponerlo “así nomás”, pero más adelante vamos a ver cómo guardar esa clave en una variable de entorno de la Lambda que va a ejecutar la llamada a Twitter.
- Nos quedamos con los campos del tweet que nos interesan.
- La query puede ser cualquier cosa… como noticias sobre Magic. Sí, esa fue la query con la que lo probé; y no, no me arrepiento de nada 😛
- Primero instanciamos el cliente con nuestra clave de acceso y buscamos los tweets con algunos parámetros de nuestra elección.
- Convertimos la respuesta a un dataframe de Pandas, ¡y listo!
Paso 2: Subir los datos a un bucket en AWS
El próximo paso es subirlos a un bucket de S3 (almacenamiento sencillo de objetos) para trabajarlos a gusto.
Veamos cómo:
- Ahora sí puse la query definitiva: vamos a buscar qué se dice sobre Ethereum, ¡pero puede ser cualquier cosa!
- Instanciamos una variable con el nombre de nuestro bucket. Esta forma indirecta de construir los destinos (no hacer la llamada directa al nombre en la creación del objeto s3.Bucket sino la variable llamada BUCKET en la celda 8) nos va a ser de mucha utilidad más adelante (y es una buena práctica).
- Justamente en la celda 8 vemos que con boto3 (la librería de Python para interactuar con los servicios de AWS) instanciamos el acceso a S3, y con eso, el acceso al bucket con el que queremos trabajar.
- Atención: para subir las cosas a S3 primero tenemos que guardar los datos en disco (dumpearlas), porque ese dataframe que levantamos desde Twitter está almacenado únicamente en la RAM (guardamos en test.csv).
- Para subirlo, mediante el objeto del bucket indicamos la ruta y nombre del archivo que queremos subir (“test.csv”) y lo mismo para el archivo como queremos que figure en la nube, cuyo path y nombre que puede perfectamente ser distinto (y seguramente lo sea, como en el ejemplo, que se llama “test2.csv”):
Paso 3: Construir las lambda layers
¡Acá empezamos a pisar un poquito el acelerador y nos vamos con todo a la nube!
Las lambdas son funciones como servicio: ponemos el código y AWS levanta una infraestructura por detrás para poder ejecutarlo. Como la idea es que todo sea lo más liviano posible, las lambdas traen de base sólo las librerías esenciales.
Por eso, para poder usar una librería no tan básica (capaz te sorprende, ¡pero eso incluye Pandas!) tenemos que agregarle capas (layers) que extiendan la funcionalidad básica.
AWS tiene algunas hechas por default para casos típicos (vamos a usar la que agrega Pandas, precisamente), pero para casos no tan clásicos (como Tweepy) tenemos que construirlas a mano, y para eso tenemos que hacer una instalación local de la librería.
Antes de hacer la lambda en sí, hagamos esa layer:
- Hacemos un clásico pip install, pero agregando el argumento -t, que indica el target donde queremos instalarla. En este caso, indicamos una carpeta llamada python que creé previamente: ese nombre es necesario y obligatorio para que la Lambda pueda funcionar.
- Vemos que una vez que termina, en la carpeta python quedan todos los elementos de la instalación de la librería.
Con eso listo, el próximo paso es comprimirlo y subirlo como una layer de lambda, cosa que podemos hacer subiéndolo o indicando una ruta a un S3 que contenga dicho .zip. Afortunadamente la misma interfaz te va llevando y no es un paso complejo: lo único obligatorio es decirle el nombre de la layer (en nuestro caso será tweepy).
Paso 4: Construir la lambda
Con todo listo, podemos construir la lambda en sí.
- Necesitamos un nombre (a elección), un runtime (Python 3.8) y una arquitectura (x86_64).
- En cuanto a los permisos, necesitamos uno que tenga acceso de escritura a S3. Si no sabés cómo, acá te dejamos un post en el que contamos cómo hacerlo.
Con eso listo, nos va a abrir un editor de texto en el que podemos poner nuestro código. La nueva versión tiene pocos cambios pero significativos:
- Hay una función llamada “lambda_handler” en el medio. Sin entrar en detalles, esa es la función que va a ser ejecutada de forma automática por AWS, por lo tanto, todo el código que querramos que se ejecute debe ir ahí adentro.
- De arriba hacia abajo: importamos librerías e instanciamos algunas variables con nombres. A través de la concatenación de estas variables vamos a construir tanto las rutas de los archivos como los propios nombres de manera programática:
- LOCAL_LAMBDA_FOLDER: Para poder dumpear a disco en el contexto de una lambda, tenemos que guardar lo que querramos dentro de la carpeta tmp/.
- FOLDER_STRUCTURE: Mediante boto3 podemos subir a cualquier ubicación que querramos, en tanto tenga el path apropiado. Con este método vamos armando una estructura prolija donde ir almacenando los datos.
- Primero obtenemos un timestamp que vamos a usar para construir el nombre del archivo, pegándole el formato del archivo en la línea inmediatamente inferior a esa.
- Ese LOCAL_FILENAME (la fecha + la extensión) la vamos a usar para armar el path al que tiene que subir en S3 (respetando la estructura del datalake) y el path donde va a estar, luego de ser dumpeado.
- Acá hacemos uso de las variables de entorno de Lambda para ocultar la API Key. Mediante OS, accedemos a una variable de entorno llamada BEARER_TOKEN que definimos en la interfaz, en el apartado correspondiente:
- Por último, agregamos algunos prints a la consola para supervisar los logs.
Con el código listo y preparado, agregamos las capas. En la parte superior, abajo del nombre de la función, está el apartado de Layers. El proceso es sencillo: clickear y agregar las capas que querramos:
- Primero, de la opción AWS Layers elegir AWSDataWrangler-Python38 (la que coincida con la versión de la Lambda).
- Luego, de Custom Layers, elegimos la opción que creamos.
¡Ahora resta probarla! Para ello clickeamos en Test, le ponemos cualquier nombre al evento y ejecutamos la prueba. Si todo salió bien, en tu carpeta de S3 deberías estar viendo un archivo .csv recién creado.
Parte 5: Programar la ejecución
El último paso es setear el evento que dispara la ejecución de la lambda de forma periódica. Para eso, clickeamos en Add Trigger y construimos la regla:
Como detalle simpático, además de las clásicas expresiones en cron (UNIX), toma algunas expresiones en inglés (como la del ejemplo). Clickeamos en Add, y si todo salió bien, cada cinco minutos se va a ejecutar.
¿Qué sigue?
- La parte del evento y su testeo: hasta acá parece esotérico, pero es una parte crucial en la orquestación mediante EventBridge.
- Al momento está guardando todo en la misma carpeta, y claramente no es la forma de guardar cosas en un datalake. Para eso tenemos que particionar la data, almacenando cada cosa en carpetas mucho más segmentadas en función de cuándo se extrajo la información de Twitter.
¡Pero eso en la parte 2!