Arquitetura K8s
Arquitetura K8s — Cilium, ArgoCD, Karmada, Velero, integracao com storage
Documento de referencia/decisao consolidando:
- Decisoes ja tomadas sobre stack K8s multi-site (Cilium, ArgoCD split, Karmada agnostico, K3s 3-node etcd, Traefik consolidado, MetalLB, WireGuard)
- Como a stack K8s se conecta com a stack de storage (LINSTOR, PBS, MinIO) ja documentada em storage-strategy.md e linstor-conceitos.md
- Tools complementares (Velero) e como eles se relacionam sem se sobrepor a o que ja existe
- Workload stateful vs stateless — onde failover automatico funciona e onde nao funciona
Plano vivo de execucao:
prompts/plano-endereçamento-rede.md(endereçamento canonico v4 — versionado no repo) e~/.claude/plans/revisar-questionar-alguns-pontos-para-steady-piglet.md(revisao detalhada — vive fora do repo, na home do autor; nao versionada). Este doc e a versao consolidada/didatica daquele material e e a fonte de verdade publica do projeto.
Principio orientador
Do usuario (decisao explicita, persistida em memoria):
- Maxima cautela com: cluster Proxmox (corosync, quorum, pmxcfs), storage ZFS das VMs, VyOS — sempre backup antes
- Flexibilidade total (reinstalavel sem dor) com: LINSTOR, K3s, Karmada, Cilium, WireGuard, ClusterMesh
Consequencia pratica: se LINSTOR/K3s/Karmada/Cilium quebra, reinstala. Se Proxmox/ZFS quebra = problema serio, rollback imediato. Isso explica por que o doc nao trata Cilium/Karmada como “experimentais a evitar” — sao parte do design.
Estado das decisoes (2026-04-22, ainda valido)
Decidido
| # | Decisao | Detalhe |
|---|---|---|
| 1 | CNI Cilium 1.17.x | Franca: migrar Flannel→Cilium in-place. SP: Cilium desde inicio. Cluster ID 1 (fca), 2 (sp). CA compartilhada (caminho recomendado, nao absoluto) |
| 2 | Cilium ClusterMesh | Interconecta cluster-fca ↔ cluster-sp. Usa Pod/Service CIDRs nao-sobrepostos |
| 3 | Cilium BGPv2 | Anuncia VIPs ao VyOS (Franca ASN 65001, SP 65002). MetalLB pra LB pool |
| 4 | K3s 3-node etcd HA em Franca | vm-cpps-02 + 03 + 04 (04 a provisionar). Elimina SPOF de control plane |
| 5 | Traefik consolidado no cluster Franca | DaemonSet em traefik-system. Dissolve K3s do debian-proxy. Reversivel via GitOps |
| 6 | ArgoCD split federated+local | 1 ArgoCD por site, mesmo repo Git, sem conflito de lock |
| 7 | Karmada como plataforma agnóstica | Member clusters intercambiaveis via labels (nao clusterNames). Timing: agora, junto com Cilium (evita migracao dupla) |
| 8 | MetalLB LB pool | Franca 192.168.0.240-250, SP 192.168.10.240-250 (inclui endpoint ClusterMesh API) |
| 9 | WireGuard site-to-site | /30: 10.255.0.1 (Franca) ↔ 10.255.0.2 (SP) |
| 10 | Pod CIDR / Service CIDR | Franca 10.41.0.0/16 / 10.141.0.0/16. SP 10.42.0.0/16 / 10.142.0.0/16 |
| 11 | Layout repo Git | clusters/<member>/ + federated/ + shared/base/ (member intercambiavel) |
Em aberto (depende de implementacao)
- Velero — sera adotado quando houver workload K8s critica fora do Tutor; backup destino MinIO local
- MinIO — ainda nao deployado; instalacao quando primeiro use case (datasets, NextCloud, ou destino Velero/PBS frio) aparecer
- linstor-csi (Piraeus) dentro do K8s — design pendente: rodar Piraeus operator nas VMs K3s ou usar CSI proxy ao LINSTOR Proxmox? Decidir antes de migrar workloads stateful
- Longhorn vs linstor-csi — alternativa K8s-native nao avaliada formalmente; default atual e LINSTOR ja existente
- drbd-reactor — failover automatico do controller LINSTOR; nao essencial pra K8s
Stack por camada (estado alvo)
┌────────────────────────────────────────────────────────────────┐│ Apps (CPPS, Tutor/OpenEdX, NextCloud, jupyter, GPU services) │ L8├────────────────────────────────────────────────────────────────┤│ GitOps / CD ........................... ArgoCD (1 por site) │ L7├────────────────────────────────────────────────────────────────┤│ Federacao multi-cluster ............... Karmada (agnostico) │ L6├────────────────────────────────────────────────────────────────┤│ Backup K8s (manifests + PVs) .......... Velero (planejado) │ L5├────────────────────────────────────────────────────────────────┤│ Ingress L7 ............................ Traefik DaemonSet │ L4'├────────────────────────────────────────────────────────────────┤│ LB / IPAM externo ..................... MetalLB + Cilium BGPv2 │ L4├────────────────────────────────────────────────────────────────┤│ K8s control plane ..................... K3s (3-node etcd HA) │ L4├────────────────────────────────────────────────────────────────┤│ CNI / rede pod / cross-cluster ........ Cilium 1.17.x + │ L3│ ClusterMesh │├────────────────────────────────────────────────────────────────┤│ CSI / StorageClass K8s ................ linstor-csi (TBD) / │ L2│ local-path (interim) │├────────────────────────────────────────────────────────────────┤│ Block storage HA ...................... LINSTOR + DRBD │ L1├────────────────────────────────────────────────────────────────┤│ Hipervisor / VMs ...................... Proxmox VE │ L0├────────────────────────────────────────────────────────────────┤│ Conectividade inter-site .............. WireGuard /30 + BGP │ L-1└────────────────────────────────────────────────────────────────┘Camadas tem responsabilidades distintas mas com dependencias de integracao reais. Trocar CNI ou CSI envolve mudancas operacionais e em manifests; Velero depende do que o CSI expoe; Karmada para stateful depende da estrategia de storage.
ArgoCD — GitOps split federated+local
Decisao
2 ArgoCDs (1 em cada cluster K8s). Mesmo repo Git. Sem ferramenta de lock cross-cluster.
Layout do repo
clusters/├── franca/│ ├── labels.yaml # site=franca, provider=onprem, capability=gpu, gpu.model=a5500, tier=prod, region=sudeste-br│ ├── apps/ # apps de plataforma so de Franca (Traefik, ingress, monitoring local)│ └── kustomization.yaml├── sp/│ ├── labels.yaml # site=sp, provider=onprem, capability=gpu, gpu.model=a5000, ...│ ├── apps/ # apps de plataforma de SP│ └── kustomization.yamlfederated/├── tutor-openedx/ # apps que rodam em ambos os sites via Karmada├── ...shared/└── base/ # bases Kustomize comunsPor que split (e nao 1 ArgoCD federado)
- Sem conflito de lock: cada ArgoCD escreve so no proprio cluster
- Falha local nao quebra deploy do outro site: ArgoCD SP ofine nao impede deploy em Franca
- Karmada cuida da federacao via API, ArgoCD nao precisa “saber” de multi-cluster
- Reversibilidade: se Karmada for removido, cada ArgoCD continua autonomo
Apps em uso
tutor-openedx— ja em producao, mergeada em main 2026-04-17 (memoryproject_tutor_openedx)- Caddy mantido como router interno do Tutor (1 NodePort vs 6); Traefik na frente
Onde NAO usar ArgoCD
- Recursos nao-K8s (Proxmox VMs, VyOS, switches, DNS) — esses moram em Pulumi/Ansible/Cloudflare
- Manifests com computacao em runtime (ArgoCD prefere declarativo puro)
Cilium — CNI + ClusterMesh + BGP
Decisao
Cilium 1.17.x em ambos os clusters. 3 features importantes:
- CNI primario: substitui Flannel em Franca (in-place), default em SP
- ClusterMesh: interconecta os 2 clusters K8s no nivel de pods/services
- BGPv2: anuncia VIPs MetalLB ao VyOS de cada site (
Cilium BGPv2 ↔ VyOS Franca/SP)
Configuracao K3s pra Cilium
k3s --flannel-backend=none \ --disable-network-policy \ --disable=servicelb \ --cluster-cidr=10.41.0.0/16 \ # ou 10.42.0.0/16 em SP --service-cidr=10.141.0.0/16 # ou 10.142.0.0/16 em SPMigracao Flannel → Cilium (Franca) — rollback
Nao basta kubectl drain se Cilium falhar ao subir. Drain so move pods entre nodes; se Cilium em si nao iniciar (CNI loader error, conflict de iptables, eBPF kernel module ausente), todos os nodes ficam com pods em CrashLoopBackOff e nao tem como Flannel retomar sozinho.
Procedure de rollback se Cilium nao subir limpo:
# 1. Em cada server K3s, restaurar os flags originais do Flannel# Editar /etc/systemd/system/k3s.service (ou /etc/rancher/k3s/config.yaml)# REMOVER: --flannel-backend=none --disable-network-policy --disable=servicelb# Adicionar: --flannel-backend=vxlan (ou o backend que estava antes)
# 2. Remover Cilium (libera o CNI socket)helm uninstall cilium -n kube-system
# 3. Restart K3s pra carregar Flannel de novosystemctl restart k3s # nos serverssystemctl restart k3s-agent # nos workers
# 4. Validar pods saindo do CrashLoopBackOffkubectl get pods -A | grep -v Running
# 5. Se ainda houver pods presos, reiniciar eles:kubectl delete pods -A --field-selector status.phase!=RunningPre-requisito de seguranca antes de migrar: snapshot Proxmox de cada VM K3s server. Rollback via snapshot e o caminho mais rapido se o procedimento acima nao restaurar.
ClusterMesh — pre-requisitos
- Pod e Service CIDRs nao-sobrepostos entre clusters (✓ — 10.41 vs 10.42, 10.141 vs 10.142)
- Conectividade IP entre cluster nodes (resolvido via WireGuard /30 + BGP)
- Mesmo datapath mode (encapsulation OU native-routing) nos dois
- Cluster ID unico (1 = fca, 2 = sp) e nome unico (
cluster-fca,cluster-sp)
Sobre CA do Cilium: a abordagem recomendada (e adotada no plano) e CA compartilhada entre os clusters — simplifica setup, e o caminho mais documentado. ClusterMesh tambem suporta CAs separadas via clustermesh-apiserver com troca de certificados explicita; mais complexo de operar, raramente justificavel pra setup de 2 clusters numa mesma organizacao.
Por que Cilium (vs Flannel/Calico)
- eBPF-based — substitui parte do datapath tradicional (kube-proxy iptables) conforme configuracao
- Hubble — observability de fluxo de rede (complementa metrics/logs/traces, nao substitui)
- Network Policies L7 — filtrar HTTP method/path, gRPC method (alem de L3/L4)
- ClusterMesh nativo — sem precisar de Submariner ou tooling externo
- BGP integrado — Cilium BGPv2 fala direto com VyOS, sem MetalLB-FRR-Bird etc
Onde NAO ajuda
Cilium so resolve rede dentro do K8s. VMs Proxmox fora de K8s (vyos, gateway, GPU bare-metal, edge) continuam usando rede tradicional — Cilium nao toca nelas.
Karmada — federacao multi-cluster agnostica
Decisao crucial
Karmada nao e “instalado pra integrar Franca+SP”. E plataforma de federacao onde member clusters sao intercambiaveis via labels.
# Cluster Francalabels: site: franca provider: onprem capability: gpu gpu.model: a5500 tier: prod region: sudeste-br
# Cluster SP (hoje)labels: site: sp provider: onprem capability: gpu gpu.model: a5000 tier: prod region: sudeste-brAnti-pattern (corrigido pelo design agnostico)
# ERRADO — acoplado a clusterNamesclusterAffinity: clusterNames: [franca, sp]# CERTO — adiciona novo cluster so com labelsplacement: clusterAffinity: labelSelector: matchExpressions: - key: provider operator: In values: [onprem] - key: capability operator: In values: [gpu]Cenarios futuros que o design absorve sem refator
| Cenario | Acao |
|---|---|
| SP cancelado, Hetzner-SP entra | Remove cluster SP do Karmada, join Hetzner com mesmas labels. Policies inalteradas |
| Adiciona DigitalOcean pra burst de GPU | Join novo cluster com provider: do, capability: gpu. Policies existentes incluem o cluster como candidato elegivel |
| Reorganiza por GPU model | Cria PropagationPolicy com gpu.model: a5000 em vez de hardcode de cluster |
Importante sobre labelSelector: ele define o conjunto de candidatos que a politica considera elegiveis. A selecao efetiva (qual cluster recebe o workload, em que peso) depende ainda de:
- ReplicaSchedulingType (
DuplicatedvsDivided) — se duplica em todos os candidatos ou divide- WeightPreference (
StaticWeightList,DynamicWeight) — distribuicao por peso- ClusterTolerations e taints
- Estado de saude reportado pelo cluster ao Karmada
Adicionar um cluster com as labels certas o torna elegivel, nao garante que recebera carga sem ajuste explicito de politica.
Limite real: workload stateful
Karmada distribui pods conforme PropagationPolicy. Mas pra workload stateful (DB, Tutor/OpenEdX, NextCloud), o storage do PV precisa existir nos 2 lados antes do failover — Karmada sozinho nao resolve isso. Opcoes:
| Estrategia stateful | Mecanismo |
|---|---|
| App stateless (3 replicas em SP, 2 em Franca, sem state local) | Karmada apenas — ideal pra services HTTP, controllers |
| App stateful com state em DB externo replicado | Karmada agenda app; DB replica via mecanismo proprio (Postgres logical replication, etc.) |
| App stateful com PV local | Karmada nao faz failover automatico. Precisa replicar PV externamente (LINSTOR snapshot ship, Velero backup, etc.) e restore manual no destino |
Recomendacao do design: comecar com workloads stateless federadas; stateful continuam pinados num site especifico via PropagationPolicy ate ter solucao de storage cross-site madura.
Reversibilidade
Se SP for cancelado e nenhum substituto externo entrar, Karmada vira debito. Opcao explicita no plano: removel-o e voltar pra ArgoCDs autonomos.
Failure mode do Karmada control plane (host cluster)
Karmada tem topologia host + members. O karmada-controller-manager, karmada-scheduler, karmada-apiserver rodam no host cluster. Pelo plano, host = Franca.
Cenario critico: Franca cai com o Karmada-host junto.
| Componente | Continua funcionando? | Por que |
|---|---|---|
| K8s cluster SP standalone | ✓ | Cada cluster member roda autonomo; control plane K3s/etcd local nao depende do Karmada |
| ArgoCD-sp (deploy de apps locais a SP) | ✓ | ArgoCD split — cada um e independente |
| Apps stateless ja rodando em SP | ✓ | Cilium + ClusterMesh continuam (ClusterMesh nao depende do Karmada) |
| Novos workloads federados | ✗ | Sem Karmada-host, nenhuma PropagationPolicy nova e processada |
| Re-federacao apos mudanca | ✗ | Karmada-host precisa estar up pra reconciliar |
| Trafego cross-cluster ja estabelecido | ✓ | ClusterMesh roda no datapath, nao precisa de Karmada |
Implicacao: durante outage de Franca, SP continua servindo trafego mas vira “estatico” — nao recebe novas decisoes de federacao. Apps em SP continuam, novos deploys via ArgoCD-sp funcionam, mas qualquer redistribuicao orquestrada pelo Karmada para.
Mitigacoes possiveis (em aberto)
- Karmada-host em cluster dedicado (nao em Franca nem SP): VM separada, possivelmente em cloud provider mais resiliente. Aumenta complexidade
- Karmada-host HA com replicas em ambos os sites: Karmada suporta etcd externo + replicas multiplas do controller. Quando Franca cair, replica em SP assume. Exige etcd cluster cross-site (latencia importa, etcd e sensivel)
- Aceitar o limite e operar Karmada como “best-effort”: Franca cai, federacao para, mas apps continuam. SP standalone segue. Quando Franca volta, Karmada reconcilia
Recomendacao pra contexto atual: opcao 3. Equipe pequena, complexidade extra de etcd cross-site nao compensa o ganho. Aceitar que durante outage de Franca, Karmada nao orquestra federacao — apps continuam servindo trafego.
Velero — backup K8s
Decisao
Adotar quando houver workload K8s critica fora do Tutor. Hoje Tutor tem snapshots LINSTOR (PV) + git (manifests via ArgoCD) — protecao basica suficiente.
O que Velero faz (precisao importante)
Velero combina mecanismos distintos, frequentemente confundidos:
| Mecanismo | O que captura | Onde mora o conteudo |
|---|---|---|
| Resource backup (manifests) | Objetos K8s (Deployment, ConfigMap, Secret, PVC) em YAML | Object store (MinIO/S3) |
| CSI snapshot | Snapshot do PV via VolumeSnapshot API (linstor-csi suporta) | Storage backend (LINSTOR) — local no cluster |
| Volume data movement (uploader) | Conteudo do PV copiado pra object store. Default em Velero ≥ 1.10: Kopia. Restic ainda suportado via --uploader-type=restic, mas nao e o caminho recomendado novo | Object store (MinIO/S3) |
Importante: CSI snapshot nao vira backup off-cluster automaticamente. Se voce so configura CSI snapshot, perde dados se o storage backend cair junto. Pra protecao real, combinar CSI snapshot + uploader Kopia — snapshot da consistencia, uploader envia pro MinIO. Velero orquestra os dois numa unica Backup resource.
Velero CSI plugin tambem suporta “data mover” baseado no snapshot CSI — copia o snapshot (em vez do filesystem) pro object store. Vantagem: backup point-in-time sem afetar a aplicacao. Esse e o caminho moderno que substitui o uso direto de Kopia/Restic em muitos casos.
Restore com Velero
- Recriacao dos objetos K8s (Deployment, Service, ConfigMap, etc.) e quase sempre automatica
- Reidratacao do PV depende: plugin usado, StorageClass do destino, modo de backup, mapeamento
--existing-resource-policy - Pra K8s pra outro cluster (DR), pode precisar
--namespace-mappings,--restore-pvs=truee ajustes manuais
Quando Velero vs PBS vs LINSTOR ship
VMs Proxmox tradicionais (vyos, gateway, GPU labs) → PBS (Proxmox Backup Server) — granularidade VM, dedup, restore granular
Apps em K8s (manifests + dados de PV) → Velero (resource backup + CSI snapshot + uploader Kopia, ou data mover via CSI) → backup destino: MinIO local em cada site, com replication MinIO cross-site
PVs em K8s (camada extra de DR de bloco) → LINSTOR backup ship (snapshot LINSTOR pro outro cluster ou S3) → granularidade resource (PV inteiro), nao K8s-aware
Datasets fora de PV (objetos, models, NextCloud blobs) → MinIO replication ou rclone scheduledPouca sobreposicao funcional direta entre Velero/PBS/LINSTOR ship — mas existe sobreposicao intencional de protecao, defesa em profundidade.
Escopo (corrige confusao comum)
- Velero nao cobre VM Proxmox fora do K8s
- Velero nao substitui PBS pra VMs tradicionais
- Velero nao substitui LINSTOR backup ship (que opera no nivel block, K8s-agnostico)
linstor-csi vs alternativas K8s-native
Decisao pendente (em aberto)
Como o K8s consome storage replicado? Tres caminhos plausiveis:
| Opcao | Como funciona | Pros | Cons |
|---|---|---|---|
| A. linstor-csi (Piraeus) | Operator dentro do K8s, satellites nas VMs K3s, controller compartilhado com LINSTOR Proxmox | Reaproveita LINSTOR ja deployado; uma so stack de storage | LINSTOR “dentro” de LINSTOR (VM disk e LINSTOR; PV dentro do VM e LINSTOR). Acoplamento operacional |
| B. linstor-csi com LINSTOR Proxmox direto | Satellites no K3s falam direto com LINSTOR controller na rede 10.10.10.x | Sem LINSTOR aninhado | Exige rede do K3s alcancar LINSTOR — dependencia entre K8s e infra Proxmox |
| C. Longhorn (K8s-native, separado) | Storage K8s desacoplado do LINSTOR. Replicacao Longhorn dentro do K3s | Desacopla K8s do LINSTOR/Proxmox; equipe pequena tem 1 stack pra cada coisa | Duas stacks de storage replicado pra operar; perde o LINSTOR existente em K8s |
Status: nao decidido. Recomendacao do storage-strategy.md foi linstor-csi sem comparar Longhorn formalmente. Vale revisitar antes de migrar workloads stateful.
Longhorn como alternativa (em aberto)
- Longhorn e storage replicado K8s-native — concorre com linstor-csi (nao com local-path)
- Vantagem: stack autocontida no K8s, desacopla totalmente do LINSTOR/Proxmox. Equipe gerencia 1 stack pra cada coisa (LINSTOR pra VMs, Longhorn pra PVs K8s)
- Desvantagem: passa a ter 2 mecanismos de replicacao DRBD-like operados em paralelo. Pra equipe pequena, mais carga operacional
- Decisao pendente: nao escolhido nenhum dos dois ainda. Comparar formalmente antes de migrar workloads stateful pra K8s
OpenEBS
Familia de opcoes K8s-native (Mayastor, ZFS-localpv, jiva). Maior flexibilidade mas adocao menor que Longhorn. Nao avaliado em detalhe — fora do escopo atual.
MetalLB + Cilium BGPv2
Decisao
Modelo de ownership (importante — tem dois caminhos plausiveis)
Quem faz IPAM (alocacao de IP pro Service) vs quem anuncia o IP (BGP/L2)? Dois modos validos, nao misturar sem decidir explicitamente:
| Modo | IPAM (alocacao) | Anuncio (rota externa) | Quando |
|---|---|---|---|
| A. MetalLB IPAM-only + Cilium BGPv2 advertise | MetalLB controller (speaker desativado) | Cilium BGP fala com VyOS | Quando o pool de IPs precisa do address-management do MetalLB (memory leasing, multi-pool com prioridade) |
| B. Cilium LB-IPAM standalone (sem MetalLB) | Cilium LB IPAM (1.13+) usa CiliumLoadBalancerIPPool | Cilium BGP fala com VyOS | Stack mais simples — uma so controller pra LoadBalancer, menos componentes |
Risco se nao decidir: se MetalLB e Cilium ambos tentarem alocar IP pro mesmo LoadBalancer Service, conflito de owner. Sintoma: Service em estado pending ou IP “flapping” entre pools.
Recomendacao pra SP/Franca: comecar com modo B (Cilium LB-IPAM standalone) — menos um componente pra operar, suficiente pro caso atual. Reverter pra modo A so se aparecer feature do MetalLB que se prove necessaria.
Decisao concreta a tomar antes de provisionar: registrar como loadBalancerClass no Service ou via flag global no controller.
Pools (independente do modo escolhido acima)
| Site | Pool LoadBalancer |
|---|---|
| Franca | 192.168.0.240-250 |
| SP | 192.168.10.240-250 |
Inclui endpoint ClusterMesh API (necessario pra Cilium descobrir o outro cluster).
WireGuard site-to-site + BGP
Decisao do tunel
/30 dedicado pro tunel inter-site:
- Franca:
10.255.0.1 - SP:
10.255.0.2
Usado pra:
- Conectividade pod-to-pod cross-cluster (via ClusterMesh)
- BGP peering inter-site (Franca ASN 65001 ↔ SP ASN 65002)
- Acesso administrativo SSH se necessario
Quais prefixes BGP sao trocados onde
BGP em duas camadas — intra-site (Cilium ↔ VyOS local) e inter-site (VyOS ↔ VyOS via WireGuard). Cada uma anuncia coisas diferentes:
| Prefix | Anunciado por | Ouvido por | Direcao | Alcance |
|---|---|---|---|---|
| LoadBalancer VIPs locais (192.168.0.240-250 / 192.168.10.240-250) | Cilium BGPv2 | VyOS local | Intra-site | Tracked pelo VyOS local pra port-forward + roteamento |
| Pod CIDR local (10.41.0.0/16 / 10.42.0.0/16) | Cilium BGPv2 | VyOS local | Intra-site | VyOS sabe rotear pods se algo na rede mgmt precisar alcancar pod (raro mas possivel pra debug) |
| Service CIDR local (10.141.0.0/16 / 10.142.0.0/16) | nao anunciado | — | — | ClusterIP nao precisa rotear externamente |
| Pod CIDR remoto (peer cluster) | VyOS (apos receber do Cilium local) | VyOS peer (via WireGuard) | Inter-site | Necessario pra ClusterMesh — pod em SP fala com pod em Franca via tunel |
| LoadBalancer VIPs remotos | Pode ou nao ser anunciado inter-site | — | Opcional | Anunciar so se quiser failover de trafego entre VIPs cross-site (geralmente NAO — mantem regional) |
| WireGuard /30 (10.255.0.0/30) | nao precisa anunciar via BGP | — | — | Rota direta na interface WG |
Decisao concreta pendente: anunciar VIPs remotos via BGP inter-site? Default recomendado: nao — mantem trafego externo regional (cada site responde pelo seu DNS). Failover cross-site fica como decisao explicita de DR, nao automatico.
Pre-requisitos pra que ClusterMesh efetivamente funcione
Alem dos pre-requisitos do Cilium (CIDRs nao-sobrepostos, datapath consistente, cluster IDs):
- VyOS Franca e VyOS SP precisam ter rota pra Pod CIDR do peer via tunel WG
- Pra cada PodIP de SP que vier do tunel, VyOS Franca rotea pro K3s node correto via Cilium BGP local
- Firewall VyOS precisa permitir trafego entre Pod CIDRs cross-site (default: bloqueado)
K3s 3-node etcd HA em Franca
Decisao
3 nos K3s servers (etcd HA): vm-cpps-02 + vm-cpps-03 + vm-cpps-04 (04 a provisionar).
Ganhos:
- Elimina SPOF de control plane — 1 nó pode cair sem quebrar API
- Permite drain pra migracao Cilium e qualquer manutencao
- Karmada control plane confiavel — Karmada-host roda com etcd HA
- Live migration entre nodes Proxmox — VMs K3s podem migrar com seus discos LINSTOR replicados
Migracao Franca
In-place com janela de manutencao. Com 3 nos, kubectl drain move pods antes do restart, downtime so do pod, nao do cluster.
Traefik consolidado
Decisao
Traefik DaemonSet em namespace traefik-system no cluster Franca principal. Dissolve o K3s separado do “debian-proxy” — menos clusters pra operar, HA ganho pelos 3 nos do cluster principal.
Reversivel via GitOps em ~1 dia se necessario (separar de novo).
Padrao de exposicao
Internet ↓ DNS Cloudflare → IP publico VyOSVyOS (firewall + roteamento) ↓ port-forward 80/443 → MetalLB VIP em 192.168.0.240MetalLB anuncia VIP via Cilium BGPv2 ↓Traefik DaemonSet (traefik-system, NetworkPolicy L7 isolada) ↓ por Host headerApps em namespaces (tutor-openedx, etc)Distribuicao de Secrets e CAs cross-site
ArgoCD split + Karmada cria pergunta nao-trivial: como Secrets chegam em cada cluster sem virar repo de plain-text?
Opcoes consideradas
| Opcao | Como funciona | Pros | Cons |
|---|---|---|---|
| Sealed Secrets (Bitnami) | Encrypta com chave do cluster destino; YAML cifrado vai pro Git | Simples, GitOps-friendly, sem dependencia externa | Cada cluster tem chave propria — Secret cifrado pra Franca nao serve em SP. Reseal ao mover entre clusters |
| External Secrets Operator (ESO) | K8s busca Secrets de backend externo (Vault, AWS SM, GCP SM, Bitwarden) em runtime | Mesmo Secret resolvido em ambos os clusters; rotacao centralizada | Dependencia de backend externo (precisa Vault ou similar) |
| SOPS + KMS | Encrypta no Git via SOPS; ArgoCD plugin descriptografa antes de aplicar | Funciona com mesmo Secret em multiplos clusters | Plugin extra no ArgoCD; gestao de chaves KMS |
| Karmada PropagationPolicy + plain Secret | Secret em texto plano no Git; Karmada propaga | Trivial | Inseguro — secrets viram plain text no Git. Nao usar |
Recomendacao pra contexto atual
External Secrets Operator + Vault self-hosted ou Sealed Secrets dependendo do volume de secrets:
- Sealed Secrets se o volume e baixo (< 50 secrets) e a maioria sao por-site (DB password do Postgres em Franca, etc.). Reseal manual quando precisar mover. Sem dependencia externa
- ESO + Vault se o volume cresce ou se varios apps compartilham os mesmos secrets cross-site (ex: token de API externa, credencial S3 do MinIO). Vault em VM dedicada ou em K8s
Status: nenhum dos dois deployado ainda. Tutor/OpenEdX usa Secrets diretos (Kustomize generators) — funcional mas nao escala. Decidir antes de federar muitos apps via Karmada.
CAs do Cilium ClusterMesh
Decisao do plano: CA compartilhada (ver secao Cilium). Em pratica:
# Gerar CA uma vez, exportar pra ambos clusterscilium clustermesh ca generatecilium clustermesh ca install --context=cluster-fcacilium clustermesh ca install --context=cluster-spMaterial da CA mora no cilium-ca Secret no namespace kube-system de cada cluster — gerenciar como secret sensivel (igual kubeconfig do admin). Nao versionar em Git em texto plano.
Workload stateful vs stateless: onde failover funciona
Stateless (Cilium ClusterMesh + Karmada cobrem se configurado)
Apps sem state local (HTTP services, controllers, web frontends, batch jobs idempotentes):
- Karmada distribui replicas em SP e Franca via PropagationPolicy
- Cilium ClusterMesh roteia entre clusters somente services marcados como global — Service nao vira cross-cluster sozinho. Exige a annotation:
Sem essa annotation, Service vive isolado no proprio clustermetadata:annotations:service.cilium.io/global: "true"service.cilium.io/affinity: "local" # prefere endpoint do mesmo cluster
- Failover de trafego cross-site nao e automatico do ClusterMesh — precisa de uma camada externa (DNS health-checking, GSLB, anycast BGP, ou load balancer L4/L7 frontal). MetalLB+BGP local divulga VIPs dentro do site, nao orquestra failover entre sites
- Storage cross-site nao precisa — pods sao descartaveis
Exemplos viaveis: Traefik (com global service), controllers Karmada, ArgoCD agents, jupyter notebooks sem disco, pods de inferencia stateless
Stateful (failover automatico NAO funciona sem trabalho extra)
Apps com state local em PVs (DB, Tutor, NextCloud, MLflow tracking):
- Karmada agenda pods, mas o PV mora em um cluster especifico
- Failover requer:
- Storage replicado cross-site (LINSTOR snapshot ship + restore — manual)
- Ou app-level replication (Postgres logical replication, MinIO bucket replication)
- Ou aceita downtime + restore via Velero/PBS
Recomendacao: stateful apps fica pinada num site via PropagationPolicy (site: franca por exemplo). Failover e assistido (downtime + restore explicito), nao automatico.
Multi-site: como rede + storage + workload se compoem
SITE FRANCA SITE SP───────────────── ─────────────────
[Apps no Git] ─── ArgoCD-fca ─── K8s [Apps no Git] ─── ArgoCD-sp ─── K8s │ │ [Karmada (host=franca)] ────────────────────────► │ PropagationPolicy: stateless = ambos sites stateful = pin no site │ ▼K3s 3-node etcd HA K3s (single ou multi node) Cilium 1.17 CNI Cilium 1.17 CNI Hubble observability Hubble observability │ ◄────── Cilium ClusterMesh ──────────► │ │ (Pod IPAM nao sobrepoe) │ │ │MetalLB pool 192.168.0.240-250 MetalLB pool 192.168.10.240-250 │ ◄────── BGPv2 → VyOS-fca / VyOS-sp ────►│ │ │ ▼ ▼Traefik DaemonSet (consolidado) Traefik (se houver) │ │PVs via linstor-csi (TBD) PVs via linstor-csi (TBD) │ │ ▼ ▼LINSTOR cluster fca LINSTOR cluster sp (SSD em prod) (apos reset) pve-ippri-11/12/31, VIP 10.10.10.1 │ ◄── snapshot shipping cron ────────────►│ │ │Proxmox VMs (K3s nodes + outros) Proxmox VMs (K3s nodes + outros) │ │PBS (cobre VMs nao-K8s) PBS (cobre VMs nao-K8s) │ ◄── PBS sync ──────────────────────────►│ │ │WireGuard /30 10.255.0.1 ◄────────────────► 10.255.0.2 │ │MinIO local (planejado) MinIO local (planejado) Velero destinos (apps K8s) Velero destinos (apps K8s) Datasets / NextCloud objstore Datasets / NextCloud objstore │ ◄── MinIO replication ─────────────────►│ │ │VyOS (firewall, BGP, port-forward) VyOS (firewall, BGP, port-forward)Tools considerados e descartados (por que nao agora)
Longhorn nao esta nesta lista — continua como decisao em aberto (ver “linstor-csi vs alternativas K8s-native” e “Decisoes pendentes”). Vale comparacao formal antes de migrar workloads stateful.
Submariner
- Considerado: alternativa a Cilium ClusterMesh pra rede multi-cluster
- Por que nao: Cilium ja escolhido como CNI, ClusterMesh vem “de graca” sem outro tooling. Submariner exigiria mais 1 componente
- Reconsiderar se: precisar federar com cluster usando outro CNI (ex: AKS/EKS gerenciado)
Flux (vs ArgoCD)
- Considerado: GitOps alternativo
- Por que nao: ArgoCD ja em producao com Tutor, time familiarizado, dashboard maduro. Trocar nao traz ganho proporcional ao churn
- Reconsiderar se: footprint operacional do ArgoCD virar problema (improvavel pra escala atual)
Kasten K10
- Considerado: backup K8s comercial
- Por que nao: Velero + Kopia + MinIO cobre o caso open-source. Orcamento academico nao justifica subscricao
- Reconsiderar se: compliance (LGPD/HIPAA) exigir features especificas do K10
OpenEBS
- Considerado: familia K8s-native (Mayastor, ZFS-localpv)
- Por que nao: adocao menor que outras opcoes; ecosistema mais fragmentado
- Reconsiderar se: precisarmos de feature especifica (ZFS-localpv, NVMe-oF)
Anti-patterns a evitar
| Erro | Por que |
|---|---|
| Tratar Cilium ClusterMesh como substituto de LINSTOR cross-site | Camadas diferentes — Cilium e rede, LINSTOR e storage. Pra apps stateful precisa dos 2; stateless basta ClusterMesh |
| Esperar failover automatico Karmada pra workload stateful sem replicacao de storage | Karmada agenda pods; storage do PV nao migra sozinho. Stateful fica pinado por site, failover e assistido |
| Snapshot CSI Velero como unica protecao de PVs | Snapshot CSI fica no mesmo storage backend. Se storage cair, perde tudo. Combinar com Restic pra MinIO off-cluster |
| Velero como backup de VMs Proxmox | Velero so cobre K8s; VM tradicional precisa PBS ou vzdump |
Karmada hardcoded com clusterNames | Anula o design agnostico. Sempre usar labelSelector |
| ArgoCD pra recursos nao-K8s diretamente | Pulumi/Ansible cobrem infra; ArgoCD so pra K8s manifests (operadores podem ser excecao) |
| Misturar StorageClasses sem default claro | Confunde devs (PVCs vao pra storage diferente sem ele saber). Padronizar default + nomes |
| Adotar Longhorn alem do linstor-csi | 2 stacks de replicacao — escolher uma |
| Adotar Submariner alem do Cilium ClusterMesh | Redundante. ClusterMesh ja resolve |
Decisoes pendentes (acompanhar)
- linstor-csi vs Longhorn — formalizar comparacao e decidir
- vm-cpps-04 — provisionar pra completar 3-node etcd HA Franca
- MinIO — onde rodar (em K8s? em VM dedicada?), quando subir
- Velero — quando comecar a usar (gatilho: primeira workload K8s critica alem do Tutor que precise de restore granular ou DR formalizada). Mesmo gatilho usado em “Em aberto” e na secao Velero
- drbd-reactor — adicionar pra failover automatico controller LINSTOR (P3)
- DR drill — definir cadencia (trimestral?) e template (ja em linstor-troubleshooting.md)
Referencias
Documentos internos
- Plano detalhado:
prompts/plano-endereçamento-rede.md(endereçamento canonico v4) - Plano de execucao detalhado:
~/.claude/plans/revisar-questionar-alguns-pontos-para-steady-piglet.md(fora do repo — home do autor) - Storage e cross-site: storage-strategy.md
- LINSTOR conceitos: linstor-conceitos.md
- Troubleshooting + DRBD verify + monitoring: linstor-troubleshooting.md
- Tutor/OpenEdX (em producao): tutor-openedx.md, memory
project_tutor_openedx - GPU platform:
roadmap/gpu-platform.md, memoryproject_gpu_platform
Upstream
- ArgoCD: https://argo-cd.readthedocs.io/
- Karmada: https://karmada.io/docs/ — agnostic design: https://karmada.io/docs/userguide/scheduling/cluster-affinity/
- Cilium: https://docs.cilium.io/
- Velero: https://velero.io/docs/main/
- MetalLB: https://metallb.universe.tf/
- Piraeus / linstor-csi: https://github.com/piraeusdatastore/piraeus-operator
- MinIO replication: https://min.io/docs/minio/linux/administration/bucket-replication.html