Servidor disponible solo los jueves de 10:30 a 12:30 y los viernes de 10:00 a 13:00 (hora de Madrid); fuera de ese horario está apagado. Fuera del horario, usa la documentación local del material que te descargaste (el README.md de cada carpeta).
G214 · CUNEF
Inicio / tema5 / 05.02-etltitanic

Tema 5 · 2 · Ejercicio 1: ETL Titanic (S3 a RDS)

Presentación del Tema 5: «Ejercicio 1: Titanic» (la demostración de ejemplo-lambda-titanic.py está en «AWS Lambda»). Duración: 15-20 min. Coste: prácticamente nulo en esta actividad (Lambda factura solo mientras se ejecuta); lo que cuesta es la infraestructura base de la carpeta 05.01, que sigue encendida (y que se apaga sola a las 4 horas: ver más abajo).

Estado de la verificación: el script (un único archivo, con el código de la Lambda embebido y los comandos crear / estado / probar / eliminar / extraer) se ha probado en AWS real sobre la infraestructura del Paso 0, incluido el caso de la RDS parada por el apagado automático. Las salidas de abajo son las que escribe el script.

¿Dudas con un script? Todos los scripts de esta carpeta traen su propia ayuda: ./gestionar-etl-titanic.sh --ayuda (también vale --help o -h) explica los pasos y las opciones. Este README.md es el paso a paso de la actividad: tenlo a mano y consúltalo antes de preguntar.

Etiquetas de lo que creas. Este ejercicio no crea recursos propios: trabaja dentro de los de 05.01-infraestructurabase, que ya llevan las etiquetas curso = G214, universidad = CUNEF y actividad = 05.01-infraestructurabase. 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 sustituir el código de marcador de la Lambda creada en la carpeta 05.01 por el código real de un proceso ETL: la función descarga titanic.csv del bucket S3 (Extract), lo limpia (Transform) y lo carga en una tabla de la base de datos MariaDB (Load). Después lo compruebas con consultas SQL en Adminer, y haces el experimento de la presentación (borrar pasajeros y volver a ejecutar).

Qué se crea en AWS: ningún recurso nuevo. Se actualiza el código de la Lambda del Paso 0 y su primera ejecución crea en la RDS la base titanic y la tabla passengers.

Qué hace la Lambda (código lambda-etl-titanic.py, embebido en el script; ver extraer más abajo): descarga titanic/raw/titanic.csv del bucket (Extract), lo limpia (Transform: edad media para rellenar edades vacías, Ticket solo con dígitos, Fare a 2 decimales, Cabin vacía a C00, nombre separado en LastName y FirstName, Survived a verdadero/falso, entre otros) y lo inserta en passengers de la base titanic (Load), creándolas si no existen. La tabla tiene PassengerId como clave primaria y la carga usa INSERT ... ON DUPLICATE KEY UPDATE. Las credenciales y el bucket los lee de las variables de entorno que puso el Paso 0 en la función; no tienes que escribir nada en el código.

Qué contiene esta carpeta

Fichero Para qué sirve En qué paso se usa
gestionar-etl-titanic.sh Un solo script con todo: empaqueta el código con sus librerías y lo sube a la Lambda del Paso 0 (crear), comprueba su estado y su última ejecución (estado), la invoca (probar) y la deja como estaba (eliminar). Lleva dentro el código de la Lambda y requirements.txt. Paso 2 (y Limpieza)
titanic.csv El dataset (891 pasajeros). Ya lo subió el Paso 0 al bucket; se incluye aquí por si hay que volver a subirlo (el script crear lo sube solo si falta; ver también Errores frecuentes, NoSuchKey). Solo si falta en S3
ejemplo-lambda-titanic.py Ejemplo corto de ETL de S3 a S3 (lee el CSV y guarda un resumen en JSON). No lo despliega ningún script. Sirve para entender el patrón Extract-Transform-Load. Solo lectura / demostración (ver más abajo)
README.md Este documento. Siempre

