Lab: Solución de problemas Lambda
Objetivo
Diagnosticar y corregir funciones Lambda defectuosas utilizando un enfoque metódico.
Ejercicio 1: Timeout
Código problemático
# buggy_timeout.py
import time
import json
def lambda_handler(event, context):
"""
Cette fonction a un problème de timeout
"""
print("Starting processing...")
# Simulation d'un traitement long
data = event.get('data', [])
for item in data:
# Traitement très lent
time.sleep(2) # 2 secondes par item !
print(f"Processed: {item}")
return {
'statusCode': 200,
'body': json.dumps({'processed': len(data)})
}
Configuración
- Timeout: 3 segundos
- Payload:
{"data": ["a", "b", "c", "d", "e"]}
Preguntas
- ¿Por qué falla esta función?
- Calcule el tiempo total necesario
- Proponga 2 soluciones diferentes
💡 Solución
1. ¿Por qué?:
- 5 ítems × 2 segundos = 10 segundos mínimo
- Timeout = 3 segundos
- La función se interrumpe antes de terminar
2. Tiempo necesario:
- Procesamiento: 5 × 2s = 10s
- Overhead Lambda: ~100ms
- Total: ~10.1 segundos
3. Soluciones:
Solución A: Aumentar el timeout
aws lambda update-function-configuration \
--function-name buggy-timeout \
--timeout 30
Solución B: Optimizar el código
import json
from concurrent.futures import ThreadPoolExecutor
def process_item(item):
# Tratamiento (simulado con sleep reducido)
return f"Processed: {item}"
def lambda_handler(event, context):
data = event.get('data', [])
# Procesamiento en paralelo
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(process_item, data))
return {
'statusCode': 200,
'body': json.dumps({'results': results})
}
Ejercicio 2: Error de permisos
Código
# buggy_permissions.py
import json
import boto3
def lambda_handler(event, context):
"""
Cette fonction tente de lire depuis S3
"""
s3 = boto3.client('s3')
bucket = event['bucket']
key = event['key']
try:
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')
return {
'statusCode': 200,
'body': json.dumps({'content': content[:100]})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
Error recibido
{
"statusCode": 500,
"body": "{\"error\": \"An error occurred (AccessDenied) when calling the GetObject operation: Access Denied\"}"
}
Preguntas
- ¿Cuál es la causa del error?
- ¿Qué política IAM se debe agregar?
- Escriba el comando AWS CLI para corregir
💡 Solución
1. Causa:
El rol de ejecución de Lambda no tiene el permiso s3:GetObject
2. Política IAM a agregar:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
3. Comandos CLI:
# Crear la política
cat > s3-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}
EOF
# Adjuntar al rol
aws iam put-role-policy \
--role-name lambda-role \
--policy-name S3ReadAccess \
--policy-document file://s3-policy.json
Ejercicio 3: Out of Memory
Código problemático
# buggy_memory.py
import json
def lambda_handler(event, context):
"""
Cette fonction a un problème de mémoire
"""
size = event.get('size', 1000000)
# Créer une énorme liste en mémoire
data = []
for i in range(size):
data.append({
'id': i,
'name': f'Item {i}',
'description': 'A' * 1000 # 1KB par item
})
return {
'statusCode': 200,
'body': json.dumps({'count': len(data)})
}
Configuración
- Memoria: 128 MB
- Payload:
{"size": 100000}(100K ítems × ~1KB = ~100MB)
Preguntas
- ¿Por qué falla la función?
- ¿Qué memoria recomienda?
- ¿Cómo optimizar sin aumentar la memoria?
💡 Solución
1. ¿Por qué?:
- 100,000 ítems × ~1KB = ~100MB de datos
- Overhead Python/Lambda: ~30-50MB
- Total: ~130-150MB > 128MB configurados
2. Memoria recomendada:
- Mínimo: 256 MB (con margen)
- Recomendado: 512 MB (por seguridad)
aws lambda update-function-configuration \
--function-name buggy-memory \
--memory-size 512
3. Optimización sin más memoria:
import json
def lambda_handler(event, context):
size = event.get('size', 1000000)
# Procesamiento por lotes con generador
def process_batch(start, end):
for i in range(start, end):
yield {'id': i, 'processed': True}
# Procesar en chunks de 10000
batch_size = 10000
total_processed = 0
for batch_start in range(0, size, batch_size):
batch_end = min(batch_start + batch_size, size)
# Procesar el lote (no mantiene todo en memoria)
for item in process_batch(batch_start, batch_end):
total_processed += 1
# Liberar memoria entre lotes
return {
'statusCode': 200,
'body': json.dumps({'processed': total_processed})
}
Ejercicio 4: Handler Not Found
Configuración
- Archivo:
my_function.py - Handler configurado:
lambda_function.handler
Error
{
"errorMessage": "Unable to import module 'lambda_function': No module named 'lambda_function'",
"errorType": "Runtime.ImportModuleError"
}
Preguntas
- ¿Cuál es el error de configuración?
- ¿Cuál es el valor correcto del handler?
- Liste 3 errores comunes de handler
💡 Solución
1. Error:
El handler está configurado como lambda_function.handler pero el archivo se llama my_function.py
2. Handler correcto:
my_function.handler
Formato: <nombre_archivo_sin_extensión>.<nombre_función>
3. Errores comunes:
- Nombre de archivo incorrecto (
lambda_functionvsmy_function) - Nombre de función incorrecto (
handlervslambda_handler) - Archivo en un subdirectorio (
src/handler.mainen lugar dehandler.main) - Extensión incluida por error (
handler.py.main) - Mayúsculas/minúsculas incorrectas (
Handlervshandler)
Ejercicio 5: Diagnóstico completo
Escenario
Una función Lambda retorna errores intermitentes. Aquí la información disponible:
Logs CloudWatch:
START RequestId: abc-123
[INFO] Connecting to database...
END RequestId: abc-123
REPORT RequestId: abc-123 Duration: 29987.45 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 89 MB Init Duration: 1234.56 ms
START RequestId: def-456
[INFO] Connecting to database...
[ERROR] Connection timed out
END RequestId: def-456
REPORT RequestId: def-456 Duration: 30000.00 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 67 MB
Configuración:
- Timeout: 30 segundos
- Memoria: 256 MB
- VPC: Sí (subred privada)
Preguntas
Complete la tabla de diagnóstico:
| Elemento | Observación | Problema Potencial | Acción |
|---|---|---|---|
| Duration | ? | ? | ? |
| Memory | ? | ? | ? |
| Init Duration | ? | ? | ? |
| Error message | ? | ? | ? |
| VPC | ? | ? | ? |
💡 Solución
| Elemento | Observación | Problema Potencial | Acción |
|---|---|---|---|
| Duration | ~30s (timeout) | Conexión a BD lenta/bloqueada | Timeout BD + retry |
| Memory | 67-89 MB / 256 MB | OK, sin problema de memoria | - |
| Init Duration | 1234ms | Cold start elevado | Optimizar o Provisioned |
| Error message | Connection timed out | BD inaccesible | Verificar SG, NAT |
| VPC | Subred privada | ¿Sin NAT para salida a Internet? | Agregar NAT Gateway |
Diagnóstico: La función está en una VPC privada sin NAT Gateway. No puede acceder a la base de datos externa.
Soluciones:
- Agregar un NAT Gateway a la subred privada
- O poner la BD en la misma VPC
- O usar VPC Endpoints si la BD es un servicio AWS
- Agregar timeouts de conexión en el código
Ejercicio 6: Escribir logs de calidad
Código antes (logs deficientes)
def lambda_handler(event, context):
print("Starting")
# ... code ...
print("Done")
return result
Ejercicio
Reescriba el código con logs de calidad que faciliten la solución de problemas:
💡 Solución
import json
import logging
# Configuración del logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
# Log estructurado de la entrada
logger.info(json.dumps({
'action': 'start',
'request_id': context.aws_request_id,
'event_keys': list(event.keys()),
'remaining_time_ms': context.get_remaining_time_in_millis()
}))
try:
# Su lógica
user_id = event.get('user_id')
logger.info(f"Processing user: {user_id}")
result = process_user(user_id)
# Log de éxito
logger.info(json.dumps({
'action': 'complete',
'request_id': context.aws_request_id,
'user_id': user_id,
'status': 'success'
}))
return {'statusCode': 200, 'body': json.dumps(result)}
except Exception as e:
# Log de error detallado
logger.error(json.dumps({
'action': 'error',
'request_id': context.aws_request_id,
'error_type': type(e).__name__,
'error_message': str(e),
'user_id': event.get('user_id')
}), exc_info=True)
return {'statusCode': 500, 'body': json.dumps({'error': str(e)})}
Entregable
Documento a producir
- Soluciones a los 6 ejercicios
- Tabla de diagnóstico completada
- Código corregido para cada bug
- Recomendaciones de buenas prácticas
Criterios de evaluación
| Criterio | Puntos |
|---|---|
| Diagnóstico correcto | /25 |
| Soluciones apropiadas | /25 |
| Código corregido funcional | /25 |
| Calidad de los logs | /15 |
| Documentación | /10 |
| Total | /100 |
Siguiente semana: MÓDULO 13 - Observabilidad