Saltar a contenido

Sandboxes y consultas

Un sandbox es una base de datos privada y desechable restaurada desde un respaldo o desde cualquier segundo de la ventana. Es el destino de restauración por defecto, porque mirar nunca pone en riesgo producción.

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

Qué es un sandbox

  • Un pod de base de datos con el motor y la versión del objetivo (spec.sandbox.image en el objetivo), en el namespace de sandboxes de Tenax, con credenciales generadas y una NetworkPolicy que lo deniega todo por defecto.
  • Un TTL (sandbox.defaultTTL, como máximo sandbox.maxTTL, ambos por política y para todo el chart). Cuando vence, Tenax borra el sandbox y su almacenamiento. tenax sandbox extend <sandbox> --by 24h da más tiempo; tenax sandbox delete <sandbox> lo termina antes.
  • Pertenece a quien lo pidió; cada acción sobre él queda auditada.

Consultas

La página Consulta de la consola y tenax query ejecutan SQL a través del proxy de consultas de Tenax, en solo lectura por defecto:

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 tiene un límite de 30 segundos y devuelve como máximo 1.000 filas; --write permite escrituras (solo en sandboxes).

En el editor, Ctrl+Enter (Cmd+Enter en un Mac) ejecuta la consulta, o solo el texto seleccionado si hay una selección. El autocompletado ofrece palabras clave, funciones, tablas y columnas: se abre al escribir dos caracteres; tras alias. lista las columnas de esa tabla, tras schema. sus tablas, y Ctrl+Space lo abre en cualquier parte. Tab o Enter eligen una sugerencia, Esc lo cierra y, en otro caso, Tab indenta.

La consola habla el SQL de cada motor (hoy PostgreSQL y MySQL); los resultados muestran los valores tal como los imprime la base de datos.

Dimensionamiento

Cada sandbox es un pod de base de datos en su propio namespace. Lo que recibe se toma del primer lugar que lo define:

  1. la restauración: los campos CPU, Memoria, Disco y Clase de almacenamiento de la página Restore (los valores en gris son los que recibe si los dejas vacíos), tenax restore … --cpu 2 --memory 8Gi --size 50Gi --storage-class local-path, o spec.destination.sandbox de un Restore;
  2. el objetivo: spec.sandbox.cpu, memory, sizeLimit, storageClass del BackupTarget;
  3. la instalación: sandbox.resources del chart;
  4. por defecto: 100m de CPU, 256Mi de memoria solicitados con un límite de 1Gi, y un disco del doble de los datos del respaldo más 512Mi (al menos 1Gi) en el nodo (emptyDir, que se elimina con el sandbox).
Ajuste Significa
CPU solicitud de CPU; sin límite de CPU, para que una restauración pueda usar núcleos ociosos
Memory solicitud y límite de memoria
Disk tamaño del volumen de datos; en el disco del nodo salvo que se defina una storage class
Storage class los datos van en su lugar a un PVC de esta clase, para restauraciones más grandes de lo que permiten los discos de los nodos

sandbox.limits del chart fija topes para CPU, memoria y disco. La consola y la API rechazan una petición por encima de ellos, y una restauración cuyo tamaño resulta superior (por un ajuste del objetivo o por el tamaño automático de un respaldo grande) falla indicando el motivo en lugar de llenar un disco a medias. Un sandbox en ejecución conserva su tamaño: vuelve a restaurar para cambiarlo.

Conectarse desde el clúster

El host, el puerto y las credenciales del sandbox están en su página (Mostrar credenciales, auditado) y en tenax sandbox show. Los pods de otros namespaces necesitan la etiqueta tenax.run/sandbox-client: <sandbox>; la network policy del sandbox rechaza todo lo demás.

Conectarse desde fuera del clúster

Cómo se llega a los sandboxes desde un portátil lo decide la instalación (expose.provider, más abajo). En todos los casos:

  • Abre el acceso externo con Abrir acceso externo en la página del sandbox, con tenax sandbox expose <sandbox> o con open external access al restaurar (tenax restore … --expose); requiere el rol operator.
  • Conéctate con tenax sandbox connect <sandbox>, que muestra una cadena de conexión para cualquier cliente de base de datos, o cópiala de la página del sandbox.
  • Ciérralo con Cerrar acceso externo o tenax sandbox unexpose <sandbox>. También se cierra cuando el sandbox vence o se borra.