Por qué el código de la Lambda va dentro del script y ejemplo-lambda-titanic.py no. El script sí despliega el código del ETL, así que va embebido (al final del .sh, entre las marcas # >>>>> INICIO ARCHIVO EMBEBIDO: lambda-etl-titanic.py y # <<<<< FIN ARCHIVO EMBEBIDO, igual que requirements.txt): un único archivo que no se puede desincronizar de su código. En cambio ejemplo-lambda-titanic.py es un ejemplo para leer que no despliega ningún script, y titanic.csv son datos: los dos siguen siendo ficheros aparte.

Quiero leer o editar el código de la Lambda. Vuélcalo a ficheros con:

./gestionar-etl-titanic.sh extraer           # escribe lambda-etl-titanic.py y requirements.txt en la carpeta actual
USAR_LOCAL=1 ./gestionar-etl-titanic.sh crear   # despliega TU copia editada en lugar de la embebida

Si en la carpeta ya existe un fichero distinto con ese nombre, extraer no lo sobrescribe (añade --si para forzarlo).

Qué hace el script crear, en orden y sin pedirte datos

Comprueba aws y tus credenciales; lee de las salidas del stack cunef-etl el nombre de la función y comprueba que existe; comprueba que titanic.csv está en el bucket (si falta y lo tienes en la carpeta, lo sube); escribe el código y requirements.txt en una carpeta temporal; comprueba que el código compila; instala pymysql==1.1.0 en esa carpeta con pip (reintenta si falla la red); copia el código como lambda_function.py; lo comprime en un zip (con zip y, si no hay, con Python); sube el zip con update-function-code (esperando si la función está ocupada); fija el handler a lambda_function.lambda_handler si hace falta (el código de marcador del Paso 0 usa otro) y muestra la configuración. Al terminar borra la carpeta temporal: ya no se queda ningún lambda_function.zip ni lambda_package/ en tu carpeta. Se puede repetir sin problema.

Antes de empezar

Cómo llevar los ficheros a CloudShell

  1. En tu PC, comprime esta carpeta entera (05.02-etltitanic) con clic derecho → Comprimir en archivo ZIP (Windows 11), Enviar a > Carpeta comprimida en zip (Windows 10) o Comprimir (Mac).
  2. En CloudShell (región eu-north-1): Actions → Upload file y elige 05.02-etltitanic.zip.
  3. Ejecuta:
cd ~
unzip -o 05.02-etltitanic.zip && cd 05.02-etltitanic
chmod -R u+rwX . && chmod +x *.sh
ls

Si ya subiste g214-materiales.zip (README principal), no necesitas este zip: entra con cd ~/g214-materiales/tema5/05.02-etltitanic y, si algún fichero da Permission denied, ejecuta allí chmod -R u+rwX . && chmod +x *.sh.

Qué verás: los ficheros de la tabla de arriba: gestionar-etl-titanic.sh, titanic.csv, ejemplo-lambda-titanic.py y README.md. Lanza el script dentro de esta carpeta (así encuentra titanic.csv si hace falta subirlo). Los ficheros de CloudShell se conservan entre sesiones, pero las variables de shell se pierden al cerrar la pestaña o por inactividad.

Comandos del script

./gestionar-etl-titanic.sh            # menú numérico (solo si hay un terminal)
./gestionar-etl-titanic.sh crear      # empaqueta y despliega (o actualiza) el ETL en la Lambda
./gestionar-etl-titanic.sh estado     # existencia, configuración, estado de la RDS, última ejecución y logs (solo lectura)
./gestionar-etl-titanic.sh probar     # invoca la Lambda y muestra el resultado (no invoca si la RDS está parada)
./gestionar-etl-titanic.sh eliminar   # devuelve la Lambda al código de marcador y borra sus logs (pide confirmación; --si o -y no pregunta)
./gestionar-etl-titanic.sh extraer    # escribe lambda-etl-titanic.py y requirements.txt en la carpeta actual
./gestionar-etl-titanic.sh ayuda      # muestra el uso

Alias que también entiende: create, desplegar, deploy, empaquetar, actualizar (= crear); status, info, ver, verificar, logs (= estado); invocar, invoke, test, ejecutar (= probar); borrar, delete, destroy, limpiar, revertir (= eliminar); extract (= extraer); help, -h, --help (= ayuda). Sin comando y en un terminal abre un menú (1) Crear, 2) Estado, 3) Probar, 4) Eliminar, 5) Extraer, 0) Salir); sin comando y sin terminal imprime el uso y termina. Atención: antes este script desplegaba directamente al lanzarlo sin nada; ahora hay que escribir crear (o elegir 1 en el menú).

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. Si alguna sale vacía, el stack cunef-etl no existe o no está en CREATE_COMPLETE: vuelve a la carpeta 05.01. Si pasaron más de 4 horas desde el Paso 0, comprueba primero que la RDS sigue en marcha (./gestionar-etl-titanic.sh estado lo dice; si está parada, arráncala como se explica en Antes de empezar). ADMINER guarda la URL del primer arranque: tras volver a arrancar la EC2, usa la URL que muestra ./gestionar-etl-base.sh estado en la carpeta 05.01.

