Pular para o conteúdo
iauaiiauai — portal de tecnologia, IA e Cloud
CloudIntermediário

Serverless ou containers para cargas de IA?

Cold start de gigabytes mata serverless. Aprenda quando cada um compensa e como construir arquitetura híbrida.

Por Equipe iauai · 15 de julho de 2026 · 10 min de leitura

Nesta página

O problema real

Um time desenha uma aplicação de IA em serverless (AWS Lambda, Google Cloud Functions). A lógica é simples: requisição chega, função invoca modelo, retorna resposta. Código fica lindo, DevOps fica fácil. Aí chega o dia da verdade: modelo quantizado em 4 bits tem 3,5 GB. Função executa, 30 segundos de cold start (buscando modelo do S3, descompactando), timeout vira. "Por quê é tão lento?" Porque serverless é otimizado pra funções leves, não pra llevar modelos de IA.

Container é o outro extremo: você gerencia tudo. Node, networking, storage, escala. Mas se vir tráfego irregular (10 requests à noite, 1000 ao meio-dia), você paga GPU 24/7 mesmo ociosa. Qual usar?

Cold start: o vilão do serverless

Serverless é estateless. Quando primeira função é invocada num horário, cloud provider instancia uma máquina virtual, baixa o container, executa. Tempo total: 5 a 30 segundos (warm start). Segunda função é mais rápida (3 a 5 segundos). Se esperar 15 minutos, próxima é fria de novo.

Modelo de 7B em quantização 4 bits = 3,5 GB. Primeira invocação:

1. Instanciar sandbox Lambda  → 3 segundos
2. Download do S3 3.5GB       → 15 segundos (fibra, sem compressão)
3. Decomprimir e carregar     → 8 segundos
4. Inicializar framework      → 4 segundos
5. TOTAL ANTES DE PROCESSAR   → 30 segundos
6. Processar prompt           → 200 ms

Cliente vê 30 segundos de latência. Timeout padrão do API Gateway é 30 segundos. Pronto, erro 504.

Solução: aumentar timeout? Máximo em Lambda é 15 minutos. Pode funcionar, mas você paga pelo tempo ocioso, e cada invocação é caro.

Serverless: quando ganha

Tráfego irregular é a situação ideal:

  • Chat de suporte ao cliente: 50 mensagens/dia em horário comercial, 0 à noite
  • Processamento de documentos: 5 uploads/dia (cada um é um PDF de 50 MB)
  • Webhook de terceiros: impredizível, às vezes 0/hora, às vezes 100/hora

Nessas situações:

Serverless Container 24/7
0 reqs → US$ 0 GPU rodando → US$ 73,60/dia
Escala automática Espera por deploy
Paga por invocação Paga por hora
Cold start < 1s (função simples) Warm (s já está rodando)

Exemplo numérico: webhook que processa 100 mensagens/dia:

Serverless:

  • 100 invocações × US$ 0,0000002 (Lambda) = US$ 0,00002
  • 1M free tier: primeira 1M/mês grátis
  • Custo: US$ 0

Container on-demand 1h/dia (ligado só quando há trabalho):

  • 1 GPU A100, 1h/dia = US$ 3,06
  • Custo: US$ 91,80 / mês

Serverless ganha de goleada.

Container: quando ganha

Tráfego previsível e contínuo:

  • Chatbot em produção: 10k requests/dia (24/7)
  • Batch processing: modelo roda todo dia 2h da manhã
  • Streaming: 1M eventos/dia vão pra modelo de classificação

Container ganha porque:

1. Cold start: 0 (container já tá quente)
2. Latência: 100-500ms constante
3. Batch: 10 requisições em 1 chamada = 100x mais throughput

Cenário: 10k requests/dia, 100ms latência média

Serverless + modelagem otimista:

  • Warm invocações: 200ms latência
  • Cold starts (10% das vezes): 30s latência
  • 10% × 30s + 90% × 0.2s = 3,18s latência média
  • Custo: 10k × US$ 0,0000002 = US$ 0,002 + storage de modelo = US$ 20/mês
  • Total: US$ 20/mês, 3.18s latência média

Container (T4 GPU, US$ 0,35/h, batching):

  • Latência: 120ms consistente
  • Throughput: 8 requests/segundo em batches de 16 (GPU T4 quantizado)
  • Custo: 24h × 30 dias × US$ 0,35 = US$ 252/mês
  • Total: US$ 252/mês, 120ms latência

Se latência importa, container vence. Se custo importa e tráfego é bursty, serverless vence.

Limites técnicos do serverless que mata IA

  1. Timeout: máximo 15 minutos (Lambda). Se modelo leva 1 minuto pra inicializar + 14 minutos processando, você tá no limite. Nenhum espaço pra erro.
  2. Memória: máximo 10 GB (Lambda). Modelo de 7B requer ~15 GB com overhead. Não cabe.
  3. Efêmero: /tmp é apagado entre invocações. Seu cache local some. Toda vez precisa recarregar.
  4. GPU: serverless não oferece GPU nativamente. Precisa de workaround (Lambda com ECS integration).

Containers não têm esses problemas. GPU é nativa, timeout é o que você quiser, memória é contígua.

Arquitetura híbrida: o melhor dos dois mundos

Combine serverless (API simples, rápida) com container (processamento pesado):

┌──────────────────────────────────────────────┐
  Cliente                                     
