Topology · MemphisLab Docs
MemphisLab Docs
k3s Platform

Topology

Nodes, domínios, secret model e isolamento do cluster

Nodes

NodeEndereçoPapelSpecs
finatto-k3s-server-01143.95.212.107server: control plane + workload8 vCPU, 32 GB, amd64, AlmaLinux 9
finatto-k3s-server-02201.33.85.173 (ssh 6985)server: control plane only, tainted2 vCPU, 2 GB, amd64, Debian 13
finatto-k3s-server-03201.33.85.174 (ssh 6985)server: control plane only, tainted2 vCPU, 2 GB, amd64, Debian 13
finatto-k3s-worker-01168.75.104.109agent: workload only4 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 managerlicense-manager.memphislab.com.br
Grafanagrafana.memphislab.com.br
ArgoCDargocd.memphislab.com.br
MinIO API / consoleminio.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 dois SealedSecret (<slug>-secrets e <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: true faz ele referenciar os Secrets que o controller materializa.
  • scripts/check-no-secrets.sh roda em CI e falha o build se algum arquivo em clients/ 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

  • NetworkPolicy default-deny por namespace. Egress libera DNS, namespaces com label finatto.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.
  • ResourceQuota e LimitRange por 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

  • barmanObjectStore in-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 plugin barman-cloud é o próximo trabalho real nessa frente.
  • spec.monitoring.enablePodMonitor está 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.