Topology
Nodes, domínios, secret model e isolamento do cluster
Nodes
| Node | Endereço | Papel | Specs |
|---|---|---|---|
| finatto-k3s-server-01 | 143.95.212.107 | server: control plane + workload | 8 vCPU, 32 GB, amd64, AlmaLinux 9 |
| finatto-k3s-server-02 | 201.33.85.173 (ssh 6985) | server: control plane only, tainted | 2 vCPU, 2 GB, amd64, Debian 13 |
| finatto-k3s-server-03 | 201.33.85.174 (ssh 6985) | server: control plane only, tainted | 2 vCPU, 2 GB, amd64, Debian 13 |
| finatto-k3s-worker-01 | 168.75.104.109 | agent: workload only | 4 vCPU, 24 GB, arm64, Ubuntu 24.04 |
Os quatro nodes ficam em três provedores diferentes e se comunicam pela internet pública — o flannel roda wireguard-native, então todo pacote node-a-node é criptografado. Latência pod-a-pod medida entre datacenters: ~3,4ms.
Dois motivos moldaram esse layout:
- etcd não pode viver atrás de NAT.
finatto-k3s-worker-01é uma instância Oracle Ampere com NAT 1:1: um membro etcd ali anunciaria o endereço privado, que nenhum peer alcança, e não pode bindar o público porque os pacotes chegam endereçados ao privado. Por isso é agent, não server. - Três membros etcd, não quatro. Quórum precisa de contagem ímpar. Com três, os dois membros de baixa latência confirmam uma escrita sem esperar o mais distante; um quarto adicionaria latência e um segundo nó fraco no caminho crítico sem melhorar a tolerância a falha.
finatto-k3s-worker-01 é arm64, o resto é amd64 — qualquer imagem de aplicação precisa ser um manifest multi-arch. Recurso single-arch precisa de nodeSelector: kubernetes.io/arch.
Firewall por node libera só os endereços dos peers: etcd (2379-2380), kubelet (10250), wireguard (51820/udp) e flannel (8472/udp); 6443, 80 e 443 ficam abertos ao público. O node Oracle precisa das mesmas regras também na VCN, porque o firewall de nuvem dele derruba tráfego antes de chegar ao host.
Domínios
| O quê | Host |
|---|---|
| Aplicações de cliente | <slug>.frota.memphislab.com.br |
| License manager | license-manager.memphislab.com.br |
| Grafana | grafana.memphislab.com.br |
| ArgoCD | argocd.memphislab.com.br |
| MinIO API / console | minio.memphislab.com.br / minio-console.memphislab.com.br |
Um certificado cobre *.frota.memphislab.com.br mais um SAN explícito por host de plataforma, emitido via cert-manager com solver Cloudflare DNS-01. Não existe wildcard em memphislab.com.br — outros serviços fora desta plataforma vivem nesse domínio, e um certificado *.memphislab.com.br seria uma credencial capaz de se passar por qualquer um deles.
Secret model
clients/<slug>.yamlé commitado e não tem segredo nenhum — é o que o ApplicationSet lê para saber que um cliente existe.sealed/<slug>.yamlé commitado e cifrado: guarda doisSealedSecret(<slug>-secretse<slug>-pg-creds). Só a chave privada do controller, que nunca sai do cluster, decifra.- O chart nunca renderiza um secret em produção:
secrets.external: truefaz ele referenciar os Secrets que o controller materializa. scripts/check-no-secrets.shroda em CI e falha o build se algum arquivo emclients/carregar algo parecido com segredo.
Backup da chave do controller (sem ela, nenhum SealedSecret existente pode ser decifrado de novo):
make secrets-backup-key > sealed-secrets-key.bak # guardar fora do Git
Isolamento
NetworkPolicydefault-deny por namespace. Egress libera DNS, namespaces com labelfinatto.io/shared=true(MinIO, monitoring) e internet pública, mas bloqueia as faixas RFC1918 — é isso que impede um cliente de alcançar o banco de outro pela rede de pods.ResourceQuotaeLimitRangepor cliente.- Banco, Redis e credenciais separados por namespace.
- PriorityClasses em camada:
platform-critical(2000000, infra compartilhada) >critical(1000000, app e datastore do cliente) >standard(1000, workers e jobs). Infra compartilhada precisa ter prioridade maior — um Traefik despejado derruba todo cliente de uma vez.
Backup
Cada Postgres via CloudNativePG faz streaming de WAL contínuo e um backup base diário às 03:00 em s3://finatto-backups/<slug> no MinIO, com retenção de 30 dias. Restaurar significa criar um novo Cluster com bootstrap.recovery apontando para o mesmo barmanObjectStore — testar a restauração antes de depender dela.
Lacunas conhecidas
barmanObjectStorein-tree é removido no CloudNativePG 1.31.0 (a próxima minor depois da 0.29.0 pinada aqui). O operador já avisa em todo apply; o backup segue funcionando enquanto a versão ficar pinada, e quebra assim que alguém atualizar. Migrar para o pluginbarman-cloudé o próximo trabalho real nessa frente.spec.monitoring.enablePodMonitorestá deprecado na mesma release — o PodMonitor vai precisar virar um recurso que o chart gerencia diretamente.- A NetworkPolicy precisa de verificação em staging: dependendo da CNI, probes do kubelet partindo do IP do node podem ser afetados.
- Alertmanager está desligado: não existe alerta para backup falhando ou certificado expirando.