Vai al contenuto

Ripristinare con Tenax

Ogni ripristino è una risorsa Restore. La console, la CLI e l'API ne creano tutte una. I ripristini non scrivono mai in un database esistente, a meno che tu non chieda il ripristino sul posto e digiti il nome del target. Scegli la modalità:

Esigenza Modalità Ruolo
Guardare i dati, verificare una correzione, eseguire un report sandbox (predefinita) operator
Recuperare una tabella o poche righe sandbox, poi copia ciò che ti serve operator
Una copia accanto alla produzione (nuovo nome di database) new restorer
Un file di dump per qualcuno fuori dal cluster download (URL prefirmato, 1 h) restorer
Riportare indietro l'intero target sul posto (dopo un backup di sicurezza) restorer-admin

1. Trovare il punto

tenax targets                                   # PITR windows and last backups
tenax backups team/app                          # versions (ids, times, sizes, verified, pinned)
tenax changes team/app --since 6h               # what changed when: per-table counts and DDL per segment
tenax diff <backup-a> <backup-b>                # schema and row changes between two versions

Nella console, apri il target, poi Modifiche. Ogni riga di segmento ha i link "sandbox prima" e "dopo" che compilano l'orario.

Con PITR funziona qualsiasi secondo dentro una finestra (UTC, RFC3339): --at 2026-01-02T03:04:05Z. Senza PITR, scegli un id di backup: --at 20260102T020000Z-1a2b3c. Se l'orario non è ripristinabile, Tenax spiega perché e stampa le finestre.

2. Ripristinare in una sandbox (sicuro, l'opzione predefinita)

tenax restore team/app --at 2026-01-02T03:04:05Z --ttl 6h --wait
tenax sandbox show <sandbox>                    # connection info, expiry
tenax query <sandbox> -e "select count(*) from orders where created_at > now() - interval '1 day'"
tenax query <sandbox> -e "select * from orders where id = 42" -o csv > order42.csv
  • La sandbox riceve un namespace proprio tenax-sbx-<name>, credenziali casuali e un TTL (predefinito 24 h, estendibile con tenax sandbox extend <sandbox> --by 24h, fino al massimo della policy).
  • La console di query è in sola lettura per impostazione predefinita, con un timeout di 30 s e un limite di 1.000 righe. Le colonne che corrispondono ai pattern mask del target vengono mascherate. Ogni query viene registrata nell'audit.
  • Da un altro pod: assegnagli l'etichetta tenax.run/sandbox-client: <sandbox> e connettiti all'indirizzo interno al cluster stampato da sandbox show.
  • Da un portatile, se la sandbox è stata creata con --expose: tenax sandbox connect <sandbox> --port 15432. Questo apre il tunnel tramite Cloudflare Access e stampa la stringa di connessione.
  • tenax sandbox delete <sandbox> quando hai finito. Le sandbox scadute vengono eliminate automaticamente.

3. Ripristinare in un nuovo database

tenax restore team/app --at 2026-01-02T03:04:05Z --to new \
  --host postgres.team.svc --database app_restored --credentials-secret tenax-admin --wait

Tenax ripristina prima in una sandbox di staging, esegue i controlli, poi copia il risultato nel nuovo database con un dump e un caricamento logici (ADR 0007). Non sovrascrive mai un database esistente. Il secret di amministratore (prefisso tenax-) si trova nel namespace del target e richiede CREATE DATABASE.

4. Download

tenax restore team/app --at <backup-id> --to download --wait

--wait stampa un URL prefirmato verso un dump decifrato e ricompresso. È valido per 1 ora. L'URL è un segreto: condividilo come tale.

5. Sul posto (ultima risorsa)

tenax restore team/app --at 2026-01-02T03:04:05Z --to in-place --confirm app --credentials-secret tenax-admin --wait
  1. Prima ferma chi scrive. Tenax non ferma le applicazioni. Scalale a zero o mettile in modalità manutenzione.
  2. Tenax esegue un backup di sicurezza dello stato attuale (motivo safety). Per saltarlo serve --skip-safety-backup.
  3. Ripristina in una sandbox di staging, controlla il risultato, poi sostituisce i database del target.
  4. Riavvia le applicazioni e controllale.

Se qualcosa va storto, il backup di sicurezza è una normale versione del target: ripristinalo allo stesso modo.

Senza la CLI

La risorsa Restore è l'API:

apiVersion: tenax.run/v1alpha1
kind: Restore
metadata: { name: app-incident-42, namespace: team }
spec:
  target: app
  source: { time: "2026-01-02T03:04:05Z" }     # or backupID: ..., or latest: true
  destination:
    mode: sandbox
    sandbox: { ttl: 6h }

kubectl -n team get restore app-incident-42 -w mostra l'avanzamento: piano, download della base, replay, controlli, Succeeded.

Quando Tenax stesso non è disponibile

Lo store è la fonte di verità, e il formato dei backup è semplice: - manifest.json elenca ogni oggetto con dimensione e SHA-256. - Gli oggetti sono compressi con zstd e cifrati con age: age -d -i key.txt obj.zst.age | zstd -d. - I backup fisici di Postgres sono stream tar di pg_basebackup più segmenti WAL. - I backup logici di Postgres sono file pg_dump -Fc, uno per database. - I backup di MySQL sono l'output di mysqldump più i file binlog.

Dopo la perdita o la ricostruzione di un cluster

Il recupero è automatico. Tenax non ha un database proprio: lo store contiene ogni backup (ed è l'unica copia che conta), Git contiene il BackupStore, le policy e i target (GitOps), e le identità age sono secret che ripristini come qualsiasi altro. Installa Tenax sul nuovo cluster con lo stesso store e le stesse identità, e applica gli stessi target. Poi:

  1. All'avvio il controller controlla ogni store (BackupStore.status.lastImportTime). Per ogni target ricrea gli oggetti Backup dei keepBackupObjects backup più recenti (predefinito 20), blocchi compresi. Nello store non viene scritto nulla. I backup più vecchi restano nello store, soggetti alla conservazione, e nel catalogo della console.
  2. Ripristini, finestre PITR e console funzionano come prima. Lo streamer avvia una nuova catena da un nuovo backup completo.

L'importazione viene eseguita anche quando i target cambiano e ogni controller.autoImport.interval (1 h). Disattivala con controller.autoImport.enabled: false. tenax catalog import [--dry-run] fa lo stesso a mano, usando il tuo kubeconfig.