Arquitetura da Infraestrutura
Este documento define o que cada componente faz, por que está onde está e o que ainda não está implementado. O Roadmap usa este modelo pra ordenar as fases.
📌 Pré-requisito de leitura: Contexto institucional — missão CPPS + Lab Multiusuário IPPRI + Colaboratório de Humanidades Digitais. Toda decisão arquitetural deste documento deve ser conectável àquele contexto.
Os 3 estados
Toda afirmação sobre a infra deve cair em uma destas categorias. Misturar os três é o erro mais comum.
| Estado | Significado | Exemplo |
|---|---|---|
| Validado em produção | Funciona hoje, com tráfego/dados reais | LINSTOR SP SSD, Tutor/Open edX, VyOS Franca |
| Declarado em IaC | Existe no código (Pulumi/Ansible) mas não foi aplicado/validado em produção | traefik-sp com ha: True no config (Pulumi não cria HA real ainda) |
| Desejado (roadmap) | Decisão arquitetural mas sem código nem validação | PBS cross-site, Cilium ClusterMesh, ArgoCD SP |
Cada item deste documento marca explicitamente seu estado.
Visão geral
A infra CPPS opera em 2 sites institucionais (Franca e SP — Praça da Sé) interligados via UnespNet, com autonomia local em cada site e operação integrada quando a rede entre-sites está disponível. Roadmap futuro inclui:
- Cloud privada (AWS, Azure) — bursting de capacidade sob demanda
- HPC externo (Grid Unesp, supercomputadores) — execução de jobs batch
Princípios
- Autonomia por site — cada site sobrevive a partição de rede; nada essencial depende de link cross-site
- Modularidade entre camadas — cada camada tem interface clara; trocar componente não cascata pras demais
- Declarativo > imperativo — config como código (Pulumi, Ansible, ArgoCD); estado reproduzível
- GitOps no plano de aplicações — main do repo é a fonte da verdade; ArgoCD reconcilia
- Backup defensivo — perda de 1 site não significa perda de dados (replicação intra-site + backup cross-site)
- Frugalidade operacional — toda peça nova custa tempo de admin; só entra com caso de uso comprovado
- Distinguir HA de DR — replicação síncrona (LINSTOR) protege contra falha de hardware; backup (PBS) protege contra erro lógico/desastre
Estrutura
Ver diagrama interativo das camadas → Catálogo visual
Por que essas camadas?
A ordenação é bottom-up por dependência operacional: cada camada N só pode ser operada sobre a N-1 estável. GPU/HPC não é uma camada porque atravessa workloads (camada 8) — é capacidade aplicada onde existe hardware GPU.
Camada 0 — Virtualização (Proxmox)
Papel: substrato de compute. Cria VMs (full-virt) e LXCs sobre hardware físico.
Componentes: Proxmox VE 8.x/9.x · cluster por site (Franca: pve-labri-*, SP: pve-ippri-*) · Pulumi (IaC) · Ansible (config OS).
Por que aqui: isola hardware das camadas acima. Trocar hardware ou migrar VM entre nodes não muda nada da camada 1+.
Estado:
- ✅ Validado: 2 clusters Proxmox em produção (Franca: 7 hosts, SP: 6 hosts)
- 🚧 Em andamento: upgrade PVE 9.x dos nodes restantes
- ❌ Desejado: cluster Proxmox cross-site rejeitado (latência incompatível com pmxcfs)
Pendências:
- Upgrade PVE 8.4 → 9.x dos nodes restantes
- Padronizar template LXC Debian 13
- HA configurado via Proxmox (declarado mas não implementado em VMs como
traefik-sp)
Camada 1 — Storage distribuído (LINSTOR + DRBD)
Papel: block storage replicado, intra-site. Volumes lineares acessíveis por todas as VMs/LXCs do site (live migration sem downtime).
Componentes: LINSTOR (controller + satellites) · DRBD 9 · LVM thin pools (linstor_ssd, linstor_sp aka linstor-ssd-01, linstor-nvme-01) · controller HA via VRRP (VIP 10.10.20.1 em SP).
Estado:
- ✅ Validado: LINSTOR SP SSD em
pve-ippri-11/12/31, controller HA - ✅ Validado: LINSTOR Franca em
labri-31/32/33(controller_primary em labri-32) - 🚧 Declarado/parcial: LINSTOR SP NVMe (pool
nvme-poolempve-ippri-31/33/34) —pve-ippri-34temlinstor_interface: ""(precisa correção antes de usar) - ❌ Desejado: cross-site replication strategy (DRBD remoto vs PBS-to-PBS)
Pendências críticas:
- Corrigir
linstor_interfacevazio empve-ippri-34antes de qualquer outro tuning place_count: 3no SP SSD (hoje 2 — perda de 1 node = degradação) — só após correção dopve-ippri-34- DRBD auto-evict tuning conforme decisão recente
- Quorum policy revisada
- Witness/tie-breaker em terceiro site (cloud VM ou outro campus) pra eliminar split-brain de minoria
Camada 2 — Backup e recuperação (PBS)
Papel: backup VM-level com retenção longa, restore granular (bloco/arquivo). Complementa LINSTOR (que protege contra falha de hardware mas não contra deleção/corrupção lógica).
Estratégia 3-camadas:
- Snapshots LINSTOR — retorno rápido (minutos), retenção curta (horas/dias)
- PBS local — retenção média (semanas/meses), restore granular VM-level e por arquivo
- PBS remoto cross-site — disaster recovery, retenção longa, sync via WireGuard
Estado:
- ❌ Desejado: PBS Franca, PBS SP, sync cross-site (nenhum implementado hoje)
- ⚠️ Backup é prioridade MVP, não defer
Decisões pendentes:
- Onde rodar PBS (VM dedicada por site)
- Cadência (incremental? horário? diário?)
- Política de retenção (4 semanas? 6 meses?)
- Encryption-at-rest + key management
Camada 3 — Rede e roteamento (VyOS + UnespNet)
Papel: roteamento entre VLANs, NAT, firewall, link upstream institucional, VPN cross-site.
Componentes: VyOS (1 router por site) · UnespNet (link upstream institucional, com possibilidade de IP público fixo) · WireGuard SP↔Franca via UnespNet.
Estado:
- ✅ Validado: VyOS Franca em produção
- ✅ Validado: VyOS SP renumerado pra
192.168.10.0/23(PR #8) - ✅ Validado: backup VyOS 3-camadas
- ❌ Desejado: renumeração Franca (range a definir)
- ❌ Desejado: WireGuard SP↔Franca configurado
- ❌ Desejado: IP público fixo via UnespNet (uso a decidir)
- ❌ Desejado: VRRP HA pra VyOS (single-node hoje)
Resiliência de conectividade (evolução):
- Hoje: 1 link UnespNet por site
- Próximo: IP público fixo + WireGuard cross-site
- Futuro: link secundário (4G/5G ou ISP comercial) com failover via VyOS
- Fallback admin: Cloudflare Tunnel ou similar pra acesso quando link primário cai
Camada 4 — Ingress / exposição (Traefik + DNS)
Papel: entrada HTTP(S) externa para serviços, terminação TLS, roteamento por hostname.
Componentes: Traefik (1 por site) · Cloudflare DNS · cert-manager (Let’s Encrypt DNS-01).
Estado:
- ✅ Validado: Traefik SP em
192.168.10.6(IP único pós-renumeração) - 🚧 Validado parcial: Traefik Franca em produção (status de consolidação?)
- ⚠️ Declarado:
traefik-sptemha: Trueno Pulumi config, mas o Pulumi não cria recurso HA Proxmox real — apenas VM normal comon_boot=True - ❌ Desejado: cert-manager + Issuers + rotação de token Cloudflare
- ❌ Desejado: failover entre sites (DNS round-robin? Cloudflare LB?)
Camada 5 — K8s rede e segurança (Cilium)
Papel: CNI (rede pod-a-pod), service mesh (mTLS opcional), policies (NetworkPolicy++), observabilidade (Hubble).
Componentes: K3s (1 cluster por site) · Cilium 1.16+ · Hubble · NetworkPolicy default-deny.
Decisão CPPS (ADR-002): Cilium roda em VMs por padrão (caminho documentado upstream). LXC privileged é exceção apenas pra hosts Tier 3 (sem iGPU, NVIDIA disputada entre display + workload — pve-ippri-31/33). Ver Tiers de hardware pra mapeamento completo.
Estado:
- 🚧 Validado parcial: K3s Franca em produção com flannel/default
- ❌ Desejado: K3s SP greenfield com Cilium + Hubble (cluster K3s SP “principal”, em VMs)
- ❌ Desejado: migração Franca pra Cilium em janela
- ✅ Decisão: cluster GPU separado fica fora do escopo do Cilium (ADR-009)
Camada 6 — GitOps (ArgoCD)
Papel: declara estado desejado dos apps em git; ArgoCD reconcilia continuamente.
Componentes: 2 ArgoCDs independentes (1 por cluster) · repo colabhd/devops como source of truth.
Por que 2 ArgoCDs (não 1 central): se ArgoCD Franca cai ou link SP↔Franca quebra, SP continua reconciliando. Autonomia por site. Ver ADR-001.
Repo structure desejada:
apps/├── franca/ ← ArgoCD Franca observa├── sp/ ← ArgoCD SP observa└── multi/ ← ApplicationSet propaga (camada 7)Estado:
- ✅ Validado: ArgoCD Franca em produção
- ❌ Desejado: ArgoCD SP (não depende de Cilium — pode subir antes/em paralelo)
- ❌ Desejado: refactor
apps/emfranca//sp//multi/
Camada 7 — Multi-cluster patterns
Papel: padrões pra propagar workloads em múltiplos clusters (ApplicationSet, MultiKueue) e, opcionalmente, federação de control plane (Karmada).
Componentes:
- Cilium ClusterMesh — service discovery cross-cluster L7 (anotação
service.cilium.io/global: "true") - ArgoCD ApplicationSet — deploy do mesmo manifest em N clusters (mais simples, declarativo)
- Kueue / MultiKueue — fila de jobs batch cross-cluster (GPU)
- Karmada — control plane federado (deferido)
Decisão: começar com Cilium ClusterMesh + ApplicationSet. Cobre 80% dos casos sem operar Karmada. Ver ADR-004.
Estado:
- ❌ Desejado: ClusterMesh + ApplicationSet (depende de Cilium nos 2 sites)
- ❌ Desejado: Kueue pra batch GPU
- 🚫 Deferido: Karmada (critério: 5+ clusters ou scheduling dinâmico real)
Camada 8 — Workloads
Aplicações, jobs e plataformas que rodam em cima da pilha.
8a — Aplicações (VMs e K3s)
| Item | Estado | Notas |
|---|---|---|
| Tutor / Open edX | ✅ Validado | Em produção (Franca), Kustomize via ArgoCD |
| OpenObserve | 🚧 Setup feito | Validar integração com OTel + Hubble |
| OnlyOffice | 🚧 Em andamento | Workstations Ryzen |
| Apps de pesquisa diversos | 🚧 Parcial | Em ramp-up |
8b — GPU + HPC (capability sobre workloads)
GPU é capacidade, não camada — atravessa o que precisa de aceleração.
| Item | Estado | Notas |
|---|---|---|
gpu-sp-01 (LXC + K3s standalone + A5000) | ✅ Validado | Mantido em LXC como cluster K3s separado (ADR-005 revisado, ADR-009). Gerenciado via ArgoCD multi-cluster |
Cluster K3s GPU Franca (hardware A5500 em labri-31/32/33) | ❌ Hardware presente, cluster não provisionado | Mesma arquitetura quando demandar: K3s standalone em LXC, sem Cilium |
| HAMi ou NVIDIA Device Plugin time-slicing | ❌ Desejado | Avaliar Device Plugin oficial primeiro |
| TensorZero (model proxy) | ❌ Desejado | Após HAMi |
| vLLM / SGLang serving | ❌ Desejado | |
| Volcano ou Kueue (batch) | ❌ Desejado | |
| Conector Grid Unesp (SLURM API) | 🔮 Futuro |
8c — Datalake / projetos de dados
| Item | Estado | Notas |
|---|---|---|
| SeaweedFS + Hudi | ❌ Desejado | Plano em Plataforma de Dados |
| Trino / Spark | ❌ Desejado | |
| Airflow / Dagster | ❌ Desejado | |
| GraphRAG sobre corpus | ❌ Desejado | |
| CAQDAS apps | 🚧 Parcial |
Capacidades transversais
Atravessam todas as camadas. Subespecificar essas capacidades é a falha mais comum em arquiteturas de plataforma — vão pra “fase futura” e nunca chegam.
◆ Secrets management
Papel: gerenciar credenciais (tokens API, senhas DB, chaves SSH, certificados privados) sem expor em git.
Decisão pendente — opções:
- SOPS + age: criptografia simétrica, secrets ficam em git criptografados, descriptografados em runtime
- External Secrets Operator (ESO) + Vault: secrets ficam fora de git, K8s consome via Operator
- Sealed Secrets: secrets criptografados em git, sealed-secrets-controller descriptografa no cluster
Recomendação MVP: SOPS + age (mais simples, sem novo serviço). ESO + Vault na fase de IDP (Backstage).
Estado: ❌ Não implementado. Prioridade MVP (não defer).
◆ Certificados (TLS)
Papel: rotação automática de certificados, terminação TLS em todas exposições.
Componentes: cert-manager (no K8s) · Let’s Encrypt via DNS-01 challenge · Cloudflare API token · ClusterIssuer / Issuer.
Estado:
- 🚧 DNS-01 mencionado mas sem cert-manager configurado formalmente
- ❌ Inventário de domínios e wildcards
- ❌ Rotação de token Cloudflare
Pendências MVP:
- cert-manager instalado nos 2 clusters
- ClusterIssuer com DNS-01 Cloudflare
- Token Cloudflare via secrets management
- Inventário em
/operacoes/dominios.md
◆ Identity & Access (IAM/RBAC)
Papel: SSO único pra todos os serviços, RBAC granular, audit trail.
Componentes:
- Authentik (IdP self-hosted) — SSO OIDC/SAML/OAuth2
- Authentik LDAP Outpost — SSH/sudo via SSSD nos servidores
- K8s RBAC — bindings por grupo Authentik
- Proxmox roles — sync com Authentik via PAM realm
Estado: ❌ Não implementado. Prioridade Fase 2-3.
Pendências:
- Authentik instalado (1 instância central com replica DR?)
- LDAP Outpost validado
- Bindings RBAC K8s + Proxmox
- MFA habilitado pra admins
◆ Observabilidade
Papel: logs, métricas, traces, alertas — fonte única pra diagnóstico.
Componentes:
- OpenObserve — backend unificado (logs/metrics/traces)
- OpenTelemetry Collector — agente em todos os nós (Proxmox + K3s)
- Hubble (Cilium) — observabilidade L7 do K8s
- Proxmox exporter — métricas de hardware, VMs, LINSTOR
- Blackbox probes — synthetic checks de endpoints externos
- Alertmanager — routing de alertas (Slack, email, webhook)
Estado:
- 🚧 OpenObserve setup feito (validar uso real)
- ❌ OTel collector em Proxmox/K3s
- ❌ Hubble integrado
- ❌ Métricas LINSTOR/DRBD/PBS
- ❌ Blackbox probes
- ❌ Alert routing + SLOs definidos
Pendências MVP:
- OTel collector universal via Ansible
- Métricas LINSTOR/PBS antes de declarar produção
- Pelo menos 5 alertas básicos: node down, LINSTOR degraded, DRBD desync, ArgoCD out-of-sync, cert expirando
◆ CI/CD de IaC
Papel: validar mudanças em Pulumi, Ansible e ArgoCD antes de aplicar em produção.
Componentes:
- Pulumi preview automático em PR (GitHub Actions)
- Ansible —check + lint em PR
- ArgoCD diff antes de sync
- Política de aprovação (1+ revisor pra mudanças em main)
- Rollback documentado por componente
Estado: ❌ Não implementado formalmente. Prioridade MVP.
Pendências:
- Workflow GitHub Actions com
pulumi previewem PR ansible-linteansible-playbook --check- Branch protection em
main - Runbook de rollback por componente
◆ Supply chain (registry e imagens)
Papel: controlar imagens de container que rodam no cluster — integridade, vulnerabilidades, base images padronizadas.
Componentes propostos:
- Harbor ou registry mirror (cache de docker.io/quay.io/etc)
- Trivy scan em CI
- cosign assinatura de imagens internas
- Política de base images (ex: só Debian slim, ou Distroless)
Estado: ❌ Não implementado.
Decisão pendente: registry self-hosted (Harbor) vs SaaS (GHCR/Quay) vs nada (puxar direto do upstream).
Recomendação MVP: registry mirror simples (Harbor ou registry:2 + cache) pra economizar bandwidth UnespNet e proteger contra rate-limits do Docker Hub. Trivy em CI. cosign na fase de IDP.
Decisões arquiteturais (resumo)
| Tópico | Decisão | Alternativa rejeitada | Motivo | ADR |
|---|---|---|---|---|
| Cluster Proxmox cross-site | Não | Sim, 1 cluster único | Latência UnespNet incompatível com pmxcfs | — |
| Storage replicado | LINSTOR + DRBD | Ceph | Footprint menor, performance melhor em poucos nodes | ADR-003 |
| ArgoCD topology | 2 instâncias independentes | 1 ArgoCD central | Autonomia por site na partição de rede | ADR-001 |
| CNI | Cilium em VM (padrão); LXC privileged só Tier 3 | Calico, flannel sem Cilium, Linkerd | Cilium em VM é caminho documentado/suportado; LXC só onde hardware Tier 3 obriga | ADR-002, Tiers |
| Multi-cluster | ClusterMesh + ApplicationSet | Karmada (default) | Karmada só com escala/scheduling real | ADR-004 |
| GPU placement | Diferenciado por site: SP em LXC (Tier 3 obriga); Franca em VM com Cilium (Tier 2A permite) | Padronização forçada (ambos LXC ou ambos VM) | Hardware atual difere entre sites — assimetria respeita hardware real | ADR-005, ADR-009, Tiers |
| Backup primário | PBS por site + cross-site sync | Object storage direto | Restore granular VM-level + bloco-level | — |
| GitOps repo | Monorepo colabhd/devops | Multi-repo | Coesão entre infra (Pulumi/Ansible) e apps (ArgoCD) | — |
| IaC Proxmox | Pulumi (Python) | Terraform/OpenTofu | Stack já em Python; type-safety; menos verbosidade | — |
| Secrets | SOPS + age (MVP) → ESO + Vault (futuro) | Sealed Secrets | Mais simples; sem novo serviço crítico | ADR-006 |
| Multi-site federação humana | WireGuard direto via VyOS (MVP) → Headscale futuro | Tailscale managed | Self-hosted, sem dependência SaaS | — |
| Acesso seguro humano | SSH keys + Authentik LDAP (MVP) → Octelium/Teleport futuro | Octelium agora | Maturidade vs valor pro time pequeno | — |
| Defesa de borda | VyOS rules + Cloudflare proxy + fail2ban (MVP) → CrowdSec futuro | CrowdSec agora | Componente novo sem caso de uso comprovado | — |
| Failover DNS | Cloudflare Tunnel (gratuito) | DNS round-robin, Cloudflare LB pago, External DNS Operator | Failover automático com plano free; sem novo serviço self-hosted | ADR-008 |
Expansão futura (fora do MVP)
A modularidade entre camadas permite expandir sem refazer o baseline:
- Cloud privada (AWS/Azure): novo cluster K8s na cloud, integrado via Cilium ClusterMesh + VPN
- HPC externo (Grid Unesp, supercomputadores): integração via API/SLURM na camada 8b; jobs batch via Kueue
- IDP (Backstage + Crossplane): quando demanda real de self-service de pesquisadores aparecer; antes disso, ArgoCD + Helm bastam
- Observabilidade SIEM (Wazuh) ou IDS/IPS (Suricata): se compliance ou ataque sofisticado exigir
- Mais sites institucionais Unesp: cada site novo replica o stack 0-7
Próximos passos
Implementação ordenada — ver Roadmap.