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.imageno 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áximosandbox.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 24hdá 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:
- 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, ouspec.destination.sandboxde um Restore; - o alvo:
spec.sandbox.cpu,memory,sizeLimit,storageClassdo BackupTarget; - a instalação:
sandbox.resourcesdo chart; - 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 papeloperator. - 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.
- 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
-
Guarde-o:
-
Configure o chart:
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.