LLM Self-Hosted · AWS

LLMs Open Source
Self-Hosted na AWS

Guia completo: modelos, hospedagem, integração com LangChain/LangGraph, ECS com GPU, Multi-LoRA e otimizações de cold start para produção.

Modelos Open Source em 2025/2026

Os principais modelos self-hostáveis com licença permissiva e suporte a OpenAI-compatible API via vLLM.

Llama 4
Meta AI
109B–400B
Parâmetros
1M–10M
Context
650M+
Downloads
O mais amplamente implantado. Scout (109B, 10M ctx) e Maverick (400B, 1M ctx). Multimodal em 12 línguas. Atenção à licença para grandes bases de usuários.
Multimodal MoE Llama License Mais popular
DeepSeek-V4
DeepSeek AI
284B–1.6T
Total Params
13B–49B
Ativos (MoE)
V4-Pro (flagship) e V4-Flash (econômico). Projetado para raciocínio longo contexto, coding e workflows agênticos. Ganhou destaque no "DeepSeek moment" de 2025.
Reasoning MoE Agentic MIT License
Devstral 2
Mistral AI
123B
Parâmetros
72.2%
SWE-bench
256K
Context
Foco em engenharia de software agêntica. Devstral Small 2 (24B) atinge 68% no SWE-bench e roda em uma única RTX 4090. Licença Apache 2.0 — a mais permissiva.
Coding Agentic Apache 2.0 RTX 4090
Qwen3
Alibaba DAMO
#1
Coding OSS
7B–235B
Variantes
Melhor modelo open source para coding atualmente. MiMo-V2.5 é o mais econômico para acesso via API. Forte em raciocínio e tarefas de programação.
Coding #1 Reasoning Apache 2.0 Econômico
Quantização 4-bit (Q4_K_M): reduz requisitos de VRAM pela metade com perda mínima de qualidade. Llama 3.3 70B com Q4_K_M roda em ~40GB de VRAM. Use 2 bytes/param para FP16 e 0.5 bytes/param para INT4 para estimar VRAM.

Opções de Hospedagem na AWS

Quatro caminhos para rodar seu LLM — cada um com trade-offs de controle, custo e operação.

Amazon SageMaker
Serviço gerenciado de ML
Controle
Médio
Custo vs EC2
+20% markup
Cold start
10–15 min
Fine-tuning
LoRA, FSDP
vLLM ou TensorRT-LLM como engine. A/B testing nativo, tensor parallelism. Ideal para times de ML com foco em experimentação. KV cache offloading via LMCache.
EC2 Direto
Controle total do hardware
Controle
Total
Custo
Raw pricing
Cold start
~2 min (warm)
Inferentia2
+40% perf/$
Máxima flexibilidade. systemd service + NLB + ASG com warm pools. Acesso a Inferentia2 (40% melhor preço-performance). Quatro comandos para subir um modelo 1B.
EKS (Kubernetes)
Para quem já usa K8s
Controle
Alto
Complexidade
Alta
Scheduling
GPU-aware
Karpenter
Dinâmico
LLM como mais um workload K8s. GPU-aware scheduling, Karpenter para provisionamento dinâmico, integração nativa com service mesh. llm-d (sobre vLLM) para distributed serving.

Instâncias GPU na AWS

Referência de hardware por tamanho de modelo — com e sem quantização.

Instância GPU VRAM Adequada para Custo/h (~)
g5.xlarge A10G (1x) 24 GB Modelos até ~13B (FP16) ou ~24B (INT4) $1.00
g5.12xlarge A10G (4x) 96 GB 70B INT4 (~40GB) ou 30B FP16 $5.67
g5.48xlarge A10G (8x) 192 GB 70B FP16 ou 120B INT4 $16.29
p4d.24xlarge A100 (8x) 320 GB 180B+ INT4, multi-modelo $32.77
p5.48xlarge H100 (8x) 640 GB Modelos muito grandes, máx throughput $98.32
inf2.48xlarge Inferentia2 384 GB Produção cost-optimized (+40% perf/$) $12.98
Estimativa de VRAM: FP16 = 2 bytes × nº de parâmetros. INT4 = 0.5 bytes × nº de parâmetros. Some 10–20% para KV cache, ativações e overhead de framework. Exemplo: 70B FP16 = 140GB; 70B INT4 = 35GB + overhead ≈ 40–42GB.

