323 lines
9.3 KiB
Markdown
323 lines
9.3 KiB
Markdown
# 07 - Terraform (IaC)
|
|
|
|
> Cómo creamos la infraestructura de `proyectosacc` con código.
|
|
|
|
---
|
|
|
|
## 1. ¿Qué es Terraform?
|
|
|
|
**Terraform** es una herramienta que permite crear infraestructura en la nube escribiendo código en lugar de hacer clic en la consola de AWS.
|
|
|
|
A esto se le llama **IaC** (Infrastructure as Code) o "Infraestructura como Código".
|
|
|
|
### Ventajas de usar Terraform
|
|
- ✅ **Repetible**: puedes crear la misma infraestructura 10 veces y siempre será idéntica.
|
|
- ✅ **Versionable**: el código se guarda en Git, así que sabes quién hizo qué cambio y cuándo.
|
|
- ✅ **Rápido**: un solo comando crea servidor, base de datos, DNS y seguridad.
|
|
- ✅ **Sin sorpresas**: todo está documentado en archivos de texto.
|
|
|
|
---
|
|
|
|
## 2. ¿Qué crea Terraform en proyectosacc?
|
|
|
|
Terraform se encarga de crear todos estos recursos en AWS:
|
|
|
|
1. **VPC y subredes**: la red virtual donde vive todo.
|
|
2. **Security Groups**: las reglas de firewall.
|
|
3. **EC2 T3.small**: el servidor de la **API backend**.
|
|
4. **RDS MariaDB**: la base de datos gestionada.
|
|
5. **S3 Bucket (artefactos)**: el almacén de archivos de la API.
|
|
6. **S3 Bucket (frontend)**: el sitio web estático que sirve el React app.
|
|
7. **CloudFront Distribution**: la CDN que distribuye el frontend globalmente con HTTPS.
|
|
8. **Route 53 Record**: el dominio `sacc.ccsoft.mx` apunta a CloudFront.
|
|
9. **ACM Certificate**: certificado SSL para CloudFront.
|
|
10. **IAM Role**: permisos para que la EC2 y CloudFront accedan a S3.
|
|
|
|
---
|
|
|
|
## 3. Archivos principales de Terraform
|
|
|
|
### `main.tf`
|
|
Este es el archivo central. Aquí se definen los recursos que Terraform va a crear.
|
|
|
|
Ejemplo de la EC2 para la API:
|
|
```hcl
|
|
resource "aws_instance" "sacc_api" {
|
|
ami = "ami-0c02fb55956c7d316" # Ubuntu 22.04
|
|
instance_type = "t3.small"
|
|
|
|
vpc_security_group_ids = [aws_security_group.sacc_sg.id]
|
|
|
|
user_data = file("user-data.sh")
|
|
|
|
tags = {
|
|
Name = "proyectosacc-api"
|
|
Environment = "develop"
|
|
Project = "proyectosacc"
|
|
}
|
|
}
|
|
```
|
|
|
|
Ejemplo del bucket S3 para el frontend React:
|
|
```hcl
|
|
resource "aws_s3_bucket" "sacc_frontend" {
|
|
bucket = "ccsoft-proyectosacc-frontend"
|
|
}
|
|
|
|
resource "aws_s3_bucket_website_configuration" "sacc_frontend" {
|
|
bucket = aws_s3_bucket.sacc_frontend.id
|
|
|
|
index_document {
|
|
suffix = "index.html"
|
|
}
|
|
|
|
error_document {
|
|
key = "index.html"
|
|
}
|
|
}
|
|
```
|
|
|
|
Ejemplo de CloudFront:
|
|
```hcl
|
|
resource "aws_cloudfront_distribution" "sacc_cdn" {
|
|
enabled = true
|
|
default_root_object = "index.html"
|
|
aliases = ["sacc.ccsoft.mx"]
|
|
|
|
# Origin 1: S3 bucket para el frontend React
|
|
origin {
|
|
domain_name = aws_s3_bucket.sacc_frontend.bucket_regional_domain_name
|
|
origin_id = "saccS3Origin"
|
|
|
|
s3_origin_config {
|
|
origin_access_identity = aws_cloudfront_origin_access_identity.sacc_oai.cloudfront_access_identity_path
|
|
}
|
|
}
|
|
|
|
# Origin 2: EC2 para la API backend
|
|
origin {
|
|
domain_name = aws_instance.sacc_api.public_dns
|
|
origin_id = "saccApiOrigin"
|
|
|
|
custom_origin_config {
|
|
http_port = 80
|
|
https_port = 443
|
|
origin_protocol_policy = "http-only"
|
|
origin_ssl_protocols = ["TLSv1.2"]
|
|
}
|
|
}
|
|
|
|
default_cache_behavior {
|
|
target_origin_id = "saccS3Origin"
|
|
viewer_protocol_policy = "redirect-to-https"
|
|
allowed_methods = ["GET", "HEAD", "OPTIONS"]
|
|
cached_methods = ["GET", "HEAD"]
|
|
}
|
|
|
|
# API requests go to the EC2 origin
|
|
ordered_cache_behavior {
|
|
path_pattern = "/api/*"
|
|
target_origin_id = "saccApiOrigin"
|
|
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
|
|
cached_methods = ["GET", "HEAD"]
|
|
viewer_protocol_policy = "redirect-to-https"
|
|
|
|
forwarded_values {
|
|
query_string = true
|
|
headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"]
|
|
cookies {
|
|
forward = "all"
|
|
}
|
|
}
|
|
}
|
|
|
|
viewer_certificate {
|
|
acm_certificate_arn = aws_acm_certificate.sacc_cert.arn
|
|
ssl_support_method = "sni-only"
|
|
minimum_protocol_version = "TLSv1.2_2021"
|
|
}
|
|
}
|
|
```
|
|
|
|
> 💡 **¿Por qué dos origins?** CloudFront sirve el frontend desde S3 por defecto. Pero cuando el navegador pide algo bajo `/api/*`, CloudFront lo envía a la EC2 donde Nginx lo redirige al backend en el puerto 8080. Así todo comparte el mismo dominio `sacc.ccsoft.mx`.
|
|
|
|
---
|
|
|
|
### `variables.tf`
|
|
Aquí se definen las variables que personalizan la infraestructura.
|
|
|
|
Ejemplo:
|
|
```hcl
|
|
variable "region" {
|
|
description = "Región de AWS"
|
|
type = string
|
|
default = "us-east-1"
|
|
}
|
|
|
|
variable "instance_type" {
|
|
description = "Tipo de instancia EC2"
|
|
type = string
|
|
default = "t3.small"
|
|
}
|
|
|
|
variable "db_password" {
|
|
description = "Contraseña de la base de datos"
|
|
type = string
|
|
sensitive = true
|
|
}
|
|
```
|
|
|
|
> ⚠️ La variable `db_password` tiene `sensitive = true` para que no aparezca en los logs.
|
|
|
|
---
|
|
|
|
### `user-data.sh`
|
|
Este archivo contiene los comandos que la EC2 ejecuta la **primera vez** que se enciende.
|
|
|
|
Ejemplo de lo que hace:
|
|
1. Actualiza el sistema operativo.
|
|
2. Instala Nginx.
|
|
3. Instala Java (para correr la aplicación SACC).
|
|
4. Instala AWS CLI.
|
|
5. Crea los usuarios `thoth` y `osiris`.
|
|
6. Inyecta la **llave pública SSH dedicada** para el pipeline en `~/.ssh/authorized_keys` del usuario `thoth`.
|
|
7. Crea los directorios necesarios para el despliegue.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Actualizar sistema
|
|
apt-get update -y
|
|
apt-get install -y nginx openjdk-21-jdk awscli
|
|
|
|
# Crear usuarios
|
|
useradd -m -s /bin/bash thoth || true
|
|
useradd -m -s /bin/bash osiris || true
|
|
|
|
# Crear directorios de despliegue
|
|
mkdir -p /home/thoth/deploy/artifacts/{backup,current,logs,pids}
|
|
chown -R thoth:thoth /home/thoth/deploy
|
|
|
|
# Configurar Nginx como proxy SOLO para la API (no para el frontend)
|
|
cat > /etc/nginx/sites-available/proyectosacc-api <<'EOF'
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
location /api/ {
|
|
proxy_pass http://localhost:8080/;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
}
|
|
EOF
|
|
ln -sf /etc/nginx/sites-available/proyectosacc-api /etc/nginx/sites-enabled/default
|
|
|
|
# Iniciar Nginx
|
|
systemctl enable nginx
|
|
systemctl start nginx
|
|
```
|
|
|
|
---
|
|
|
|
### `outputs.tf`
|
|
Aquí definimos qué información queremos que Terraform nos muestre al final.
|
|
|
|
Ejemplo:
|
|
```hcl
|
|
output "ec2_public_ip" {
|
|
description = "IP pública del servidor de la API"
|
|
value = aws_instance.sacc_api.public_ip
|
|
}
|
|
|
|
output "rds_endpoint" {
|
|
description = "Dirección de conexión a la base de datos"
|
|
value = aws_db_instance.sacc_db.endpoint
|
|
}
|
|
|
|
output "s3_artifacts_bucket" {
|
|
description = "Nombre del bucket de artefactos de la API"
|
|
value = aws_s3_bucket.artifacts.bucket
|
|
}
|
|
|
|
output "s3_frontend_bucket" {
|
|
description = "Nombre del bucket del frontend React"
|
|
value = aws_s3_bucket.sacc_frontend.bucket
|
|
}
|
|
|
|
output "cloudfront_domain" {
|
|
description = "Dominio de CloudFront"
|
|
value = aws_cloudfront_distribution.sacc_cdn.domain_name
|
|
}
|
|
```
|
|
|
|
Después de ejecutar `terraform apply`, verás estos valores en la pantalla.
|
|
|
|
---
|
|
|
|
## 4. Cómo ejecutar Terraform paso a paso
|
|
|
|
### Paso 1: Ir a la carpeta de Terraform
|
|
```bash
|
|
cd /home/evert/Servidores/Nuve/AWS/proyectosacc/terraform
|
|
```
|
|
|
|
### Paso 2: Inicializar Terraform
|
|
```bash
|
|
terraform init
|
|
```
|
|
Esto descarga los plugins necesarios para hablar con AWS.
|
|
|
|
### Paso 3: Revisar el plan
|
|
```bash
|
|
terraform plan
|
|
```
|
|
Terraform te muestra **todo lo que va a crear, modificar o destruir**. Revísalo con cuidado.
|
|
|
|
### Paso 4: Aplicar los cambios
|
|
```bash
|
|
terraform apply
|
|
```
|
|
Terraform te pedirá que escribas `yes` para confirmar. Luego empezará a crear los recursos en AWS.
|
|
|
|
### Paso 5: Ver los outputs
|
|
Al finalizar, Terraform mostrará los valores definidos en `outputs.tf`:
|
|
- IP pública de la EC2 (API backend)
|
|
- Endpoint de la base de datos
|
|
- Nombre del bucket S3 de artefactos
|
|
- Nombre del bucket S3 del frontend
|
|
- Dominio de CloudFront
|
|
|
|
---
|
|
|
|
## 5. Comandos útiles adicionales
|
|
|
|
| Comando | Para qué sirve |
|
|
|---------|----------------|
|
|
| `terraform validate` | Revisa que la sintaxis de los archivos esté correcta. |
|
|
| `terraform fmt` | Ordena y da formato a los archivos `.tf`. |
|
|
| `terraform show` | Muestra el estado actual de la infraestructura. |
|
|
| `terraform destroy` | **Elimina** todos los recursos creados. ¡Úsalo con cuidado! |
|
|
|
|
---
|
|
|
|
## 6. Checklist antes de ejecutar Terraform
|
|
|
|
- [ ] Tienes configuradas las credenciales de AWS (`aws configure`).
|
|
- [ ] La variable `db_password` no está escrita en el código, viene de un archivo seguro o variable de entorno.
|
|
- [ ] Revisaste el `terraform plan` antes de aplicar.
|
|
- [ ] Sabes en qué región de AWS se va a crear todo (`us-east-1`, `us-west-2`, etc.).
|
|
- [ ] El bucket S3 para el estado remoto ya existe (si usas backend remoto).
|
|
- [ ] El `user_data.sh` incluye la llave pública SSH dedicada para el pipeline.
|
|
- [ ] El bucket S3 del frontend tiene habilitado el versionamiento de objetos (para rollback del frontend).
|
|
- [ ] CloudFront tiene configurado el Origin Access Identity (OAI) u Origin Access Control (OAC) para leer de S3.
|
|
- [ ] Route 53 apunta a CloudFront, no a la IP de la EC2.
|
|
- [ ] CloudFront tiene un behavior `/api/*` que apunta a la EC2.
|
|
- [ ] La EC2 tiene el Security Group configurado para recibir tráfico HTTP desde CloudFront.
|
|
|
|
---
|
|
|
|
*Anterior: [`06-scripts-de-despliegue.md`](06-scripts-de-despliegue.md)*
|
|
*Siguiente: [`08-seguridad-y-secretos.md`](08-seguridad-y-secretos.md)*
|