ADR-009 — Cluster GPU separado, não worker do cluster K3s SP
Status
Vigente — 2026-05-02. Atualizado após ADR-002 revisado (Cilium em LXC é o caminho CPPS).
Contexto
Hardware GPU disponível
| Site | Hosts com GPU | Modelo | Estado |
|---|---|---|---|
| Franca | pve-labri-31, pve-labri-32, pve-labri-33 | NVIDIA A5500 | Em produção como LINSTOR controllers; sem cluster K3s GPU dedicado ainda |
| SP | pve-ippri-31, pve-ippri-33, pve-ippri-34 | NVIDIA A5000 | gpu-sp-01 (LXC standalone com pve-ippri-31) em produção |
Ou seja: Franca tem hardware GPU, mas não tem cluster K3s GPU operacional. Provisionar GPU cluster em Franca é trabalho futuro — não bloqueador desta decisão.
Arquitetura em discussão
Em SP haverá 2 ambientes K3s distintos:
- K3s SP principal: workloads gerais (apps stateful tipo Tutor, OpenObserve, ArgoCD SP, Authentik)
- K3s GPU: workloads que precisam de aceleração (vLLM, treinamento, inference)
Há duas arquiteturas possíveis pra integrá-los:
(A) Clusters separados, cada um com CNI próprio, conectados via Traefik/DNS (B) Cluster único com node pool GPU dedicado (worker node + taint/toleration)
CPPS já tem gpu-sp-01 rodando como K3s standalone em LXC (estado atual no Pulumi __main__.py). Decisão de arquitetura precisa formalizar se isso é definitivo ou transicional.
Decisão
Arquitetura (A) — Clusters separados, ambos em LXC.
Cluster K3s SP "principal" Cluster K3s SP "GPU"├─ Cilium + Hubble (LXC priv.) ├─ K3s standalone├─ Apps gerais (Tutor, etc.) ├─ HAMi/Device Plugin time-slicing├─ NetworkPolicy default-deny ├─ gpu-sp-01 em LXC privileged└─ ([ADR-002](/roadmap/adrs/cilium-vm-only/)) └─ Cilium opcional (decidido conforme demanda) ▲ ▲ └─── ArgoCD SP gerencia ambos ────────┘ (multi-cluster registration)Cilium no cluster GPU é opcional (ADR-005 revisado):
- Default: começar sem Cilium no GPU (validação focada no principal)
- Adicionar quando ClusterMesh entre os 2 clusters virar útil (bursting, MultiKueue avançado, Hubble unificado)
Service discovery cross-cluster: Traefik + DNS Cloudflare (não ClusterMesh). vLLM/TGI/SGLang expõem HTTP OpenAI-compatible; clientes (apps, notebooks) consomem via URL.
ArgoCD SP gerencia ambos os clusters via argocd cluster add + Application apontando pro destino certo.
Esclarecimento importante: Cilium é CNI por cluster
Cilium não é “serviço cross-cluster que se consome” — é o CNI do cluster onde está instalado. Cada K3s tem seu próprio CNI.
Implicações desse fato:
- ✅ Cluster GPU sem Cilium pode falar com cluster principal (que tem Cilium) via rede comum: HTTP via Traefik, ou TCP via Service LoadBalancer com IP roteável. Flannel default basta no cluster GPU.
- ✅ Apps cross-cluster funcionam: app no cluster principal chama
https://vllm.colabh.orgque está no cluster GPU — só precisa rota de rede entre eles. - ❌ ClusterMesh exige Cilium nos dois lados. Se algum dia ClusterMesh virar requisito (service discovery transparente cross-cluster), aí o cluster GPU também precisaria de Cilium — voltando ao tema ADR-002.
- ❌ Hubble e Cilium NetworkPolicy avançada ficam restritos ao cluster onde Cilium roda. Cluster GPU usa NetworkPolicy padrão K8s (com limites do flannel).
Pra MVP, comunicação cross-cluster via Traefik HTTP é suficiente — vLLM/TGI/SGLang expõem APIs OpenAI-compatible naturalmente.
Por que LXC pro cluster GPU faz sentido (motivação completa)
Além de ADR-005, há uma razão arquitetural importante: manter o K3s GPU dentro da lógica de gestão do Proxmox.
| Aspecto | K3s direto no Proxmox host | K3s em LXC | K3s em VM |
|---|---|---|---|
Aparece em pct/qm list, dashboard Proxmox | ❌ | ✅ | ✅ |
| Backup via PBS | ❌ | ✅ | ✅ |
| Storage via LINSTOR | ❌ | ✅ | ✅ |
| Snapshot/rollback simples | ❌ | ✅ | ✅ |
| Upgrade do Proxmox afeta K3s? | ✅ diretamente | ⚠️ pode afetar | ❌ isolado |
| RAM overhead | menor | menor | maior |
| Kernel próprio (isolamento real) | ❌ | ❌ | ✅ |
| Performance GPU | nativa | nativa | -5 a -15% (passthrough) |
LXC é o sweet spot pro cluster GPU: integrado à lógica Proxmox (PBS, LINSTOR, snapshots), performance GPU preservada, e como não roda Cilium nele, o risco operacional principal não aplica.
Por que o cluster principal está em VM, não LXC
| Critério | Resultado |
|---|---|
| Cilium é necessário (apps gerais, NetworkPolicy avançada, ClusterMesh com Franca) | VM = caminho documentado |
| Apps stateful críticos (Tutor, Authentik, ArgoCD) merecem isolamento de kernel | VM |
| RAM overhead da VM é aceitável (1-2 nodes K3s SP) | OK |
| Live migration via LINSTOR | Funciona com VM |
Trade-offs cross-site (cluster GPU sem Cilium na integração SP-Franca)
Como o cluster GPU não roda Cilium, ele fica fora do ClusterMesh SP↔Franca. Isso tem custo e benefício explícitos.
O que se perde sem Cilium no cluster GPU
| Perda | Severidade pro CPPS | Mitigação |
|---|---|---|
Service discovery transparente cross-cluster — app em Franca não chama vllm.sp-gpu.svc.cluster.local, usa URL externa | 🟡 baixa | vLLM/TGI/SGLang são HTTP nativos (OpenAI-compatible) — URL via Traefik é o uso normal, não workaround |
Failover Cilium Global Services (annotation service.cilium.io/global: "true") | 🟡 baixa | GPU não está replicada cross-site hoje (só SP tem cluster GPU operacional); failover Cilium não teria pra onde fazer fallback. Mitigação real: DNS LB se virar requisito (ADR-008) |
| Hubble (observabilidade L7) dos pods GPU | 🟡 baixa | vLLM/TGI já logam cada request; OTel + OpenObserve cobrem logs/metrics. Falta apenas packet flow visualization L7 |
| Cilium NetworkPolicy avançada (L7, identity-based, regras por método HTTP/path) | 🟢 quase nula | NetworkPolicy padrão K8s (L3/L4) funciona com flannel; CPPS hoje não tem caso de uso pra L7 NetworkPolicy no GPU |
| mTLS automático entre services | 🟢 quase nula | TLS via Traefik + cert-manager já existe |
O que NÃO se perde
| Capacidade | Funciona sem Cilium no GPU? |
|---|---|
| Conectividade SP↔Franca | ✅ via VyOS + WireGuard |
| TLS entre services cross-cluster | ✅ Let’s Encrypt + cert-manager |
| Monitoring (logs, metrics) | ✅ OpenObserve via OTel collector |
| GitOps multi-cluster | ✅ ArgoCD multi-cluster registration |
| NetworkPolicy básica L3/L4 | ✅ flannel + iptables |
| Backup (PBS), Storage (LINSTOR) | ✅ |
Análise honesta
Workloads de inference (vLLM, TGI, SGLang) são desenhadas pra HTTP — não são “services típicos K8s que se descobrem por nome”. A interface natural delas é URL pública, então a “perda” de service discovery transparente é mais teórica que prática.
As features Cilium perdidas no GPU seriam realmente relevantes se:
- 50+ workloads cross-site se descobrindo entre si
- Compliance exigisse audit L7 de cada chamada
- Multi-tenant real no cluster GPU (vários grupos de pesquisa isolados)
- GPU em Franca também (justificaria ClusterMesh entre 2 GPU clusters)
Hoje nenhum desses sinais existe. Manter cluster GPU sem Cilium é decisão correta com a informação atual.
Casos de uso GPU completos (não só inference LLM)
A Plataforma GPU planejada (ver /roadmap/gpu-platform/) cobre múltiplos casos:
Serviços interativos (always-on):
- vLLM, SGLang — chat, RAG, code generation, embeddings BGE-M3
Serviços batch (com início e fim):
- Fine-tuning distribuído (Unsloth multi-GPU) — gang scheduling, topology-aware
- OCR em lote (Docling/Marker) — teses, acervos, microfilmes
- Dataset generation — geração sintética pra fine-tuning de modelos locais
- GraphRAG / embedding generation em corpus
Plataforma:
- Kueue (filas batch + quotas por lab)
- KAI Scheduler + DRA + HAMi (GPU sharing)
- MLflow (model registry com lineage)
- DVC (dataset versioning)
- Qdrant (vector DB)
- SeaweedFS (datasets, checkpoints — já replicado cross-site)
Análise por caso de uso (cross-site)
| Caso de uso | Cross-site faz diferença? | Cilium ClusterMesh exige? | Alternativa sem ClusterMesh |
|---|---|---|---|
| Inference (vLLM, SGLang, embeddings) | 🟡 failover desejável | ❌ HTTP-nativo | DNS LB / Cloudflare Tunnel |
| Fine-tuning distribuído (multi-GPU NCCL) | ❌ inviável cross-site | — | Single-cluster, intra-site (latência UnespNet) |
| Fine-tuning batch single-GPU | ✅ sim — fila cross-site | ❌ não | MultiKueue (não exige Cilium) |
| OCR em lote (Docling/Marker) | ✅ paraleliza entre sites | ❌ não | MultiKueue + SeaweedFS replicado |
| Dataset generation batch | ✅ paraleliza | ❌ não | MultiKueue + datasets em SeaweedFS |
| GraphRAG / embedding em corpus grande | ✅ paraleliza | ❌ não | MultiKueue + storage compartilhado |
| Vector search (Qdrant) | 🟡 só se replicado | ❌ não | Replicação Qdrant ou consulta via API |
| Model serving (MLflow models) | ✅ usar modelo de qualquer site | ❌ não | MLflow centralizado + S3 replicado |
Insight crítico: MultiKueue NÃO exige ClusterMesh
Como MultiKueue funciona:
- Cluster manager (1 dos clusters) recebe submissão de job
- Conecta com clusters worker via kubeconfig (não via service discovery)
- Despacha job pro cluster com capacidade
- Não exige que pods se descubram entre si — exige acesso ao API server cross-cluster
MultiKueue cross-site funciona com:
- ✅ Network connectivity entre API servers (já temos via VyOS+WireGuard)
- ✅ kubeconfigs corretos (ArgoCD multi-cluster já registra)
- ✅ ApplicationSet pra Kueue/ClusterQueue config consistente
- ❌ NÃO precisa Cilium ClusterMesh
Pro batch GPU cross-site, MultiKueue + SeaweedFS replicado + ApplicationSet entrega 90% do benefício sem ClusterMesh.
Onde ClusterMesh GPU realmente ganharia
Cenários muito específicos onde sem ClusterMesh fica feio:
- Pipeline distribuído stateful cross-site (stage 1 SP fala com stage 2 Franca em tempo real) — raro em academia, padrão é stages no mesmo cluster
- Service mesh transparente entre microservices GPU cross-site — workaround com Traefik HTTP funciona pra TensorZero ↔ vLLM Franca
- Hubble-driven debugging de tráfego GPU cross-site — útil em produção, não-essencial em academia
Critério revisado pra reabrir Cilium no GPU
Reabrir a questão “vale Cilium no cluster GPU” se aparecer caso de uso real que MultiKueue + Traefik + SeaweedFS replicado não cobre:
- Pipeline stateful distribuído cross-site com latência baixa (workloads que falam entre si em tempo real, não só batch)
- Compliance L7 formal (audit detalhado de chamadas a APIs GPU)
- Multi-tenant GPU estrito com L7 NetworkPolicy (vários grupos de pesquisa isolados)
- GPU bursting com pod migração (workload em execução é movido entre clusters automaticamente — exige service mesh)
Hoje, nenhum desses sinais existe. Casos de uso planejados (inference serving, batch fine-tuning, OCR em lote, dataset generation, GraphRAG) são todos cobertos por MultiKueue + Traefik + SeaweedFS sem precisar de Cilium ClusterMesh.
Quando o cluster GPU Franca for provisionado
Replicar mesma arquitetura: K3s standalone em LXC privileged em algum dos hosts labri-31/32/33, sem Cilium. Comunicação com cluster Franca principal via Traefik HTTP. ArgoCD Franca registra cluster GPU Franca como destino.
MultiKueue + ApplicationSet ligam os 2 GPU clusters (SP + Franca) via control plane, sem precisar Cilium. Storage compartilhado via SeaweedFS replicado. Modelos compartilhados via MLflow centralizado.
Se aparecer caso de uso 1-4 acima depois disso, aí avalia migrar pra VM + Cilium com POC validado primeiro.
Alternativas rejeitadas
(B) Cluster K3s SP único com node pool GPU
Rejeitada porque:
- Forçaria gpu-sp-01 a rodar Cilium → exigiria migrar LXC pra VM (ADR-005 — versão anterior)
- Performance GPU perdida (~5-15% em VM com PCIe Passthrough)
- Setup atual em produção exigiria migração com downtime
- Falha do driver NVIDIA em workload de pesquisador afeta cluster inteiro (apps gerais também)
(C) Cluster único K3s SP com gpu-sp-01 como LXC node + Cilium
Rejeitada porque conflita com ADR-002 (Cilium não roda confiavelmente em LXC).
(D) Cluster GPU separado mas com ClusterMesh entre os 2 clusters K3s SP
Rejeitada porque exige Cilium nos 2 clusters → cluster GPU teria que rodar Cilium em LXC (volta ao problema de ADR-002). Service discovery via Traefik HTTP cobre o caso de uso (inference serving).
Consequências
Positivas:
- Performance GPU máxima (LXC com zero overhead)
- Falha do cluster GPU não derruba apps gerais e vice-versa (isolamento de blast radius)
- Setup atual permanece válido — sem migração
- Driver NVIDIA gerenciado uma vez no Proxmox host, sem replicar em N VMs
- Padrão da indústria: GKE, EKS, AKS recomendam node pools separados pra GPU
- Cilium com escopo claro (cluster principal apenas)
Negativas:
- Service discovery manual: pra app no SP principal chamar vLLM no GPU, precisa Traefik route ou DNS interno apontando pro cluster GPU
- Sem Cilium NetworkPolicy no cluster GPU — segurança intra-K8s mais fraca
- Sem ClusterMesh entre os 2 — pra todos os efeitos, são clusters distintos
- 2 clusters pra operar — mais kubeconfigs, mais ArgoCD Apps
- Apps que precisam de GPU + dado em Postgres do cluster principal acessam DB via rede (sem service mesh)
Trade-offs aceitos:
- Cluster GPU sem observability L7 (Hubble) — fica com logs/metrics via OpenObserve agent
- Multi-tenant GPU é responsabilidade do HAMi/Device Plugin no nível K8s, não do CNI
Critério de revisão
Mudar pra arquitetura (B) “cluster único” se:
- Gestão de 2 clusters virar maior overhead que ganho de isolamento
- Workloads GPU + apps gerais misturados virarem padrão (ex: Tutor usando vLLM dentro do mesmo deployment)
- Cilium NetworkPolicy virar requisito formal pra workloads GPU (compliance, multi-tenant)
- Driver NVIDIA + LXC mostrarem instabilidade em produção (kernel panics, etc.)
- Aparecer hardware GPU adicional que justifique node pool em vez de cluster dedicado
Notas operacionais
Service discovery cross-cluster
App no SP principal precisa chamar vllm-llama3 no cluster GPU:
Opção 1 — Via Traefik externo (mais simples):
# vllm exposto em https://vllm.colabh.org via Traefik no cluster GPU# App principal usa essa URLOpção 2 — Via Service interno + LoadBalancer (sem Traefik):
# Service no cluster GPU com type=LoadBalancer, IP em VLAN compartilhada# App principal chama esse IP diretoOpção 3 — Via External Service no cluster principal:
# Service do tipo ExternalName apontando pro endpoint do cluster GPU# App principal usa Service local que faz CNAME pro cluster GPURecomendação MVP: Opção 1 (Traefik) — já tem TLS + observabilidade L7 pelo Traefik access log.
ArgoCD multi-cluster
# No ArgoCD SP, registrar cluster GPUargocd cluster add gpu-sp-context --name gpu-sp --kubeconfig ...
# Apps no repo:apps/├── sp/ # destination: cluster K3s SP principal│ ├── tutor/│ └── openobserve/└── sp-gpu/ # destination: cluster K3s SP GPU ├── vllm/ └── triton/Operação do driver NVIDIA
- Documentar versão exata em runbook
- Atualização do driver no Proxmox = janela de manutenção do cluster GPU
- Plano de rollback: snapshot LINSTOR do LXC antes
- Monitoring: dmesg + nvidia-smi via OpenObserve, alerta em erros NVRM