Serverless
"Serverless no significa 'sin servidores'. Significa 'no son MIS servidores y solo pago cuando se usan'."
Qué vas a aprender en este capítulo
Serverless lleva la abstracción cloud al extremo: vos solo escribís código de funciones; el proveedor maneja todo lo demás (servidores, escalado, monitoreo). Pagás solo por las invocaciones reales. Este capítulo cubre cuándo es la herramienta correcta y cuándo no.
4.1 ¿Qué es serverless?
📐 Fundamento
Características de serverless:
- Sin gestión de servidores — no provisionás VMs ni contenedores.
- Pago por uso — pagás por invocaciones y tiempo de ejecución, no por tiempo idle.
- Escalado automático — de 0 a millones de invocaciones automáticamente.
- Stateless — cada invocación es independiente.
- Event-driven — se ejecuta en respuesta a eventos.
Tipos de serverless:
| Tipo | Ejemplos | Para qué |
|---|---|---|
| FaaS (Function as a Service) | Lambda, Cloud Functions, Azure Functions | Código que reacciona a eventos |
| BaaS (Backend as a Service) | Firebase, Supabase, Auth0 | Auth, DB, storage gestionados |
| Serverless containers | Cloud Run, ECS Fargate | Tus contenedores, sin gestionar infraestructura |
| Serverless DBs | DynamoDB, Aurora Serverless, Neon | BD que escala automáticamente |
Cuándo usar serverless:
- Tráfico irregular o impredecible.
- Workloads esporádicos (cron jobs, procesamiento de archivos).
- APIs simples con baja latencia.
- Procesamiento event-driven (file uploaded → procesar).
- MVPs y prototipos rápidos.
Cuándo NO usar:
- Workloads constantes y predecibles (sale más caro que VMs reservadas).
- Aplicaciones con requisito de baja latencia consistente (cold starts).
- Procesos que tardan más del límite de la plataforma (Lambda: 15 min máx).
- Procesamiento que requiere mucho estado.
4.2 AWS Lambda en práctica
📐 Fundamento
Estructura de una función Lambda en Python:
# handler.py
import json
import boto3
from PIL import Image
from io import BytesIO
s3 = boto3.client('s3')
def lambda_handler(event, context):
"""
Trigger: S3 ObjectCreated event.
Crea thumbnails para imágenes subidas.
"""
# Extraer bucket y key del event
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
# Skip si ya es thumbnail
if key.startswith('thumbnails/'):
return {'statusCode': 200, 'body': 'Already a thumbnail'}
# Descargar la imagen
obj = s3.get_object(Bucket=bucket, Key=key)
image_data = obj['Body'].read()
# Generar thumbnail
img = Image.open(BytesIO(image_data))
img.thumbnail((200, 200))
buffer = BytesIO()
img.save(buffer, format='WEBP', quality=85)
# Subir thumbnail
thumb_key = f"thumbnails/{key}"
s3.put_object(
Bucket=bucket,
Key=thumb_key,
Body=buffer.getvalue(),
ContentType='image/webp'
)
return {
'statusCode': 200,
'body': json.dumps({'original': key, 'thumbnail': thumb_key})
}
Desplegar con Terraform:
# Empaquetar el código
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "${path.module}/lambda_code"
output_path = "${path.module}/lambda.zip"
}
# La función Lambda
resource "aws_lambda_function" "thumbnail_generator" {
function_name = "thumbnail-generator"
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
handler = "handler.lambda_handler"
runtime = "python3.12"
timeout = 30
memory_size = 512
role = aws_iam_role.lambda_role.arn
environment {
variables = {
LOG_LEVEL = "INFO"
}
}
}
# IAM role
resource "aws_iam_role" "lambda_role" {
name = "thumbnail-generator-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "lambda_s3" {
role = aws_iam_role.lambda_role.id
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject", "s3:PutObject"]
Resource = "arn:aws:s3:::la-esquina-images/*"
}, {
Effect = "Allow"
Action = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
Resource = "*"
}]
})
}
# Trigger desde S3
resource "aws_s3_bucket_notification" "image_upload" {
bucket = "la-esquina-images"
lambda_function {
lambda_function_arn = aws_lambda_function.thumbnail_generator.arn
events = ["s3:ObjectCreated:*"]
filter_prefix = "originals/"
}
}
Triggers comunes:
| Trigger | Caso de uso |
|---|---|
| API Gateway | HTTP endpoints serverless |
| S3 events | Procesar archivos uploaded |
| EventBridge / SQS | Mensajes y eventos |
| DynamoDB Streams | Reaccionar a cambios en BD |
| CloudWatch Events | Cron jobs (rate(1 hour) o cron expressions) |
| Cognito | Triggers en signup/login |
4.3 Cold starts y limitaciones
💡 Intuición
Cold start: la primera vez que se invoca una Lambda (o después de mucho tiempo idle), AWS necesita: aprovisionar un container, cargar tu código, inicializar la runtime. Esto puede tardar 100ms-2000ms — significativo si querés latencia baja.
Las invocaciones siguientes (mientras el container sigue caliente) son rápidas.
📐 Fundamento
Mitigar cold starts:
- Provisioned Concurrency — pagar para mantener N instances calientes.
- Lenguaje correcto — Python/Node.js ~200ms, Java/.NET ~1000ms+.
- Reducir el package size — menos código = más rápido el load.
- Inicialización fuera del handler — solo se ejecuta una vez por container.
# Inicializar conexiones FUERA del handler
db_client = boto3.client('dynamodb') # se reusa entre invocaciones
config = load_config_from_ssm() # cargado una vez
def lambda_handler(event, context):
# Solo lo específico del request va aquí
return db_client.get_item(...)
Límites de Lambda:
| Recurso | Límite |
|---|---|
| Tiempo de ejecución | 15 minutos |
| Memoria | 128 MB - 10 GB |
| Tamaño del package | 50 MB zipped, 250 MB unzipped, 10 GB con Container Image |
/tmp storage |
512 MB - 10 GB |
| Concurrent executions | 1000 (default, ampliable) |
| Payload size | 6 MB sync, 256 KB async |
Container Images en Lambda:
Para casos donde necesitás más de 250 MB (ej: ML models), Lambda soporta Container Images de hasta 10 GB:
FROM public.ecr.aws/lambda/python:3.12
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY handler.py modelo.pkl ${LAMBDA_TASK_ROOT}/
CMD ["handler.lambda_handler"]
4.4 Arquitecturas event-driven
📐 Fundamento
Patrón: API serverless con DynamoDB:
Client
│
▼
[API Gateway] ←─── HTTP routing, throttling, auth
│
▼
[Lambda] ────→ [DynamoDB] ←─── BD serverless
Ejemplo: API completa de pedidos serverless con SAM/Terraform:
# pedidos_api.py
import json
import boto3
import uuid
from datetime import datetime
dynamodb = boto3.resource('dynamodb')
tabla_pedidos = dynamodb.Table('pedidos')
def crear_pedido(event, context):
body = json.loads(event['body'])
pedido = {
'pedido_id': str(uuid.uuid4()),
'mesa_id': body['mesa_id'],
'items': body['items'],
'total': sum(i['precio'] * i['cantidad'] for i in body['items']),
'estado': 'ABIERTO',
'creado_en': datetime.utcnow().isoformat()
}
tabla_pedidos.put_item(Item=pedido)
return {
'statusCode': 201,
'body': json.dumps(pedido),
'headers': {'Content-Type': 'application/json'}
}
def listar_pedidos(event, context):
response = tabla_pedidos.scan(Limit=20)
return {
'statusCode': 200,
'body': json.dumps({'items': response['Items']})
}
Patrón: procesamiento async con SQS:
[API] → [SQS Queue] → [Lambda Worker] → [DB]
Ventajas:
- Si hay un pico, los mensajes se acumulan en SQS
- Lambda escala automáticamente para procesar
- Si Lambda falla, mensaje vuelve a la cola (retry automático)
Patrón: fan-out con SNS:
[Pedido creado] → [SNS Topic]
│
┌───────────┼───────────┐
▼ ▼ ▼
[Lambda email] [Lambda SMS] [Lambda Analytics]
Un evento → múltiples acciones independientes en paralelo.
Patrón: orquestación con Step Functions:
# state-machine.json
{
"StartAt": "ValidarPedido",
"States": {
"ValidarPedido": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:validar",
"Next": "VerificarStock"
},
"VerificarStock": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:stock",
"Next": "ProcesarPago"
},
"ProcesarPago": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:pago",
"Catch": [{
"ErrorEquals": ["PagoFallido"],
"Next": "RevertirStock"
}],
"Next": "ConfirmarPedido"
},
"RevertirStock": { "Type": "Task", "Resource": "...", "End": true },
"ConfirmarPedido": { "Type": "Succeed" }
}
}
Step Functions = orquestación visual de Lambdas con manejo de errores y retries declarativo. Útil para sagas (visto en SIS615).
4.5 Frameworks serverless
📐 Fundamento
Configurar Lambda + API Gateway + IAM + S3 manualmente es tedioso. Frameworks lo simplifican.
AWS SAM (Serverless Application Model):
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
PedidosFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./
Handler: pedidos_api.crear_pedido
Runtime: python3.12
Events:
ApiEvent:
Type: Api
Properties:
Path: /pedidos
Method: POST
sam build
sam deploy --guided
Serverless Framework (multi-cloud):
# serverless.yml
service: la-esquina-api
provider:
name: aws
runtime: python3.12
functions:
crearPedido:
handler: pedidos_api.crear_pedido
events:
- http: POST /pedidos
listarPedidos:
handler: pedidos_api.listar_pedidos
events:
- http: GET /pedidos
npm install -g serverless
sls deploy
Otras alternativas:
- Vercel — frontend + serverless functions, super simple para web apps.
- Cloudflare Workers — edge compute (corre en 300+ datacenters).
- Netlify Functions — similar a Vercel.
- Cloud Run (GCP) — contenedores serverless.
🛠️ En la práctica
La Esquina Cloud — arquitectura híbrida (containers + serverless):
┌──────────────────────────────────────────────┐
│ Frontend (Vercel) │ ← SPA estática
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ API Gateway / CloudFront │
└──────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ EKS │ │ Lambda │
│ (apps core) │ │ (workloads │
│ - Pedidos │ │ esporádicos)│
│ - Pagos │ │ - Thumbnails│
│ - Usuarios │ │ - Reports │
│ │ │ - Webhooks │
└──────────────┘ └──────────────┘
│ │
└────────────┬──────────────┘
▼
[PostgreSQL RDS]
[DynamoDB]
[S3]
[Redis ElastiCache]
Decisión de qué va dónde:
| Workload | Plataforma | Por qué |
|---|---|---|
| API principal de pedidos (24/7, alto tráfico) | EKS | Latencia consistente, sin cold starts |
| Generación de thumbnails (eventos esporádicos) | Lambda | Pagás solo por uploads |
| Reportes diarios a las 2 AM | Lambda + EventBridge | Solo ejecuta una vez por día |
| Webhooks de Stripe (impredecible) | Lambda | Maneja picos sin pre-provisionar |
| WebSockets de chat (conexiones largas) | EKS | Lambda no maneja conexiones persistentes bien |
4.6 Ejercicios
✏️ Ejercicio 4.1 — Diseñar arquitectura serverless
Diseñá una arquitectura serverless para los siguientes requisitos:
- Los usuarios suben videos a un servicio.
- El video debe convertirse a múltiples resoluciones (480p, 720p, 1080p).
- El usuario recibe un email cuando la conversión termina.
- Los videos se sirven desde un CDN.
¿Qué servicios usarías? ¿Cómo se conectan?
Solución
[Usuario]
│ upload
▼
[S3 bucket: uploads/]
│ S3 ObjectCreated event
▼
[Lambda: video_processor]
│ inicia trabajo en MediaConvert
▼
[AWS MediaConvert] (o ECS task con ffmpeg)
│ convierte a 480p, 720p, 1080p
▼
[S3 bucket: videos/]
│ S3 ObjectCreated event (cuando MediaConvert termina)
▼
[Lambda: notificador]
│ envía email con SES
▼
[Usuario recibe email]
[CloudFront] ←── sirve desde s3://videos/ con caché global
Servicios:
- S3 — almacenamiento de videos originales y procesados.
- Lambda — orquestación, ligera (los Lambdas no procesan video, solo coordinan).
- MediaConvert — servicio managed para transcoding de video (Lambda no es bueno para esto: timeout 15 min, sin GPU).
- SES — envío de emails.
- CloudFront — CDN para servir videos rápidamente.
- DynamoDB — guardar estado del procesamiento (qué resoluciones ya están listas).
Por qué serverless:
- Los uploads son esporádicos (no constantes).
- No hay servidores que mantener.
- Escala automáticamente desde 1 usuario a millones.
- Pagás solo por procesamiento real.
4.7 Para profundizar
- AWS Serverless docs — aws.amazon.com/serverless.
- Serverless Patterns Collection — serverlessland.com/patterns.
- The Serverless Framework docs.
- Siguiente: FinOps y arquitectura.
Definiciones nuevas: serverless, FaaS, BaaS, AWS Lambda, cold start, provisioned concurrency, trigger, event source, API Gateway, EventBridge, SQS, SNS, Step Functions, AWS SAM, Serverless Framework, Cloud Run, Cloudflare Workers, fan-out.