Saltate à u cuntenutu

Copia di salvezza, ripresa à un mumentu precisu è prova di risturazione

Fate copie di salvezza di Quire, risturate lu à un mumentu precisu è dimustrate la cù a prova di risturazione.

Vede cum’è Markdown

U disignu hè in a sezzione 8 di docs/architecture/23-ops.md. Questu hè u manuale per u pruduttu Docker Compose. Hè scrittu per esse seguitatu da qualchissia chì ùn l’hà micca scrittu; s’è un passu ùn hè chjaru, hè un difettu di stu ducumentu.

Ciò chì hè prutettu, è cumu

Risorsa Cumu Induve
A basa di dati WAL archiviatu di cuntinuu, almenu ogni 60 seconde, da u primu avviu Volume pgwal
A basa di dati Copie di salvezza di basa cù pg_basebackup, predefinitu ogni ghjornu (backup-scheduler) Volume pgbackup
A basa di dati Copie cifrate di e copie di salvezza di basa è di u WAL, ogni cinque minuti (backup-offsite) Un magazinu separatu chì sceglite
Schedarii U volume files. Copiatelu cù u strumentu di copia di salvezza di u vostru host, o aduprate un almacenamentu d’ogetti cù versioni Volume files
Sicreti docker/.env, soprattuttu QUIRE_MASTER_KEY (è qualsiasi QUIRE_MASTER_KEY_RETIRED sempre adupratu), QUIRE_BACKUP_ENCRYPTION_KEY, è docker/secrets/audit-signing-key.pem Cunservate una copia fora di questu host
Indici di ricerca, cache, rendizioni Ùn sò micca salvati; ricustruiti

Obiettivi: un puntu di ripresa à 60 seconde à u più da u guastu, è una risturazione in 60 minuti per una basa di dati di 500 GB.

Dui sbagli sò cumuni. Una basa di dati risturata senza i so schedarii mostra pagine rotte. Una basa di dati risturata senza QUIRE_MASTER_KEY ùn pò decifrà e credenziali SSO, webhook è d’integrazione ch’ella cuntene; finch’è a rotazione di a chjave maestra ùn hè finita senza valori irrisolvibili (key-rotation.md), ciò include e chjave ritirate. Tramindui facenu parte di a copia di salvezza.

Fà copie di salvezza

Una copia di salvezza di basa di tuttu u cluster:

docker compose -f docker/compose.yaml --profile backup run --rm backup

Cunserva e copie di salvezza di basa più recenti indicate da QUIRE_BACKUP_KEEP (predefinitu 5) è sguassa u WAL chì a più vechja ùn hà più bisognu, cusì l’archiviu ùn pò cresce senza limite. Pianificate lu ogni ghjornu cù cron o un timer systemd nant’à l’host:

15 2 * * * cd /srv/quire && docker compose -f docker/compose.yaml --profile backup run --rm backup >> /var/log/quire-backup.log 2>&1

O lasciate chì u stack u pianifichi: u prufilu backup esegue backup-scheduler, chì face una copia di salvezza di basa ogni QUIRE_BACKUP_INTERVAL_HOURS (predefinitu 24), è backup-offsite, discrittu dopu.

docker compose -f docker/compose.yaml --profile backup up -d

Copie cifrate fora di l’host

I dui volumi sò nant’à u listessu host chè a basa di dati, è una copia di salvezza nant’à a macchina guasta ùn hè micca una copia di salvezza. backup-offsite copia ogni copia di salvezza di basa è ogni segmentu WAL archiviatu in un magazinu separatu via a porta di almacenamentu, cifrati, è i cunserva quì secondu a pulitica di retenzione:

  • Cifratura. AES-256-GCM cù QUIRE_BACKUP_ENCRYPTION_KEY (o u schedariu indicatu da QUIRE_BACKUP_ENCRYPTION_KEY_FILE): 32 byte, generati cù openssl rand -hex 32. Ogni schedariu hà u so propiu nonce è una etichetta d’autentificazione, dunque una copia hè illeggibile senza a chjave è ogni mudificazione hè rilevata. Cunservate a chjave cù QUIRE_MASTER_KEY, luntanu da questu host è luntanu da u magazinu di salvezza. Senza a chjave ùn si pò risturà.
  • Locu. QUIRE_BACKUP_STORAGE_DRIVER hè s3, azure o local (un discu remotu muntatu in QUIRE_BACKUP_STORAGE_ROOT). I paràmetri sò quelli di almacenamentu di schedarii cù u prefissu QUIRE_BACKUP_: QUIRE_BACKUP_S3_ENDPOINT, QUIRE_BACKUP_S3_BUCKET, QUIRE_BACKUP_S3_ACCESS_KEY_ID, è cetara. Aduprate un bucket sfarente è, di preferenza, un contu sfarente da quellu di i schedarii, cù credenziali chì ponu scrive ma micca sguassà, s’è u fornitore u permette.
  • Retenzione. E copie di salvezza di basa più recenti indicate da QUIRE_BACKUP_OFFSITE_KEEP (predefinitu QUIRE_BACKUP_KEEP, altrimenti 7) è u WAL chì a più vechja di elle hà bisognu; l’insemi è i segmenti più vechji sò sguassati da u magazinu.
  • Quandu. Ogni QUIRE_BACKUP_SHIP_INTERVAL_SECONDS (predefinitu 300). L’inviu hè idempotente: ciò chì hè digià cunservatu hè saltatu, è una copia di salvezza di basa conta cum’è cunservata solu quandu u so manifestu hè scrittu, à a fine.

