Slaan oor na inhoud

Rugsteun, tydstipherstel en die hersteloefening

Rugsteun Quire, herstel tot ’n tydstip en oefen die herstel met Docker Compose.

Bekyk as Markdown

Die ontwerp staan in afdeling 8 van docs/architecture/23-ops.md. Dit is die handleiding vir die Docker Compose-produk. Dit is geskryf sodat iemand wat dit nie geskryf het nie, dit kan volg; as ’n stap onduidelik is, is dit ’n gebrek in hierdie dokument.

Wat beskerm word, en hoe

Hulpbron Hoe Waar
Die databasis WAL word aanhoudend geargiveer, hoogstens elke 60 sekondes, vanaf die eerste aanvang pgwal-volume
Die databasis Basisrugsteun met pg_basebackup, by verstek daagliks (backup-scheduler) pgbackup-volume
Die databasis Geënkripteerde kopieë van basisrugsteun en WAL, elke vyf minute (backup-offsite) ’n Aparte winkel wat jy benoem
Lêers Die files-volume. Kopieer dit met jou gasheer se rugsteunnutsding of gebruik weergawebeheer vir objekberging files-volume
Geheime docker/.env, veral QUIRE_MASTER_KEY (en enige QUIRE_MASTER_KEY_RETIRED wat nog gebruik word), QUIRE_BACKUP_ENCRYPTION_KEY en docker/secrets/audit-signing-key.pem Hou ’n kopie buite hierdie gasheer
Soekindekse, kasberging, weergawes Word nie gerugsteun nie; word herbou

Teikens: herstelpunt binne 60 sekondes van die fout, en herstel binne 60 minute vir ’n databasis van 500 GB.

Twee foute kom algemeen voor. ’n Databasis wat sonder sy lêers herstel word, lewer gebroke bladsye. ’n Databasis wat sonder QUIRE_MASTER_KEY herstel word, kan nie die SSO-, webhook- en integrasie-aanmeldbewyse daarin ontsyfer nie; totdat ’n hoofsleutelrotasie sonder onopgeloste waardes klaarmaak (key-rotation.md), sluit dit ook die afgetrede sleutels in. Albei hoort by die rugsteun.

Neem rugsteun

’n Basisrugsteun van die hele groepering:

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

Dit behou die nuutste QUIRE_BACKUP_KEEP basisrugsteun (verstek 5) en snoei WAL wat die oudste nie meer benodig nie, sodat die argief onbeperk kan groei nie. Skeduleer dit daagliks met cron of ’n systemd-tydhouer op die gasheer:

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

Of laat die stapel dit skeduleer: die backup-profiel loop backup-scheduler, wat elke QUIRE_BACKUP_INTERVAL_HOURS uur ’n basisrugsteun neem (verstek 24), en backup-offsite, wat hierna beskryf word.

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

Geënkripteerde kopieë buite die gasheer

Albei volumes woon op dieselfde gasheer as die databasis, en ’n rugsteun op die masjien wat misluk het, is nie ’n rugsteun nie. backup-offsite kopieer elke basisrugsteun en elke geargiveerde WAL-segment deur die bergingpoort na ’n aparte winkel, geënkripteer en met behoudsbeleid:

  • Enkripsie. AES-256-GCM met QUIRE_BACKUP_ENCRYPTION_KEY (of die lêer benoem deur QUIRE_BACKUP_ENCRYPTION_KEY_FILE): 32 grepe uit openssl rand -hex 32. Elke lêer het sy eie nonce en verifikasiekode, dus is ’n kopie onleesbaar sonder die sleutel en word enige verandering opgespoor. Hou die sleutel saam met QUIRE_MASTER_KEY, weg van hierdie gasheer en rugsteunwinkel. Sonder die sleutel kan daar nie herstel word nie.
  • Waar. QUIRE_BACKUP_STORAGE_DRIVER is s3, azure of local (’n gekoppelde afgeleë skyf by QUIRE_BACKUP_STORAGE_ROOT). Die instellings is dié vir lêerberging met ’n QUIRE_BACKUP_-voorvoegsel: QUIRE_BACKUP_S3_ENDPOINT, QUIRE_BACKUP_S3_BUCKET, QUIRE_BACKUP_S3_ACCESS_KEY_ID, ensovoorts. Gebruik ’n ander emmer en verkieslik ’n ander rekening as vir lêers, met aanmeldbewyse wat kan skryf maar nie skrap nie, as die verskaffer dit toelaat.
  • Bewaring. Die nuutste QUIRE_BACKUP_OFFSITE_KEEP basisrugsteun (verstek QUIRE_BACKUP_KEEP, anders 7) en die WAL wat die oudste daarvan nodig het; ouer stelle en segmente word uit die winkel geskrap.
  • Wanneer. Elke QUIRE_BACKUP_SHIP_INTERVAL_SECONDS (verstek 300). Versending is idempotent: wat reeds gestoor is, word oorgeslaan; ’n basisrugsteun tel eers as gestoor wanneer sy manifes laaste geskryf is.

Dieselfde opdrag loop met die hand:

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

Om op ’n nuwe gasheer te herstel, haal eers ’n stel terug en volg dan die stappe hieronder met die afgelaaide gids in die plek van die pgbackup-volume en die afgelaaide wal-archive in die plek van pgwal:

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

Herstel tot ’n tydstip

