Skip to content

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

SiteHosts com GPUModeloEstado
Francapve-labri-31, pve-labri-32, pve-labri-33NVIDIA A5500Em produção como LINSTOR controllers; sem cluster K3s GPU dedicado ainda
SPpve-ippri-31, pve-ippri-33, pve-ippri-34NVIDIA A5000gpu-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.org que 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.

AspectoK3s direto no Proxmox hostK3s em LXCK3s 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 overheadmenormenormaior
Kernel próprio (isolamento real)
Performance GPUnativanativa-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érioResultado
Cilium é necessário (apps gerais, NetworkPolicy avançada, ClusterMesh com Franca)VM = caminho documentado
Apps stateful críticos (Tutor, Authentik, ArgoCD) merecem isolamento de kernelVM
RAM overhead da VM é aceitável (1-2 nodes K3s SP)OK
Live migration via LINSTORFunciona 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

PerdaSeveridade pro CPPSMitigação
Service discovery transparente cross-cluster — app em Franca não chama vllm.sp-gpu.svc.cluster.local, usa URL externa🟡 baixavLLM/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")🟡 baixaGPU 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🟡 baixavLLM/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 nulaNetworkPolicy 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 nulaTLS via Traefik + cert-manager já existe

O que NÃO se perde

CapacidadeFunciona 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 usoCross-site faz diferença?Cilium ClusterMesh exige?Alternativa sem ClusterMesh
Inference (vLLM, SGLang, embeddings)🟡 failover desejável❌ HTTP-nativoDNS LB / Cloudflare Tunnel
Fine-tuning distribuído (multi-GPU NCCL)❌ inviável cross-siteSingle-cluster, intra-site (latência UnespNet)
Fine-tuning batch single-GPU✅ sim — fila cross-site❌ nãoMultiKueue (não exige Cilium)
OCR em lote (Docling/Marker)✅ paraleliza entre sites❌ nãoMultiKueue + SeaweedFS replicado
Dataset generation batch✅ paraleliza❌ nãoMultiKueue + datasets em SeaweedFS
GraphRAG / embedding em corpus grande✅ paraleliza❌ nãoMultiKueue + storage compartilhado
Vector search (Qdrant)🟡 só se replicado❌ nãoReplicação Qdrant ou consulta via API
Model serving (MLflow models)✅ usar modelo de qualquer site❌ nãoMLflow 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:

  1. 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
  2. Service mesh transparente entre microservices GPU cross-site — workaround com Traefik HTTP funciona pra TensorZero ↔ vLLM Franca
  3. 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:

  1. Pipeline stateful distribuído cross-site com latência baixa (workloads que falam entre si em tempo real, não só batch)
  2. Compliance L7 formal (audit detalhado de chamadas a APIs GPU)
  3. Multi-tenant GPU estrito com L7 NetworkPolicy (vários grupos de pesquisa isolados)
  4. 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:

  1. Gestão de 2 clusters virar maior overhead que ganho de isolamento
  2. Workloads GPU + apps gerais misturados virarem padrão (ex: Tutor usando vLLM dentro do mesmo deployment)
  3. Cilium NetworkPolicy virar requisito formal pra workloads GPU (compliance, multi-tenant)
  4. Driver NVIDIA + LXC mostrarem instabilidade em produção (kernel panics, etc.)
  5. 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 URL

Opção 2 — Via Service interno + LoadBalancer (sem Traefik):

# Service no cluster GPU com type=LoadBalancer, IP em VLAN compartilhada
# App principal chama esse IP direto

Opçã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 GPU

Recomendação MVP: Opção 1 (Traefik) — já tem TLS + observabilidade L7 pelo Traefik access log.

ArgoCD multi-cluster

Terminal window
# No ArgoCD SP, registrar cluster GPU
argocd 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