Saltar al contenido

lección 9

Lambda: funciones serverless para datos

Triggers S3, ingesta event-driven, límites de memoria/tiempo, cold starts y patrones comunes de Lambda para pipelines de datos.

⏱ 50 min

Lambda es la pieza más elegante del puzzle serverless de AWS. La idea es brutalmente simple: escribes una función (Python, Node.js, Java...), la subes a AWS, y se ejecuta automáticamente cuando algo pasa. ¿Llegó un archivo nuevo a S3? Lambda se ejecuta. ¿Llegó un mensaje a SQS? Lambda se ejecuta. ¿Es medianoche? Lambda se ejecuta. No hay servidores que gestionar, no hay clústeres que dimensionar. Pagas solo por los milisegundos que tu función está ejecutándose.

En ingeniería de datos, Lambda es el "pegamento ligero" — ideal para tareas cortas y event-driven: validar un archivo que acaba de llegar, disparar un pipeline, transformar un JSON pequeño, enviar una notificación si los datos fallan un check de calidad. NO es para procesar 500GB de Parquet (para eso tienes Glue/EMR). Es para la lógica de orquestación y las transformaciones ligeras.

LocalStack ejecuta cada Lambda en su propio contenedor Docker, así que necesita el socket de Docker. El docker-compose de la lección de setup ya lo monta (-v /var/run/docker.sock:/var/run/docker.sock) y eso es el único requisito, en Windows, Mac y Linux: no hay variable que lo evite. Si has leído por ahí que se pone LAMBDA_EXECUTOR=local para ejecutar Lambda sin contenedores, es un consejo viejo: esa variable desapareció con el nuevo proveedor de Lambda de LocalStack 2.x y en la imagen 3.5 que usamos se ignora sin decir nada. Si la invocación falla: comprueba que Docker Desktop está arrancado y con WSL2 activo, y mira docker logs del contenedor de LocalStack, que dice qué le ha pasado. Mientras lo arreglas, get_function sí funciona, así que puedes verificar que la función se creó bien.

## Anatomía de una función Lambda

1# handler.py — una función Lambda básica
2import json
3import boto3
4
5def handler(event, context):
6 """
7 event: lo que disparó la función (datos del trigger)
8 context: metadata de la ejecución (tiempo restante, memoria, etc.)
9 """
10 # Ejemplo: trigger de S3 — un archivo nuevo llegó al bucket
11 for record in event.get('Records', []):
12 bucket = record['s3']['bucket']['name']
13 key = record['s3']['object']['key']
14 size = record['s3']['object']['size']
15
16 print(f"Nuevo archivo: s3://{bucket}/{key} ({size} bytes)")
17
18 # Lógica: validar, mover, notificar, disparar pipeline...
19 if key.endswith('.csv') and size > 0:
20 procesar_csv(bucket, key)
21 elif size == 0:
22 notificar_error(f"Archivo vacío: {key}")
23
24 return {'statusCode': 200, 'body': json.dumps('OK')}
25
26def procesar_csv(bucket, key):
27 """Ejemplo: mover CSV a zona de procesamiento."""
28 s3 = boto3.client('s3')
29 # Copiar a processed/
30 s3.copy_object(
31 Bucket=bucket,
32 Key=f"processing/{key.split('/')[-1]}",
33 CopySource={'Bucket': bucket, 'Key': key}
34 )
35 print(f"Movido a processing/")

La función handler recibe un event (quién la disparó) y un context (metadata)

## Triggers: qué puede disparar una Lambda

  • S3 Events: archivo creado, eliminado o modificado → Lambda procesa el archivo.
  • SQS: mensaje llega a la cola → Lambda lo procesa.
  • EventBridge: regla de evento (schedule, patrón) → Lambda se ejecuta.
  • API Gateway: petición HTTP → Lambda responde (APIs serverless).
  • CloudWatch Events/Schedule: cron serverless (cada hora, cada día...).
  • Step Functions: como paso dentro de un workflow.

## Límites que DEBES conocer

  • Tiempo máximo: 15 minutos. Si tu tarea tarda más, no es para Lambda.
  • Memoria: 128MB a 10GB. Más memoria = más CPU (son proporcionales).
  • Paquete desplegable: 50MB comprimido, 250MB descomprimido.
  • Concurrencia: 1000 ejecuciones simultáneas por defecto (ampliable).
  • /tmp: 512MB por defecto, configurable hasta 10GB (ajuste EphemeralStorage, en incrementos de 1MB). Se paga aparte de la memoria. Para más espacio, o para compartir estado entre invocaciones, monta EFS: es un sistema de ficheros de red y no tiene ese tope.

## Cold starts: el problema del arranque en frío

Cuando Lambda no ha ejecutado tu función recientemente, necesita levantar un contenedor nuevo: descargar tu código, inicializar el runtime, importar librerías. Esto añade 1-3 segundos extra a la primera invocación (cold start). Las siguientes invocaciones reutilizan el contenedor (warm start) y son rápidas. Para pipelines batch esto no importa. Para APIs con requisito de latencia baja, es un problema.

Consejo de senior: para reducir cold starts en Python, minimiza los imports. No hagas "import pandas" si solo necesitas json y boto3. Pandas añade ~1.5s de cold start. Si necesitas Pandas en Lambda, usa Lambda Layers con una versión precompilada.

## Crear y desplegar Lambda con boto3

1import boto3
2import json
3import zipfile
4import io
5
6lambda_client = boto3.client('lambda', endpoint_url='http://localhost:4566',
7 aws_access_key_id='test', aws_secret_access_key='test',
8 region_name='eu-west-1')
9
10# Crear el ZIP con el código de la función
11code = '''
12import json
13def handler(event, context):
14 print(f"Evento recibido: {json.dumps(event)}")
15 return {"statusCode": 200, "body": "Procesado OK"}
16'''
17
18zip_buffer = io.BytesIO()
19with zipfile.ZipFile(zip_buffer, 'w') as zf:
20 zf.writestr('handler.py', code)
21zip_buffer.seek(0)
22
23# Crear la función Lambda
24lambda_client.create_function(
25 FunctionName='procesar-archivo-raw',
26 Runtime='python3.11',
27 Role='arn:aws:iam::000000000000:role/LambdaETLRole',
28 Handler='handler.handler',
29 Code={'ZipFile': zip_buffer.read()},
30 Timeout=300, # 5 minutos máximo
31 MemorySize=256, # 256MB RAM
32 Environment={
33 'Variables': {
34 'BUCKET_OUTPUT': 'fashionstore-datalake',
35 'PREFIX_OUTPUT': 'processing/'
36 }
37 }
38)
39print("[OK] Lambda creada: procesar-archivo-raw")

Crear función Lambda programáticamente — el código va en un ZIP

No pongas secretos (passwords, API keys) en las variables de entorno de Lambda en texto plano. Usa AWS Secrets Manager o SSM Parameter Store y léelos desde el código. Las variables de entorno son visibles en la consola para cualquiera con permisos de Lambda.

El patrón más común: S3 event dispara Lambda que inicia el pipeline

Consejo de senior: Lambda + S3 events es el patrón más potente para ingesta en tiempo real. Cada archivo que llega a raw/ dispara una Lambda que lo valida, lo mueve a processing/, y arranca el pipeline. Sin polling, sin cron. Los datos se procesan SEGUNDOS después de llegar.

Regístrate para guardar tu progreso.