Módulo 4: LocalStack — AWS Local Development
7. Debugging LocalStack
Descripción
En esta cápsula vas a aprender a diagnosticar y resolver los problemas más comunes cuando trabajas con LocalStack. No es teoría — es el kit de supervivencia práctico: leer logs, verificar disponibilidad de servicios, resolver errores de permisos y formato, y usar herramientas de diagnóstico. Cuando algo falla (y algo siempre falla), sabrás exactamente dónde mirar.
Contexto: En las cápsulas anteriores construiste un pipeline S3 + Lambda con environment switching. Todo funcionó cuando seguiste los pasos. Pero en la realidad, los errores aparecen: LocalStack no arranca, Lambda retorna timeout, S3 dice que el bucket no existe. Esta cápsula te prepara para esos momentos. Es la diferencia entre "no funciona y no sé por qué" y "no funciona, ya sé dónde buscar."
El Flujo de Diagnóstico
Cuando algo falla, sigue este orden
1. ¿LocalStack está corriendo?
└── docker compose ps / curl health endpoint
↓
2. ¿El servicio que necesitas está disponible?
└── curl /_localstack/health → revisar services
↓
3. ¿Tu request llega a LocalStack?
└── docker compose logs localstack --tail 20
↓
4. ¿El error está en tu código o en LocalStack?
└── Probar la misma operación con awslocal CLI
↓
5. ¿Es un problema conocido?
└── Revisar la tabla de errores comunes abajo
Este flujo resuelve el 90% de los problemas. Memorízalo.
Nivel 1: ¿LocalStack Está Corriendo?
Verificar el container
# ¿Está el container up?
docker compose ps localstack
# Output posibles:
# localstack Up (healthy) ← Todo bien
# localstack Up (health: starting) ← Aún arrancando, espera
# localstack Exited (1) ← Crasheó
# (nada) ← No está definido en Compose
Si el container no está corriendo
# Ver por qué se detuvo
docker compose logs localstack --tail 30
# Causas comunes:
# 1. Puerto 4566 en uso
lsof -i :4566
# Si hay otro proceso, mátalo o cambia el puerto
# 2. Docker daemon no está corriendo
docker info
# Si falla, arranca Docker Desktop
# 3. Imagen no descargada
docker pull localstack/localstack:latest
Si el container está en "health: starting"
# LocalStack tarda 10-20 segundos en arrancar
# Espera y verifica:
for i in {1..10}; do
STATUS=$(docker compose ps localstack --format json | python3 -c "import json,sys; data=json.load(sys.stdin); print(data.get('Health','unknown'))" 2>/dev/null || echo "checking")
echo "Intento $i: $STATUS"
if [ "$STATUS" = "healthy" ]; then
echo "LocalStack ready"
break
fi
sleep 3
done
Nivel 2: ¿Los Servicios Están Disponibles?
Health endpoint
# El health endpoint muestra el estado de cada servicio
curl -s http://localhost:4566/_localstack/health | python3 -m json.tool
# Output esperado:
# {
# "services": {
# "s3": "available",
# "lambda": "available"
# },
# "version": "3.x.x"
# }
Interpretar los estados
"available" → Servicio listo para usar
"running" → Servicio arrancando (espera unos segundos)
"disabled" → No incluido en SERVICES
"error" → Problema al inicializar (ver logs)
(no aparece) → No está configurado en SERVICES
Script de health check completo
# health_check.py
import requests
import json
import sys
ENDPOINT = "http://localhost:4566"
def check_localstack():
"""Diagnóstico completo de LocalStack."""
print("=== LocalStack Health Check ===\n")
# 1. Conectividad
try:
resp = requests.get(f"{ENDPOINT}/_localstack/health", timeout=5)
health = resp.json()
print(f"Versión: {health.get('version', 'unknown')}")
except requests.ConnectionError:
print("ERROR: No se puede conectar a LocalStack")
print(f" Verificar: docker compose ps localstack")
print(f" Verificar: puerto 4566 accesible")
sys.exit(1)
except Exception as e:
print(f"ERROR: {e}")
sys.exit(1)
# 2. Servicios
services = health.get("services", {})
print(f"\nServicios ({len(services)}):")
all_ok = True
for name, status in services.items():
icon = "✅" if status == "available" else "❌"
print(f" {icon} {name}: {status}")
if status != "available":
all_ok = False
# 3. Verificar S3
print("\nVerificación S3:")
try:
import boto3
s3 = boto3.client(
"s3", endpoint_url=ENDPOINT,
aws_access_key_id="test", aws_secret_access_key="test",
region_name="us-east-1",
)
buckets = s3.list_buckets()
print(f" ✅ S3 responde — {len(buckets['Buckets'])} buckets")
except Exception as e:
print(f" ❌ S3 error: {e}")
all_ok = False
# 4. Verificar Lambda
print("\nVerificación Lambda:")
try:
lam = boto3.client(
"lambda", endpoint_url=ENDPOINT,
aws_access_key_id="test", aws_secret_access_key="test",
region_name="us-east-1",
)
functions = lam.list_functions()
print(f" ✅ Lambda responde — {len(functions['Functions'])} funciones")
except Exception as e:
print(f" ❌ Lambda error: {e}")
all_ok = False
# Resultado
print(f"\n{'='*30}")
if all_ok:
print("Estado: HEALTHY — todo funcionando")
else:
print("Estado: DEGRADED — revisar servicios con errores")
return all_ok
check_localstack()
Nivel 3: Leer Logs del Container
Logs básicos
# Últimas 50 líneas
docker compose logs localstack --tail 50
# Seguir en tiempo real (útil mientras debuggeas)
docker compose logs localstack -f
# Solo errores
docker compose logs localstack 2>&1 | grep -i "error\|exception\|failed"
# Logs con timestamp
docker compose logs localstack --timestamps --tail 20
Habilitar DEBUG mode
Si los logs normales no dan suficiente información, habilita DEBUG:
# docker-compose.yml
localstack:
environment:
- DEBUG=1 # Cambiar de 0 a 1
# Reiniciar para aplicar
docker compose restart localstack
# Ahora los logs muestran detalles de cada request
docker compose logs localstack -f
# Verás cada request HTTP que llega a LocalStack
Vuelve a DEBUG=0 cuando termines — DEBUG genera muchos logs.
Logs de Lambda específicamente
# Cuando invocas una Lambda, LocalStack loguea la ejecución
# Busca las líneas relevantes:
docker compose logs localstack 2>&1 | grep -A5 "lambda.*invoke\|lambda.*create"
# Si LAMBDA_EXECUTOR=docker, las Lambdas corren en containers separados
# Lista containers de Lambda:
docker ps | grep "lambda"
# Ver logs de un container Lambda específico:
docker logs <container-id>
Nivel 4: Errores Comunes y Soluciones
Error 1: "Unable to connect to endpoint URL"
botocore.exceptions.EndpointConnectionError:
Could not connect to the endpoint URL: "http://localhost:4566/"
Causa: LocalStack no está corriendo o el puerto no es accesible.
# Diagnóstico:
docker compose ps localstack
curl http://localhost:4566/_localstack/health
# Solución:
docker compose up -d localstack
# Esperar al health check
sleep 15
curl http://localhost:4566/_localstack/health
Error 2: "NoSuchBucket"
botocore.exceptions.ClientError: An error occurred (NoSuchBucket)
when calling the PutObject operation
Causa: El bucket no existe. En LocalStack Community, los buckets se pierden al reiniciar.
# Diagnóstico:
awslocal s3 ls
# ¿Tu bucket aparece?
# Solución:
awslocal s3 mb s3://ai-input
awslocal s3 mb s3://ai-output
# Prevención: usa init scripts
# init-scripts/setup.sh crea los buckets al arrancar
Error 3: "ResourceNotFoundException" (Lambda)
botocore.exceptions.ClientError: An error occurred (ResourceNotFoundException)
when calling the Invoke operation: Function not found
Causa: La función Lambda no existe o el nombre no coincide.
# Diagnóstico:
awslocal lambda list-functions --query 'Functions[].FunctionName'
# ¿Tu función aparece? ¿El nombre coincide exactamente?
# Lambda names son case-sensitive
# Solución:
# Si no existe, despliégala:
awslocal lambda create-function --function-name ai-processor ...
# Si el nombre no coincide, usa el nombre correcto
Error 4: "Lambda timeout"
Task timed out after X seconds
Causa: Tu handler tarda más que el timeout configurado.
# Diagnóstico:
awslocal lambda get-function-configuration \
--function-name ai-processor \
--query 'Timeout'
# ¿Es suficiente para tu AI workload?
# Solución:
awslocal lambda update-function-configuration \
--function-name ai-processor \
--timeout 120
Error 5: "ModuleNotFoundError en Lambda"
[ERROR] Runtime.ImportModuleError:
Unable to import module 'handler': No module named 'openai'
Causa: Las dependencias no están en el zip de deployment.
# Diagnóstico:
unzip -l lambda/handler.zip | head -20
# ¿Aparecen los módulos de openai?
# Solución: re-empaquetar con dependencias
pip install -r lambda/requirements.txt -t lambda/package/
cp lambda/handler.py lambda/package/
cd lambda/package && zip -r ../handler.zip . && cd ../..
# Actualizar:
awslocal lambda update-function-code \
--function-name ai-processor \
--zip-file fileb://lambda/handler.zip
Error 6: "Lambda no puede conectar a S3"
botocore.exceptions.EndpointConnectionError:
Could not connect to the endpoint URL: "http://localhost:4566/"
Causa: Lambda corre en un container Docker separado. localhost dentro de ese container no es tu máquina.
# Diagnóstico:
awslocal lambda get-function-configuration \
--function-name ai-processor \
--query 'Environment.Variables.AWS_ENDPOINT_URL'
# Si dice "http://localhost:4566" y LAMBDA_EXECUTOR=docker → ese es el problema
# Solución:
awslocal lambda update-function-configuration \
--function-name ai-processor \
--environment "Variables={
AWS_ENDPOINT_URL=http://host.docker.internal:4566,
OPENAI_API_KEY=${OPENAI_API_KEY},
MODEL_NAME=gpt-4o-mini
}"
# host.docker.internal resuelve al host desde dentro de containers
# Si LAMBDA_EXECUTOR=local, localhost:4566 sí funciona
Error 7: "Port 4566 already in use"
Error starting container: port is already allocated
Causa: Otro proceso o container usa el puerto 4566.
# Diagnóstico:
lsof -i :4566
# Solución 1: matar el proceso que ocupa el puerto
kill -9 <PID>
# Solución 2: cambiar el puerto en Compose
localstack:
ports:
- "4567:4566"
# Y actualizar AWS_ENDPOINT_URL a http://localhost:4567
# Solución 3: si es otro container LocalStack
docker stop localstack-test && docker rm localstack-test
Herramientas de Diagnóstico
Script de diagnóstico completo
#!/bin/bash
# scripts/diagnose.sh — Diagnóstico completo de LocalStack
echo "=== DIAGNÓSTICO LOCALSTACK ==="
echo ""
echo "1. Docker"
docker --version
docker compose version
echo ""
echo "2. Container Status"
docker compose ps
echo ""
echo "3. Health Check"
HEALTH=$(curl -s http://localhost:4566/_localstack/health 2>/dev/null)
if [ $? -ne 0 ]; then
echo " ERROR: No se puede conectar a LocalStack"
echo " → Verificar: docker compose up -d localstack"
exit 1
fi
echo "$HEALTH" | python3 -m json.tool
echo ""
echo "4. S3 Buckets"
awslocal s3 ls 2>/dev/null || echo " S3 no disponible"
echo ""
echo "5. Lambda Functions"
awslocal lambda list-functions --query 'Functions[].{Name:FunctionName,Runtime:Runtime,Memory:MemorySize}' --output table 2>/dev/null || echo " Lambda no disponible"
echo ""
echo "6. Últimos errores en logs"
docker compose logs localstack --tail 50 2>&1 | grep -i "error\|exception\|failed" | tail -5
if [ $? -ne 0 ]; then
echo " No se encontraron errores recientes"
fi
echo ""
echo "7. Recursos Docker"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" 2>/dev/null | grep -E "NAME|localstack"
echo ""
echo "8. Puerto 4566"
lsof -i :4566 2>/dev/null | head -5
echo ""
echo "=== FIN DIAGNÓSTICO ==="
Verificar una operación específica
# Si una operación falla, pruébala aislada con awslocal:
# S3: crear bucket
awslocal s3 mb s3://test-debug 2>&1
# Si funciona aquí pero no en Python → problema en tu código
# Si falla aquí también → problema en LocalStack
# Lambda: invocar
awslocal lambda invoke \
--function-name ai-processor \
--payload '{"document_key": "test.txt"}' \
--cli-binary-format raw-in-base64-out \
/tmp/debug.json 2>&1
cat /tmp/debug.json | python3 -m json.tool
# Esto aísla si el problema es tu código o LocalStack
Monitorear requests en tiempo real
# Con DEBUG=1, LocalStack loguea cada request
# En una terminal:
docker compose logs localstack -f
# En otra terminal, ejecuta tu operación:
awslocal s3 ls
# En los logs verás la request HTTP exacta
# Esto ayuda a entender qué está enviando tu código
Problemas de Rendimiento
LocalStack consume mucha RAM
# Verificar uso de memoria
docker stats --no-stream localstack
# Si consume >2GB:
# 1. Reduce los servicios habilitados
environment:
- SERVICES=s3,lambda # Solo lo necesario
# 2. Si LAMBDA_EXECUTOR=docker, cada Lambda crea un container
# Limpia containers viejos:
docker container prune -f
LocalStack tarda mucho en arrancar
# Causa: muchos servicios habilitados
# Solución: solo los que usas
environment:
- SERVICES=s3,lambda
# No: SERVICES= (todos)
# El arranque con s3,lambda tarda ~10s
# El arranque con todos los servicios puede tardar ~30-60s
Lambda tarda mucho en ejecutar
# Con LAMBDA_EXECUTOR=docker, la primera invocación es lenta
# porque crea un container Docker para la función
# Para iteración rápida, usa LAMBDA_EXECUTOR=local
environment:
- LAMBDA_EXECUTOR=local
# Ejecuta en el proceso de LocalStack — más rápido, menos aislado
Ejercicios
Ejercicio 1: Crear el script de diagnóstico
Copia el script diagnose.sh de arriba, ejecútalo, e interpreta cada sección del output. Si algo falla, resuélvelo.
Ver solución
# Crear el script
mkdir -p scripts
cat > scripts/diagnose.sh << 'SCRIPT'
#!/bin/bash
echo "=== DIAGNÓSTICO LOCALSTACK ==="
echo "1. Container Status"
docker compose ps
echo "2. Health Check"
curl -s http://localhost:4566/_localstack/health | python3 -m json.tool 2>/dev/null || echo "LocalStack no responde"
echo "3. S3"
awslocal s3 ls 2>/dev/null || echo "S3 no disponible"
echo "4. Lambda"
awslocal lambda list-functions --query 'Functions[].FunctionName' --output text 2>/dev/null || echo "Lambda no disponible"
echo "5. Errores recientes"
docker compose logs localstack --tail 20 2>&1 | grep -i "error" | tail -3 || echo "Sin errores"
echo "=== FIN ==="
SCRIPT
chmod +x scripts/diagnose.sh
./scripts/diagnose.sh
Interpretación:
- Si Container Status no muestra localstack →
docker compose up -d - Si Health Check falla → LocalStack no arrancó, ver logs
- Si S3/Lambda no disponible → verificar SERVICES en Compose
- Si hay errores → leer el mensaje y buscar en la tabla de esta cápsula
Ejercicio 2: Provocar y resolver cada error
Provoca intencionalmente 3 errores comunes y resuélvelos:
- Invocar una Lambda que no existe
- Hacer put_object a un bucket que no existe
- Hacer una request con el endpoint incorrecto
Ver solución
import boto3
import json
endpoint = "http://localhost:4566"
kwargs = {
"endpoint_url": endpoint,
"aws_access_key_id": "test",
"aws_secret_access_key": "test",
"region_name": "us-east-1",
}
# Error 1: Lambda que no existe
print("--- Error 1: Lambda inexistente ---")
lam = boto3.client("lambda", **kwargs)
try:
lam.invoke(FunctionName="no-existe", Payload=b'{}')
except Exception as e:
print(f" Error: {type(e).__name__}: {e}")
print(" Solución: verificar nombre con awslocal lambda list-functions")
# Error 2: Bucket que no existe
print("\n--- Error 2: Bucket inexistente ---")
s3 = boto3.client("s3", **kwargs)
try:
s3.put_object(Bucket="no-existe", Key="test.txt", Body=b"data")
except Exception as e:
print(f" Error: {type(e).__name__}: {e}")
print(" Solución: crear bucket con awslocal s3 mb s3://no-existe")
# Error 3: Endpoint incorrecto
print("\n--- Error 3: Endpoint incorrecto ---")
bad_s3 = boto3.client(
"s3",
endpoint_url="http://localhost:9999",
aws_access_key_id="test",
aws_secret_access_key="test",
region_name="us-east-1",
)
try:
bad_s3.list_buckets()
except Exception as e:
print(f" Error: {type(e).__name__}: {e}")
print(" Solución: verificar que AWS_ENDPOINT_URL=http://localhost:4566")
Ejercicio 3: Health check con alertas
Extiende el script health_check.py para que verifique:
- Que los buckets
ai-inputyai-outputexisten - Que la función
ai-processorestá desplegada - Que una invocación de prueba retorna status 200
Ver solución
import boto3
import json
import sys
ENDPOINT = "http://localhost:4566"
KWARGS = {
"endpoint_url": ENDPOINT,
"aws_access_key_id": "test",
"aws_secret_access_key": "test",
"region_name": "us-east-1",
}
checks = {}
# 1. Conectividad
try:
import requests
resp = requests.get(f"{ENDPOINT}/_localstack/health", timeout=5)
checks["connectivity"] = "PASS"
except Exception:
checks["connectivity"] = "FAIL — LocalStack no responde"
for name, result in checks.items():
print(f" {'✅' if result == 'PASS' else '❌'} {name}: {result}")
sys.exit(1)
# 2. Buckets requeridos
s3 = boto3.client("s3", **KWARGS)
for bucket in ["ai-input", "ai-output"]:
try:
s3.head_bucket(Bucket=bucket)
checks[f"bucket_{bucket}"] = "PASS"
except Exception:
checks[f"bucket_{bucket}"] = f"FAIL — bucket '{bucket}' no existe"
# 3. Lambda function
lam = boto3.client("lambda", **KWARGS)
try:
lam.get_function(FunctionName="ai-processor")
checks["lambda_ai_processor"] = "PASS"
except Exception:
checks["lambda_ai_processor"] = "FAIL — función 'ai-processor' no existe"
# 4. Invocación de prueba (solo si la función existe)
if checks.get("lambda_ai_processor") == "PASS":
try:
resp = lam.invoke(
FunctionName="ai-processor",
InvocationType="RequestResponse",
Payload=json.dumps({"document_key": "test-health.txt"}),
)
result = json.loads(resp["Payload"].read())
status = result.get("statusCode", 0)
checks["lambda_invoke"] = f"PASS (status={status})" if status in [200, 400, 404] else f"FAIL (status={status})"
except Exception as e:
checks["lambda_invoke"] = f"FAIL — {e}"
# Report
print("=== Pipeline Health Check ===\n")
all_pass = True
for name, result in checks.items():
icon = "✅" if "PASS" in result else "❌"
print(f" {icon} {name}: {result}")
if "FAIL" in result:
all_pass = False
print(f"\nEstado: {'HEALTHY' if all_pass else 'NEEDS ATTENTION'}")
Ejercicio 4: Automatizar recovery
Crea un script que detecte problemas y los resuelva automáticamente: si los buckets no existen, los crea. Si la Lambda no está desplegada, la despliega. Si LocalStack no responde, lo reinicia.
Ver solución
#!/bin/bash
# scripts/auto-recover.sh
set -e
echo "=== Auto-Recovery ==="
# 1. ¿LocalStack responde?
echo "Verificando LocalStack..."
if ! curl -s http://localhost:4566/_localstack/health > /dev/null 2>&1; then
echo " LocalStack no responde — reiniciando..."
docker compose restart localstack
echo " Esperando 20 segundos..."
sleep 20
if ! curl -s http://localhost:4566/_localstack/health > /dev/null 2>&1; then
echo " FATAL: LocalStack no arranca. Revisar docker compose logs localstack"
exit 1
fi
fi
echo " OK"
# 2. ¿Buckets existen?
echo "Verificando buckets..."
for BUCKET in ai-input ai-output; do
if ! awslocal s3 ls "s3://$BUCKET" > /dev/null 2>&1; then
echo " Creando bucket: $BUCKET"
awslocal s3 mb "s3://$BUCKET"
else
echo " Bucket $BUCKET: OK"
fi
done
# 3. ¿Lambda existe?
echo "Verificando Lambda..."
if ! awslocal lambda get-function --function-name ai-processor > /dev/null 2>&1; then
echo " Lambda ai-processor no existe"
if [ -f "lambda/processor.zip" ]; then
echo " Desplegando desde lambda/processor.zip..."
awslocal lambda create-function \
--function-name ai-processor \
--runtime python3.11 \
--handler processor.handler \
--zip-file fileb://lambda/processor.zip \
--role arn:aws:iam::000000000000:role/lambda-role \
--timeout 90 --memory-size 768 \
--environment "Variables={OPENAI_API_KEY=${OPENAI_API_KEY},MODEL_NAME=gpt-4o-mini,INPUT_BUCKET=ai-input,OUTPUT_BUCKET=ai-output,AWS_ENDPOINT_URL=http://host.docker.internal:4566}" > /dev/null
echo " Lambda desplegada"
else
echo " WARN: lambda/processor.zip no encontrado — despliega manualmente"
fi
else
echo " Lambda ai-processor: OK"
fi
echo ""
echo "=== Recovery completo ==="
Troubleshooting Adicional
"LocalStack funciona pero mi app en Docker Compose no conecta"
# Dentro de Compose, usa el hostname del servicio, no localhost
# Tu app debe usar: http://localstack:4566
# NO: http://localhost:4566
# Verificar:
docker compose exec api env | grep AWS_ENDPOINT
# Debe mostrar: http://localstack:4566
"Los datos se pierden al reiniciar"
# Community Edition: persistence limitada
# Solución 1: volumen Docker
volumes:
- localstack_data:/var/lib/localstack
# Solución 2: init scripts (recomendado)
# Recrea todo al arrancar automáticamente
volumes:
- ./init-scripts:/etc/localstack/init/ready.d
"awslocal retorna 'command not found'"
# Instalar
pip install awscli-local
# Si pip no está en PATH:
python -m pip install awscli-local
# Alternativa: usar aws con --endpoint-url
aws --endpoint-url=http://localhost:4566 s3 ls
Resumen
- El flujo de diagnóstico es: container corriendo → servicio disponible → request llega → error en código vs LocalStack → error conocido.
curl /_localstack/healthes tu primer comando cuando algo falla.docker compose logs localstackmuestra qué está pasando internamente.DEBUG=1activa logs detallados de cada request (útil pero verbose).- Los errores más comunes: bucket no existe, Lambda no desplegada, endpoint incorrecto (localhost vs host.docker.internal), dependencias no empaquetadas.
- Scripts de diagnóstico automatizan la verificación y ahorran tiempo.
- Auto-recovery detecta y resuelve problemas comunes sin intervención manual.
Recursos Adicionales
- LocalStack Troubleshooting Guide — Guía oficial de troubleshooting
- LocalStack GitHub Issues — Issues conocidos y soluciones
- Docker Compose Logs — Referencia de
docker compose logs - LocalStack Internal Endpoints — Endpoints de diagnóstico
- Docker Debug Commands — Comandos de debugging Docker
- boto3 Error Handling — Manejo de errores en boto3