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

AspectoZipContainer
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áximo50MB zip / 250MB unzipped10GB
Herramientas necesariaspip, zipDocker, ECR
CI/CD complejidadBajaMedia (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

EscenarioZipContainer
Solo openai SDK (~5MB)✅ IdealOverkill
openai + anthropic (~12MB)✅ FuncionaOverkill
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

AspectoZipContainer
Testing localSAM CLI (sim Lambda)Docker run (igual que producción)
Debuggingsam local invokedocker run + logs
Consistencia local/prodMedia (pip puede diferir)Alta (mismo container)
Hot reloadNo nativoVolume mount en dev
Familiaridad equipoBaja (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 AIRecomendaciónRazón
API que invoca OpenAI/AnthropicZipSDK ligero, build rápido
API con LangChain simpleContainerLangChain + deps ~80MB
RAG con embeddings localesContainerNecesita numpy, posiblemente torch
Procesamiento de imágenes con IAContainerPillow, posiblemente ffmpeg
Clasificador con sklearnContainersklearn + 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 deploy para ambos modos.
  • La diferencia en warm starts es cero — el impacto es solo en cold starts.

Recursos Adicionales

  1. Lambda Deployment Packages — Zip — Documentación oficial de zip packaging
  2. Lambda Container Images — Documentación de container deployment
  3. AWS ECR — Pushing Images — Push de imágenes a ECR
  4. Lambda Base Images — Imágenes base oficiales de Lambda
  5. SAM Build Reference — SAM build para zip y container
  6. Docker Multi-stage Builds — Optimización de imágenes Docker
  7. Lambda Package Size Limits — Límites oficiales de tamaño