Gebruik dit ná dataverlies: ’n slegte invoer, ’n geskrapte kursus of ’n kontrakmigrasie wat jy moet ongedaan maak. Dit vervang die lewendige databasis, dus oefen dit eers met die oefening hieronder.

  1. Kies die teikentyd, in UTC net voor die skade: 2026-09-24 09:30:00+00. Die ouditlog (/admin/audit) wys gewoonlik die oomblik.
  2. Stop alles wat skryf: docker compose -f docker/compose.yaml stop web content worker scheduler collab
  3. Hou die beskadigde groepering totdat herstel geverifieer is:
    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. Pak die nuutste basisrugsteun ouer as die teiken uit in datavolume en versoek geteikende herstel:
    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. Herstel: begin Postgres een keer met die herstelinstellings, vanaf ’n Compose-oorheersingslêer sodat die gewone lêer onaangeraak bly:
    # 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]
    Die oorheersing vervang die hele opdrag, dus herhaal dit die twee instellings waarvan herstel afhang: max_connections nie laer as die primêre databasis s’n nie (anders staak herstel met “insufficient parameter settings”) en die gekoppelde pg_hba.conf.
    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. Kontroleer dit voordat jy iemand inlaat: die ouditketting (docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts) en dat verlore data terug is.
  7. Keer terug na normaal: docker compose -f docker/compose.yaml up -d. Dit herbegin Postgres met argivering aan, en ’n nuwe WAL-tydlyn begin. Neem dadelik ’n nuwe basisrugsteun.

Die projeknaam quire word voor elke volume gevoeg; docker volume ls wys die presiese name.

Die geskeduleerde verifikasieoefening

backup-offsite voer ook elke QUIRE_BACKUP_DRILL_INTERVAL_HOURS ’n oefening uit (verstek 168, weekliks), en weer met die volgende lopie ná ’n mislukking. Dit haal die nuutste basisrugsteun buite die gasheer en elke daaropvolgende WAL-segment af, ontsyfer hulle (wat bewys dat die sleutel hulle nog oopmaak en niks verander is nie), vergelyk elke lêer met sy manifes, kontroleer dat die argief ’n Postgres-datagids is en dat daar van die rugsteun af geen gaping in WAL is nie. Die verslag word as reports/drill-<time>.json in die winkel en in die dienslog geskryf; ’n mislukte oefening benoem die lêer of eerste ontbrekende segment.

Die hersteloefening

’n Rugsteun wat nooit herstel is nie, is nie ’n rugsteun nie. Die oefening herstel die nuutste volledige basisrugsteun wat voor die teiken geneem is, plus die WAL-argief, in ’n tydelike Postgres wat niks met die lewendige een deel nie, en bewys die resultaat:

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

Die teiken is UTC in presies daardie vorm. Dit benodig ’n basisrugsteun wat ouer is en geargiveerde WAL daarna: op ’n nuwe installasie, neem ’n basisrugsteun en wag vir die volgende geargiveerde segment (hoogstens ’n minuut met skrywings) voordat jy ’n teiken ná die rugsteun kies. Die oefening benodig net Docker en bash op die gasheer.

Elke stap kan die oefening laat misluk:

  1. Herstelbaarheid: die tydelike groepering speel tot die teiken en maak oop.
  2. Volledigheid: elke tabel se rystal teen die lewendige databasis (tooling/restore-drill). Die lewendige databasis het sedert die teiken verander, dus kan ’n tabel in enige rigting met die grootste van 500 rye of ’n tiende van sy grootte verskil (skrywings laat herstel agter, skrappings laat herstel meer bevat); ’n partisie wat ná die teiken geskep is, is nie ’n verlore tabel nie. Maak die toelae ruimer op ’n besiger installasie met QUIRE_DRILL_MAX_BEHIND en QUIRE_DRILL_MAX_DRIFT_RATIO. ’n Ontbrekende of leeggemaakte tabel laat dit misluk.
  3. Integriteit: die oudit-hashketting verifieer op die herstelde kopie.
  4. Bruikbaarheid: die toepassingrol lees deur ryvlak-sekuriteit.
  5. Tyd: begin tot sukses, teen QUIRE_DRILL_RTO_SECONDS (verstek 3600).

Dit skryf nooit na die lewendige databasis of sy volumes nie: rugsteun- en WAL-volumes is leesalleen gemonteer en die tydelike groepering word aan die einde verwyder, hetsy suksesvol of nie.

Stel QUIRE_DRILL_REPORT op ’n pad om ’n JSON-verslag te laat skryf, suksesvol of nie; skeduleer dit vanaf die Docker-gasheer:

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

Voer dit maandeliks en voor elke opgradering uit. ’n Mislukte oefening blokkeer opgradering. Laat een keer per kwartaal iemand wat nie hierdie handleiding geskryf het nie, ’n werklike tydstipherstel op ’n spaargasheer doen deur net hierdie dokument te gebruik.

Lêers

Plaaslike lêers woon in die files-volume. Rugsteun dit gelyktydig met die databasis en herstel albei saam:

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 .

Skakel weergawebeheer vir die emmer aan met objekberging en hou 35 dae se nie-huidige weergawes; tydstipherstel vir lêers is dan die emmer s’n.

Navigasie

Tik om te soek…

↑↓ navigeer↵ kiesEsc sluit