Cloud Platforms para Data Engineering
AWS, Google Cloud y Azure son los tres grandes proveedores cloud. Cada uno tiene su ecosistema de servicios de datos. Conocer los equivalentes entre plataformas y las mejores prácticas de cada una es fundamental para el mercado laboral de 2026.
AWS: El Ecosistema de Datos
Amazon Web Services es el cloud líder con la mayor cuota de mercado. Su ecosistema de datos es el más maduro y extenso.
Stack de Datos AWS 2026
S3
Object storage. El Data Lake base. Duradero 11 nines, infinitamente escalable.
Redshift
Data Warehouse columnar. Redshift Serverless para pago por uso.
Glue
ETL serverless + Data Catalog. PySpark/Python sin gestionar clusters.
Athena
Query SQL sobre S3 sin infraestructura. Pago por query (5$/TB).
EMR
Hadoop/Spark managed. EMR Serverless para escalar automáticamente.
Kinesis
Streaming equivalente a Kafka. Kinesis Data Streams + Firehose.
MSK
Apache Kafka managed. Para equipos que prefieren Kafka sobre Kinesis.
Lambda
Funciones serverless. Para transformaciones ligeras event-driven.
Step Functions
Orquestación serverless de workflows. Alternativa a Airflow para AWS-native.
Lake Formation
Data Lake governance y permisos fine-grained sobre S3 + Glue Catalog.
AWS S3: Mejores Prácticas
"""
Trabajar con S3 eficientemente desde Python
boto3 + s3fs + pandas
"""
import boto3
import s3fs
import pandas as pd
from pathlib import Path
# ── BOTO3: AWS SDK ──────────────────────────────────────────────────────────
s3 = boto3.client('s3',
region_name='us-east-1',
# Usar IAM roles en producción, nunca credenciales hardcoded
)
# Subir archivo
s3.upload_file('local_file.parquet', 'my-data-lake', 'raw/orders/2026/01/01/data.parquet')
# Listar objetos con paginación
paginator = s3.get_paginator('list_objects_v2')
pages = paginator.paginate(Bucket='my-data-lake', Prefix='raw/orders/2026/')
all_keys = []
for page in pages:
if 'Contents' in page:
all_keys.extend([obj['Key'] for obj in page['Contents']])
# Leer Parquet directamente desde S3 (sin descargar)
fs = s3fs.S3FileSystem()
df = pd.read_parquet('s3://my-data-lake/silver/orders/year=2026/month=1/',
filesystem=fs,
columns=['order_id', 'customer_id', 'amount'])
# ── BEST PRACTICES para S3 Data Lake ─────────────────────────────────────
# 1. Particionamiento Hive-style: year=YYYY/month=MM/day=DD/
# 2. Usar prefixes para distribución de carga (key prefix sharding)
# 3. Tamaño ideal de archivos: 128MB - 1GB
# 4. Habilitar S3 Versioning para tablas críticas
# 5. S3 Lifecycle policies para archivar datos antiguos a Glacier
# 6. Encryption at rest: S3-SSE o KMS para datos sensibles
# 7. VPC Endpoints: acceso a S3 sin internet (seguridad)
# ── AWS GLUE: ETL Serverless ───────────────────────────────────────────────
# Glue Job en PySpark - se ejecuta en un cluster managed por AWS
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job
args = getResolvedOptions(sys.argv, ['JOB_NAME', 'run_date'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)
# Leer desde Glue Data Catalog
orders_dyf = glueContext.create_dynamic_frame.from_catalog(
database='raw_data',
table_name='orders',
push_down_predicate=f"run_date = '{args['run_date']}'",
)
# Convertir a DataFrame Spark para transformaciones complejas
orders_df = orders_dyf.toDF()
# ... transformaciones ...
# Escribir de vuelta al Data Lake con Glue
output_dyf = DynamicFrame.fromDF(orders_df, glueContext, "output")
glueContext.write_dynamic_frame.from_options(
frame=output_dyf,
connection_type='s3',
connection_options={
'path': 's3://my-data-lake/silver/orders/',
'partitionKeys': ['year', 'month'],
},
format='glueparquet',
)
job.commit()
# ── AMAZON ATHENA: SQL sobre S3 ────────────────────────────────────────────
import boto3
import time
import pandas as pd
athena = boto3.client('athena', region_name='us-east-1')
def run_athena_query(sql: str, database: str, output_bucket: str) -> pd.DataFrame:
"""Ejecuta una query en Athena y retorna un DataFrame."""
response = athena.start_query_execution(
QueryString=sql,
QueryExecutionContext={'Database': database},
ResultConfiguration={
'OutputLocation': f's3://{output_bucket}/athena-results/',
'EncryptionConfiguration': {'EncryptionOption': 'SSE_S3'}
},
WorkGroup='primary'
)
execution_id = response['QueryExecutionId']
# Esperar a que termine
while True:
result = athena.get_query_execution(QueryExecutionId=execution_id)
state = result['QueryExecution']['Status']['State']
if state == 'SUCCEEDED':
break
elif state in ['FAILED', 'CANCELLED']:
raise Exception(f"Query failed: {result['QueryExecution']['Status']}")
time.sleep(2)
# Leer resultados desde S3
output_path = result['QueryExecution']['ResultConfiguration']['OutputLocation']
return pd.read_csv(output_path)
# Uso
df = run_athena_query(
"SELECT customer_id, SUM(amount) FROM silver.orders WHERE year='2026' GROUP BY 1",
database='silver',
output_bucket='my-query-results'
)
Google Cloud: El Ecosistema de Datos
Stack de Datos GCP 2026
Cloud Storage (GCS)
Equivalente a S3. Object storage para Data Lakes.
BigQuery
DWH serverless líder. Analítica a escala de petabytes con SQL.
Dataflow
Apache Beam managed. Batch + Stream en una sola API.
Pub/Sub
Messaging serverless. Equivalente a Kinesis para streaming.
Dataproc
Hadoop/Spark managed. Equivalente a EMR de AWS.
Cloud Composer
Apache Airflow managed. Orquestación de workflows.
Vertex AI
ML Platform. AutoML, Workbench, Feature Store, MLOps.
Dataplex
Data Mesh y governance. Cataloga y gestiona activos de datos.
# ── GOOGLE CLOUD STORAGE ─────────────────────────────────────────────────
from google.cloud import storage
storage_client = storage.Client()
bucket = storage_client.bucket('my-data-lake-gcs')
# Upload
blob = bucket.blob('raw/orders/2026/01/01/data.parquet')
blob.upload_from_filename('local_data.parquet')
# Con metadata y content type
blob.content_type = 'application/octet-stream'
blob.metadata = {'source': 'orders-api', 'version': '2'}
blob.patch()
# Leer Parquet directamente con GCSFileSystem
import gcsfs
fs = gcsfs.GCSFileSystem()
df = pd.read_parquet('gcs://my-data-lake-gcs/silver/orders/', filesystem=fs)
# ── BIGQUERY: Analytics Serverless ────────────────────────────────────────
from google.cloud import bigquery
bq = bigquery.Client()
# Insertar desde DataFrame
job_config = bigquery.LoadJobConfig(
schema=[
bigquery.SchemaField("order_id", "INTEGER"),
bigquery.SchemaField("amount", "FLOAT"),
bigquery.SchemaField("created_at", "TIMESTAMP"),
],
write_disposition="WRITE_APPEND",
time_partitioning=bigquery.TimePartitioning(
type_=bigquery.TimePartitioningType.DAY,
field="created_at"
),
clustering_fields=["customer_id", "status"]
)
job = bq.load_table_from_dataframe(df, "project.dataset.orders", job_config=job_config)
job.result() # Esperar que termine
# Ejecutar query y obtener resultado
query = """
SELECT customer_id, SUM(amount) AS total
FROM `project.dataset.orders`
WHERE DATE(created_at) = @run_date
GROUP BY customer_id
"""
job_config_q = bigquery.QueryJobConfig(
query_parameters=[bigquery.ScalarQueryParameter("run_date", "DATE", "2026-01-15")]
)
df_result = bq.query(query, job_config=job_config_q).to_dataframe()
# ── APACHE BEAM con Dataflow ───────────────────────────────────────────────
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions, GoogleCloudOptions
options = PipelineOptions()
google_cloud_options = options.view_as(GoogleCloudOptions)
google_cloud_options.project = 'my-project'
google_cloud_options.region = 'us-central1'
google_cloud_options.staging_location = 'gs://my-bucket/staging'
google_cloud_options.temp_location = 'gs://my-bucket/temp'
options.view_as(beam.options.pipeline_options.StandardOptions).runner = 'DataflowRunner'
with beam.Pipeline(options=options) as p:
orders = (
p
| 'ReadFromPubSub' >> beam.io.ReadFromPubSub(topic='projects/myproject/topics/orders')
| 'ParseJSON' >> beam.Map(json.loads)
| 'FilterValid' >> beam.Filter(lambda x: x.get('amount', 0) > 0)
| 'TransformOrders' >> beam.Map(lambda x: {**x, 'revenue_cat': 'large' if x['amount'] > 500 else 'small'})
| 'WriteToBigQuery' >> beam.io.WriteToBigQuery(
'my-project:analytics.orders_stream',
schema='order_id:INTEGER,amount:FLOAT,revenue_cat:STRING',
write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,
)
)
Azure: El Ecosistema de Datos
Stack de Datos Azure 2026
ADLS Gen2
Azure Data Lake Storage. Object storage con filesystem semantics.
Azure Synapse
Analytics unificado: DWH + Spark + Pipelines en una plataforma.
Azure Data Factory
ETL/ELT managed. 90+ conectores. Orquestación visual.
Event Hubs
Kafka-compatible streaming. Ingestión de millones de eventos/seg.
Databricks on Azure
Spark + Delta Lake. Muy común en el stack Azure-Databricks.
Power BI
BI y visualización. Integración nativa con todo el stack Azure.
Azure Purview
Data Catalog y Data Governance para todo el stack Azure.
Azure ML
MLOps y Feature Store. Comparable a Vertex AI de GCP.
Comparativa Multi-Cloud e IaC con Terraform
Equivalencias entre Clouds
| Categoría | AWS | Google Cloud | Azure |
|---|---|---|---|
| Object Storage | S3 | Cloud Storage (GCS) | ADLS Gen2 / Blob Storage |
| Data Warehouse | Redshift | BigQuery | Synapse Analytics |
| Spark Managed | EMR | Dataproc | Databricks / HDInsight |
| Serverless ETL | AWS Glue | Dataflow (Beam) | Azure Data Factory |
| SQL on Lake | Athena | BigQuery | Synapse Serverless SQL |
| Event Streaming | Kinesis / MSK | Pub/Sub | Event Hubs |
| Workflow Orchestration | MWAA (Airflow) | Cloud Composer | Managed Airflow |
| Data Catalog | Glue Catalog / Lake Formation | Dataplex | Microsoft Purview |
| ML Platform | SageMaker | Vertex AI | Azure ML |
Infrastructure as Code con Terraform
# ── TERRAFORM: Infraestructura como código para Data Engineering ──────────
# Nunca crear infraestructura manualmente en producción. Siempre IaC.
# main.tf - Stack de datos AWS con Terraform
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
backend "s3" {
bucket = "my-terraform-state"
key = "data-platform/terraform.tfstate"
region = "us-east-1"
}
}
# ── S3 Data Lake ────────────────────────────────────────────────────────────
resource "aws_s3_bucket" "data_lake" {
bucket = "${var.environment}-data-lake-${var.account_id}"
}
resource "aws_s3_bucket_versioning" "data_lake" {
bucket = aws_s3_bucket.data_lake.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_lifecycle_configuration" "data_lake" {
bucket = aws_s3_bucket.data_lake.id
rule {
id = "raw-to-glacier"
status = "Enabled"
filter { prefix = "raw/" }
transition { days = 90; storage_class = "GLACIER" }
expiration { days = 365 }
}
}
# ── Redshift Serverless ────────────────────────────────────────────────────
resource "aws_redshiftserverless_namespace" "main" {
namespace_name = "${var.environment}-data-warehouse"
admin_username = "admin"
admin_user_password = var.redshift_password # Usar Secrets Manager en producción
db_name = "analytics"
}
resource "aws_redshiftserverless_workgroup" "main" {
namespace_name = aws_redshiftserverless_namespace.main.namespace_name
workgroup_name = "${var.environment}-workgroup"
base_capacity = 8 # RPUs
config_parameter {
parameter_key = "max_query_execution_time"
parameter_value = "3600"
}
}
# ── MSK (Managed Kafka) ───────────────────────────────────────────────────
resource "aws_msk_cluster" "main" {
cluster_name = "${var.environment}-kafka"
kafka_version = "3.6.0"
number_of_broker_nodes = 3
broker_node_group_info {
instance_type = "kafka.m5.large"
client_subnets = var.private_subnet_ids
storage_info {
ebs_storage_info { volume_size = 100 }
}
}
encryption_info {
encryption_in_transit {
client_broker = "TLS"
in_cluster = true
}
}
}
# ── AWS Glue Catalog ──────────────────────────────────────────────────────
resource "aws_glue_catalog_database" "raw" {
name = "${var.environment}_raw"
}
resource "aws_glue_crawler" "orders" {
name = "${var.environment}-orders-crawler"
role = aws_iam_role.glue_role.arn
database_name = aws_glue_catalog_database.raw.name
s3_target {
path = "s3://${aws_s3_bucket.data_lake.bucket}/raw/orders/"
}
schedule = "cron(0 */6 * * ? *)" # Cada 6 horas
}
# Aplicar: terraform init, terraform plan, terraform apply
Si empiezas de cero: AWS si necesitas el ecosistema más maduro y grande, GCP si tu caso de uso central es analytics/ML (BigQuery es el mejor DWH cloud), Azure si tu empresa ya tiene Microsoft Stack (Office 365, Windows Azure AD). Para entrevistas: Conocer los conceptos de uno en profundidad es más valioso que conocer los tres superficialmente.