1. Despliega el código:

./gestionar-etl-titanic.sh crear

Qué verás (menos de 1 minuto): "Comprobando el entorno" (AWS CLI y credenciales en verde), "Función Lambda encontrada: cunef-etl-ETLLambda-...", "titanic.csv está en s3://.../titanic/raw/titanic.csv", "Empaquetando la función Lambda ETL Titanic" con "Instalando dependencias de Python (pymysql==1.1.0 )...", "Copiando código Lambda (como lambda_function.py)...", "Creando archivo ZIP..." y "Paquete creado (NNNNN bytes)"; después "Desplegando código en Lambda...", "Código desplegado exitosamente en ...", "Ajustando el handler a lambda_function.lambda_handler (ahora es 'index.lambda_handler')..." (si ya era ese, dice "El handler ya es ..."), una tabla "Información de la Función Lambda" (Runtime python3.12, Handler, Timeout 900, Memoria 512, Estado normalmente Active), "¡Despliegue completado!" con el comando para probar y el de los logs, y por último "¡Proceso completado!". Si la función está ocupada con otra actualización, verás líneas "Esperando a que la función esté lista..." con un contador y el script espera solo (hasta 5 minutos).

2. Invoca la función:

./gestionar-etl-titanic.sh probar

(o a mano, como antes:)

aws lambda invoke --function-name "$FN" --region eu-north-1 output.json
cat output.json

Qué verás: tarda unos segundos. Con probar: la línea StatusCode / FunctionError: 200 / None y la respuesta de la función; si falla, 200 / Unhandled y el aviso "La función ha FALLADO". Con el comando manual: "StatusCode": 200 y "ExecutedVersion": "$LATEST", y si además aparece "FunctionError": "Unhandled", la función ha fallado: mira la respuesta y los logs (paso 3). Si fue bien, la respuesta es un JSON con "statusCode": 200 y un body que incluye el mensaje ETL completado exitosamente, records_processed con el número de filas cargadas, database titanic y table passengers.

3. Mira los logs (la herramienta que más te va a servir para depurar):

./gestionar-etl-titanic.sh estado
aws logs tail /aws/lambda/$FN --since 10m --region eu-north-1

Qué verás: estado enseña la configuración de la función (si tiene el código del ETL o aún el marcador), si titanic.csv está en S3, las últimas líneas de la última ejecución y un veredicto (La última ejecución terminó bien o parece haber FALLADO con pistas). Con aws logs tail ves los mensajes completos de la función, en orden: "Paso 1: Descargando archivo desde S3...", "Paso 2: Procesando y limpiando datos...", "Paso 3: Conectando a la base de datos...", "Paso 4: Verificando/creando base de datos y tabla...", "Paso 5: Insertando datos en la tabla...", varias líneas "Insertados N/891 registros" (de 100 en 100), "Total de registros en la tabla: ..." y "ETL completado exitosamente". Añade --follow en lugar de --since 10m para verlos en directo mientras invocas desde otra pestaña. Si justo después de invocar solo ves el arranque anterior (o nada), espera 5-10 segundos y repite el comando: los logs tardan unos segundos en aparecer en CloudWatch.

