# Tema 2 · 9 · Ejercicio C: los tres almacenamientos sobre un dataset real (BiciMAD) Presentación del Tema 2: «Ejercicio C» (los tres almacenamientos sobre un dataset real) y «Ejercicio C · paso a paso». Duración: 1,5 a 2 horas. Coste: céntimos si borras todo el mismo día (dos `t3.small`, un volumen de 10 GB, un EFS y un bucket). Las máquinas las crea un script desde CloudShell y se apagan solas a las 4 horas. Esta carpeta trae el script que crea y borra las máquinas (`gestionar-bicimad.sh`, que se ejecuta en CloudShell y lleva dentro `procesa-viajes.sh`), el script que procesa el CSV mensual de BiciMAD (`procesa-viajes.sh`), la política mínima de permisos para publicar el resultado en S3 sin guardar claves en la máquina (`politica-rol-s3.json`) y la política de confianza para crear el rol por CLI (`confianza-ec2.json`). El enunciado está en la presentación del Tema 2; aquí está **cómo se hace paso a paso**, qué verás y qué hacer si falla. > **¿Dudas con un script?** Todos los scripts de esta carpeta traen su propia ayuda: `./gestionar-bicimad.sh --ayuda` (en CloudShell) y `bash ~/procesa-viajes.sh --ayuda` (en la máquina; también vale `--help` o `-h`) explican 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.** Todo recurso que crees a mano (máquina, volumen, bucket, base de datos, tabla, función...) ponle estas etiquetas, al crearlo (apartado *Tags* / *Etiquetas*) o después desde su pestaña de etiquetas: `curso` = `G214`, `universidad` = `CUNEF` y `actividad` = `02.09-ejerciciocbicimad`. Si algún día hay que borrar recursos a mano, se localizan todos de golpe con `aws resourcegroupstaggingapi get-resources --region eu-north-1 --tag-filters Key=actividad,Values=02.09-ejerciciocbicimad`. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): [README principal](../../README.md), sección «Etiquetas». ## 1. Qué se construye - **EBS (10 GB)** en la VM de proceso: ficheros intermedios y resultados (`/mnt/ebs`): un solo equipo, baja latencia. - **EFS** montado en las dos VM: el log de progreso (`~/efs/logs/proceso.log`), que la VM 2 lee mientras la VM 1 escribe. - **S3**: el resumen final (`out/resumen.csv`), compartido con una URL firmada. - Dos instancias `t3.small` (Amazon Linux 2023), un volumen EBS de 10 GB en la **misma zona** que la VM de proceso, un EFS y un bucket. Un Security Group con **SSH (22)** y **NFS (2049) con origen el propio grupo** (no `0.0.0.0/0`). `./gestionar-bicimad.sh crear` crea todo salvo el bucket y el rol de IAM (esas dos partes las haces tú, pasos 7 y siguientes) y deja `procesa-viajes.sh` ya copiado en la máquina `proceso`. ## 2. Prerrequisitos - Los laboratorios de archivos (EFS) y de bloques del Tema 2 hechos, y haber hecho [`02.01-s3primerbucket`](../02.01-s3primerbucket/README.md) (S3 y CloudShell), [`02.05-EFSCarpetaCompartida.md`](../02.05-EFSCarpetaCompartida.md) y [`02.06-laboratoriocafeteria`](../02.06-laboratoriocafeteria/README.md). - Conocer `lsblk`, `mkfs`, `mount`, `chown` (anexo de comandos Linux del Tema 0). Nuevos aquí: `curl -L -o`, `unzip`, `awk -F';'`, `sort | uniq -c`, `tail -F`, `blkid`. - CloudShell abierto con la región **eu-north-1**, con la carpeta `02.09-ejerciciocbicimad` subida (clic derecho > *Comprimir en ZIP* en tu PC, **Actions > Upload file** en CloudShell, `unzip -o 02.09-ejerciciocbicimad.zip`, `cd 02.09-ejerciciocbicimad` y `chmod -R u+rwX . && chmod +x gestionar-bicimad.sh`). No hay que copiar ni pegar ningún script: las máquinas se conectan con el botón *Connect* de la consola (EC2 Instance Connect), sin par de claves. ## 3. Datos: BiciMAD (Madrid) El portal de Madrid **no ofrece un CSV mensual descargable con un solo `wget`**: publica un ZIP anual (2017-2023) que contiene un ZIP por mes con el CSV dentro. Hay que descargar y descomprimir dos veces. Mira siempre el formato antes de procesar (`file viajes.csv ; head -3 viajes.csv`): el separador es `;`, las líneas terminan en CRLF, hay filas vacías (`;;;;;`) intercaladas y la geolocalización es un texto con comas. **Por eso `awk -F,` no sirve** con este fichero. Alternativa rápida y directa (Barcelona, Bicing, enero de 2019, 269 KB, separador coma; son bicis en uso cada 5 minutos, no viajes): `curl -L -o viajes.csv https://opendata-ajuntament.barcelona.cat/data/dataset/4a469cf6-dbab-4aa1-b492-aa0af9af93c9/resource/453abff9-fb75-4f31-a699-23509bf0eca2/download`. Con ese fichero `procesa-viajes.sh` no sirve (otras columnas): úsalo solo para ensayar el montaje. ## 4. Paso a paso **1. Crea las dos máquinas, el disco y el EFS (en CloudShell):** ```bash ./gestionar-bicimad.sh crear ``` > Si ya subiste `g214-materiales.zip` (README principal), no necesitas este zip: entra con `cd ~/g214-materiales/tema2/02.09-ejerciciocbicimad` y, si algún fichero da `Permission denied`, ejecuta allí `chmod -R u+rwX . && chmod +x *.sh`. Qué verás: el Security Group, el EFS (1-2 minutos hasta que están sus puntos de montaje), los ficheros subidos a un bucket temporal, `proceso` y `observa` lanzadas, el aviso `Apagado automático: se apagará a las HH:MM UTC (dentro de 4 h 0 min)` y un recuadro **Tu laboratorio** con los IDs, las IPs y el comando para montar el EFS. Tarda unos 4 minutos y es seguro repetirlo (lo que existe se reutiliza). `./gestionar-bicimad.sh estado` te lo vuelve a mostrar en cualquier momento. El volumen de 10 GB llega **en blanco** a `proceso`: lo formateas tú en el paso 2. Si más adelante necesitas más tiempo: `./gestionar-bicimad.sh extender --horas 8`. Después conéctate a cada máquina con EC2 → Instances → *Connect* → EC2 Instance Connect. **2. Disco de trabajo en `proceso`.** Identifica el disco por su **tamaño** (10G) en `lsblk`, no por el nombre (`nvme1n1` solo es seguro con un único disco extra). Comprueba que está **vacío** antes de formatear: ```bash lsblk sudo blkid /dev/nvme1n1 # no debe mostrar NADA (disco sin formato); si muestra TYPE="ext4", ya tiene datos: no hagas mkfs sudo mkfs -t ext4 /dev/nvme1n1 sudo mkdir -p /mnt/ebs sudo mount /dev/nvme1n1 /mnt/ebs sudo chown ec2-user:ec2-user /mnt/ebs ``` `mkfs` se niega a formatear el disco del sistema (`apparently in use by the system`), pero **sobre un disco que ya tiene ext4 formatea sin preguntar si no hay terminal** (script, `ssh host 'comando'`): por eso el `blkid` previo. No interrumpas un `mkfs` a medias (Ctrl+C o una tubería cortada): el disco queda sin poder montarse. **3. EFS en las dos VM** (desde `cd ~`; el script ya instaló `amazon-efs-utils` y `unzip` y creó la carpeta `~/efs`): ```bash sudo mount -t efs -o tls fs-XXXXXXXX:/ ~/efs # el comando que te dio «crear» (o «estado») sudo chown ec2-user:ec2-user ~/efs # solo hace falta en una VM: la raíz del EFS es compartida mkdir -p ~/efs/logs ``` Si el `mount` se cuelga, es casi siempre el puerto 2049 del Security Group: el script lo deja bien, así que solo pasa si alguien lo cambió a mano. No insistas: ejecuta `./gestionar-bicimad.sh crear` en CloudShell (recompone el grupo) y repite. Si falla **al instante** con `Failed to resolve "fs-...efs..."`, el EFS es reciente: aunque los mount targets ya estén *Available*, el nombre DNS puede tardar **hasta 5-7 minutos** en resolverse en una zona (en las pruebas, 6-7 minutos en `proceso` y a la primera en `observa`). Repite el comando cada minuto. **No ejecutes `chown` ni `mkdir -p ~/efs/logs` hasta que `df -h | grep efs` muestre `127.0.0.1:/`**: tras un montaje fallido esos comandos crean las carpetas en el disco local (el montaje posterior las tapa). **4. Descarga y descomprime en `proceso`** (en el disco EBS): ```bash cd /mnt/ebs curl -L -o bicimad_2023.zip https://media.emtmadrid.es/-9Nii5DXo4x # ZIP anual, 22,5 MB unzip bicimad_2023.zip 'bicimad_2023/trips_23_02_February-csv.zip' unzip bicimad_2023/trips_23_02_February-csv.zip # -> trips_23_02_February.csv (56,6 MB) mv trips_23_02_February.csv viajes.csv file viajes.csv ; head -3 viajes.csv | cut -c1-160 # mira el formato: ';' y CRLF ``` Qué verás: algo como `Unicode text, UTF-8 text, with CRLF line terminators` y una cabecera `fecha;idBike;fleet;trip_minutes;...;lock_station_name` (si `file` no existe, `sudo dnf install -y file`, o fíjate solo en `head`). Si `file` dice `Zip archive data`, el fichero sigue siendo un ZIP. **5. Observa desde `observa`, ANTES de lanzar el proceso.** Primero crea el fichero del log en `proceso`: sin él, `tail -F` en la otra VM tarda hasta **un minuto** en darse cuenta de que existe (caché negativa de directorios de NFS) y verás todas las líneas de golpe al final. ```bash touch ~/efs/logs/proceso.log # en proceso tail -F ~/efs/logs/proceso.log # en observa (con -F espera a que el fichero exista; con -f falla si todavía no está) ``` **6. Procesa en `proceso`** (`procesa-viajes.sh` ya está en tu carpeta personal: no hay que copiarlo): ```bash cd /mnt/ebs bash ~/procesa-viajes.sh viajes.csv ``` Qué verás en las dos VM: líneas con la hora (`HH:MM:SS inicio ...`, `viajes por hora listos: 24 horas`, `estaciones listas`, `fin`). **En la VM 2 llegan en ráfagas y con unos segundos de retraso** (3-8 s en las pruebas, hasta unos 10 s; caché de atributos de NFS): es normal. Si no aparece nada durante un minuto y luego salen todas de golpe, falta el `touch` previo. El script hace pausas de 5 segundos (`PAUSA=2 bash ~/procesa-viajes.sh viajes.csv` para acortarlas). Antes de empezar comprueba el CSV (que exista, no esté vacío, no sea un ZIP, cabecera `fecha;idBike;...`), que puedas escribir el log en el EFS y que haya espacio, y si algo falla te dice qué hacer. Otros usos (probados en una VM real; sin argumentos extra funciona como antes): `bash ~/procesa-viajes.sh ayuda`; `bash ~/procesa-viajes.sh estado` (CSV, resultados, disco, EFS y últimas líneas del log; no modifica nada); `bash ~/procesa-viajes.sh eliminar` (borra `resumen_hora.csv`, `resumen.csv` y el log; no borra el CSV; `--si` no pregunta). Resultados con el CSV de febrero de 2023: `resumen_hora.csv` con el pico a las **18 h (13.049 viajes)** y a las **08 h (12.258)**, y `resumen.csv` con la estación de salida más usada, `39 - Plaza de la Cebada` (**2.189 viajes**), seguida de `53 - Plaza de Lavapiés` (1.789) y `45 - Puerta de Toledo` (1.542). **7. Publica el resumen en S3 SIN guardar claves en la VM.** `aws configure` con tus claves personales dentro de una VM es mala práctica (la clave queda en disco, en `~/.aws/credentials`; si es de la cuenta raíz, es gravísimo). Sin credenciales, `aws s3 cp` da `Unable to locate credentials`. Dos formas correctas: - **A) A mano (el fichero es pequeño).** Muestra el resumen en `proceso`, cópialo y pégalo en un fichero de CloudShell, y publícalo desde allí: ```bash cat /mnt/ebs/resumen.csv # en proceso: selecciona el texto y copia cat > resumen.csv # en CloudShell: pega, Enter y Ctrl+D aws s3 cp resumen.csv s3://g214-apellido-bici/out/ aws s3 presign s3://g214-apellido-bici/out/resumen.csv --expires-in 600 ``` (Antes crea el bucket: `aws s3 mb s3://g214-apellido-bici --region eu-north-1`. Es la única parte que se copia a mano y solo son unas líneas.) - **B) Con un rol de instancia (sin claves, lo recomendable).** Hace falta permiso de IAM (en tu cuenta personal lo tienes; en una cuenta de curso restringida puede no estar: usa la opción A). Con esta opción el resumen se publica directamente desde `proceso`. Un rol de instancia son credenciales temporales que EC2 entrega a la máquina; la política `politica-rol-s3.json` es la mínima: `s3:PutObject` (subir) y `s3:GetObject` (para que la URL firmada generada desde la VM abra el fichero) solo sobre `out/*` de tu bucket. Con el simulador de políticas de IAM se comprobó que permite esas dos acciones sobre `out/*` y deniega todo lo demás (otro prefijo, `DeleteObject`, `ListBucket`). **B1. Por la consola (recomendado):** 1. IAM → *Roles* → *Create role* → *Trusted entity type*: *AWS service* → *Use case*: *EC2* → *Next*. 2. No marques ninguna política → *Next* → *Role name* `g214-bici-s3` → *Create role*. La consola crea además el **perfil de instancia** (*instance profile*) con el mismo nombre; es lo que se asocia a la instancia. 3. Abre el rol → *Permissions* → *Add permissions* → *Create inline policy* → pestaña *JSON*: pega `politica-rol-s3.json` cambiando `g214-apellido-bici` por tu bucket → *Next* → *Policy name* `publicar-resumen` → *Create policy*. (Para la práctica también vale `AmazonS3FullAccess`, pero es más permiso del necesario.) 4. EC2 → *Instances* → `proceso` → *Actions* → *Security* → *Modify IAM role* → `g214-bici-s3` → *Update IAM role*. **B2. Por CLI** (desde CloudShell o un PC con permisos de IAM; probada en AWS real el 8 de octubre de 2026: con el rol asociado, `aws s3 cp` a `out/` y `presign` funcionan, y `ls` del bucket, otro prefijo y `rm` dan `AccessDenied`). Con la CLI el perfil de instancia hay que crearlo y rellenarlo a mano: ```bash sed -i 's/g214-apellido-bici/MI-BUCKET/' politica-rol-s3.json # en CloudShell/Linux; en Mac: sed -i '' ... aws iam create-role --role-name g214-bici-s3 --assume-role-policy-document file://confianza-ec2.json aws iam put-role-policy --role-name g214-bici-s3 --policy-name publicar-resumen --policy-document file://politica-rol-s3.json aws iam create-instance-profile --instance-profile-name g214-bici-s3 aws iam add-role-to-instance-profile --instance-profile-name g214-bici-s3 --role-name g214-bici-s3 aws ec2 associate-iam-instance-profile --instance-id i-XXXXXXXX --iam-instance-profile Name=g214-bici-s3 --region eu-north-1 ``` **B3. Comprueba, en la VM `proceso`** (espera ~30 segundos tras asociar el rol): `aws sts get-caller-identity` debe mostrar `assumed-role/g214-bici-s3/i-...`; después `aws s3 cp resumen.csv s3://g214-apellido-bici/out/` y `aws s3 presign s3://g214-apellido-bici/out/resumen.csv --expires-in 600`. La URL firmada con un rol lleva un token y deja de valer cuando caduca la sesión del rol. Qué verás: `upload: ./resumen.csv to s3://g214-apellido-bici/out/resumen.csv` y una URL que abre el CSV en el navegador sin credenciales (sin firmar da `403`). ## 5. Errores frecuentes | Qué ves | Causa | Solución | |---|---|---| | `viajes.csv` es un ZIP (`file` lo dice) | `curl -o viajes.csv` sobre el ZIP anual | Usa `bicimad_2023.zip` y descomprime dos veces (paso 4) | | `awk` devuelve líneas con números y comas raras | `-F,` en un CSV con `;` | `-F';'` y mira `head -3` antes de agregar | | `tail: cannot open ... No such file or directory` | `tail -f` antes de que exista el log | `tail -F`, o `touch ~/efs/logs/proceso.log` antes | | En `observa` no aparece nada durante ~1 minuto y luego salen todas las líneas de golpe | `tail -F` lanzado con el log sin crear (caché negativa de directorios de NFS) | `touch ~/efs/logs/proceso.log` en `proceso` antes de lanzar `tail -F` | | `Failed to resolve "fs-...efs..."` al montar | EFS recién creado: el DNS tarda hasta 5-7 minutos en una zona | Espera y repite el montaje; no sigas con `chown`/`mkdir` hasta ver `127.0.0.1:/` en `df -h` | | `mount` se queda colgado | Puerto 2049 cerrado (alguien cambió el Security Group) | `./gestionar-bicimad.sh crear` en CloudShell recompone el grupo | | La máquina no responde | Se apagó sola (4 horas) | `./gestionar-bicimad.sh crear` la arranca y renueva el plazo; la IP cambia y hay que **montar el EFS de nuevo** (y el disco: `sudo mount /dev/nvme1n1 /mnt/ebs`, sin `mkfs`) | | `mkfs` formatea sin preguntar | No hay terminal (script) y el disco ya tenía datos | `sudo blkid /dev/nvme1n1` vacío antes del `mkfs` | | `Unable to locate credentials` | La VM no tiene credenciales (es lo correcto) | Opción A (a mano) u opción B (rol de instancia) del paso 7 | | `unzip: command not found` | Amazon Linux mínimo | `sudo dnf install -y unzip` | | `No space left on device` | Disco de 10 GB sin montar (escribes en el disco raíz de 8 GB) | `df -h /mnt/ebs` debe mostrar `/dev/nvme1n1` | ## 6. Limpieza 1. En CloudShell, carpeta del ejercicio: `./gestionar-bicimad.sh eliminar` (pide confirmación; `--si` no la pide). Borra las dos máquinas, el volumen de 10 GB, el EFS con sus puntos de montaje, el Security Group y los ficheros temporales. Es repetible: si algo se resiste, vuelve a ejecutarlo. No hace falta desmontar nada antes. Si quieres borrar antes los resultados, en `proceso`: `bash ~/procesa-viajes.sh eliminar`. 2. Lo que creaste tú a mano no lo borra el script: vacía y borra el bucket (`aws s3 rb s3://g214-apellido-bici --force`, sin versionado). 3. Si usaste la opción B del paso 7: desasocia el rol de la instancia (o ya la habrá terminado el script) y después IAM → Roles → borrar `g214-bici-s3`. Por CLI: `aws ec2 disassociate-iam-instance-profile --association-id ...` (el id sale de `aws ec2 describe-iam-instance-profile-associations`), `aws iam remove-role-from-instance-profile`, `aws iam delete-instance-profile`, `aws iam delete-role-policy --policy-name publicar-resumen` y `aws iam delete-role`, todos con `--role-name`/`--instance-profile-name g214-bici-s3`. 4. Comprueba que no queda nada: `./gestionar-bicimad.sh estado` y, en la consola, EC2 → Volumes y EFS vacíos. ## 7. Cómo sabes que has terminado - [ ] Has rellenado la tabla de decisión (dato → servicio → motivo) **antes** de crear nada. - [ ] `tail -F` en `observa` mostró las líneas del log que escribía `proceso`. - [ ] `resumen_hora.csv` muestra el pico a las 18 h (13.049 viajes) y `resumen.csv` la estación `39 - Plaza de la Cebada` (2.189 viajes). - [ ] El resumen está en S3 y se abre con una URL firmada, sin haber guardado claves en la VM. - [ ] Has ejecutado `./gestionar-bicimad.sh eliminar`, has borrado el bucket (y el rol, si lo creaste) y lo has comprobado en la consola. Anterior: [`02.08-ejerciciobdatosabiertos`](../02.08-ejerciciobdatosabiertos/README.md) · Siguiente: Tema 3.