Módulo 3: Serverless & Lambda for AI
3. Container vs Zip Deployment
Descripción
En esta cápsula vas a aprender las dos formas de desplegar una función Lambda — zip package y container image — con foco en AI workloads. No es una comparación abstracta: vas a hacer el deployment de ambas formas con la misma función AI y medir las diferencias en tiempo de build, cold start, y gestión de dependencias. Al terminar, sabrás exactamente cuándo usar cada una para tu caso.
Contexto: En la cápsula anterior escribiste tu primer Lambda para AI con packaging básico. Aquí profundizas en la decisión más importante de packaging: ¿zip o container? Para una función que solo usa el SDK de OpenAI (~5MB), zip es suficiente. Para una función con LangChain, numpy, o dependencias compiladas, container es la única opción viable. Pero no es solo tamaño — cold starts, velocidad de build, y experiencia de desarrollo también cambian.
Las Dos Formas de Desplegar Lambda
Overview rápido
Zip Package:
├── Tu código + dependencias en un .zip
├── Subes el .zip a Lambda directamente
├── Límite: 50MB comprimido / 250MB descomprimido
├── Cold start: más rápido (Lambda cachea el zip)
└── Ideal para: dependencias ligeras (openai, httpx)
Container Image:
├── Dockerfile basado en imagen Lambda
├── Buildeas imagen, subes a ECR (registry)
├── Límite: 10GB de imagen
├── Cold start: más lento (pull de imagen)
└── Ideal para: dependencias pesadas (langchain, numpy, pandas)
La analogía
Piensa en las dos opciones como dos formas de entregar ingredientes a una cocina:
- Zip es una bolsa de supermercado. Rápida de preparar, fácil de cargar, pero tiene límite de peso. Si intentas meter 50 ingredientes, la bolsa se rompe.
- Container es una caja de mudanza. Más grande, más pesada, tarda más en cargar, pero cabe todo lo que necesites. Incluso puedes poner un refrigerador dentro.
Zip Deployment: Step-by-Step
Cuándo usar zip
- ✅ Dependencias totales < 50MB comprimido
- ✅ Solo SDKs de API (openai, anthropic, httpx)
- ✅ No necesitas librerías con binarios compilados (numpy en Linux)
- ✅ Quieres el cold start más rápido posible
- ✅ Deploy rápido en ciclos de desarrollo
Paso 1: Estructura del proyecto
lambda-zip/
├── handler.py
├── requirements.txt
└── build.sh
# handler.py
import json
import os
import time
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
def handler(event, context):
start = time.time()
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return _response(400, {"error": "Invalid JSON"})
prompt = body.get("prompt", "").strip()
if not prompt:
return _response(400, {"error": "prompt is required"})
try:
result = client.chat.completions.create(
model=os.environ.get("MODEL_NAME", "gpt-4o-mini"),
messages=[{"role": "user", "content": prompt}],
max_tokens=int(os.environ.get("MAX_TOKENS", "500")),
)
except Exception as e:
return _response(502, {"error": f"LLM error: {str(e)}"})
return _response(200, {
"answer": result.choices[0].message.content,
"tokens": result.usage.total_tokens,
"duration_ms": int((time.time() - start) * 1000),
})
def _response(status, body):
return {
"statusCode": status,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
"body": json.dumps(body),
}
# requirements.txt
openai>=1.0.0
Paso 2: Empaquetar dependencias
#!/bin/bash
# build.sh — Empaqueta Lambda zip para deployment
set -e
PACKAGE_DIR="package"
ZIP_FILE="deployment.zip"
rm -rf $PACKAGE_DIR $ZIP_FILE
# Instala dependencias en directorio temporal
# --platform manylinux2014_x86_64 asegura binarios compatibles con Lambda
pip install \
-r requirements.txt \
-t $PACKAGE_DIR \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
# Crea zip con dependencias
cd $PACKAGE_DIR
zip -r ../$ZIP_FILE . -x '*.pyc' '__pycache__/*'
cd ..
# Agrega handler al zip
zip $ZIP_FILE handler.py
# Verifica tamaño
echo "Package size:"
ls -lh $ZIP_FILE
unzip -l $ZIP_FILE | tail -1
echo "Done. Deploy with:"
echo "aws lambda update-function-code --function-name ai-endpoint --zip-file fileb://$ZIP_FILE"
# Ejecutar
chmod +x build.sh
./build.sh
# Output esperado:
# Package size:
# -rw-r--r-- 1 user staff 5.2M deployment.zip
# 142 files, 18234567 bytes uncompressed
Paso 3: Deploy
# Primera vez: crear función
aws lambda create-function \
--function-name ai-endpoint-zip \
--runtime python3.11 \
--handler handler.handler \
--zip-file fileb://deployment.zip \
--role arn:aws:iam::123456789:role/lambda-role \
--timeout 60 \
--memory-size 512 \
--environment "Variables={OPENAI_API_KEY=sk-xxx,MODEL_NAME=gpt-4o-mini}"
# Actualizar código existente
aws lambda update-function-code \
--function-name ai-endpoint-zip \
--zip-file fileb://deployment.zip
Paso 4: Verificar
# Invocación de test
aws lambda invoke \
--function-name ai-endpoint-zip \
--payload '{"body": "{\"prompt\": \"What is Lambda?\"}"}' \
output.json
cat output.json
# {"statusCode": 200, "body": "{\"answer\":\"...\",\"tokens\":42,\"duration_ms\":2100}"}
Container Deployment: Step-by-Step
Cuándo usar container
- ✅ Dependencias totales > 50MB (langchain, numpy, pandas)
- ✅ Necesitas librerías con binarios compilados
- ✅ Quieres consistencia build local = build Lambda
- ✅ Tu equipo ya trabaja con Docker (guía #15)
- ✅ Necesitas herramientas del sistema (ffmpeg, imagemagick)
Paso 1: Estructura del proyecto
lambda-container/
├── handler.py
├── requirements.txt
└── Dockerfile
# handler.py — Mismo código que zip, con dependencias extra
import json
import os
import time
from openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# LangChain chain inicializada fuera del handler
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI assistant. Be concise."),
("user", "{input}"),
])
chain = prompt_template | ChatOpenAI(
model="gpt-4o-mini",
api_key=os.environ["OPENAI_API_KEY"],
max_tokens=500,
)
def handler(event, context):
start = time.time()
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return _response(400, {"error": "Invalid JSON"})
prompt = body.get("prompt", "").strip()
use_chain = body.get("use_chain", False)
if not prompt:
return _response(400, {"error": "prompt is required"})
try:
if use_chain:
result = chain.invoke({"input": prompt})
answer = result.content
tokens = None
else:
result = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=500,
)
answer = result.choices[0].message.content
tokens = result.usage.total_tokens
except Exception as e:
return _response(502, {"error": f"AI error: {str(e)}"})
return _response(200, {
"answer": answer,
"tokens": tokens,
"used_chain": use_chain,
"duration_ms": int((time.time() - start) * 1000),
})
def _response(status, body):
return {
"statusCode": status,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
"body": json.dumps(body),
}
# requirements.txt
openai>=1.0.0
langchain>=0.2.0
langchain-openai>=0.1.0
langchain-core>=0.2.0
Paso 2: Dockerfile
# Dockerfile
# Imagen base oficial de AWS Lambda para Python
FROM public.ecr.aws/lambda/python:3.11
# Instala dependencias primero (layer caching de Docker)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copia código
COPY handler.py .
# Handler entrypoint
CMD ["handler.handler"]
Paso 3: Build y test local
# Build
docker build -t ai-endpoint-lambda .
# Verifica tamaño de imagen
docker images ai-endpoint-lambda
# REPOSITORY TAG SIZE
# ai-endpoint-lambda latest ~650MB (langchain + deps)
# Test local — Lambda Runtime Interface Emulator incluido en la imagen base
docker run -p 9000:8080 \
-e OPENAI_API_KEY=sk-xxx \
ai-endpoint-lambda
# En otra terminal:
curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
-d '{"body": "{\"prompt\": \"What is Lambda?\"}"}'
Paso 4: Push a ECR y deploy
# Crear repositorio en ECR
aws ecr create-repository --repository-name ai-endpoint-lambda
# Login a ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.us-east-1.amazonaws.com
# Tag y push
docker tag ai-endpoint-lambda:latest \
123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest
docker push \
123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest
# Crear función Lambda desde container
aws lambda create-function \
--function-name ai-endpoint-container \
--package-type Image \
--code ImageUri=123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest \
--role arn:aws:iam::123456789:role/lambda-role \
--timeout 60 \
--memory-size 512 \
--environment "Variables={OPENAI_API_KEY=sk-xxx}"
Paso 5: Verificar
aws lambda invoke \
--function-name ai-endpoint-container \
--payload '{"body": "{\"prompt\": \"What is Lambda?\"}"}' \
output.json
cat output.json
Comparación Detallada
Build y deployment
| Aspecto | Zip | Container |
|---|---|---|
| Build time (primera vez) | ~10s | ~60-120s |
| Build time (incremental) | ~5s | ~10-20s (Docker cache) |
| Deploy time | ~5s (upload zip) | ~30-60s (push imagen) |
| Tamaño máximo | 50MB zip / 250MB unzipped | 10GB |
| Herramientas necesarias | pip, zip | Docker, ECR |
| CI/CD complejidad | Baja | Media (build + push) |
Cold start
Cold start medido (misma función, diferente packaging):
Zip (openai ~5MB):
├── Init: ~800ms
├── Handler: ~2500ms (LLM call)
└── Total: ~3300ms
Container (openai ~5MB, misma función):
├── Image pull: ~1200ms
├── Init: ~900ms
├── Handler: ~2500ms (LLM call)
└── Total: ~4600ms
Container (langchain ~100MB):
├── Image pull: ~3500ms
├── Init: ~2000ms
├── Handler: ~2500ms (LLM call)
└── Total: ~8000ms
El overhead de container es ~1-2s extra en cold start por el pull de imagen. Para warm starts, la diferencia desaparece.
Gestión de dependencias
| Escenario | Zip | Container |
|---|---|---|
| Solo openai SDK (~5MB) | ✅ Ideal | Overkill |
| openai + anthropic (~12MB) | ✅ Funciona | Overkill |
| langchain + deps (~80MB) | ⚠️ Apretado (250MB limit) | ✅ Ideal |
| numpy + pandas (~120MB) | ❌ Excede límite | ✅ Funciona |
| torch (~800MB) | ❌ Imposible | ⚠️ Funciona pero cold start largo |
| Binarios del sistema (ffmpeg) | ❌ No soportado | ✅ Puedes instalar lo que quieras |
Experiencia de desarrollo
| Aspecto | Zip | Container |
|---|---|---|
| Testing local | SAM CLI (sim Lambda) | Docker run (igual que producción) |
| Debugging | sam local invoke | docker run + logs |
| Consistencia local/prod | Media (pip puede diferir) | Alta (mismo container) |
| Hot reload | No nativo | Volume mount en dev |
| Familiaridad equipo | Baja (Lambda-specific) | Alta (Docker, guía #15) |
Decision Tree: ¿Zip o Container?
¿Tus dependencias totales (unzipped) superan 250MB?
├── SÍ → Container
│ (numpy+pandas, langchain+chromadb, torch)
└── NO → ¿Necesitas binarios del sistema (ffmpeg, etc.)?
├── SÍ → Container
└── NO → ¿Tu equipo ya usa Docker en todo?
├── SÍ → Container (consistencia)
└── NO → ¿Necesitas minimizar cold start?
├── SÍ → Zip (1-2s menos en cold start)
└── NO → Zip (más simple)
Recomendación para casos AI comunes
| Tu app AI | Recomendación | Razón |
|---|---|---|
| API que invoca OpenAI/Anthropic | Zip | SDK ligero, build rápido |
| API con LangChain simple | Container | LangChain + deps ~80MB |
| RAG con embeddings locales | Container | Necesita numpy, posiblemente torch |
| Procesamiento de imágenes con IA | Container | Pillow, posiblemente ffmpeg |
| Clasificador con sklearn | Container | sklearn + numpy > 100MB |
SAM Template para Ambos Modos
Zip deployment con SAM
# template-zip.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
AIEndpointZip:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
Runtime: python3.11
CodeUri: lambda-zip/
Timeout: 60
MemorySize: 512
Environment:
Variables:
OPENAI_API_KEY: !Ref OpenAIKey
Events:
Ask:
Type: Api
Properties:
Path: /ask
Method: post
Parameters:
OpenAIKey:
Type: String
NoEcho: true
Container deployment con SAM
# template-container.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
AIEndpointContainer:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Timeout: 60
MemorySize: 512
Environment:
Variables:
OPENAI_API_KEY: !Ref OpenAIKey
Events:
Ask:
Type: Api
Properties:
Path: /ask
Method: post
Metadata:
Dockerfile: Dockerfile
DockerContext: lambda-container/
DockerTag: latest
Parameters:
OpenAIKey:
Type: String
NoEcho: true
# Deploy zip
sam build -t template-zip.yaml
sam deploy --guided
# Deploy container
sam build -t template-container.yaml
sam deploy --guided
Optimización de Tamaño de Paquete
Para zip: minimizar dependencias
# Excluir tests, docs, y archivos innecesarios
pip install openai -t package/ --no-deps
pip install httpx anyio certifi -t package/
# Excluir al empaquetar
cd package
zip -r ../deployment.zip . \
-x '*.pyc' \
-x '__pycache__/*' \
-x '*.dist-info/*' \
-x 'tests/*' \
-x '*.md' \
-x '*.txt'
Para container: multi-stage builds
# Dockerfile optimizado — multi-stage
FROM public.ecr.aws/lambda/python:3.11 AS builder
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Elimina archivos innecesarios para reducir imagen
RUN find /var/lang/lib/python3.11/site-packages -type d -name "tests" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -name "*.pyc" -delete 2>/dev/null; \
exit 0
FROM public.ecr.aws/lambda/python:3.11
COPY --from=builder /var/lang/lib/python3.11/site-packages /var/lang/lib/python3.11/site-packages
COPY handler.py .
CMD ["handler.handler"]
# Comparar tamaños
docker build -t ai-lambda-optimized -f Dockerfile.optimized .
docker build -t ai-lambda-standard -f Dockerfile .
docker images | grep ai-lambda
# ai-lambda-standard latest 650MB
# ai-lambda-optimized latest 520MB
# ~20% reducción = cold start más rápido
Troubleshooting
Problema 1: "Zip file exceeds the 50 MB limit"
# Verifica qué ocupa más espacio
du -sh package/* | sort -hr | head -10
# 25M package/numpy
# 18M package/pandas
# ...
# Opciones:
# 1. Usa container en vez de zip
# 2. Mueve dependencias grandes a layers
# 3. Elimina dependencias no necesarias
Problema 2: "Unable to import module 'handler': No module named 'X'"
# En zip: verifica la estructura del zip
unzip -l deployment.zip | head -20
# handler.py debe estar en la raíz
# Dependencias deben estar en la raíz (no en subdirectorio)
# Mal:
# deployment.zip/
# package/
# openai/
# handler.py
# Bien:
# deployment.zip/
# openai/
# handler.py
Problema 3: "Container no arranca — exec format error"
# Buildeaste para ARM (Mac M1/M2) pero Lambda usa x86_64
docker build --platform linux/amd64 -t ai-lambda .
# O en docker buildx
docker buildx build --platform linux/amd64 -t ai-lambda .
Problema 4: "Container image no se encuentra en ECR"
# Verifica que hiciste push al repo correcto
aws ecr describe-images \
--repository-name ai-endpoint-lambda
# Verifica que la URI en la función Lambda coincide
aws lambda get-function \
--function-name ai-endpoint-container \
--query 'Code.ImageUri'
Problema 5: "Build de container tarda mucho en CI/CD"
# Usa Docker layer caching en CI
# En GitHub Actions:
# - name: Build with cache
# uses: docker/build-push-action@v5
# with:
# cache-from: type=gha
# cache-to: type=gha,mode=max
# Ordena el Dockerfile: deps primero, código después
# COPY requirements.txt . ← cambia poco
# RUN pip install ... ← se cachea
# COPY handler.py . ← cambia mucho
Ejercicios Prácticos
Ejercicio 1: Deploy zip completo
Crea un zip deployment de la función AI handler (la de la cápsula 02) con el SDK de OpenAI. Verifica el tamaño del paquete y compáralo con el límite de 50MB. Invócala localmente con SAM.
Ver solución
# Estructura
mkdir -p lambda-zip-exercise
cd lambda-zip-exercise
# handler.py (del ejercicio anterior)
cat > handler.py << 'HANDLER'
import json
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "test"))
def handler(event, context):
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return {"statusCode": 400, "body": json.dumps({"error": "Invalid JSON"})}
prompt = body.get("prompt", "")
if not prompt:
return {"statusCode": 400, "body": json.dumps({"error": "prompt required"})}
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=500,
)
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"answer": response.choices[0].message.content}),
}
HANDLER
echo "openai>=1.0.0" > requirements.txt
# Build
mkdir package
pip install -r requirements.txt -t package/ \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
cd package && zip -r ../deployment.zip . && cd ..
zip deployment.zip handler.py
# Verificar
ls -lh deployment.zip
# ~5MB — bien dentro del límite de 50MB
echo "Ratio: $(ls -l deployment.zip | awk '{print $5}') bytes / 52428800 bytes limit"
echo "$(echo "scale=1; $(ls -l deployment.zip | awk '{print $5}') * 100 / 52428800" | bc)% del límite"
Ejercicio 2: Deploy container completo
Crea un container deployment con LangChain. Build la imagen, verifica el tamaño, y testea localmente con Docker.
Ver solución
mkdir -p lambda-container-exercise
cd lambda-container-exercise
# requirements.txt con langchain
cat > requirements.txt << 'EOF'
openai>=1.0.0
langchain>=0.2.0
langchain-openai>=0.1.0
EOF
# handler.py
cat > handler.py << 'HANDLER'
import json
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOpenAI(
model="gpt-4o-mini",
api_key=os.environ.get("OPENAI_API_KEY", "test"),
)
prompt = ChatPromptTemplate.from_messages([
("system", "Respond concisely in one paragraph."),
("user", "{input}"),
])
chain = prompt | llm
def handler(event, context):
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return {"statusCode": 400, "body": json.dumps({"error": "Invalid JSON"})}
user_input = body.get("prompt", "")
if not user_input:
return {"statusCode": 400, "body": json.dumps({"error": "prompt required"})}
result = chain.invoke({"input": user_input})
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"answer": result.content}),
}
HANDLER
# Dockerfile
cat > Dockerfile << 'DOCKER'
FROM public.ecr.aws/lambda/python:3.11
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY handler.py .
CMD ["handler.handler"]
DOCKER
# Build (forzar plataforma x86 para compatibilidad Lambda)
docker build --platform linux/amd64 -t ai-lambda-langchain .
# Verificar tamaño
docker images ai-lambda-langchain --format "{{.Size}}"
# ~600-700MB
# Test local
docker run -d -p 9000:8080 \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
--name lambda-test \
ai-lambda-langchain
curl -s -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
-d '{"body": "{\"prompt\": \"What is LangChain?\"}"}'
docker stop lambda-test && docker rm lambda-test
Ejercicio 3: Mide la diferencia de cold start
Despliega la misma función (solo openai SDK) en ambos formatos y mide el cold start de cada uno. Documenta los resultados.
Ver solución
# Método: usar SAM local para simular cold starts
# SAM local siempre simula cold start (crea container nuevo)
# Zip
cd lambda-zip-exercise
cat > template.yaml << 'TEMPLATE'
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
Runtime: python3.11
CodeUri: .
Timeout: 60
MemorySize: 512
TEMPLATE
cat > env.json << 'ENV'
{"TestFunction": {"OPENAI_API_KEY": "sk-test"}}
ENV
# Medir tiempo (sin llamada real a OpenAI — mide solo init)
time sam local invoke TestFunction \
--event '{"body": "{\"prompt\": \"test\"}"}' \
--env-vars env.json \
--skip-pull-image 2>&1 | tail -5
# Container
cd ../lambda-container-exercise
cat > template.yaml << 'TEMPLATE'
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
TestFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Timeout: 60
MemorySize: 512
Metadata:
Dockerfile: Dockerfile
DockerContext: .
TEMPLATE
time sam local invoke TestFunction \
--event '{"body": "{\"prompt\": \"test\"}"}' \
--env-vars env.json 2>&1 | tail -5
# Documenta resultados:
# Zip (openai only): Init ~Xs, Total ~Xs
# Container (openai): Init ~Xs, Total ~Xs
# Diferencia: ~X segundos
La diferencia típica es 1-2 segundos de cold start adicional para container vs zip con las mismas dependencias. En warm starts, la diferencia es prácticamente cero.
Ejercicio 4: Optimiza el Dockerfile con multi-stage
Toma el Dockerfile del ejercicio 2 (con LangChain) y crea una versión optimizada con multi-stage build que elimine archivos innecesarios. Compara los tamaños de imagen.
Ver solución
# Dockerfile.optimized
FROM public.ecr.aws/lambda/python:3.11 AS builder
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Limpieza agresiva de archivos no necesarios en runtime
RUN find /var/lang/lib/python3.11/site-packages -type d -name "tests" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -type d -name "test" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -type d -name "docs" -exec rm -rf {} + 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -name "*.pyc" -delete 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -name "*.pyo" -delete 2>/dev/null; \
find /var/lang/lib/python3.11/site-packages -name "*.md" -delete 2>/dev/null; \
exit 0
FROM public.ecr.aws/lambda/python:3.11
COPY --from=builder /var/lang/lib/python3.11/site-packages /var/lang/lib/python3.11/site-packages
COPY handler.py .
CMD ["handler.handler"]
# Build ambas versiones
docker build --platform linux/amd64 -t ai-lambda-standard -f Dockerfile .
docker build --platform linux/amd64 -t ai-lambda-optimized -f Dockerfile.optimized .
# Comparar
docker images --format "table {{.Repository}}\t{{.Size}}" | grep ai-lambda
# ai-lambda-standard ~650MB
# ai-lambda-optimized ~520MB
# Ahorro: ~130MB (~20%)
echo "Reducción: $(echo "scale=1; (650-520)*100/650" | bc)%"
Un 20% de reducción en tamaño de imagen se traduce directamente en cold starts más rápidos porque Lambda necesita menos tiempo para pull la imagen.
Resumen
- Zip deployment es más simple y produce cold starts más rápidos, pero tiene límite de 50MB/250MB. Ideal para SDKs ligeros (openai, anthropic).
- Container deployment soporta hasta 10GB y cualquier dependencia, pero añade 1-2s de cold start por el pull de imagen. Necesario para langchain, numpy, pandas.
- Para AI workloads, la decisión depende del tamaño de tus dependencias: si caben en zip, úsalo. Si no, container.
- Optimiza siempre: excluir tests/docs en zip, multi-stage builds en container. Menos tamaño = menos cold start.
- SAM CLI unifica el desarrollo: mismos comandos
sam build,sam deploypara ambos modos. - La diferencia en warm starts es cero — el impacto es solo en cold starts.
Recursos Adicionales
- Lambda Deployment Packages — Zip — Documentación oficial de zip packaging
- Lambda Container Images — Documentación de container deployment
- AWS ECR — Pushing Images — Push de imágenes a ECR
- Lambda Base Images — Imágenes base oficiales de Lambda
- SAM Build Reference — SAM build para zip y container
- Docker Multi-stage Builds — Optimización de imágenes Docker
- Lambda Package Size Limits — Límites oficiales de tamaño