Integração com LangChain / LangGraph

vLLM expõe API OpenAI-compatible — substituição drop-in no ChatOpenAI.

Drop-in replacement: o vLLM implementa o mesmo contrato de API da OpenAI (/v1/chat/completions, /v1/models). No LangChain, basta mudar base_url — zero mudança de lógica.
python — ChatOpenAI apontando para vLLM self-hosted
from langchain_openai import ChatOpenAI

# Aponta para o NLB interno — zero mudança de interface
llm = ChatOpenAI(
    base_url="http://vllm-nlb.internal:8000/v1",
    api_key="EMPTY",                # qualquer string
    model="meta-llama/Meta-Llama-3.1-70B-Instruct",
    temperature=0,
)

# Com LoRA específico — mesmo padrão
finance_llm = ChatOpenAI(
    base_url="http://vllm-nlb.internal:8000/v1",
    api_key="EMPTY",
    model="finance-specialist",  # nome do LoRA registrado
    temperature=0,
)
python — LangGraph com vLLM self-hosted
from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI
from typing import Literal

VLLM_BASE = "http://vllm-nlb.internal:8000/v1"

def make_agent_llm(lora_name: str | None = None) -> ChatOpenAI:
    model = lora_name or "meta-llama/Meta-Llama-3.1-70B-Instruct"
    return ChatOpenAI(base_url=VLLM_BASE, api_key="EMPTY", model=model)

def finance_node(state):
    llm = make_agent_llm("finance-specialist")
    # lógica do agent finance...

def route_to_specialist(state) -> Literal["finance", "legal", "support"]:
    return state["specialist"]

graph = StateGraph(AgentState)
graph.add_node("finance", finance_node)
graph.add_node("legal", legal_node)
graph.add_conditional_edges("supervisor", route_to_specialist, {
    "finance": "finance",
    "legal": "legal",
})

ECS Task Definition para GPU

Configuração completa do container vLLM no ECS com EC2 GPU.

Fargate não suporta GPU: workloads GPU no ECS exigem EC2. Use a AMI otimizada para ECS com GPU (Amazon Linux 2023 GPU) — ela já inclui NVIDIA Container Toolkit configurado.
json — ECS Task Definition (pontos críticos)
{
  "family": "vllm-inference",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["EC2"],   // não Fargate
  "containerDefinitions": [{
    "name": "vllm-server",
    "image": "vllm/vllm-openai:latest",
    "command": [
      "--model", "meta-llama/Meta-Llama-3.1-70B-Instruct",
      "--tensor-parallel-size", "4",       // = nº GPUs
      "--max-model-len", "32768",
      "--gpu-memory-utilization", "0.90",
      "--enable-auto-tool-choice",           // para tool calling
      "--tool-call-parser", "llama3_json"
    ],
    "resourceRequirements": [{
      "type": "GPU",
      "value": "4"                           // deve bater com tensor-parallel-size
    }],
    "healthCheck": {
      "command": ["CMD-SHELL", "curl -f http://localhost:8000/health"],
      "startPeriod": 300                     // 5 min p/ modelo carregar
    },
    "mountPoints": [{
      "sourceVolume": "model-cache",
      "containerPath": "/root/.cache/huggingface"
    }]
  }],
  "placementConstraints": [{
    "type": "memberOf",
    "expression": "attribute:ecs.instance-type =~ g5.*"
  }]
}
typescript — CDK — ECS Service + NLB interno
// NLB interno — só acessível dentro do VPC
const nlb = new elbv2.NetworkLoadBalancer(this, 'VllmNlb', {
  vpc,
  internetFacing: false,        // INTERNO — crítico
});

const listener = nlb.addListener('VllmListener', { port: 8000 });
listener.addTargets('VllmTargets', {
  port: 8000,
  protocol: elbv2.Protocol.TCP,
  targets: [service],
  healthCheck: { path: '/health', port: '8080' }, // porta do proxy
  deregistrationDelay: Duration.seconds(30),
});

Multi-LoRA no vLLM

Um único servidor GPU com múltiplos adaptadores por especialidade — sem instâncias separadas.

