Serverless y microservicios son dos enfoques arquitectónicos que pueden convivir o usarse por separado. Serverless ejecuta funciones individuales sin gestionar servidores. Microservicios descomponen una aplicación en servicios independientes que se despliegan por separado. La decisión entre uno y otro depende del tipo de carga de trabajo, el equipo y los costos.
Serverless y Microservicios: No Son lo Mismo
Un error frecuente es tratar serverless y microservicios como si fueran alternativas directas. En realidad, operan en niveles diferentes:
- Microservicios es un patrón de arquitectura: cómo divides tu aplicación en componentes independientes
- Serverless es un modelo de ejecución: cómo y dónde se ejecuta tu código
Puedes tener microservicios que corren en servidores tradicionales, en contenedores, o en funciones serverless. Y puedes usar serverless para ejecutar un monolito o funciones independientes que no siguen el patrón de microservicios.
Dicho esto, en la práctica la decisión suele ser entre estas dos opciones concretas:
- Microservicios en contenedores (Docker + Kubernetes/ECS)
- Funciones serverless (AWS Lambda, Azure Functions, Google Cloud Functions)
Esta guía compara ambos enfoques en profundidad.
Arquitectura de Microservicios
Los microservicios descomponen una aplicación en servicios pequeños e independientes, cada uno responsable de una función de negocio específica.
Características Principales
- Independencia de despliegue: Cada servicio se despliega por separado
- Base de datos propia: Cada servicio gestiona sus propios datos
- Comunicación por API: Los servicios se comunican via HTTP/REST, gRPC o eventos
- Autonomía de equipo: Un equipo pequeño es dueño de un servicio completo
- Tecnología heterogénea: Cada servicio puede usar el lenguaje y stack que mejor le sirva
Ejemplo: E-commerce con Microservicios
┌─────────────┐
│ API Gateway │
└──────┬──────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ User │ │ Product │ │ Order │
│ Service │ │ Service │ │ Service │
│ (Node.js) │ │ (Python) │ │ (Java) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ MongoDB │ │ PostgreSQL │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└───────────────┼───────────────┘
▼
┌──────────────┐
│ Event Bus │
│ (Kafka/SQS) │
└──────────────┘
Microservicio Típico en Producción
# Kubernetes deployment para un microservicio
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
spec:
replicas: 3
selector:
matchLabels:
app: product-service
template:
spec:
containers:
- name: product-service
image: registry/product-service:2.1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: product-db
key: host
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
Arquitectura Serverless
En serverless, el código se ejecuta en funciones efímeras que el proveedor cloud provisiona automáticamente. No hay servidores que gestionar, no hay instancias que escalar manualmente.
Características Principales
- Sin gestión de servidores: El proveedor maneja toda la infraestructura
- Pago por ejecución: Solo pagas cuando tu código se ejecuta
- Escalado automático a cero: Sin tráfico, sin costo
- Funciones de corta duración: Timeouts de 15 minutos (Lambda) a 60 minutos (Azure)
- Event-driven: Se activan por eventos (HTTP, colas, archivos, schedules)
Ejemplo: E-commerce con Serverless
┌──────────────┐
│ API Gateway │
└──────┬───────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Lambda: │ │ Lambda: │ │ Lambda: │
│ getUser │ │ getProducts │ │ createOrder │
│ createUser │ │ search │ │ getOrder │
│ updateUser │ │ updateStock │ │ cancelOrder │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ DynamoDB │ │ DynamoDB │ │ DynamoDB │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└───────────────┼───────────────┘
▼
┌──────────────┐
│ EventBridge│
│ / SQS │
└──────────────┘
Función Serverless Típica
# AWS Lambda: crear orden de compra
import json
import boto3
import uuid
from datetime import datetime
dynamodb = boto3.resource('dynamodb')
orders_table = dynamodb.Table('orders')
sqs = boto3.client('sqs')
def handler(event, context):
body = json.loads(event['body'])
order = {
'order_id': str(uuid.uuid4()),
'user_id': body['user_id'],
'items': body['items'],
'total': calculate_total(body['items']),
'status': 'pending',
'created_at': datetime.utcnow().isoformat()
}
# Guardar orden
orders_table.put_item(Item=order)
# Publicar evento para procesamiento async
sqs.send_message(
QueueUrl='https://sqs.us-east-1.amazonaws.com/123/order-processing',
MessageBody=json.dumps({
'event': 'order.created',
'order_id': order['order_id'],
'items': order['items']
})
)
return {
'statusCode': 201,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({
'order_id': order['order_id'],
'status': 'pending'
})
}
def calculate_total(items):
return sum(item['price'] * item['quantity'] for item in items)
Comparación Detallada
Costos
| Factor | Microservicios (Contenedores) | Serverless |
|---|---|---|
| Costo base | Si (instancias siempre encendidas) | No (pago por ejecución) |
| Con 0 tráfico | ~$50-200/mes por servicio | ~$0 |
| Con tráfico constante | Predecible, optimizable | Puede ser más caro |
| Picos de tráfico | Costo proporcional a capacidad reservada | Costo proporcional al uso real |
| Ejemplo: 1M requests/mes | ~$100-300/mes (ECS Fargate) | ~$20-50/mes (Lambda) |
| Ejemplo: 100M requests/mes | ~$500-1,500/mes | ~$2,000-5,000/mes |
Regla general: Serverless es más barato con tráfico bajo o variable. Microservicios en contenedores son más baratos con tráfico alto y constante.
Escalabilidad
| Aspecto | Microservicios | Serverless |
|---|---|---|
| Velocidad de escalado | 30s-5min (nuevo contenedor) | Instantáneo (ms) |
| Escala a cero | No (mínimo 1 instancia) | Si |
| Escalado máximo | Limitado por nodos/presupuesto | 1000+ ejecuciones concurrentes |
| Granularidad | Por servicio | Por función individual |
| Cold start | No aplica | 100ms-10s según runtime |
Desarrollo y Mantenimiento
| Aspecto | Microservicios | Serverless |
|---|---|---|
| Complejidad de desarrollo | Media (frameworks estándar) | Baja-Media (funciones simples) |
| Testing local | Excelente (Docker Compose) | Limitado (emuladores imprecisos) |
| Debugging | Fácil (logs locales, debugger) | Complejo (CloudWatch, tracing) |
| Vendor lock-in | Bajo (contenedores portables) | Alto (APIs propietarias) |
| Time-to-market | Medio | Rápido |
| Operación día a día | Clusters, patching, updates | Casi nula |
| Equipo necesario | DevOps/SRE dedicado | Developers pueden operar |
Arquitectura y Diseño
| Aspecto | Microservicios | Serverless |
|---|---|---|
| Estado (state) | Stateful o stateless | Solo stateless |
| Conexiones a DB | Connection pools persistentes | Conexiones efímeras (problema!) |
| Procesos largos | Sin límite | 15 min máximo (Lambda) |
| WebSockets | Si | Limitado (API Gateway WS) |
| Background jobs | Si (workers, queues) | Si (SQS + Lambda) |
| Tamaño del código | Sin límite práctico | 50-250MB según proveedor |
Cuándo Elegir Microservicios
Los microservicios en contenedores son la mejor opción cuando:
1. Tráfico Alto y Constante
Si tu aplicación maneja tráfico predecible y constante (como un SaaS con miles de usuarios concurrentes), los contenedores siempre encendidos son más eficientes que pagar por cada ejecución serverless.
2. Procesos de Larga Duración
Procesamiento de video, entrenamiento de modelos ML, ETL pesados, WebSockets para real-time: cualquier proceso que exceda el timeout de Lambda (15 minutos) necesita contenedores.
3. Estado y Conexiones Persistentes
Aplicaciones que mantienen conexiones abiertas a bases de datos, caches en memoria, o sesiones de usuario se benefician de contenedores que no se destruyen entre requests.
4. Control y Portabilidad
Si necesitas controlar el runtime, el sistema operativo, las versiones de librerías, o poder migrar entre clouds sin reescribir, los contenedores ofrecen esa flexibilidad.
5. Equipos Grandes con Dominio Claro
Cuando tienes equipos dedicados por dominio de negocio (equipo de pagos, equipo de usuarios, equipo de productos), cada uno puede ser dueño de su microservicio.
Cuándo Elegir Serverless
Serverless es la mejor opción cuando:
1. Tráfico Variable o Impredecible
APIs que van de 0 a 10,000 requests en minutos y luego vuelven a 0. Colas que se llenan esporádicamente. Webhooks que se reciben intermitentemente.
2. Event-Driven Processing
Procesamiento de archivos subidos a S3, reacción a eventos de bases de datos, procesamiento de streams de datos, envío de notificaciones disparadas por eventos.
3. APIs Simples y CRUD
Si tu API es principalmente operaciones de lectura/escritura contra una base de datos, Lambda + API Gateway + DynamoDB es extremadamente eficiente.
4. Equipos Pequeños sin DevOps
Si no tienes un equipo dedicado a operar infraestructura, serverless elimina la necesidad de gestionar clusters, patchar servidores y configurar autoscaling.
5. Funciones Auxiliares
Procesamiento de imágenes, envío de emails, generación de reportes, tareas scheduladas, integración entre servicios: funciones que no justifican un servicio completo.
# Lambda ideal: procesar imagen subida a S3
def handler(event, context):
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
# Descargar imagen
image = s3.get_object(Bucket=bucket, Key=key)
# Generar thumbnails
for size in [(150, 150), (300, 300), (800, 600)]:
thumbnail = resize_image(image, size)
s3.put_object(
Bucket=bucket,
Key=f"thumbnails/{size[0]}x{size[1]}/{key}",
Body=thumbnail
)
return {'processed': key, 'thumbnails': 3}
Patrón Híbrido: Lo Mejor de Ambos Mundos
En la práctica, muchas arquitecturas exitosas combinan microservicios y serverless:
┌─────────────────────────────────────────────────────┐
│ API Gateway │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────────┐ ┌──────────────┐
│ Lambda │ │ Microservicio│ │ Lambda │
│ Auth │ │ Core API │ │ Notifications│
│ │ │ (ECS/K8s) │ │ │
└─────────┘ └──────┬──────┘ └──────────────┘
│
┌───────┼───────┐
▼ ▼ ▼
┌────────┐ ┌────┐ ┌──────────┐
│ RDS │ │Redis│ │ Lambda │
│ │ │ │ │ Image │
│ │ │ │ │ Process │
└────────┘ └────┘ └──────────┘
Qué Va en Microservicios
- Core business logic (procesamiento de pagos, gestión de usuarios)
- Servicios con conexiones persistentes a DB
- APIs con tráfico alto y constante
- Servicios que necesitan WebSockets o streaming
Qué Va en Serverless
- Autenticación y autorización (funciones simples, stateless)
- Procesamiento de eventos y archivos
- Notificaciones (email, push, SMS)
- Tareas scheduladas (reportes, limpieza, backups)
- Endpoints de baja frecuencia
Problemas Comunes y Soluciones
Cold Starts en Serverless
El cold start ocurre cuando Lambda necesita inicializar un nuevo entorno de ejecución. Puede agregar 100ms a 10 segundos de latencia.
Soluciones:
- Usar Provisioned Concurrency para funciones críticas
- Elegir runtimes rápidos (Python, Node.js vs Java)
- Minimizar el tamaño del paquete de despliegue
- Usar SnapStart en Java (Lambda)
Distributed Tracing en Microservicios
Con muchos servicios comunicándose, un request puede pasar por 5-10 servicios. Sin tracing, debuggear es una pesadilla.
Soluciones:
- Implementar OpenTelemetry para traces distribuidos
- Usar correlation IDs que se propagan entre servicios
- Centralizar logs con un stack como ELK o Graylog
Connection Pooling en Serverless
Cada invocación de Lambda puede abrir una nueva conexión a la base de datos, agotando el pool rápidamente.
Soluciones:
- Usar RDS Proxy o PgBouncer
- Preferir DynamoDB (sin connection pooling)
- Reutilizar conexiones entre invocaciones calientes
Matriz de Decisión
| Pregunta | Microservicios | Serverless |
|---|---|---|
| ¿Tráfico constante > 10K req/min? | X | |
| ¿Procesos > 15 minutos? | X | |
| ¿Necesitas WebSockets? | X | |
| ¿Equipo DevOps dedicado? | X | |
| ¿Tráfico variable o esporádico? | X | |
| ¿Event-driven processing? | X | |
| ¿Equipo pequeño sin infra? | X | |
| ¿Time-to-market es prioridad? | X | |
| ¿Multi-cloud obligatorio? | X | |
| ¿Presupuesto ajustado, bajo uso? | X |
Conclusión
No existe una respuesta universal. La decisión entre serverless y microservicios depende de tu contexto específico:
- Elige microservicios en contenedores si tienes tráfico alto y constante, procesos de larga duración, o necesitas control total sobre el entorno de ejecución
- Elige serverless si tienes tráfico variable, equipo pequeño, o muchas funciones event-driven independientes
- Combina ambos en una arquitectura híbrida donde cada componente usa el modelo que mejor le sirve
Lo peor que puedes hacer es elegir por hype. Evalúa tu tráfico, tu equipo, tu presupuesto y tus requisitos técnicos. La arquitectura correcta es la que resuelve tus problemas con la menor complejidad posible.
Para complementar tu decisión, consulta nuestra guía de contenedores vs serverless que profundiza en los aspectos de infraestructura, y nuestra guía sobre seguridad en microservicios para consideraciones de seguridad en ambos enfoques.
Top comments (0)