Pular para conteúdo

Sandboxes e consultas

Um sandbox é um banco privado e descartável restaurado a partir de um backup ou de qualquer segundo da janela. É o destino padrão das restaurações, porque olhar os dados nunca coloca a produção em risco.

tenax restore team-a/app --at 2026-10-07T09:41:27Z --ttl 6h --wait
tenax sandbox list
tenax sandbox show <sandbox>

O que é um sandbox

  • Um pod de banco com a engine e a versão do alvo (spec.sandbox.image no alvo), no namespace de sandboxes do Tenax, com credenciais geradas e uma NetworkPolicy que nega tudo por padrão.
  • Um TTL (sandbox.defaultTTL, no máximo sandbox.maxTTL, ambos por política e para todo o chart). Quando ele expira, o Tenax exclui o sandbox e o armazenamento dele. tenax sandbox extend <sandbox> --by 24h dá mais tempo; tenax sandbox delete <sandbox> o encerra antes.
  • Pertence a quem o pediu; toda ação sobre ele é auditada.

Consultas

A página Consulta do console e o tenax query executam SQL pelo proxy de consultas do Tenax, somente leitura por padrão:

tenax query <sandbox> -e "select count(*) from orders where created_at > now() - interval '1 day'"
tenax query <sandbox> -e "select * from coupons" -o csv > coupons.csv

Cada consulta tem um limite de 30 segundos e retorna no máximo 1.000 linhas; --write permite escritas (apenas em sandboxes).

No editor, Ctrl+Enter (Cmd+Enter no Mac) executa a consulta, ou só o texto selecionado quando há uma seleção. O autocompletar sugere palavras-chave, funções, tabelas e colunas: ele abre ao digitar dois caracteres; depois de alias. lista as colunas daquela tabela, depois de schema. as tabelas dele, e Ctrl+Space o abre em qualquer lugar. Tab ou Enter escolhe uma sugestão, Esc fecha a lista, e fora dela Tab indenta.

O console fala o SQL de cada engine (PostgreSQL e MySQL hoje); os resultados mostram os valores como o banco os imprime.

Dimensionamento

Cada sandbox é um pod de banco no próprio namespace. O que ele recebe vem do primeiro lugar que define o valor:

  1. a restauração: os campos CPU, Memória, Disco e Classe de armazenamento da página Restore (os valores em cinza são o que ele recebe se você deixá-los vazios), tenax restore … --cpu 2 --memory 8Gi --size 50Gi --storage-class local-path, ou spec.destination.sandbox de um Restore;
  2. o alvo: spec.sandbox.cpu, memory, sizeLimit, storageClass do BackupTarget;
  3. a instalação: sandbox.resources do chart;
  4. o padrão embutido: 100m de CPU, 256Mi de memória solicitados com limite de 1Gi, e um disco de duas vezes os dados do backup mais 512Mi (no mínimo 1Gi) no nó (emptyDir, removido junto com o sandbox).
Configuração Significado
CPU CPU solicitada; sem limite de CPU, para que uma restauração possa usar núcleos ociosos
Memória solicitação e limite de memória
Disco tamanho do volume de dados; no disco do nó, a menos que uma storage class seja definida
Storage class os dados vão para um PVC dessa classe, para restaurações maiores do que os discos dos nós permitem

O sandbox.limits do chart define tetos para CPU, memória e disco. O console e a API recusam uma solicitação acima deles, e uma restauração cujo tamanho fique acima deles (uma configuração do alvo, ou o tamanho automático de um backup grande) falha com o motivo, em vez de encher um disco no meio do caminho. Um sandbox em execução mantém o seu tamanho: restaure de novo para mudá-lo.

Conectar de dentro do cluster

O host, a porta e as credenciais do sandbox estão na página dele (Revelar credenciais, auditado) e em tenax sandbox show. Pods em outros namespaces precisam do label tenax.run/sandbox-client: <sandbox>; a network policy do sandbox recusa todo o resto.

Conectar de fora do cluster

Como os sandboxes são acessados de um notebook é escolha da instalação (expose.provider, abaixo). Em todos os casos:

  • Abra o acesso externo com Abrir acesso externo na página do sandbox, com tenax sandbox expose <sandbox> ou com open external access ao restaurar (tenax restore … --expose); é preciso ter o papel operator.
  • Conecte-se com tenax sandbox connect <sandbox>, que mostra uma string de conexão para qualquer cliente de banco, ou copie-a da página do sandbox.
  • Feche com Fechar acesso externo ou tenax sandbox unexpose <sandbox>. O acesso também fecha quando o sandbox expira ou é excluído.