Specialist Agent
model="finance-specialist"
→
vLLM Server (Llama 70B base)
LoRA: finance-specialist
LoRA: legal-specialist
LoRA: support-specialist
→
Resposta
Fine-tuned inference
shell — Flags de Multi-LoRA no vLLM
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3.1-70B-Instruct \
  --tensor-parallel-size 4 \
  --enable-lora \
  --max-loras 4          # simultâneos na GPU \
  --max-cpu-loras 8      # swap para CPU quando necessário \
  --max-lora-rank 64     # deve bater com o rank do fine-tuning \
  --enable-auto-tool-choice \
  --tool-call-parser llama3_json
shell — Gerenciamento de LoRAs via API REST (runtime)
# Registrar LoRA
curl -X POST http://vllm:8000/v1/load_lora_adapter \
  -H "Content-Type: application/json" \
  -d '{"lora_name": "finance-specialist", "lora_path": "/loras/finance"}'

# Listar LoRAs carregados
curl http://vllm:8000/v1/models

# Remover LoRA
curl -X POST http://vllm:8000/v1/unload_lora_adapter \
  -d '{"lora_name": "finance-specialist"}'

Warm Pool no Auto Scaling Group

Instâncias pré-aquecidas em standby — elimina cold start de 20+ minutos.

Pool State Custo idle Tempo p/ InService Uso recomendado
Stopped ~$0.10/h (só EBS) 3–4 min Produção econômica ✅
Running ~$16/h (GPU idle) 1–2 min Latência crítica
typescript — CDK — Warm Pool com Lifecycle Hook
// Warm Pool — instâncias paradas (paga só EBS)
const cfnAsg = asg.node.defaultChild as autoscaling.CfnAutoScalingGroup;
cfnAsg.warmPool = {
  minSize: 1,
  maxGroupPreparedCapacity: 2,
  poolState: 'Stopped',              // economiza GPU $$$
  instanceReusePolicy: {
    reuseOnScaleIn: true,             // devolve ao pool, não termina
  },
};

// Lifecycle Hook — pausa até modelo estar pronto
asg.addLifecycleHook('WaitForModelLoad', {
  lifecycleTransition: autoscaling.LifecycleTransition.INSTANCE_LAUNCHING,
  heartbeatTimeout: Duration.minutes(20),
  defaultResult: autoscaling.DefaultResult.ABANDON,  // descarta se falhar
  notificationTarget: new hooks.TopicHook(warmupTopic),
});
EBS Snapshot Strategy: crie um snapshot do EBS com o modelo já baixado (~140GB para 70B FP16). Use GP3 com IOPS 16.000 e throughput 1.000 MB/s. O modelo carrega do disco em 2–3 min em vez de baixar do S3 em 15+ min. Versione snapshots junto com releases de LoRA.
typescript — CDK — EBS com Snapshot no Launch Template
blockDevices: [
  { deviceName: '/dev/xvda', volume: ec2.BlockDeviceVolume.ebs(100) },
  {
    deviceName: '/dev/sdb',
    volume: ec2.BlockDeviceVolume.ebsFromSnapshot('snap-0abc123def', {
      volumeSize: 500,                    // GB — modelo + LoRAs
      volumeType: ec2.EbsDeviceVolumeType.GP3,
      iops: 16000,                         // máximo GP3
      throughput: 1000,                     // MB/s — máximo GP3
    }),
  },
],

LoRA Warmup Script

NLB só recebe tráfego após todos os LoRAs estarem carregados e aquecidos.

Porta 8080 como gate: o NLB aponta o health check para a porta 8080 (proxy). Essa porta só fica acessível após o warmup completo. O tráfego real vai para a porta 8000 (vLLM). Isso garante que a primeira requisição nunca sofra lazy-load de LoRA.
bash — warmup.sh (entrypoint do container)
#!/bin/bash
set -e

# 1. Inicia vLLM em background
python -m vllm.entrypoints.openai.api_server \
  --model "${MODEL_NAME}" \
  --enable-lora --max-loras "${MAX_LORAS:-4}" \
  --port "${VLLM_PORT:-8000}" &
VLLM_PID=$!

# 2. Aguarda vLLM base estar healthy
until curl -sf "http://localhost:${VLLM_PORT}/health" > /dev/null; do
  sleep 5
  kill -0 $VLLM_PID 2>/dev/null || { echo "vLLM morreu!"; exit 1; }
done

