Introducción
Cualquier canal de datos, fuentes de datos, especialmente bases de datos, son la columna vertebral. Para simular una tubería realista, necesitaba un entorno de base de datos seguro y confiable para servir como fuente de verdad para los trabajos ETL aguas abajo.
En lugar de aprovisionar esto manualmente, elegí automatizar todo usando Terraformadoalineando con la ingeniería de datos moderna y las mejores prácticas de DevOps. Esto no solo ahorró tiempo, sino que también aseguró que el medio ambiente podría recrearse, escalar o destruir fácilmente con un solo comando, como en la producción. Y si está trabajando en el nivel gratuito de AWS, esto es aún más importante: la automatización asegura que pueda limpiar todo sin olvidar un recurso que podría generar costos.
Requisitos previos
Para seguir con este proyecto, necesitará las siguientes herramientas y configuración:
- Terraformado instalado https://developer.hashicorp.com/terraform/install
- Configuración de AWS CLI y IAM
- Instale el AWS CLI
- Cree un usuario de IAM con acceso programático que tenga permiso para:
- Adjuntar la política
AdministratorAccess(o crear una política personalizada con permisos limitados para crear todos los recursos incluidos) - Descargue la ID de clave de acceso y la tecla de acceso secreto
- Adjuntar la política
- Configurar el AWS CLI
- Un par de claves de AWS
Requerido para ssh en el host de bastión. Puede crear uno en la consola AWS en pares de teclas EC2>. - Un entorno basado en unix (Linux/macOS, o WSL para Windows)
Esto garantiza la compatibilidad con los comandos de script y terraza de shell.
Comenzando: lo que estamos construyendo
Pasemos a través de cómo construir un Configuración de base de datos AWS segura y automatizada Usando Terraform.
Descripción general de la infraestructura
Este proyecto provoca un entorno AWS completo de estilo de producción utilizando Terraform. Se crearán los siguientes recursos:
Networking
- A VPC personalizado con un bloque CIDR (
10.0.0.0/16) - Dos subredes privadas En diferentes zonas de disponibilidad (para la instancia de RDS)
- Una subred pública (para el anfitrión de Bastion)
- Puerta de enlace de Internet y Mesas de ruta para enrutamiento de subred público
- A Grupo de subred de DB para la implementación de Multi-AZ RDS
Calcular
- A instancia de Bastion EC2 En la subred pública
- Se utiliza para ssh en las subredes privadas y acceder a la base de datos de forma segura
- Provisado con un grupo de seguridad personalizado que permite solo el acceso al puerto 22 (SSH)
Base de datos
- A Instancia mysql rds
- Implementado en subredes privadas (no accesible desde Internet público)
- Configurado con un grupo de seguridad dedicado que permite el acceso solo desde el host de Bastion
Seguridad
- Grupos de seguridad:
- Bastion SG: Permite SSH entrante (puerto 22) desde su IP
- RDS SG: Permite MySQL entrante (Puerto 3306) del SG del bastión
Automatización
- A Configuración de script (
setup.sh) eso:- Exporta variables de terraformación
Diseño modular con terraform
Rompí la infraestructura en módulos como Network, Bastion y RDS. Esto me permite reutilizar, escalar y probar diferentes componentes de forma independiente.
El siguiente diagrama ilustra cómo Terraform entiende y estructura las dependencias entre diferentes componentes de la infraestructura, donde cada nodo representa un recurso o módulo.
Esta visualización ayuda a verificar eso:
- Los recursos están correctamente conectados (por ejemplo, la instancia de RDS depende de las subredes privadas),
- Los módulos están aislados pero interoperables (por ejemplo,
network,bastionyrds), - No hay dependencias circulares.
Para mantener la configuración modular mencionada anteriormente, estructuré el proyecto en consecuencia y proporcioné explicaciones para cada componente para aclarar sus roles dentro de la configuración.
.
├── data
│ └── mysqlsampledatabase.sql # Sample dataset to be imported into the RDS database
├── scripts
│ └── setup.sh # Bash script to export environment variables (TF_VAR_*), fetch dynamic values, and upload Glue scripts (optional)
└── terraform
├── modules # Reusable infrastructure modules
│ ├── bastion
│ │ ├── compute.tf # Defines EC2 instance configuration for the Bastion host
│ │ ├── network.tf # Uses data sources to reference existing public subnet and VPC (does not create them)
│ │ ├── outputs.tf # Outputs Bastion host public IP address
│ │ └── variables.tf # Input variables required by the Bastion module (AMI ID, key pair name, etc.)
│ ├── network
│ │ ├── network.tf # Provisions VPC, public/private subnets, Internet gateway, and route tables
│ │ ├── outputs.tf # Exposes VPC ID, subnet IDs, and route table IDs for downstream modules
│ │ └── variables.tf # Input variables like CIDR blocks and availability zones
│ └── rds
│ ├── network.tf # Defines DB subnet group using private subnet IDs
│ ├── outputs.tf # Outputs RDS endpoint and security group for other modules to consume
│ ├── rds.tf # Provisions a MySQL RDS instance inside private subnets
│ └── variables.tf # Input variables such as DB name, username, password, and instance size
└── rds-bastion # Root Terraform configuration
├── backend.tf # Configures the Terraform backend (e.g., local or remote state file location)
├── main.tf # Top-level orchestrator file that connects and wires up all modules
├── outputs.tf # Consolidates and re-exports outputs from the modules (e.g., Bastion IP, DB endpoint)
├── provider.tf # Defines the AWS provider and required version
└── variables.tf # Project-wide variables passed to modules and referenced across files
Con la estructura modular en su lugar, el main.tf el archivo se encuentra en el rds-bastion el directorio actúa como el orquestador. Une los componentes centrales: la red, la base de datos RDS y el host Bastion. Cada módulo se invoca con las entradas requeridas, la mayoría de las cuales se definen en variables.tf o pasado a través de variables de entorno (TF_VAR_*).
module "network" {
source = "../modules/network"
region = var.region
project_name = var.project_name
availability_zone_1 = var.availability_zone_1
availability_zone_2 = var.availability_zone_2
vpc_cidr = var.vpc_cidr
public_subnet_cidr = var.public_subnet_cidr
private_subnet_cidr_1 = var.private_subnet_cidr_1
private_subnet_cidr_2 = var.private_subnet_cidr_2
}
module "bastion" {
source = "../modules/bastion"
region = var.region
vpc_id = module.network.vpc_id
public_subnet_1 = module.network.public_subnet_id
availability_zone_1 = var.availability_zone_1
project_name = var.project_name
instance_type = var.instance_type
key_name = var.key_name
ami_id = var.ami_id
}
module "rds" {
source = "../modules/rds"
region = var.region
project_name = var.project_name
vpc_id = module.network.vpc_id
private_subnet_1 = module.network.private_subnet_id_1
private_subnet_2 = module.network.private_subnet_id_2
availability_zone_1 = var.availability_zone_1
availability_zone_2 = var.availability_zone_2
db_name = var.db_name
db_username = var.db_username
db_password = var.db_password
bastion_sg_id = module.bastion.bastion_sg_id
}
En esta configuración modular, cada componente de infraestructura está libremente acoplado pero se conecta a través de bien definido entradas y salidas.
Por ejemplo, después de aprovisionar el VPC y subredes en el network módulo, recupero sus ID usando su salidasy pasarlos como Variables de entrada a otros módulos como rds y bastion. Esto evita la codificación difícil y permite que Terraform Resolver dependencias y construir el gráfico de dependencia internamente.
En algunos casos (como dentro del bastion módulo), también uso data fuentes para Referencia a los recursos existentes Creado por módulos anteriores, en lugar de recrearlos o duplicarlos.
El dependencia entre módulos confía en el correcto Definición y exposición de resultados de módulos creados anteriormente. Estas salidas se pasan como variables de entrada a módulos dependientes, lo que permite a Terraform para construir un gráfico de dependencia interna y orquestar el orden de creación correcto.
Por ejemplo, el network módulo exponido la ID de VPC y las ID de subred usando outputs.tf. Estos valores son entonces consumado por módulos posteriores como rds y bastion a través del main.tf Archivo de la configuración raíz.
A continuación se muestra cómo funciona esto en la práctica:
Adentro modules/network/outputs.tf:
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
Adentro modules/bastion/variables.tf:
variable "vpc_id" {
description = "ID of the VPC"
type = string
}
Adentro modules/bastion/network.tf:
data "aws_vpc" "main" {
id = var.vpc_id
}
Para aprovisionar la instancia de RDS, creé Dos subredes privados en diferentes zonas de disponibilidadcomo lo requiere AWS al menos dos subredes en AZS separados Para configurar un grupo de subred DB.
Aunque cumplí con este requisito para la configuración correcta, yo despliegue múltiple discapacitado Durante la creación de RDS a Manténgase dentro de los límites de nivel gratuito de AWS y Evite los costos adicionales. Esta configuración aún simula un diseño de red de grado de producción mientras permanece rentable para el desarrollo y las pruebas.
Flujo de trabajo de implementación
Con todos los módulos correctamente conectados a través de entradas y salidas, y la lógica de infraestructura encapsulada en bloques reutilizables, el siguiente paso es automatizar el proceso de aprovisionamiento. En lugar de pasar manualmente las variables cada vez, un guión auxiliar setup.sh se utiliza para exportar las variables de entorno necesarias (TF_VAR_*).
Una vez que se obtiene el script de configuración, la implementación de la infraestructura se vuelve tan simple como ejecutar algunos comandos de Terraform.
source scripts/setup.sh
cd terraform/rds-bastion
terraform init
terraform plan
terraform apply
Para optimizar el proceso de implementación de Terraform, creé un script de ayuda (setup.sh) que exporta automáticamente las variables de entorno requeridas utilizando el TF_VAR_ Convención de nombres. Terraform recoge automáticamente variables con prefijo con TF_VAR_por lo que este enfoque evita los valores de codificación en .tf archivos o requerir entrada manual cada vez.
#!/bin/bash
set -e
export de_project=""
export AWS_DEFAULT_REGION=""
# Define the variables to manage
declare -A TF_VARS=(
["TF_VAR_project_name"]="$de_project"
["TF_VAR_region"]="$AWS_DEFAULT_REGION"
["TF_VAR_availability_zone_1"]="us-east-1a"
["TF_VAR_availability_zone_2"]="us-east-1b"
["TF_VAR_ami_id"]=""
["TF_VAR_key_name"]=""
["TF_VAR_db_username"]=""
["TF_VAR_db_password"]=""
["TF_VAR_db_name"]=""
)
for var in "${!TF_VARS[@]}"; do
value="${TF_VARS[$var]}"
if grep -q "^export $var=" "$HOME/.bashrc"; then
sed -i "s|^export $var=.*|export $var=$value|" "$HOME/.bashrc"
else
echo "export $var=$value" >> "$HOME/.bashrc"
fi
done
# Source updated .bashrc to make changes available immediately in this shell
source "$HOME/.bashrc"
Después de correr terraform applyTerraform aprovisionará todos los recursos definidos: VPC, subredes, tablas de ruta, instancia de RDS y host de bastión. Una vez que el proceso se complete con éxito, verá valores de salida similares a los siguientes:
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
Outputs:
bastion_public_ip = "<Bastion EC2 Public IP>"
bastion_sg_id = "<Security Group ID for Bastion Host>"
db_endpoint = "<RDS Endpoint>:3306"
instance_public_dns = "<EC2 Public DNS>"
rds_db_name = "<Database Name>"
vpc_id = "<VPC ID>"
vpc_name = "<VPC Name>"
Estas salidas se definen en el outputs.tf archivos de sus módulos y reexportados en el módulo raíz (rds-bastion/outputs.tf). Son cruciales para:
- Ssh-ing en el anfitrión de Bastion
- Conectarse de forma segura a la instancia privada de RDS
- Validación de la creación de recursos
Conectarse con el RDS a través de Bastion Host y Sembrar la base de datos
Ahora que la infraestructura está aprovisionada, el siguiente paso es Sembra la base de datos MySQL alojada en la instancia de RDS. Dado que la base de datos está dentro de un subred privadano podemos acceder a ella directamente desde nuestra máquina local. En su lugar, usaremos el Instancia de Bastion EC2 Como un anfitrión de salto para:
- Transfiera el conjunto de datos de muestra (
mysqlsampledatabase.sql) al bastión.
- Conéctese desde el bastión a la instancia de RDS.
- Importe los datos SQL para inicializar la base de datos.
Puede mover dos directorios del directorio principal de Terraform y enviar el contenido SQL al EC2 remoto (Bastion) después de leer el archivo SQL local dentro del directorio de datos.
cd ../..
cat data/mysqlsampledatabase.sql | ssh -i your-key.pem ec2-user@<BASTION_PUBLIC_IP> 'cat > ~/mysqlsampledatabase.sql'
Una vez que el conjunto de datos se copia en la instancia de Bastion EC2, el siguiente paso es SSH en la máquina remota y::
ssh -i ~/.ssh/new-key.pem ec2-user@<BASTION_PUBLIC_IP>
Después de conectarse, puede usar el cliente MySQL (ya instalado si usó mariadb105 En su configuración de EC2) para importar el archivo SQL en su base de datos RDS:
mysql -h <DATABASE_ENDPOINT> -P 3306 -u <DATABASE_USERNAME> -p < mysqlsampledatabase.sql
Ingrese la contraseña cuando se le solicite.
Una vez que se completa la importación, puede conectarse a la base de datos RDS MySQL nuevamente para verificar que la base de datos y sus tablas se hayan creado con éxito.
Ejecute el siguiente comando desde el host Bastion:
mysql -h <DATABASE_ENDPOINT> -P 3306 -u <DATABASE_USERNAME> -p
Después de ingresar su contraseña, puede enumerar las bases de datos y tablas disponibles:
Para garantizar que el conjunto de datos se importara correctamente a la instancia de RDS, ejecuté una consulta simple:
Esto devolvió una fila del customers tabla, confirmando que:
- La base de datos y las tablas se crearon con éxito
- El conjunto de datos de muestra se sembró en la instancia de RDS
- La configuración de Bastion Host y Private RDS funcionan según lo previsto
Esto completa la configuración de infraestructura y el proceso de importación de datos.
Destruyendo la infraestructura
Una vez que haya terminado de probar o demostrar su configuración, es importante destruir los recursos de AWS para Evite los cargos innecesarios.
Dado que todo se aprovisionó con Terraform, derribar toda la infraestructura es tan simple como ejecutar un comando después de navegar a su directorio de configuración raíz:
cd terraform/rds-bastion
terraform destroy
Conclusión
En este proyecto, demostré cómo provocar una infraestructura de base de datos segura y similar a la producción utilizando Terraformado en AWS. En lugar de exponer la base de datos a Internet público, implementé las mejores prácticas colocando la instancia de RDS en subredes privadasaccesible solo a través de un anfitrión de Bastion En una subred pública.
Estructurando el proyecto con Configuraciones modulares de TerraformAseguré cada componente (Neta, Base de datos y Host de Bastion) fue acoplado librementereutilizable y fácil de administrar. También exhibí cómo la Terraform es interna gráfico de dependencia Maneja la orquestación y secuenciación de la creación de recursos sin problemas.
Gracias a la infraestructura como código (IAC), todo el entorno puede ser criado o derribado con un solo comando, lo que lo hace ideal para Prototipos ETL, práctica de ingeniería de datoso tuberías de prueba de concepto. Lo más importante, esta automatización ayuda a evitar costos inesperados al permitirle destruir todos los recursos de manera limpia una vez que haya terminado.
Puede encontrar el código fuente completo, la configuración de Terraform y la configuración de scripts en mi repositorio de GitHub:
https://github.com/yagmurgulec/rds-ec2-terraform.git
Siéntase libre de explorar el código, clonar el repositorio y adaptarlo a sus propios proyectos de AWS. ¡Las contribuciones, los comentarios y las estrellas siempre son bienvenidas!
¿Qué sigue?
Puede extender esta configuración por:
- Conectando un trabajo de pegamento AWS a la instancia de RDS para el procesamiento de ETL.
- Agregar monitoreo para su base de datos RDS e instancia de EC2