Tema 5 · 5 · Ejercicio 2, tipo 3: CoinGecko a S3 y RDS
Presentación del Tema 5: «Ejercicio 2: CoinGecko (Tipo 3)» (Power BI sobre la tabla) y «Ejercicio 2 · programar la ejecución con EventBridge». Duración: 25-30 min (sin contar Power BI ni EventBridge). Coste: prácticamente nulo en esta actividad; lo que cuesta es la infraestructura base de la carpeta 05.01 (RDS y EC2 por hora).
Solo haces UNA variante del Ejercicio 2. La carpeta 05.03 (S3), la 05.04 (DynamoDB) y esta (S3 + RDS) son las tres variantes. Haz la que te asigne tu docente. No hagas las tres.
¿Dudas con un script? Todos los scripts de esta carpeta traen su propia ayuda:
./gestionar-crypto-s3-rds.sh --ayuda(también vale--helpo-h) explica los pasos y las opciones. EsteREADME.mdes el paso a paso de la actividad: tenlo a mano y consúltalo antes de preguntar.
Etiquetas de lo que creas.
./gestionar-crypto-s3-rds.sh crearetiqueta el stack (Lambda, rol) y la regla de EventBridge concurso=G214,universidad=CUNEF,actividad=05.05-cryptoas3yrdsyscript=gestionar-crypto-s3-rds.sh. Las verás en la consola (pestaña Tags del recurso) y, si hiciera falta borrar a mano, se localizan conaws resourcegroupstaggingapi get-resources --region eu-north-1 --tag-filters Key=actividad,Values=05.05-cryptoas3yrds. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): README principal, sección «Etiquetas».
Qué vas a hacer
Vas a crear una función Lambda que pide a CoinGecko (API pública, sin clave) el precio en USD y EUR de cinco monedas (bitcoin, ethereum, cardano, solana, polkadot) con su capitalización, volumen y variación en 24 horas, guarda los datos originales en S3 y después inserta una fila por moneda y divisa en una tabla de la RDS MariaDB.
Qué se crea en AWS: el stack cunef-etl-crypto-rds (rol + función crypto-price-tracker-rds, 60 s, 256 MB). La primera ejecución crea en la RDS la base crypto y la tabla coins. Lo crea, lo comprueba y lo borra un único script: gestionar-crypto-s3-rds.sh.
Qué hace la función: guarda los datos originales en S3 (crypto/raw/history/crypto_prices_AAAAMMDD_HHMMSS.json y crypto/raw/crypto_prices_latest.json) e inserta en la RDS una fila por moneda y divisa (5 x 2 = 10 filas por ejecución) en la tabla crypto.coins, que crea si no existe. Las credenciales y el bucket los toma de variables de entorno que fija el script (S3_BUCKET, DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_TABLE).
Importante: si falla la parte de RDS, la función no devuelve 500: devuelve statusCode 200 y dentro de body el campo rds con "success": false y el motivo en rds.message. Mira siempre rds.success.
Lo que debes saber antes de empezar (vale para las tres variantes)
- Un solo script hace todo.
./gestionar-crypto-s3-rds.sh creardetecta la RDS y el bucket del Paso 0, empaqueta el código con la libreríapymysql, crea el stack de CloudFormation (con la plantilla que lleva dentro), sube el código de la función y, si quieres, la prueba. Ya no hay que crear el stack a mano. Es seguro repetirlo: si todo existe, solo actualiza el código; si quedó algo a medias de un intento anterior, lo repara. - Menú o comandos. Sin argumentos y en una terminal, el script muestra un menú numérico. Con argumentos ejecuta directamente un comando:
crear,estado,probar,logs,programar,extender,desprogramar,eliminar,extraeryayuda(lista completa en Los comandos del script). - El script pregunta
¿Deseas probar la función ahora? (s/n):al terminarcrear. Escribesy pulsa Intro. Connsolo despliega. Para no tener que contestar:crear --si(prueba) ocrear --no(no prueba). Sin terminal interactiva nunca se queda esperando: no prueba. - El script ya no crea un rol de IAM propio. La función usa el rol del stack. Si en una versión anterior se te creó un rol sobrante (
LambdaCryptoTrackerRDSRole),eliminarlo borra. - El script marca el resultado con el
statusCodede la función. Tras la prueba verás✓ Función ejecutada correctamente (statusCode 200)si fue bien, o✗ La función devolvió statusCode 500seguido del motivo (campoerror) si falló por dentro. Con el CLI a mano ocurre lo contrario:"StatusCode": 200solo dice que la llamada de invocación terminó, aunque dentro vayastatusCode500. Comprueba siempre el dato en el destino. - Cada ejecución añade datos nuevos (no se sobrescribe lo anterior), salvo
crypto_prices_latest.json. Repetir la invocación es seguro, pero acumula ficheros y filas. - Los "Comandos útiles" que imprime el script ya llevan
--cli-binary-format raw-in-base64-out(AWS CLI v2). - CoinGecko limita las peticiones y el límite es bajo. Tras unas 5 peticiones seguidas desde la misma IP responde error 429 (
Status 429oHTTP Error 429en el campoerror; en una prueba, 5 respuestas 200 y 7 con 429 en una ráfaga de 12). No encadenes elcurlde comprobación, el script cons, elinvokea mano y la regla de cada minuto: deja 1-2 minutos entre pruebas. Si toda la clase sale por la misma IP, el 429 aparecerá antes. No es culpa de tu código: espera uno o dos minutos y vuelve a invocar. La función no reintenta sola.
Qué deberías ver tras la primera ejecución correcta: 2 ficheros JSON bajo crypto/raw/ y 10 filas en crypto.coins (5 monedas x 2 divisas); en cada ejecución siguiente, 2 ficheros nuevos y 10 filas más.
Qué contiene esta carpeta
| Fichero | Para qué sirve | En qué paso se usa |
|---|---|---|
gestionar-crypto-s3-rds.sh |
Todo el ejercicio en un solo archivo: detecta la RDS y el bucket del Paso 0, empaqueta el código con pymysql, crea el stack y sube el código, comprueba el estado, prueba la función, programa EventBridge y lo elimina todo (incluida la tabla de la RDS). Lleva dentro el código de la Lambda y la plantilla de CloudFormation. |
Pasos 3, 4, 5 y 7, y la Limpieza |
README.md |
Este documento. | Siempre |
El código de la función y la plantilla viajan dentro del script. Para verlos o editarlos, extráelos a la carpeta actual:
./gestionar-crypto-s3-rds.sh extraer
Esto escribe lambda-crypto-s3-rds.py (el código de la función) y plantilla-crypto-s3-rds.yaml (la plantilla de CloudFormation). No son necesarios para desplegar. En este ejercicio no tienes que editar nada (el bucket llega a la función por variable de entorno), pero si editas el .py y ejecutas crear, el script despliega tu versión (también detecta un lambda_function.py en la carpeta, o el fichero que le indiques con --codigo mi-codigo.py; si no compila, no despliega nada y te lo dice). La plantilla extraída solo se usa con crear --plantilla plantilla-crypto-s3-rds.yaml. Si el fichero ya existe, extraer no lo sobrescribe (extraer --forzar lo hace y guarda una copia .bak).
Antes de empezar
- Depende de Tema 5 · 1 · Infraestructura base: necesita el stack
cunef-etlcreado (bucket y RDS). Si no existe, el script dice✗ No se encontró el stack cunef-etly se detiene antes de crear nada; no usa buckets de demostración ni crea uno propio. - Región eu-north-1 (Estocolmo) seleccionada en la consola (el script ya trabaja en esa región).
- CloudShell (trae
aws,zip,python3ypip3). No hace faltajq.pip3hace falta porque el script instalapymysqldentro del paquete de la función.
Cómo llevar los ficheros a CloudShell
- En tu PC, comprime esta carpeta entera (
05.05-cryptoas3yrds): clic derecho → Comprimir en archivo ZIP (Windows 11), Enviar a > Carpeta comprimida en zip (Windows 10) o Comprimir (Mac). - En CloudShell (región eu-north-1): Actions → Upload file y elige
05.05-cryptoas3yrds.zip. - Ejecuta:
cd ~
unzip -o 05.05-cryptoas3yrds.zip && cd 05.05-cryptoas3yrds
chmod -R u+rwX . && chmod +x *.sh
ls
Si ya subiste
g214-materiales.zip(README principal), no necesitas este zip: entra concd ~/g214-materiales/tema5/05.05-cryptoas3yrdsy, si algún fichero daPermission denied, ejecuta allíchmod -R u+rwX . && chmod +x *.sh.
Qué verás: gestionar-crypto-s3-rds.sh y README.md. Ejecuta el script siempre dentro de esta carpeta. Las variables de shell se pierden al cerrar la pestaña.
Los comandos del script
./gestionar-crypto-s3-rds.sh COMANDO [opciones] (ayuda los lista; sin comando y con terminal, abre el menú).
| Comando (alias) | Qué hace |
|---|---|
crear (desplegar, deploy) |
Detecta RDS y bucket, empaqueta pymysql, crea o actualiza el stack, sube el código y, si quieres, prueba la función. Repetible. |
estado (status, verificar) |
Muestra qué existe (infraestructura base, stack, función y si tiene la RDS configurada, rol, datos en S3, reglas, logs), la última ejecución, las últimas líneas del log y qué hacer a continuación. |
probar (test, invocar) |
Ejecuta la función una vez y comprueba statusCode, los ficheros en S3 y rds.success. |
logs |
Últimas líneas de los logs (logs --seguir: en directo). |
programar [N] (eventbridge) |
Ejecuta la función cada N minutos (por defecto 1) con EventBridge durante 4 h: después la regla se desactiva sola (ver Apagado automático). Si la regla ya existe, la reactiva y renueva el plazo. Ver 05.06. |
extender (ampliar) |
Solo cambia el plazo del apagado automático (otras 4 h; --horas N o --permanente). |
desprogramar (parar) |
Quita (borra) la programación. |
eliminar (borrar, limpiar) |
Borra todo lo de este ejercicio (incluidos los datos de S3 y la tabla de la RDS) y comprueba al final qué queda. Pide confirmación (con --si o -y no). |
extraer |
Escribe en la carpeta el .py y el .yaml embebidos para verlos o editarlos. |
Opciones: --si/-y (responde sí: crear prueba la función, eliminar no pregunta), --no (crear no prueba; eliminar no borra), --codigo F.py, --plantilla F.yaml, --forzar (con extraer), --seguir (con logs), --cada "rate(1 hour)", --regla NOMBRE, --horas N (1-24) y --permanente (con programar y extender).
Paso a paso
0. Recupera las variables del Paso 0 (también si cerraste CloudShell):
OUT() { aws cloudformation describe-stacks --stack-name cunef-etl --region eu-north-1 \
--query "Stacks[0].Outputs[?OutputKey=='$1'].OutputValue" --output text; }
BUCKET=$(OUT BucketName); RDS=$(OUT RDSEndpoint); FN=$(OUT LambdaName); ADMINER=$(OUT AdminerURL)
echo "Bucket : $BUCKET"; echo "RDS : $RDS"; echo "Lambda : $FN"; echo "Adminer: $ADMINER"
Qué verás: cuatro líneas con valores (aquí necesitas BUCKET, y RDS y ADMINER para Adminer). Si salen vacías, el stack cunef-etl no existe: vuelve a la carpeta 05.01.
1. Comprueba que CoinGecko responde desde CloudShell:
curl -s -o /dev/null -w "%{http_code}\n" "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=eur"
Qué verás: 200. Si ves 429 u otro número, la API te está limitando o no responde: espera un poco y repite antes de seguir.
2. (Opcional) Mira el código de la función: ./gestionar-crypto-s3-rds.sh extraer y abre lambda-crypto-s3-rds.py. No hay que cambiar nada: el bucket y la RDS le llegan por variables de entorno.
3. Crea todo y despliega el código:
./gestionar-crypto-s3-rds.sh crear
Qué verás, en orden (los textos pueden variar ligeramente): "Verificando prerequisitos"; "Detectando Configuración de RDS" con "RDS detectado automáticamente" (host, usuario, contraseña, base crypto y tabla coins); "Configurando Bucket S3" con "Usando bucket del stack ETL: ..." (sin el Paso 0 se para aquí sin crear nada); "Preparando el código y la plantilla"; "Creando Paquete Lambda" (instala pymysql, "Tamaño del paquete"; se hace antes de crear nada, así que si falla no queda nada a medias); "Stack de CloudFormation" ("Creando el stack cunef-etl-crypto-rds ..." con puntos de progreso y "Stack ... creado"; tarda 1-2 minutos); "Desplegando Función Lambda" con "Actualizando función existente...", "Código actualizado" y "Configuración actualizada"; y la pregunta ¿Deseas probar la función ahora? (s/n):. Responde s.
Tras la prueba verás un JSON con "statusCode": 200; en body.s3 las rutas guardadas y en body.rds "success": true con "records_inserted": 10, "database": "crypto" y "table": "coins". El script añade "Datos guardados en RDS exitosamente" y "Registros insertados: 10". Si esas dos líneas no aparecen, rds.success no fue true: el script avisa con ⚠ La parte de RDS no se completó y el motivo; lee también rds.message. El script espera a que la configuración de la función esté activa antes de probar; si aun así el motivo dice Host: NO CONFIGURADO, espera 30 segundos y repite la prueba con ./gestionar-crypto-s3-rds.sh probar.
4. Comprueba el estado cuando quieras:
./gestionar-crypto-s3-rds.sh estado
Qué verás: una línea por elemento con ✓ o ⚠ (RDS y bucket de la infraestructura base, stack, función con su estado, tamaño de código y si tiene la RDS configurada, rol, datos en S3, reglas de EventBridge, grupo de logs), la fecha de la última ejecución, las últimas líneas del log y una última línea "Siguiente paso: ...". Las filas de la tabla se miran en Adminer.
5. Prueba la función a mano:
aws lambda invoke --function-name crypto-price-tracker-rds \
--cli-binary-format raw-in-base64-out --payload '{}' \
--region eu-north-1 out.json
cat out.json
Qué verás: "StatusCode": 200 del CLI y, en out.json, statusCode 200 con rds.success en true. Esta vez no le pasas el bucket en el evento: lo toma de su variable de entorno S3_BUCKET. (./gestionar-crypto-s3-rds.sh probar hace lo mismo pasando el bucket y resumiendo el resultado.)
6. Comprueba los dos destinos:
aws s3 ls s3://$BUCKET/crypto/ --recursive --human-readable
En Adminer (abre $ADMINER y entra como en la carpeta 05.01: Sistema MySQL, Servidor $RDS, usuario admin, contraseña CunefAdmin!; base crypto, enlace SQL command):
SELECT COUNT(*) FROM crypto.coins;
SELECT * FROM crypto.coins ORDER BY timestamp DESC LIMIT 10;
Qué verás: en S3, crypto/raw/history/crypto_prices_....json y crypto/raw/crypto_prices_latest.json. En Adminer, 10 tras la primera ejecución (10 más por cada ejecución siguiente) y las filas con las columnas id, timestamp, coin_id, currency, price, market_cap, volume_24h, change_24h, last_updated_at, created_at. Para Power BI (si la presentación te lo pide) usa el mismo servidor, usuario y contraseña que en el Ejercicio 1 (carpeta 05.02, paso 6) y la base crypto.
7. (Opcional) Programar la ejecución cada minuto: ./gestionar-crypto-s3-rds.sh programar (y desprogramar al terminar), o los comandos a mano de la carpeta Tema 5 · 6 · Programar con EventBridge con FN=crypto-price-tracker-rds. Se apaga sola a las 4 h (ver Apagado automático); bórrala al terminar. En esta variante, cada minuto añade dos ficheros y diez filas.
Apagado automático
La programación de EventBridge se apaga sola a las 4 horas: una regla de cada minuto que se olvida sigue ejecutando la función (y llamando a CoinGecko) para siempre. La regla se desactiva, no se borra; solo desprogramar o eliminar la borran.
| Quiero... | Comando |
|---|---|
| Programar la función (4 h por defecto) | ./gestionar-crypto-s3-rds.sh programar (cada minuto) o, por ejemplo, ./gestionar-crypto-s3-rds.sh programar 5 --horas 2 |
| Ver cuánto queda | ./gestionar-crypto-s3-rds.sh estado (línea Apagado automático: se apagará a las HH:MM UTC (dentro de N min)) |
| Ampliar el plazo otras 4 h | ./gestionar-crypto-s3-rds.sh extender (otro plazo: extender --horas 8) |
| Que no se apague nunca | ./gestionar-crypto-s3-rds.sh programar --permanente o extender --permanente (¡la función seguirá ejecutándose hasta que la quites!) |
| Reactivar tras el apagado | ./gestionar-crypto-s3-rds.sh programar (reactiva la regla y renueva el plazo) |
| Quitar la regla / borrarlo todo | ./gestionar-crypto-s3-rds.sh desprogramar / ./gestionar-crypto-s3-rds.sh eliminar (funcionan también con la regla desactivada) |
Cómo funciona: programar guarda el plazo (hora UTC de vencimiento, o permanente) en la variable de entorno APAGAR_TRAS de la función. En cada ejecución programada, la función la mira y, si ya venció, desactiva su propia regla (su rol de IAM solo puede desactivar esa regla) y no ejecuta el ETL. Se nota en la primera ejecución tras el vencimiento (con rate(1 hour) puede tardar hasta 1 h). probar y las invocaciones a mano no se ven afectadas. Si editas el código con extraer, conserva la función apagado_automatico y su llamada al principio de lambda_handler.
Errores frecuentes
Permission deniedal ejecutar./gestionar-crypto-s3-rds.sh. Solución:chmod +x *.sh. Si dicebad interpretero^M, el fichero se guardó con saltos de línea de Windows:tr -d '\r' < gestionar-crypto-s3-rds.sh > x.sh && mv x.sh gestionar-crypto-s3-rds.sh && chmod +x gestionar-crypto-s3-rds.sh.- El script se queda mostrando el menú / no hace nada. Sin argumentos y con terminal muestra el menú: elige una opción o usa un comando (
crear,estado...). Sin terminal imprime la ayuda y sale. - El script dice
No se encontró el stack cunef-etl. El Paso 0 no existe en esta cuenta: este tipo lo necesita (escribe en su bucket y en su RDS). Créalo en la carpeta 05.01 (./gestionar-etl-base.sh) y repite el script. No se ha creado nada. statusCode500 conAccessDenied ... is not authorized to perform: s3:PutObject. El rol de la función no deja escribir en tu bucket (stack antiguo creado con una plantilla antigua,cunef-etl-demo-*). Solución, sin borrar nada: vuelve a ejecutar./gestionar-crypto-s3-rds.sh crear; el script actualiza el stack con la plantilla correcta. Si no se arregla, lee el campoerroro usaeliminarycrear.Status 429oHTTP Error 429enerror. Límite de peticiones de CoinGecko, que es bajo. Espera uno o dos minutos, no encadenes pruebas y vuelve a invocar (probar). Con otro código distinto (403,5xx) la API ha cambiado sus condiciones o está caída: avisa a tu docente.- "No se pudo instalar pymysql" / "No encuentro pip".
pipno pudo descargar la librería (sin red) o no está instalado. Pruebapip3 install pymysql -t /tmp/prueba --quiet && echo OK(en CloudShell funciona) y repite el script. No se ha creado nada en AWS: el paquete se prepara antes que el stack. rds.successesfalse. Leerds.message:Host: NO CONFIGURADO(si ocurre justo tras el despliegue) significa que la prueba corrió antes de que la configuración estuviera activa: espera 30 segundos y repiteprobar;Can't connect ... (timed out)indica que la RDS no es alcanzable;Access denied for userindica contraseña distinta;Unable to import module ... pymysql(error de función, sinstatusCode) indica que el paquete no llevaba la librería: repitecrear.ResourceConflictExceptional desplegar o al probar. La función seguía actualizándose. El script ya espera y reintenta; si aun así sale, espera 10 segundos y repite el comando.- La prueba dice
✗ La función devolvió statusCode 500, no se ha guardado nada. Es la forma que tiene el script de avisarte; el motivo va justo debajo (Motivo: ...). Los más habituales:AccessDenied(arriba) y429de CoinGecko. El script continúa y muestra el resumen. creardice que el stack está enROLLBACK_COMPLETE,CREATE_FAILEDu otro estado fallido. No tienes que hacer nada: el script lo borra y lo vuelve a crear. Si lo prefieres,eliminar --siy luegocrear.- El script avisa de "una función fuera de CloudFormation". Es un resto de la versión antigua del script (que creaba la función por su cuenta): lo borra y deja que el stack la cree.
eliminardice que no pudo borrar la tabla de la RDS. El script la borra ejecutando una pequeña limpieza dentro de la propia función. Si no es posible (la función ya no existe, la RDS está parada o no haypip), te enseña qué ejecutar a mano en Adminer:DROP TABLE crypto.coins; DROP DATABASE crypto;. El resto de la limpieza continúa.- Adminer no carga o no deja entrar: ver "Errores frecuentes" de la carpeta 05.01.
Limpieza
Hazla cuando termines:
./gestionar-crypto-s3-rds.sh eliminar
Te enseña qué va a borrar y pide confirmación (con --si no pregunta). Borra, forzando si hace falta y de forma repetible (puedes lanzarlo otra vez sin riesgo): las reglas de EventBridge de la función y su permiso, los ficheros crypto/raw/ que la función guardó en el bucket, la tabla crypto.coins de la RDS (y la base crypto si queda vacía; la hace borrar la propia función justo antes de eliminarse), el stack cunef-etl-crypto-rds (función y rol; si CloudFormation se atasca, reintenta y retiene solo lo imprescindible), el rol sobrante LambdaCryptoTrackerRDSRole de versiones antiguas (si existe) y el grupo de logs. No toca el stack cunef-etl (bucket, RDS y Adminer) ni otros ejercicios.
Qué verás: cinco bloques ("1/5 Programación de EventBridge", "2/5 Datos del ejercicio", "3/5 Stack...", "4/5 Recursos sueltos", "5/5 Comprobación final") y, al final, ✓ Todo eliminado. No queda nada de este ejercicio en AWS. Si algo no se pudo borrar, lo dice con ⚠ y termina con error: repite el comando. Para comprobar la RDS, en Adminer: SHOW DATABASES; (no debe aparecer crypto).
Opcional: ficheros de CloudShell (cd ~ && rm -rf 05.05-cryptoas3yrds 05.05-cryptoas3yrds.zip).
No borres la infraestructura base todavía si vas a hacer más actividades (05.07 y 05.08 también la usan). El borrado de la infraestructura base (carpeta 05.01, sección Limpieza) debe ser el último paso de todo el Tema 5.
Cómo sabes que has terminado
- [ ]
./gestionar-crypto-s3-rds.sh estadomuestra el stackcunef-etl-crypto-rdsenCREATE_COMPLETE, la función con el código real y la RDS configurada. - [ ] El
statusCodede la función es 200 yrds.successestrue(records_inserted10). - [ ] Has comprobado el dato en los dos destinos: ficheros en
crypto/raw/del bucket y 10 filas encrypto.coins(Adminer) tras la primera ejecución. - [ ] Has hecho la limpieza de esta actividad (
eliminarterminó con "Todo eliminado").
Anterior: Tema 5 · 4 · CoinGecko a DynamoDB · Siguiente: Tema 5 · 6 · Programar con EventBridge