U listessu cumandu pò esse eseguitu à manu:

docker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts ship
docker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts verify

Per risturà nant’à un host novu, ricuperate prima un inseme, dopu seguitate i passi sottu cù u cartulare ricuperatu à u locu di u volume pgbackup è u wal-archive ricuperatu à u locu di pgwal:

bun apps/worker/src/backups/main.ts fetch base-20260924T021500Z /srv/restore

Risturà à un mumentu precisu

Aduprate questu dopu una perdita di dati: una impurtazione sbagliata, un corsu sguassatu, una migrazione di cuntrazione ch’è vo avete bisognu d’annullà. Rimpiazza a basa di dati attiva, dunque pruvate lu prima cù l’eserciziu sottu.

  1. Sceglite u mumentu di destinazione, in UTC, ghjustu nanzu à u dannu: 2026-09-24 09:30:00+00. U registru d’audit (/admin/audit) mostra di solitu u mumentu.
  2. Arrestate tuttu ciò chì scrive: docker compose -f docker/compose.yaml stop web content worker scheduler collab
  3. Cunservate u cluster dannighjatu finch’è a risturazione ùn hè verificata:
    docker compose -f docker/compose.yaml stop postgres
    docker run --rm -v quire_postgres18-data:/from -v quire_postgres-damaged:/to alpine cp -a /from/. /to/
  4. Scumpressate a copia di salvezza di basa più recente, più vechja chè u mumentu di destinazione, in u volume di dati, è dumandate una ripresa mirata:
    docker run --rm -v quire_pgbackup:/backups:ro -v quire_postgres18-data:/var/lib/postgresql postgres:18-alpine sh -euc '
      base="$(ls -1d /backups/base-* | sort | tail -n 1)"   # or the one before the target
      rm -rf /var/lib/postgresql/18/docker && mkdir -p /var/lib/postgresql/18/docker
      tar -xzf "$base/base.tar.gz" -C /var/lib/postgresql/18/docker
      touch /var/lib/postgresql/18/docker/recovery.signal
      chown -R postgres:postgres /var/lib/postgresql/18/docker && chmod 700 /var/lib/postgresql/18/docker'
  5. Ripigliate: avviate Postgres una volta cù i paràmetri di ripresa, da un override Compose affinchì u schedariu nurmale resti intattu:
    # docker/compose.recover.yaml
    services:
      postgres:
        command: [postgres, -c, "restore_command=cp /var/lib/postgresql/wal-archive/%f %p",
                  -c, "recovery_target_time=2026-09-24 09:30:00+00",
                  -c, recovery_target_action=promote, -c, archive_mode=off,
                  -c, max_connections=200, -c, hba_file=/etc/postgresql/pg_hba.conf]
    L’override rimpiazza tuttu u cumandu, dunque ripete i dui paràmetri chì a ripresa richiede: max_connections micca più bassu chè quellu di a basa primaria (altrimenti a ripresa si ferma cù «insufficient parameter settings») è u pg_hba.conf muntatu.
    docker compose -f docker/compose.yaml -f docker/compose.recover.yaml up -d postgres
    docker compose -f docker/compose.yaml logs -f postgres   # wait for "database system is ready"
  6. Verificate la prima di lascià entre qualchissia: a catena d’audit (docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts), è chì i dati persi sò stati ricuperati.
  7. Tornate à u funziunamentu nurmale: docker compose -f docker/compose.yaml up -d. Questu riavvia Postgres cù l’archiviazione attivata, è principia una nova cronulugia WAL. Fate subitu una nova copia di salvezza di basa.

U nome di u prughjettu quire hè prefissatu à ogni volume; docker volume ls mostra i nomi esatti.

Prova di verificazione pianificata