expose.provider Qué hace tenax sandbox connect El cliente necesita
none (por defecto) Reenvía un puerto local al sandbox a través de tu kubeconfig, hasta Ctrl+C Un kubeconfig con permiso de port-forward en tenax-sbx-<sandbox>
service Muestra la dirección propia del sandbox (postgres://…@sbx-7f3a.db.example.com:5432/app?sslmode=require) Nada: cualquier cliente de base de datos, por TLS
cloudflare Abre cloudflared access tcp en un puerto local tras un inicio de sesión de Access, hasta Ctrl+C cloudflared y un email autorizado

Los sandboxes de Postgres sirven TLS con un certificado propio (autofirmado: los clientes usan sslmode=require, que cifra sin comprobar el emisor); los de MySQL usan el certificado que genera MySQL.

Acceso a los sandboxes desde fuera del clúster (administradores)

Tenax no abre nada por sí solo: cada opción usa algo que tu clúster ya tiene.

Opción service: un Service de Kubernetes por sandbox

Cada sandbox expuesto recibe un Service construido a partir de expose.service; lo que tu clúster use para los Services le da una dirección, y Tenax muestra esa dirección. Es lo más sencillo para los desarrolladores (cualquier cliente de base de datos) y funciona con cualquier Kubernetes:

Tu clúster tiene Define Los desarrolladores se conectan a
Un balanceador de carga en la nube (AKS, EKS, GKE) type: LoadBalancer, sourceRanges: [<office or VPN CIDRs>] La IP del balanceador de carga
Lo mismo, más external-dns además annotations: {external-dns.alpha.kubernetes.io/hostname: "sbx-{id}.db.example.com"} y hostnamePattern: "sbx-{id}.db.example.com" sbx-<id>.db.example.com
MetalLB (on-premises) type: LoadBalancer Una dirección de tu red
El operador de Tailscale loadBalancerClass: tailscale El nombre del sandbox en la tailnet; nada es público
Nada de lo anterior type: NodePort, hostnamePattern: "nodes.example.com" con un nombre que apunte a tus nodos 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

Los sandboxes contienen copias de datos de producción

Un balanceador de carga público protege un sandbox solo con su contraseña aleatoria, TLS y su límite de tiempo. Define sourceRanges con las direcciones desde las que se conectan tus desarrolladores, o usa una opción privada (Tailscale, un balanceador de carga interno mediante la anotación de tu nube, MetalLB en una red privada).

Mientras un sandbox está expuesto, una NetworkPolicy adicional admite expose.service.allowFrom (por defecto: todo; el filtrado lo hace sourceRanges del balanceador, ya que su tráfico suele llegar desde direcciones de nodos) en el puerto de la base de datos. Con el balanceador de carga integrado de k3s, cada sandbox expuesto ocupa el puerto de la base de datos en los nodos, así que allí solo se puede exponer a la vez un sandbox de Postgres y uno de MySQL.

Opción cloudflare: un túnel detrás de un inicio de sesión de Access

Los túneles necesitan un token de API de Cloudflare, que pertenece a la instalación, no a un usuario. Es un Secret de Kubernetes en el namespace de Tenax, que se configura una vez como cualquier otra credencial (con tus herramientas de secretos de GitOps, por ejemplo SOPS). La consola nunca lo pide, ni lo guarda, ni lo muestra: puede modificar el DNS y las aplicaciones de Access de la zona.

  1. En Cloudflare, My Profile → API Tokens → Create Token → Custom token con estos permisos, para la zona en la que están los nombres de host:
    • Account: Cloudflare Tunnel: Edit
    • Account: Access: Apps and Policies: Edit
    • Zone: DNS: Edit
    • Zone: Zone: Read
  2. Guárdalo:

    kubectl -n tenax-system create secret generic tenax-cloudflare-api --from-file=token=api-token.txt
    
  3. Configura el 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
    

Mantén los nombres de host un nivel por debajo de la zona (sbx-{id}.example.com, no sbx-{id}.db.example.com): el certificado gratuito de Cloudflare solo cubre *.example.com, y el cliente llega al nombre de host por HTTPS. Un puerto de base de datos no se puede proteger con un inicio de sesión en el navegador, así que los desarrolladores ejecutan cloudflared, y tenax sandbox connect lo hace por ellos.

Con el token, Tenax encuentra la cuenta a partir de la zona, crea un túnel llamado tenax (expose.cloudflare.tunnelName) o lo reutiliza, y escribe el token del conector del túnel en el Secret <release>-tunnel-token, con el que arrancan los pods de cloudflared. Revisa ese Secret cada 15 minutos, así que un token rotado en Cloudflare llega al Secret por sí solo (reinicia los pods de cloudflared para que lo tomen). allowEmails acepta direcciones y dominios completos (*@example.com); allowGroups acepta IDs de grupos de Access.

Un túnel que ya gestionas

Define expose.cloudflare.tunnelID para usar un túnel existente gestionado de forma remota en lugar de tenax, y expose.cloudflared.tunnelTokenSecret con un Secret (clave token) que contenga su token de conector si prefieres proporcionarlo tú; en ese caso Tenax no toca ese Secret. Tenax solo añade y quita sus propias reglas sbx-…, así que las demás rutas del túnel siguen como están. Puedes definir accountID y zoneID para evitar las búsquedas.

El mismo túnel puede servir también la propia consola: añade al túnel un nombre de host público para tenax-api (Zero Trust → Networks → Tunnels → tenax → Public hostnames) detrás de una aplicación de Access, y define expose.cloudflared.consoleHostname con auth.provider: cloudflare-access: consulta Consola, CLI y acceso.