沙盒與查詢¶
沙盒是一個從備份或時間窗內任意一秒還原出來的私有、一次性資料庫。它是預設的還原目的地,因為檢視資料永遠不會給正式環境帶來風險。
tenax restore team-a/app --at 2026-10-07T09:41:27Z --ttl 6h --wait
tenax sandbox list
tenax sandbox show <sandbox>
沙盒是什麼¶
- 一個與目標引擎和版本相同的資料庫 Pod(目標上的
spec.sandbox.image),位於 Tenax 的沙盒名稱空間中, 帶有自動產生的憑證和預設拒絕的 NetworkPolicy。 - 一個 TTL(
sandbox.defaultTTL,最長sandbox.maxTTL,兩者都可以依策略或在整個 Chart 範圍內設定)。到期後, Tenax 會刪除沙盒及其儲存。tenax sandbox extend <sandbox> --by 24h可以延長時間;tenax sandbox delete <sandbox>可以提前結束。 - 歸請求者所有;對它的每個操作都會被稽核。
查詢¶
控制台的 查詢 頁面和 tenax query 透過 Tenax 的查詢代理執行 SQL,預設唯讀:
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
每個查詢限時 30 秒,最多傳回 1,000 列;--write 允許寫入(僅限沙盒)。
在編輯器中,按 Ctrl+Enter(Mac 上為 Cmd+Enter)執行查詢;如果有選取內容,則只執行選取的文字。
自動補全提供關鍵字、函式、表和欄:輸入兩個字元後自動跳出,在 alias. 之後列出該表的欄,在 schema. 之後列出其中的表,
按 Ctrl+Space 可在任意位置開啟。Tab 或 Enter 選擇一項建議,Esc 關閉補全,其他情況下 Tab 用於縮排。
控制台能理解每種引擎的 SQL(目前為 PostgreSQL 和 MySQL);結果中的值依資料庫自身的輸出格式顯示。
資源規格¶
每個沙盒都是位於獨立名稱空間中的一個資料庫 Pod。它獲得的資源取自以下第一個設定了該值的地方:
- 還原本身:Restore 頁面上的 CPU、記憶體、磁碟 和 儲存類別 欄位(留空時,灰色顯示的值就是它將獲得的值),
tenax restore … --cpu 2 --memory 8Gi --size 50Gi --storage-class local-path,或 Restore 的spec.destination.sandbox; - 目標:BackupTarget 的
spec.sandbox.cpu、memory、sizeLimit、storageClass; - 安裝:Chart 的
sandbox.resources; - 內建預設值:請求 100m CPU、256Mi 記憶體(上限 1Gi),以及節點上大小為備份資料兩倍再加 512Mi(至少 1Gi)的磁碟
(
emptyDir,隨沙盒一起刪除)。
| 設定 | 含義 |
|---|---|
| CPU | CPU 請求;不設 CPU 上限,因此還原可以使用閒置核心 |
| Memory | 記憶體請求和上限 |
| Disk | 資料磁碟區大小;除非設定了儲存類別,否則使用節點的磁碟 |
| Storage class | 資料改為放在該儲存類別的 PVC 上,適用於超出節點磁碟容量的還原 |
Chart 的 sandbox.limits 設定 CPU、記憶體和磁碟的上限。控制台和 API 會拒絕超出上限的請求;如果某次還原計算出的規格超出上限
(來自目標的設定,或大型備份自動計算出的大小),它會直接失敗並給出原因,而不是在中途把磁碟寫滿。
執行中的沙盒保持其規格不變:如需更改,請重新還原。
從叢集內連線¶
沙盒的主機、連接埠和憑證顯示在它的頁面上(顯示憑證,會被稽核),也可以透過 tenax sandbox show 檢視。
其他名稱空間中的 Pod 需要帶有標籤 tenax.run/sandbox-client: <sandbox>;沙盒的網路策略會拒絕其他一切存取。
從叢集外連線¶
從筆電如何存取沙盒,由安裝時的設定決定(expose.provider,見下文)。無論哪種方式:
- 開啟外部存取:在沙盒頁面上點選 開啟外部存取、執行
tenax sandbox expose <sandbox>,或在還原時選擇 open external access(tenax restore … --expose);需要operator角色。 - 連線:執行
tenax sandbox connect <sandbox>,它會印出一個適用於任何資料庫用戶端的連線字串;也可以從沙盒頁面複製。 - 關閉:點選 關閉外部存取 或執行
tenax sandbox unexpose <sandbox>。沙盒過期或被刪除時也會自動關閉。
expose.provider |
tenax sandbox connect 做什麼 |
用戶端需要 |
|---|---|---|
none(預設) |
透過你的 kubeconfig 把本機連接埠轉送到沙盒,直到按下 Ctrl+C | 一個允許在 tenax-sbx-<sandbox> 中執行 port-forward 的 kubeconfig |
service |
印出沙盒自己的位址(postgres://…@sbx-7f3a.db.example.com:5432/app?sslmode=require) |
無需任何東西:任何資料庫用戶端,透過 TLS 連線 |
cloudflare |
在 Access 登入後,於本機連接埠上開啟 cloudflared access tcp,直到按下 Ctrl+C |
cloudflared 和一個被允許的電子郵件 |
Postgres 沙盒使用自己的憑證提供 TLS(自我簽署:用戶端使用 sslmode=require,只加密而不驗證簽發者);
MySQL 沙盒使用 MySQL 自動產生的憑證。
從叢集外存取沙盒(管理員)¶
Tenax 自己不會開放任何東西:每個選項都利用你的叢集中已有的設施。
選項 service:每個沙盒一個 Kubernetes Service¶
每個對外公開的沙盒都會獲得一個依 expose.service 建置的 Service;你的叢集為 Service 提供位址的機制會給它分配位址,
Tenax 則顯示這個位址。這對開發者來說最簡單(任何資料庫用戶端都能用),並且適用於任何 Kubernetes:
| 你的叢集有 | 設定 | 開發者連線到 |
|---|---|---|
| 雲端負載平衡器(AKS、EKS、GKE) | type: LoadBalancer,sourceRanges: [<office or VPN CIDRs>] |
負載平衡器的 IP |
| 同上,外加 external-dns | 另外設定 annotations: {external-dns.alpha.kubernetes.io/hostname: "sbx-{id}.db.example.com"} 和 hostnamePattern: "sbx-{id}.db.example.com" |
sbx-<id>.db.example.com |
| MetalLB(地端部署) | type: LoadBalancer |
你網路中的一個位址 |
| Tailscale Operator | loadBalancerClass: tailscale |
沙盒在 tailnet 中的名稱;不對公用網路開放 |
| 以上都沒有 | type: NodePort,hostnamePattern: "nodes.example.com",並為你的節點設定對應的名稱 |
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
沙盒中儲存著正式環境資料的副本
公用網路負載平衡器只能靠隨機密碼、TLS 和時間限制來保護沙盒。請把 sourceRanges 設為開發者所在的位址,
或者使用私有方案(Tailscale、透過雲端供應商註解建立的內部負載平衡器、私有網路上的 MetalLB)。
沙盒對外公開期間,會有一個額外的 NetworkPolicy 在資料庫連接埠上放行 expose.service.allowFrom(預設:全部放行;
過濾由負載平衡器的 sourceRanges 完成,因為其流量通常來自節點位址)。在 k3s 內建的負載平衡器上,每個公開的沙盒都會
在節點上繫結資料庫連接埠,因此在那裡同一時間只能公開一個 Postgres 沙盒和一個 MySQL 沙盒。
選項 cloudflare:位於 Access 登入之後的通道¶
通道需要一個 Cloudflare API 權杖,它屬於這套安裝,而不屬於某個使用者。它是 Tenax 名稱空間中的一個 Kubernetes Secret, 像其他憑證一樣只需設定一次(使用你的 GitOps 金鑰工具,例如 SOPS)。控制台從不索取、儲存或顯示它: 它可以修改該區域中的 DNS 和 Access 應用程式。
- 在 Cloudflare 中,進入 My Profile → API Tokens → Create Token → Custom token,為主機名所在的區域授予以下權限:
- Account: Cloudflare Tunnel: Edit
- Account: Access: Apps and Policies: Edit
- Zone: DNS: Edit
- Zone: Zone: Read
-
儲存它:
-
設定 Chart:
讓主機名位於區域下一級(sbx-{id}.example.com,而不是 sbx-{id}.db.example.com):Cloudflare 的免費憑證只覆蓋
*.example.com,而用戶端透過 HTTPS 存取該主機名。資料庫連接埠無法用瀏覽器登入來保護,因此開發者需要執行 cloudflared,
而 tenax sandbox connect 會替他們完成這一步。
有了權杖,Tenax 會根據區域找到帳號,建立一個名為 tenax 的通道(expose.cloudflare.tunnelName)或複用已有的通道,
並把通道的連接器權杖寫入 Secret <release>-tunnel-token,cloudflared Pod 就從這裡啟動。它每 15 分鐘檢查一次該 Secret,
因此在 Cloudflare 中輪替的權杖會自動同步到 Secret(重啟 cloudflared Pod 即可生效)。allowEmails 接受電子郵件地址和整個網域
(*@example.com);allowGroups 接受 Access 群組 ID。
使用你已在管理的通道
設定 expose.cloudflare.tunnelID 即可使用一個現有的遠端管理通道來代替 tenax;如果你更想自己提供連接器權杖,
可以把 expose.cloudflared.tunnelTokenSecret 設為儲存該權杖的 Secret(鍵為 token),Tenax 便不會再動這個 Secret。
Tenax 只新增和刪除它自己的 sbx-… 規則,因此通道的其他路由保持不變。可以設定 accountID 和 zoneID 以跳過查詢。
同一個通道也可以為控制台本身提供服務:在通道中為 tenax-api 新增一個公共主機名(Zero Trust →
Networks → Tunnels → tenax → Public hostnames),放在一個 Access 應用程式之後,並在設定 auth.provider: cloudflare-access
的同時設定 expose.cloudflared.consoleHostname:參見控制台、CLI 與存取。