Bacs à sable et requêtes¶
Un bac à sable est une base privée et jetable, restaurée depuis une sauvegarde ou depuis n'importe quelle seconde de la fenêtre. C'est la destination de restauration par défaut, car regarder ne met jamais la production en danger.
tenax restore team-a/app --at 2026-10-07T09:41:27Z --ttl 6h --wait
tenax sandbox list
tenax sandbox show <sandbox>
Ce qu'est un bac à sable¶
- Un pod de base de données du moteur et de la version de la cible (
spec.sandbox.imagesur la cible), dans le namespace des bacs à sable de Tenax, avec des identifiants générés et une NetworkPolicy qui refuse tout par défaut. - Un TTL (
sandbox.defaultTTL, au plussandbox.maxTTL, tous deux par politique et pour tout le chart). À son expiration, Tenax supprime le bac à sable et son stockage.tenax sandbox extend <sandbox> --by 24hdonne plus de temps ;tenax sandbox delete <sandbox>y met fin plus tôt. - Il appartient à la personne qui l'a demandé ; chaque action sur lui est auditée.
Requêtes¶
La page Requête de la console et tenax query exécutent le SQL via le proxy de requêtes de Tenax, en lecture
seule par défaut :
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
Chaque requête est limitée à 30 secondes et renvoie au plus 1 000 lignes ; --write autorise les écritures (bacs à
sable uniquement).
Dans l'éditeur, Ctrl+Enter (Cmd+Enter sur Mac) exécute la requête, ou seulement le texte sélectionné s'il y
a une sélection. L'autocomplétion propose mots-clés, fonctions, tables et colonnes : elle s'ouvre après deux
caractères tapés, liste les colonnes de la table après alias. et ses tables après schema., et Ctrl+Space
l'ouvre n'importe où. Tab ou Enter choisit une suggestion, Esc la ferme, et Tab indente sinon.
La console parle le SQL de chaque moteur (PostgreSQL et MySQL à ce jour) ; les résultats affichent les valeurs telles que la base les imprime.
Dimensionnement¶
Chaque bac à sable est un pod de base de données dans son propre namespace. Ce qu'il reçoit, d'après la première source qui le définit :
- la restauration : les champs CPU, Mémoire, Disque et Classe de stockage de la page Restore (les valeurs
grisées sont celles qu'il reçoit si vous les laissez vides),
tenax restore … --cpu 2 --memory 8Gi --size 50Gi --storage-class local-path, ouspec.destination.sandboxd'un Restore ; - la cible :
spec.sandbox.cpu,memory,sizeLimit,storageClassdu BackupTarget ; - l'installation :
sandbox.resourcesdu chart ; - les valeurs intégrées : 100m de CPU, 256Mi de mémoire demandés avec une limite de 1Gi, et un disque de deux fois
les données de la sauvegarde plus 512Mi (au moins 1Gi) sur le nœud (
emptyDir, supprimé avec le bac à sable).
| Paramètre | Signification |
|---|---|
| CPU | requête CPU ; pas de limite CPU, pour qu'une restauration puisse utiliser les cœurs inoccupés |
| Memory | requête et limite mémoire |
| Disk | taille du volume de données ; sur le disque du nœud, sauf si une storage class est définie |
| Storage class | les données vont alors sur un PVC de cette classe, pour les restaurations plus grandes que ce que permettent les disques des nœuds |
sandbox.limits du chart fixe des plafonds pour le CPU, la mémoire et le disque. La console et l'API refusent une
demande au-delà, et une restauration dont la taille calculée les dépasse (un réglage de la cible, ou la taille
automatique d'une grosse sauvegarde) échoue en donnant la raison, au lieu de remplir un disque à mi-chemin. Un bac à
sable en cours d'exécution garde sa taille : restaurez à nouveau pour la changer.
Se connecter depuis le cluster¶
L'hôte, le port et les identifiants du bac à sable figurent sur sa page (Afficher les identifiants, audité) et dans
tenax sandbox show. Les pods d'autres namespaces ont besoin du label
tenax.run/sandbox-client: <sandbox> ; la network policy du bac à sable refuse tout le reste.
Se connecter depuis l'extérieur du cluster¶
La manière d'atteindre les bacs à sable depuis un poste relève de l'installation (expose.provider, ci-dessous).
Dans tous les cas :
- Ouvrez l'accès externe avec Ouvrir l'accès externe sur la page du bac à sable, avec
tenax sandbox expose <sandbox>, ou en cochant open external access lors de la restauration (tenax restore … --expose) ; il faut le rôleoperator. - Connectez-vous avec
tenax sandbox connect <sandbox>, qui affiche une chaîne de connexion pour n'importe quel client de base de données, ou copiez-la depuis la page du bac à sable. - Fermez-le avec Fermer l'accès externe ou
tenax sandbox unexpose <sandbox>. Il se ferme aussi quand le bac à sable expire ou est supprimé.
expose.provider |
Ce que fait tenax sandbox connect |
Ce qu'il faut au client |
|---|---|---|
none (par défaut) |
Redirige un port local vers le bac à sable via votre kubeconfig, jusqu'à Ctrl+C | Un kubeconfig autorisé à faire du port-forward dans tenax-sbx-<sandbox> |
service |
Affiche l'adresse propre du bac à sable (postgres://…@sbx-7f3a.db.example.com:5432/app?sslmode=require) |
Rien : n'importe quel client de base de données, en TLS |
cloudflare |
Ouvre cloudflared access tcp sur un port local après une connexion Access, jusqu'à Ctrl+C |
cloudflared et une adresse e-mail autorisée |
Les bacs à sable Postgres servent du TLS avec leur propre certificat (auto-signé : les clients utilisent
sslmode=require, qui chiffre sans vérifier l'émetteur) ; les bacs à sable MySQL utilisent le certificat que MySQL
génère.
Atteindre les bacs à sable depuis l'extérieur du cluster (administrateurs)¶
Tenax n'ouvre rien de lui-même : chaque option s'appuie sur quelque chose que votre cluster possède déjà.
Option service : un Service Kubernetes par bac à sable¶
Chaque bac à sable exposé reçoit un Service construit à partir de expose.service ; ce que votre cluster utilise
pour les Services lui attribue une adresse, et Tenax affiche cette adresse. C'est l'option la plus simple pour les
développeurs (n'importe quel client de base de données) et elle fonctionne avec tout Kubernetes :
| Votre cluster a | Définissez | Les développeurs se connectent à |
|---|---|---|
| Un load balancer cloud (AKS, EKS, GKE) | type: LoadBalancer, sourceRanges: [<office or VPN CIDRs>] |
L'IP du load balancer |
| Le même, plus external-dns | aussi annotations: {external-dns.alpha.kubernetes.io/hostname: "sbx-{id}.db.example.com"} et hostnamePattern: "sbx-{id}.db.example.com" |
sbx-<id>.db.example.com |
| MetalLB (sur site) | type: LoadBalancer |
Une adresse de votre réseau |
| L'opérateur Tailscale | loadBalancerClass: tailscale |
Le nom tailnet du bac à sable ; rien n'est public |
| Rien de tout cela | type: NodePort, hostnamePattern: "nodes.example.com" avec un nom correspondant pour vos nœuds |
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
Les bacs à sable contiennent des copies de données de production
Un load balancer public ne protège un bac à sable que par son mot de passe aléatoire, le TLS et sa durée de vie
limitée. Réglez sourceRanges sur les adresses d'où viennent vos développeurs, ou utilisez une option privée
(Tailscale, un load balancer interne via l'annotation de votre cloud, MetalLB sur un réseau privé).
Tant qu'un bac à sable est exposé, une NetworkPolicy supplémentaire admet expose.service.allowFrom (par défaut :
tout ; c'est le sourceRanges du load balancer qui filtre, car son trafic arrive souvent depuis les adresses des
nœuds) sur le port de la base. Avec le load balancer intégré de k3s, chaque bac à sable exposé occupe le port de la
base sur les nœuds : on ne peut donc y exposer qu'un bac à sable Postgres et un bac à sable MySQL à la fois.
Option cloudflare : un tunnel derrière une connexion Access¶
Les tunnels nécessitent un jeton d'API Cloudflare, qui appartient à l'installation et non à un utilisateur. C'est un Secret Kubernetes dans le namespace de Tenax, configuré une fois comme n'importe quel autre identifiant (avec votre outillage GitOps de secrets, par exemple SOPS). La console ne le demande jamais, ne le détient pas et ne l'affiche pas : il peut modifier le DNS et les applications Access de la zone.
- Dans Cloudflare, My Profile → API Tokens → Create Token → Custom token avec ces permissions, pour la zone qui
contient les noms d'hôte :
- Account: Cloudflare Tunnel: Edit
- Account: Access: Apps and Policies: Edit
- Zone: DNS: Edit
- Zone: Zone: Read
-
Stockez-le :
-
Configurez le chart :
Gardez les noms d'hôte à un seul niveau sous la zone (sbx-{id}.example.com, et non sbx-{id}.db.example.com) :
le certificat gratuit de Cloudflare ne couvre que *.example.com, et le client joint le nom d'hôte en HTTPS. Un
port de base de données ne peut pas être protégé par une connexion dans le navigateur ; les développeurs exécutent
donc cloudflared, ce que tenax sandbox connect fait pour eux.
Avec le jeton, Tenax trouve le compte à partir de la zone, crée un tunnel nommé tenax
(expose.cloudflare.tunnelName) ou le réutilise, et écrit le jeton de connecteur du tunnel dans le Secret
<release>-tunnel-token, à partir duquel démarrent les pods cloudflared. Il vérifie ce Secret toutes les
15 minutes : un jeton renouvelé dans Cloudflare atteint donc le Secret de lui-même (redémarrez les pods
cloudflared pour le prendre en compte). allowEmails accepte des adresses et des domaines entiers
(*@example.com) ; allowGroups accepte des identifiants de groupes Access.
Un tunnel que vous gérez déjà
Définissez expose.cloudflare.tunnelID pour utiliser un tunnel existant géré à distance au lieu de tenax, et
expose.cloudflared.tunnelTokenSecret vers un Secret (clé token) contenant son jeton de connecteur si vous
préférez le fournir vous-même ; Tenax ne touche alors pas à ce Secret. Tenax n'ajoute et ne retire que ses
propres règles sbx-…, si bien que les autres routes du tunnel restent telles quelles. accountID et zoneID
peuvent être définis pour éviter les recherches.
Le même tunnel peut aussi servir la console elle-même : ajoutez au tunnel un nom d'hôte public pour tenax-api
(Zero Trust → Networks → Tunnels → tenax → Public hostnames) derrière une application Access, et définissez
expose.cloudflared.consoleHostname avec auth.provider: cloudflare-access : voir
Console, CLI et accès.