└──────────────┬───────────────────────────────┘
               
      ┌────────▼─────────┐
        API Gateway +   
        Lambda (HTTP)      Serverless: rápido, escalável
        Valida input    
        Enfileira job   
      └────────┬─────────┘
               
      ┌────────▼──────────────┐
        SQS / Pub-Sub           Fila: desacoplamento
        (armazena job)       
      └────────┬──────────────┘
               
    ┌──────────▼──────────────┐
      ECS / Kubernetes          Container: GPU, modelo
      Worker Pool                (1-10 workers, escala)
      (processa em batch)    
    └──────────┬──────────────┘
               
      ┌────────▼─────────────┐
        S3 / DB                Resultado persistido
        (armazena output)   
      └──────────────────────┘

Código do Lambda (Python):

import json
import boto3
import uuid

sqs = boto3.client('sqs')
QUEUE_URL = "https://sqs.sa-east-1.amazonaws.com/123/ml-jobs"

def lambda_handler(event, context):
    # Recebe prompt de chat
    body = json.loads(event['body'])
    prompt = body['prompt']
    user_id = body['user_id']
    
    # Valida (rápido)
    if len(prompt) > 10000:
        return {'statusCode': 400, 'body': 'prompt muito longo'}
    
    # Enfileira (rápido)
    job_id = str(uuid.uuid4())
    sqs.send_message(
        QueueUrl=QUEUE_URL,
        MessageBody=json.dumps({
            'job_id': job_id,
            'prompt': prompt,
            'user_id': user_id
        })
    )
    
    # Retorna imediatamente ao cliente
    return {
        'statusCode': 202,
        'body': json.dumps({'job_id': job_id})
    }

Worker (container, roda em GPU):

import json
import boto3
from transformers import pipeline
import threading
import time

sqs = boto3.client('sqs')
dynamodb = boto3.resource('dynamodb')
QUEUE_URL = "..."
results_table = dynamodb.Table('ml-results')

# Carrega modelo UMA VEZ (não a cada job)
nlp = pipeline('text-generation', model='meta-llama/Llama-2-7b-hf',
               device=0, torch_dtype='float16')

def process_job(job):
    job_id = job['job_id']
    prompt = job['prompt']
    user_id = job['user_id']
    
    try:
        # Processa (model já tá warm)
        output = nlp(prompt, max_length=100, num_return_sequences=1)
        result = output[0]['generated_text']
        
        # Persiste resultado
        results_table.put_item(Item={
            'job_id': job_id,
            'user_id': user_id,
            'result': result,
            'status': 'done'
        })
    except Exception as e:
        results_table.put_item(Item={
            'job_id': job_id,
            'user_id': user_id,
            'error': str(e),
            'status': 'failed'
        })

# Loop: pega jobs da fila, processa
while True:
    response = sqs.receive_message(
        QueueUrl=QUEUE_URL,
        MaxNumberOfMessages=8,  # Batch!
        WaitTimeSeconds=20
    )
    
    if 'Messages' in response:
        for message in response['Messages']:
            job = json.loads(message['Body'])
            process_job(job)
            
            # Deleta da fila
            sqs.delete_message(
                QueueUrl=QUEUE_URL,
                ReceiptHandle=message['ReceiptHandle']
            )
    else:
        # Sem jobs, worker fica escutando (warm)
        # Com escala automática, desliga em 5 minutos
        pass

Latência: usuário vê resposta em 2 segundos (fila + primeira batch iniciada). Resultado tá pronto em 15 segundos (processamento no container).

Custo (10k requests/dia):

  • Lambda: 10k × US$ 0,0000002 = ~US$ 2
  • SQS: negligenciável
  • Container (2 GPUs T4, 6h/dia rodando): 6 × 30 × US$ 0,70 = US$ 126
  • Total: ~US$ 130/mês

Vs. container 24/7: 24 × 30 × US$ 0,70 × 2 = US$ 1.008/mês

Economiza US$ 880.

Armadilhas comuns

Serverless pura com big model: "Rodava localmente", não roda em Lambda. Blame do provider errado.

Container ocioso 99% do tempo: 1 request por dia mas máquina ligada 24/7. Use fila + auto-scale, ou serverless mesmo.

Fila sem DLQ (Dead Letter Queue): job falha 3x, desaparece da fila, ninguém sabe. Sempre configure DLQ pra jobs que falharam.

Quando NÃO usar serverless

Modelos grandes (GPT-3, 70B parameter). Timeout vai explodir.

Tráfego contínuo e previsível. Container é mais barato.

Lógica que requer estado compartilhado entre invocações (cache, conexão aberta). Serverless reset a cada invocação.

Quando NÃO usar container

Tráfego absolutamente irregular (5-10 reqs/dia aleatoriamente). Serverless é mais barato.

Experimentos e POC de curta duração. Não vale orquestração.

Função super simples (validação, transform, webhook). Lambda + serverless é 10x mais rápido de deployar.

Próximos passos

Implemente observabilidade de fila com Observabilidade em aplicações com LLM, rastreando tempo de latência end-to-end.

Gerencie sua infra com Terraform na prática: sua primeira infra versionada, criando filas, workers e auto-scale como código.

Crie worker pool containerizado em Kubernetes e orquestre com Kubernetes para quem vem do Docker Compose, escalando baseado em tamanho da fila.

Aprenda jogando

Defensor da Nuvem

Posicione WAF, cache e auto-scaler para segurar ondas de ataque na sua infra.

Jogar Defensor da Nuvem

Teste seu conhecimento

Teste o que você aprendeu sobre serverless vs containers

Pergunta 1 de 5

Seu retriever devolve um trecho que corta no meio de uma frase importante, perdendo contexto. Qual é a causa mais provável?

Continue lendo