# 3. Registra e aquece cada LoRA
for lora_path in "${LORAS_DIR}"/*/; do
  lora_name=$(basename "$lora_path")

  # Registra
  curl -sf -X POST "http://localhost:${VLLM_PORT}/v1/load_lora_adapter" \
    -H "Content-Type: application/json" \
    -d "{\"lora_name\": \"${lora_name}\", \"lora_path\": \"${lora_path}\"}"

  # Warmup (aquece CUDA kernels)
  curl -sf -X POST "http://localhost:${VLLM_PORT}/v1/chat/completions" \
    -d "{\"model\": \"${lora_name}\", \"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}], \"max_tokens\": 1}" \
    > /dev/null
done

# 4. Sobe proxy — NLB só roteia agora
python /healthcheck_proxy.py --proxy-port 8080 &

# 5. Mantém container vivo
wait $VLLM_PID

Arquitetura Completa

Visão end-to-end: Supervisor Account → Agents Account → vLLM com Multi-LoRA.

┌─────────────────────────────────────────────────────────────────────┐
│  SUPERVISOR ACCOUNT                                                  │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  ECS Fargate — LangGraph Supervisor                          │  │
│  │  ChatOpenAI(base_url=NLB_DNS)  ← zero mudança de código       │  │
│  └────────────────────────────────────┬─────────────────────────┘  │
└───────────────────────────────────────┼─────────────────────────────┘
                                          │ JWT Bearer / VPC Peering
┌───────────────────────────────────────┼─────────────────────────────┐
│  AGENTS ACCOUNT                        │                             │
│                                          ▼                             │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  NLB Interno  (porta 8000 tráfego · porta 8080 health)      │  │
│  └────────────────────────────────────┬─────────────────────────┘  │
│                                          │                             │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  ECS EC2 (g5.12xlarge — 4× A10G GPU, 96GB VRAM)             │  │
│  │                                                              │  │
│  │  vLLM Server  (Llama 3.1 70B base — carregado na GPU)       │  │
│  │  ├── LoRA: finance-specialist    ← GPU                       │  │
│  │  ├── LoRA: legal-specialist       ← GPU                       │  │
│  │  ├── LoRA: support-specialist     ← CPU swap                  │  │
│  │  │                                                            │  │
│  │  healthcheck_proxy :8080  ← gate do NLB                     │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  ASG Warm Pool  (2× Stopped · EBS snapshot c/ modelo)       │  │
│  │  Lifecycle Hook → Lambda → wait /health → CONTINUE          │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  S3: s3://models-bucket/loras/   ← adaptadores LoRA            │
└─────────────────────────────────────────────────────────────────────┘

Timeline de Cold Start Otimizado

Com Warm Pool + EBS Snapshot + LoRA Warmup: ~8 min vs 20–25 min sem otimizações.

T+0:00
ASG detecta necessidade de escalar
Métrica de CPU/GPU ou requisição enfileirada dispara scale-out
T+0:30
Warm Pool promove instância Stopped → Pending
Instância pré-existente no pool, sem aguardar provisionamento AWS
T+1:30
Instância InService, ECS Agent registra
ECS Agent já instalado na AMI GPU, registra no cluster automaticamente
T+2:30
vLLM carrega modelo do EBS snapshot
GP3 com 16.000 IOPS — modelo carrega em ~2–3 min do disco local
T+5:00
vLLM base healthy (porta 8000)
Modelo base carregado na GPU, pronto para receber requests
T+5:30 — T+7:00
Warmup.sh registra e aquece todos os LoRAs
Cada LoRA: load_lora_adapter + inference de 1 token para aquecer kernels CUDA
T+7:30
healthcheck_proxy sobe (porta 8080)
Gate liberado — NLB começa a ver health check passing
T+8:00 ✅
Instância recebe tráfego real
NLB roteia. Primeira requisição de qualquer LoRA já está aquecida — sem lazy-load
Cenário ECS Warm Pool EBS Snapshot LoRA Warmup Total
Sem otimizações ❌ Cold EC2 ❌ S3 download ❌ Lazy load 20–25 min
Com todas as otimizações ✅ Pool Stopped ✅ GP3 local ✅ Pre-warmed ~8 min
vLLM Multi-LoRA LangChain / LangGraph ECS + EC2 GPU Warm Pool ASG OpenAI-Compatible API