Un script para exportar selectivamente tablas de una BD en AWS
Tags: aws, rds, cli, s3
Published: 2024-03-08 11:14:42

Eres el sysadmin de una empresa y el departamento destinado a mejorar el producto te solicita un "data lake" donde albergar datos en bruto procedentes de bases de datos para cocinarlos y tomar decisiones. Una de las partes fundamentales de un proyecto así es la extracción de datos, así como su fiabilidad y rapidez en el proceso, que en este caso es diario. Preparas una extracción pero dura demasiado y son gigas y gigas de transferencia que no necesitan, por lo que, finalmente, comprobáis que sólo requieren información de unas 90 tablas de un total de más de 14000 que tiene el proyecto completo. La plataforma está montada en AWS y hemos de ver cómo hacerles llegar esa información totalmente optimizada en tiempo, filtrado y orden. Contamos con una pequeña máquina de EC2 en la infraestructura donde alojamos scripts, por lo que es el sitio idóneo para ello. A partir de aquí, el comando [`start-export-task`](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/start-export-task.html) se convierte en nuestro aliado.

## ¿Qué servicios AWS usaremos?

El listado de servicios que usaremos son los siguientes:

- S3, donde se albergan los datos.
- RDS, la instancia de la base de datos. En este caso, postgres.
- EC2, la máquina donde configuramos el script.
- SNS, el servicio de notificaciones para envíos por correo.

## ¿Qué requisitos necesitamos?

La máquina donde alojaremos el script ha de tener instalado AWS CLI. Se presupone que en una máquina de AWS ha de venir de serie, pero por si queremos hacerlo en otra instancia, ya sea una máquina virtual o en nuestro propio equipo, es muy sencillo, sólo hay que copiar [el siguiente comando proporcionado por AWS](https://docs.aws.amazon.com/es_es/cli/latest/userguide/getting-started-install.html):

```bash
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
```

Para comprobar que está todo correcto:

```
 ~ aws --version
aws-cli/2.7.35 Python/3.9.11 Linux/5.15.0-97-generic exe/x86_64.ubuntu.20 prompt/off
```

Ya lo tenemos todo para dejar preparado un script que haga lo siguiente:

1. Eliminar la copia del día anterior. Los datos no son necesario albergarlos y no pueden mezclarse, por lo que se pueden borrar.
2. Mediante la opción `start-export-task` del comando `rds`, identificar la base de datos y las tablas que queremos exportar con el parámetro `--export-only`. En este caso, hemos puesto 6 tablas.
3. Ha de existir un rol en IAM que tenga acceso de lectura en RDS y escritura en S3, así como la capacidad de crear "buckets".
4. Se hizo un `case` para enviar un correo según el estado en el que se encontraba la exportación. Se hacen comprobaciones cada 120 segundos y mientras fuera distinto de "COMPLETE", vuelve a esperarse 120 segundos antes de otra comprobación del estado. Para conocer el estado, la línea es `identifier postgres-partial-export-$DATE | jq -r `.

```bash
#!/bin/bash
set -ex
DATE=$(date +%Y-%m-%d-%H-%M)
PRO_DBNAME=database
echo "Removing old data"
aws s3 rm s3://datalake/raw_internal/export --recursive
echo "Copying ${PRO_DBNAME} @ postgres tables to the Data Lake directory"
aws rds start-export-task \
--export-task-identifier postgres-partial-export-$DATE \
--source-arn arn:aws:rds:us-west-2:[AWS_ID]:cluster:[nombre_cluster_rds] \
--s3-bucket-name datalake \
--s3-prefix raw_internal/export \
--export-only database.public.tabla1 database.public.tabla1 database.public.tabla2 database.public.tabla3 database.public.tabla4 database.public.tabla5 database.public.tabla6 \
--iam-role-arn arn:aws:iam::[AWS_ID]:role/rds-export-to-s3 \
--kms-key-id [KMS_ID] \
--no-cli-pager
echo "Process started"
while [ "$status" != "COMPLETE" ]; do
echo "What's the status?"
echo $status
status=$(aws rds describe-export-tasks --export-task-identifier postgres-partial-export-$DATE | jq -r ".ExportTasks[].Status")
case $status in
"STARTING") sleep 120 ;;
"IN_PROGRESS") sleep 120 ;;
"CANCELING") sleep 120 ;;
"CANCELED")
echo "Canceled, sending an email"
aws ses send-email --from [report@domain.com](mailto:report@domain.com) --to datascience[@domain.com](mailto:datalake@domain.com) --subject "Export status $status" --text "Hi, the export has been canceled. This email has been sent at $(date) via AWS CLI"
break
;;
"COMPLETE")
echo "Ready to move it to the main directory"
sleep 5
aws s3 mv s3://datalake/raw_internal/export/postgres-partial-export-$DATE/database/ s3://datalake/raw_internal/export --recursive
echo "Leaving successfully"
aws ses send-email --from [report@domain.com](mailto:report@domain.com) --to datascience[@domain.com](mailto:datalake@domain.com) --subject "Export status $status" --html "<p>Hi,</p> <p>the export status is $status.</p><p>The report: [https://us-west-2.console.aws.amazon.com/rds/home?region=us-west-2#exports-in-s3:](https://us-west-2.console.aws.amazon.com/rds/home?region=us-west-2#exports-in-s3:) </p><p>The export bucket: [https://us-west-2.console.aws.amazon.com/s3/buckets/datalake?region=us-west-2&prefix=raw_internal/export/&showversions=false](https://us-west-2.console.aws.amazon.com/s3/buckets/datalake?region=us-west-2&prefix=raw_internal/export/&showversions=false)</p> <p>This automated message has been sent at $(date) via AWS CLI, please do not reply.</p>"
;;
"FAILED")
echo "Failed, sending an e-mail"
aws ses send-email --from [report@domain.com](mailto:report@domain.com) --to datascience[@domain.com](mailto:datalake@domain.com) --subject "Export status $status" --text "Hi, the export has failed. Exports in Amazon S3: [https://us-west-2.console.aws.amazon.com/rds/home?region=us-west-2#exports-in-s3:](https://us-west-2.console.aws.amazon.com/rds/home?region=us-west-2#exports-in-s3:) //  This email has been sent at $(date) via AWS CLI"
break
;;
*) echo "Unknown response" ;;
esac
done
echo "Process finished"
exit 0
```
## Programar el script

Hay dos opciones para hacer que el script se ejecute con periodicidad:

1. [Incluirlo en crontab](https://linuxhandbook.com/crontab/).
2. [Crear un servicio systemd](https://linuxhandbook.com/create-systemd-services/).