expose.provider O que tenax sandbox connect faz O cliente precisa de
none (padrão) Encaminha uma porta local para o sandbox pelo seu kubeconfig, até Ctrl+C Um kubeconfig com permissão de port-forward em tenax-sbx-<sandbox>
service Mostra o endereço do próprio sandbox (postgres://…@sbx-7f3a.db.example.com:5432/app?sslmode=require) Nada: qualquer cliente de banco, via TLS
cloudflare Abre cloudflared access tcp em uma porta local depois de um login no Access, até Ctrl+C cloudflared e um e-mail autorizado

Os sandboxes Postgres servem TLS com um certificado próprio (autoassinado: os clientes usam sslmode=require, que criptografa sem verificar o emissor); os sandboxes MySQL usam o certificado que o MySQL gera.

Acessar sandboxes de fora do cluster (administradores)

O Tenax não abre nada por conta própria: cada opção usa algo que o seu cluster já tem.

Opção service: um Service do Kubernetes por sandbox

Cada sandbox exposto recebe um Service montado a partir de expose.service; o que o seu cluster usa para Services dá um endereço a ele, e o Tenax mostra esse endereço. É a opção mais simples para os desenvolvedores (qualquer cliente de banco) e funciona com qualquer Kubernetes:

O seu cluster tem Defina Os desenvolvedores se conectam a
Um load balancer de nuvem (AKS, EKS, GKE) type: LoadBalancer, sourceRanges: [<office or VPN CIDRs>] O IP do load balancer
O mesmo, mais external-dns também annotations: {external-dns.alpha.kubernetes.io/hostname: "sbx-{id}.db.example.com"} e hostnamePattern: "sbx-{id}.db.example.com" sbx-<id>.db.example.com
MetalLB (on-premises) type: LoadBalancer Um endereço na sua rede
O operator do Tailscale loadBalancerClass: tailscale O nome do sandbox na tailnet; nada fica público
Nada disso type: NodePort, hostnamePattern: "nodes.example.com" com um nome correspondente para os seus nós nodes.example.com:<node port>
expose:
  provider: service
  service:
    type: LoadBalancer
    sourceRanges: ["203.0.113.0/24"]   # who may connect; leave empty only on a private network

Os sandboxes guardam cópias de dados de produção

Um load balancer público protege um sandbox apenas com a senha aleatória, o TLS e o limite de tempo dele. Defina sourceRanges com os endereços de onde vêm os seus desenvolvedores, ou use uma opção privada (Tailscale, um load balancer interno pela annotation da sua nuvem, MetalLB em uma rede privada).

Enquanto um sandbox está exposto, uma NetworkPolicy extra admite expose.service.allowFrom (padrão: tudo; o sourceRanges do load balancer faz a filtragem, já que o tráfego dele costuma chegar pelos endereços dos nós) na porta do banco. No load balancer embutido do k3s, cada sandbox exposto ocupa a porta do banco nos nós, então lá só é possível expor um sandbox Postgres e um MySQL por vez.

Opção cloudflare: um túnel atrás de um login do Access

Os túneis precisam de um token de API do Cloudflare, que pertence à instalação, não a um usuário. É um Secret do Kubernetes no namespace do Tenax, configurado uma vez como qualquer outra credencial (com a ferramenta de secrets do seu GitOps, por exemplo SOPS). O console nunca o pede, guarda ou mostra: com ele é possível alterar DNS e aplicações do Access na zona.

  1. No Cloudflare, My Profile → API Tokens → Create Token → Custom token com estas permissões, para a zona em que ficam os hostnames:
    • Account: Cloudflare Tunnel: Edit
    • Account: Access: Apps and Policies: Edit
    • Zone: DNS: Edit
    • Zone: Zone: Read
  2. Guarde-o:

    kubectl -n tenax-system create secret generic tenax-cloudflare-api --from-file=token=api-token.txt
    
  3. Configure o chart:

    expose:
      provider: cloudflare
      cloudflare:
        zone: example.com
        apiTokenSecret: tenax-cloudflare-api
        hostnamePattern: "sbx-{id}.example.com"
        allowEmails: ["*@example.com"]      # who may connect to an open tunnel
      cloudflared:
        enabled: true
    

Mantenha os hostnames um nível abaixo da zona (sbx-{id}.example.com, não sbx-{id}.db.example.com): o certificado gratuito do Cloudflare cobre apenas *.example.com, e o cliente acessa o hostname via HTTPS. Uma porta de banco não pode ser protegida por um login no navegador, então os desenvolvedores rodam o cloudflared, o que o tenax sandbox connect faz por eles.

Com o token, o Tenax encontra a conta a partir da zona, cria um túnel chamado tenax (expose.cloudflare.tunnelName) ou o reutiliza, e grava o token do conector do túnel no Secret <release>-tunnel-token, a partir do qual os pods do cloudflared iniciam. Ele confere esse Secret a cada 15 minutos, então um token rotacionado no Cloudflare chega ao Secret sozinho (reinicie os pods do cloudflared para aplicá-lo). allowEmails aceita endereços e domínios inteiros (*@example.com); allowGroups aceita IDs de grupos do Access.

Um túnel que você já gerencia

Defina expose.cloudflare.tunnelID para usar um túnel existente gerenciado remotamente em vez de tenax, e expose.cloudflared.tunnelTokenSecret com um Secret (chave token) contendo o token do conector, se preferir fornecê-lo você mesmo; nesse caso o Tenax não mexe nesse Secret. O Tenax só adiciona e remove as próprias regras sbx-…, então as outras rotas do túnel ficam como estão. accountID e zoneID podem ser definidos para pular as buscas.

O mesmo túnel também pode servir o próprio console: adicione ao túnel um hostname público para o tenax-api (Zero Trust → Networks → Tunnels → tenax → Public hostnames) atrás de uma aplicação do Access e defina expose.cloudflared.consoleHostname com auth.provider: cloudflare-access: veja Console, CLI e acesso.