Sandbox e query¶
Una sandbox è un database privato e usa e getta, ripristinato da un backup o da qualsiasi secondo della finestra. È la destinazione di ripristino predefinita, perché guardare non mette mai a rischio la produzione.
tenax restore team-a/app --at 2026-10-07T09:41:27Z --ttl 6h --wait
tenax sandbox list
tenax sandbox show <sandbox>
Cos'è una sandbox¶
- Un pod di database con l'engine e la versione del target (
spec.sandbox.imagesul target), nel namespace delle sandbox di Tenax, con credenziali generate e una NetworkPolicy che nega tutto per impostazione predefinita. - Un TTL (
sandbox.defaultTTL, al massimosandbox.maxTTL, entrambi per policy e per l'intero chart). Alla scadenza, Tenax elimina la sandbox e il suo storage.tenax sandbox extend <sandbox> --by 24hconcede più tempo;tenax sandbox delete <sandbox>la chiude prima. - Appartiene a chi l'ha richiesta; ogni azione su di essa viene registrata nell'audit.
Query¶
La pagina Query della console e tenax query eseguono SQL tramite il query proxy di Tenax, in sola lettura per
impostazione predefinita:
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
Ogni query ha un limite di 30 secondi e restituisce al massimo 1.000 righe; --write consente le scritture (solo
sandbox).
Nell'editor, Ctrl+Enter (Cmd+Enter su Mac) esegue la query, o solo il testo selezionato se c'è una
selezione. Il completamento propone parole chiave, funzioni, tabelle e colonne: si apre digitando dopo due caratteri,
dopo alias. elenca le colonne di quella tabella, dopo schema. le sue tabelle, e Ctrl+Space lo apre ovunque.
Tab o Enter sceglie un suggerimento, Esc lo chiude, e altrimenti Tab indenta.
La console parla l'SQL di ciascun engine (oggi PostgreSQL e MySQL); i risultati mostrano i valori così come li stampa il database.
Dimensionamento¶
Ogni sandbox è un pod di database nel proprio namespace. Ciò che riceve viene dal primo livello che lo imposta:
- il ripristino: i campi CPU, Memoria, Disco e Storage class nella pagina Ripristina (i valori in
grigio sono quelli che riceve se li lasci vuoti),
tenax restore … --cpu 2 --memory 8Gi --size 50Gi --storage-class local-path, ospec.destination.sandboxdi un Restore; - il target:
spec.sandbox.cpu,memory,sizeLimit,storageClassdel BackupTarget; - l'installazione:
sandbox.resourcesdel chart; - i valori integrati: 100m di CPU, 256Mi di memoria richiesta con un limite di 1Gi, e un disco pari al doppio dei
dati del backup più 512Mi (almeno 1Gi) sul nodo (
emptyDir, rimosso insieme alla sandbox).
| Impostazione | Significato |
|---|---|
| CPU | CPU richiesta; nessun limite di CPU, così un ripristino può usare i core inattivi |
| Memoria | request e limit della memoria |
| Disco | dimensione del volume dati; sul disco del nodo, a meno che non sia impostata una storage class |
| Storage class | i dati vanno invece su un PVC di questa classe, per ripristini più grandi di quanto consentano i dischi dei nodi |
sandbox.limits del chart imposta i tetti per CPU, memoria e disco. La console e l'API rifiutano una richiesta che li
supera, e un ripristino la cui dimensione risulta superiore (per un'impostazione del target, o per la dimensione
automatica di un backup grande) fallisce indicando il motivo, invece di riempire un disco a metà strada. Una sandbox
in esecuzione mantiene la sua dimensione: ripristina di nuovo per cambiarla.
Connettersi dal cluster¶
Host, porta e credenziali della sandbox sono nella sua pagina (Mostra credenziali, registrato nell'audit) e in tenax sandbox show.
I pod di altri namespace richiedono l'etichetta tenax.run/sandbox-client: <sandbox>; la network policy
della sandbox rifiuta tutto il resto.
Connettersi dall'esterno del cluster¶
Come si raggiungono le sandbox da un portatile lo decide l'installazione (expose.provider, più sotto). In ogni caso:
- Apri l'accesso esterno con Apri accesso esterno nella pagina della sandbox,
tenax sandbox expose <sandbox>, o apri accesso esterno durante il ripristino (tenax restore … --expose); serve il ruolooperator. - Connettiti con
tenax sandbox connect <sandbox>, che stampa una stringa di connessione per qualsiasi client di database, oppure copiala dalla pagina della sandbox. - Chiudilo con Chiudi accesso esterno o
tenax sandbox unexpose <sandbox>. Si chiude anche quando la sandbox scade o viene eliminata.
expose.provider |
Cosa fa tenax sandbox connect |
Cosa serve al client |
|---|---|---|
none (predefinito) |
Inoltra una porta locale alla sandbox tramite il tuo kubeconfig, fino a Ctrl+C | Un kubeconfig autorizzato al port-forward in tenax-sbx-<sandbox> |
service |
Stampa l'indirizzo della sandbox stessa (postgres://…@sbx-7f3a.db.example.com:5432/app?sslmode=require) |
Niente: qualsiasi client di database, via TLS |
cloudflare |
Apre cloudflared access tcp su una porta locale dopo un login di Access, fino a Ctrl+C |
cloudflared e un indirizzo email autorizzato |
Le sandbox Postgres servono TLS con un certificato proprio (autofirmato: i client usano sslmode=require, che cifra
senza verificare l'emittente); le sandbox MySQL usano il certificato generato da MySQL.
Raggiungere le sandbox dall'esterno del cluster (amministratori)¶
Tenax non apre nulla da solo: ogni opzione usa qualcosa che il tuo cluster ha già.
Opzione service: un Service Kubernetes per sandbox¶
Ogni sandbox esposta riceve un Service costruito da expose.service; ciò che il tuo cluster usa per i Service le
assegna un indirizzo, e Tenax mostra quell'indirizzo. È l'opzione più semplice per gli sviluppatori (qualsiasi client
di database) e funziona con qualsiasi Kubernetes:
| Il tuo cluster ha | Imposta | Gli sviluppatori si connettono a |
|---|---|---|
| Un load balancer cloud (AKS, EKS, GKE) | type: LoadBalancer, sourceRanges: [<office or VPN CIDRs>] |
L'IP del load balancer |
| Lo stesso, più external-dns | anche 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 |
Un indirizzo della tua rete |
| L'operator Tailscale | loadBalancerClass: tailscale |
Il nome tailnet della sandbox; niente è pubblico |
| Niente di tutto ciò | type: NodePort, hostnamePattern: "nodes.example.com" con un nome corrispondente per i tuoi nodi |
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
Le sandbox contengono copie dei dati di produzione
Un load balancer pubblico protegge una sandbox solo con la sua password casuale, il TLS e il suo limite di tempo.
Imposta sourceRanges sugli indirizzi da cui arrivano i tuoi sviluppatori, o usa un'opzione privata (Tailscale,
un load balancer interno tramite l'annotazione del tuo cloud, MetalLB su una rete privata).
Mentre una sandbox è esposta, una NetworkPolicy aggiuntiva ammette expose.service.allowFrom (predefinito: tutto;
il filtro lo fa sourceRanges del load balancer, dato che il suo traffico arriva spesso dagli indirizzi dei nodi)
sulla porta del database. Con il load balancer integrato di k3s ogni sandbox esposta occupa la porta del database sui
nodi, quindi lì si può esporre una sola sandbox Postgres e una sola sandbox MySQL alla volta.
Opzione cloudflare: un tunnel dietro un login di Access¶
I tunnel richiedono un token API di Cloudflare, che appartiene all'installazione, non a un utente. È un Secret Kubernetes nel namespace di Tenax, configurato una volta come qualsiasi altra credenziale (con gli strumenti GitOps per i secret, per esempio SOPS). La console non lo chiede, non lo conserva e non lo mostra mai: può modificare il DNS e le applicazioni Access della zona.
- In Cloudflare, My Profile → API Tokens → Create Token → Custom token con questi permessi, per la zona in cui
si trovano gli hostname:
- Account: Cloudflare Tunnel: Edit
- Account: Access: Apps and Policies: Edit
- Zone: DNS: Edit
- Zone: Zone: Read
-
Salvalo:
-
Configura il chart:
Tieni gli hostname un livello sotto la zona (sbx-{id}.example.com, non sbx-{id}.db.example.com): il certificato
gratuito di Cloudflare copre solo *.example.com, e il client raggiunge l'hostname via HTTPS. Una porta di database
non può essere protetta da un login nel browser, quindi gli sviluppatori eseguono cloudflared, cosa che
tenax sandbox connect fa per loro.
Con il token, Tenax trova l'account a partire dalla zona, crea un tunnel chiamato tenax (expose.cloudflare.tunnelName)
o lo riutilizza, e scrive il connector token del tunnel nel Secret <release>-tunnel-token, da cui partono i pod
cloudflared. Controlla quel Secret ogni 15 minuti, così un token ruotato in Cloudflare arriva da solo nel Secret
(riavvia i pod cloudflared perché lo recepiscano). allowEmails accetta indirizzi e domini interi
(*@example.com); allowGroups accetta id di gruppi Access.
Un tunnel che gestisci già
Imposta expose.cloudflare.tunnelID per usare un tunnel esistente gestito da remoto al posto di tenax, e
expose.cloudflared.tunnelTokenSecret su un Secret (chiave token) con il suo connector token se preferisci
fornirlo tu; in quel caso Tenax non tocca quel Secret. Tenax aggiunge e rimuove solo le proprie regole sbx-…,
quindi le altre route del tunnel restano invariate. accountID e zoneID possono essere impostati per saltare
le ricerche.
Lo stesso tunnel può servire anche la console: aggiungi al tunnel un hostname pubblico per tenax-api (Zero Trust →
Networks → Tunnels → tenax → Public hostnames) dietro un'applicazione Access, e imposta
expose.cloudflared.consoleHostname con auth.provider: cloudflare-access: vedi Console, CLI e accesso.