backup-offsite esegue ancu una prova ogni QUIRE_BACKUP_DRILL_INTERVAL_HOURS (predefinitu 168, una volta à settimana), è torna à a prossima esecuzione dopu à un fiascu. Ricupera l’ultima copia di salvezza di basa da fora di l’host è ogni segmentu WAL dopu à ella, decifra ogni schedariu (ciò chì prova chì a chjave pò sempre apre li è ch’elli ùn sò micca stati alterati), paraguneghja ogni schedariu cù u so manifestu, verifica chì l’archiviu sia un cartulare di dati Postgres, è verifica chì ùn ci sia lacune in u WAL da a copia di salvezza in avanti. U rapportu hè scrittu in u magazinu cum’è reports/drill-<time>.json è in u registru di u serviziu; una prova fallita nomina u schedariu o u primu segmentu mancante.

Prova di risturazione

Una copia di salvezza chì ùn hè mai stata risturata ùn hè micca una copia di salvezza. A prova ristura a copia di salvezza di basa cumpleta più recente fatta prima di u mumentu di destinazione, cù l’archiviu WAL, in un Postgres pruvisoriu chì ùn hà nunda in cumunu cù quellu attivu, è prova u risultatu:

docker/scripts/restore-drill.sh                                  # to ninety minutes ago
docker/scripts/restore-drill.sh --target "2026-09-24 09:30:00+00"

U mumentu di destinazione hè UTC esattamente in quella forma. Ci vole una copia di salvezza di basa più vechja chè ella è WAL archiviatu dopu à ella: nant’à una nova installazione, fate una copia di salvezza di basa è aspettate u prossimu segmentu archiviatu (à u più un minutu s’ellu ci sò scritture) prima di sceglie un mumentu di destinazione dopu à a copia. A prova hà bisognu di Docker è bash nant’à l’host, nunda altru.

Ogni passu face fallisce a prova:

  1. Pussibilità di risturazione: u cluster pruvisoriu riproduce i registri finu à u mumentu sceltu è s’avvia.
  2. Cumpletezza: u numeru di filari di ogni tavula hè paragunatu à a basa attiva (tooling/restore-drill). A basa attiva hè avanzata dapoi u mumentu sceltu, dunque una tavula pò differisce di u più grande trà 500 filari è un decimu di a so dimensione, in e duie direzzioni (e scritture facenu chì a risturazione sia in ritardu, e cancellazioni facenu chì cuntene di più); una partizione creata dopu à u mumentu ùn hè micca una tavula persa. Allargate a tolleranza in un’installazione più attiva cù QUIRE_DRILL_MAX_BEHIND è QUIRE_DRILL_MAX_DRIFT_RATIO. Una tavula mancante o sviutata face fallisce a prova.
  3. Integrità: a catena d’hash d’audit hè verificata nantu à a copia risturata.
  4. Usabilità: u rollu d’applicazione leghje cù a sicurezza à livellu di fila.
  5. Durata: da l’iniziu à u risultatu verde, secondu QUIRE_DRILL_RTO_SECONDS (predefinitu 3600).

Ùn scrive mai in a basa attiva o in i so volumi: i volumi di copia di salvezza è di WAL sò muntati in sola lettura, è u cluster pruvisoriu hè sguassatu à a fine, sia ch’ella passi sia ch’ella fallisca.

Stabilite QUIRE_DRILL_REPORT à un percorsu per scrive un rapportu JSON, ch’ella passi o fallisca, è pianificate l’eserciziu da l’host Docker:

30 3 1 * * cd /srv/quire && QUIRE_DRILL_REPORT=/var/log/quire-drill.json docker/scripts/restore-drill.sh >> /var/log/quire-drill.log 2>&1

Eseguite lu ogni mese è nanzu à ogni aghjurnamentu. Una prova fallita impedisce l’aghjurnamentu. Una volta à u trimestre, fate chì qualchissia chì ùn hà micca scrittu stu manuale eseguisca una risturazione vera à un mumentu precisu nant’à un host di riserva, aduprendu solu stu ducumentu.

Schedarii

I schedarii lucali sò in u volume files. Fate ne una copia di salvezza à tempu chè a basa di dati, è risturate tramindui inseme:

docker run --rm -v quire_files:/files:ro -v "$PWD":/out alpine tar -czf /out/files-$(date -u +%Y%m%d).tar.gz -C /files .

Cù un almacenamentu d’ogetti, attivate e versioni di bucket è cunservate 35 ghjorni di versioni precedenti; a ripresa à un mumentu precisu di i schedarii hè tandu quella propria di u bucket.

Navigazione

Scrivite per circà…

↑↓ per navigà↵ per selezziunàEsc per chjude