4. Comprueba el resultado en Adminer. Abre la URL de $ADMINER (http://, raíz) e inicia sesión como se explica en la carpeta 05.01: Sistema MySQL (o MySQL / MariaDB), Servidor $RDS (sin http://), usuario admin, contraseña CunefAdmin!; puedes dejar la base de datos vacía. En la columna izquierda elige la base titanic y pulsa SQL command. Ejecuta:

SELECT COUNT(*) FROM passengers;

Qué verás: 891, tantas filas como pasajeros tiene titanic.csv. Si pulsas en la tabla passengers y luego en Select data, verás las filas con las columnas PassengerId, Survived, Pclass, LastName, FirstName, Sex, Age, SibSp, Parch, Ticket, Fare, Cabin, Embarked, created_at.

Comprobaciones de referencia (en el mismo SQL command):

SELECT Sex, COUNT(*) AS pasajeros, SUM(Survived) AS supervivientes,
       ROUND(100 * AVG(Survived), 2) AS tasa_pct
FROM passengers GROUP BY Sex ORDER BY Sex;
SELECT Pclass, COUNT(*) AS pasajeros, SUM(Survived) AS supervivientes,
       ROUND(100 * AVG(Survived), 2) AS tasa_pct
FROM passengers GROUP BY Pclass ORDER BY Pclass;
SELECT COUNT(*) FROM passengers WHERE Cabin = 'C00';
SELECT COUNT(*) FROM passengers WHERE Age = 29.70;

Qué verás: female 314 / 233 / 74.20; male 577 / 109 / 18.89; clase 1: 216 / 136 / 62.96, clase 2: 184 / 87 / 47.28, clase 3: 491 / 119 / 24.24; Cabin = 'C00': 687 filas; Age = 29.70: 177 filas (el redondeo puede salir como 74.2 según la versión).

5. Sigue las diapositivas del Ejercicio 1. Puedes invocar la función cuantas veces quieras. Lo que pide la presentación (borrar pasajeros en Adminer y volver a ejecutar la Lambda) es el experimento que debes hacer y razonar tú: observa qué pasa con las filas antes y después.

6. (Si te lo piden) Informe de Power BI. Usa como servidor el endpoint de la RDS, como base titanic, como tabla passengers, usuario admin y contraseña CunefAdmin!; la RDS acepta conexiones desde cualquier IP (puerto 3306 abierto), así que no hay que configurar red. Aviso: el conector MySQL database del servicio web de Power BI (powerbi.com) no se ha podido verificar con una cuenta de CUNEF y puede no aparecer o pedir una puerta de enlace (gateway). Plan B: en Adminer ejecuta SELECT * FROM titanic.passengers, pulsa Export (formato CSV) y carga ese CSV en Power BI (o usa Power BI Desktop, que necesita instalar antes MySQL Connector/NET). Para la tasa de supervivencia crea una medida DAX: Tasa supervivencia = AVERAGE(passengers[Survived]) (si Survived llega como número 0/1) o DIVIDE(CALCULATE(COUNTROWS(passengers), passengers[Survived] = TRUE()), COUNTROWS(passengers)) (si llega como verdadero/falso), con formato porcentaje. Valores de control: mujeres 74,20 %, hombres 18,89 %. Los datos solo cambian en Power BI si cambia la tabla: borra 10 pasajeros en Adminer, refresca (881), vuelve a ejecutar la Lambda y refresca (891).

Este ejercicio no requiere cambios en IAM. El rol que creó el Paso 0 ya permite escribir logs y leer del bucket, que es todo lo que necesita esta función. La conexión a la base de datos va con usuario y contraseña, no con permisos de IAM. Tampoco hay que tocar la red: la función no está dentro de una VPC, sale a Internet y llega a la RDS por su endpoint público; que la RDS acepte el puerto 3306 lo decide el grupo de seguridad de la RDS, no el rol de la función.

Sobre ejemplo-lambda-titanic.py (solo lectura)

Es un ejemplo de referencia, más corto, de ETL de S3 a S3: lee raw/titanic.csv, calcula el número de pasajeros, la edad media y la tarifa media, y guarda un resumen en curated/titanic_summary.json. No forma parte del Ejercicio 1 ni lo despliega ningún script. Lleva un nombre de bucket de ejemplo (cunef-etl-demo-tu-nombre) y rutas propias (raw/..., curated/...) distintas de las del Paso 0 (titanic/raw/titanic.csv), y el rol del Paso 0 no puede escribir en S3. Úsalo para leer el patrón Extract-Transform-Load en pocas líneas. Si lo pruebas (demostración), cambia BUCKET por tu propio bucket (con raw/titanic.csv) y usa una función con un rol propio que pueda leer y escribir en él (la demo le adjunta AmazonS3FullAccess); el fichero lo avisa en su cabecera.

Errores frecuentes

Limpieza

Esta actividad no crea recursos propios en AWS: la función, el rol, el bucket y la base titanic (con la tabla passengers) pertenecen al stack cunef-etl y se borran con él (carpeta 05.01, al final del Tema 5). Por eso eliminar aquí no borra la función ni toca la infraestructura base. Lo que hace es deshacer el ejercicio:

./gestionar-etl-titanic.sh eliminar

Te pide confirmación (¿Continuar? [s/N]; con --si o -y no pregunta) y entonces: (1) devuelve la Lambda al código de marcador del Paso 0 (y su handler index.lambda_handler), (2) borra el grupo de logs /aws/lambda/<función> y (3) borra los restos locales lambda_function.zip / lambda_package/ de versiones antiguas del script, si existen. Después verifica y lo cuenta. Es idempotente: si no hay nada que deshacer, lo dice y termina bien. No toca la base de datos: la base titanic y la tabla passengers siguen en la RDS (se borran con la RDS en la carpeta 05.01); si solo quieres vaciar los datos, en Adminer ejecuta DROP DATABASE titanic;. Para volver a dejar el ETL desplegado: ./gestionar-etl-titanic.sh crear.

Lo opcional:

  1. Si probaste ejemplo-lambda-titanic.py (demostración) con recursos propios, borra solo lo que hayas creado tú:
    • La función de la demo (por ejemplo lambda-etl-demo): aws lambda delete-function --function-name NOMBRE --region eu-north-1. Después su rol (la consola le pone un nombre como lambda-etl-demo-role-xxxxxxxx): aws iam list-attached-role-policies --role-name ROL, aws iam detach-role-policy --role-name ROL --policy-arn ARN_DE_CADA_POLITICA, aws iam delete-role-policy --role-name ROL --policy-name NOMBRE (si tiene políticas en línea) y aws iam delete-role --role-name ROL.
    • La regla de EventBridge de la demo (la tiene un nombre aleatorio), si la creaste: ver la carpeta 05.06, o EventBridge → Rules → Delete.
    • El bucket de la demo (cunef-etl-demo-<tu-nombre>): aws s3 rb s3://NOMBRE_DEL_BUCKET --force --region eu-north-1. Comprueba antes con aws s3 ls | grep cunef-etl-demo que es tuyo.
  2. Opcional: borra el grupo de logs de tu función de demo (/aws/lambda/lambda-etl-demo, si lo creaste) con aws logs delete-log-group --log-group-name /aws/lambda/NOMBRE --region eu-north-1. El grupo de logs de la Lambda del Paso 0 (/aws/lambda/cunef-etl-ETLLambda-...) lo borra eliminar (aquí y en la carpeta 05.01).
  3. Ficheros de CloudShell: cd ~ && rm -rf 05.02-etltitanic 05.02-etltitanic.zip.

No borres la infraestructura base todavía (carpeta 05.01): la necesitan las actividades siguientes. Su borrado debe ser el último paso de todo el Tema 5. Mientras tanto, recuerda que se apaga sola a las 4 horas (con ./gestionar-etl-base.sh extender la amplías); la RDS parada conserva la base titanic y sus datos.

Cómo sabes que has terminado


Anterior: Tema 5 · 1 · Infraestructura base · Siguiente: Tema 5 · 3 · CoinGecko a S3