Projekto aprašas yra docs/architecture/23-ops.md 8 skyrius. Šis yra
Docker Compose produkto veiksmų vadovas. Jis parašytas tam, kad galėtų sekti
kas jo nerašė; jei koks nors žingsnis neaiškus, tai yra šio dokumento brokas.
Kas apsaugota ir kaip
Turtas
Kaip
Kur
Duomenų bazė
WAL archyvuojamas nuolat, dažniausiai kas 60 sekundžių, nuo pirmojo paleidimo
pgwal tomas
Duomenų bazė
Bazinės atsarginės kopijos su pg_basebackup, pagal nutylėjimą kasdien (backup-scheduler)
pgbackup tomas
Duomenų bazė
Užšifruotos bazinių atsarginių kopijų ir WAL kopijos, kas penkias minutes (backup-offsite)
Atskira saugykla, kurią įvardijate
Failai
files tomas. Nukopijuokite jį savo sistemos atsarginių kopijų įrankiu arba naudokite versijas turinčią objektų saugyklą
files tomas
Paslaptys
docker/.env, visų pirma QUIRE_MASTER_KEY (ir bet koks QUIRE_MASTER_KEY_RETIRED vis dar naudojamas), QUIRE_BACKUP_ENCRYPTION_KEY bei docker/secrets/audit-signing-key.pem
Laikykite kopiją už šios ribų
Paieškos indeksai, podėliai, pavidalai
Neatsarginės kopijos nedaromos; atkuriami iš naujo
Tikslai: atsigavimo taškas per 60 sekundžių nuo gedimo ir atkūrimas per 60
minučių 500 GB duomenų bazei.
Dvi klaidos yra dažnos. Duomenų bazė, atkurta be savo failų, rodo
sugadintus puslapius. Duomenų bazė, atkurta be QUIRE_MASTER_KEY, negali
iššifruoti joje esančių SSO, „webhook“ ir integracijos prisijungimo duomenų;
kol pagrindinio rakto rotavimas nebaigiamas nieko neišsprendus
(key-rotation.md), tai apima ir atsargos raktus. Abu yra
atsarginės kopijos dalis.
Atsarginių kopijų darymas
Bazinė atsarginė kopija viso klasterio:
docker compose -f docker/compose.yaml --profile backup run --rm backup
Ji išlaiko naujausias QUIRE_BACKUP_KEEP bazines atsatines kopijas (pagal
nutylėjimą 5) ir išvalo WAL, kurio seniausiai kopijai nebereikia, todėl
archyvas negali augti be ribos. Suplanuokite tai kasdien su cron arba systemd
laikmačiu sistemoje:
15 2 * * * cd /srv/quire && docker compose -f docker/compose.yaml --profile backup run --rm backup >> /var/log/quire-backup.log 2>&1
Arba leiskite masyvui tai suplanuoti: profilis backup paleidžia
backup-scheduler, kuris daro bazinę atsatinę kopiją kas
QUIRE_BACKUP_INTERVAL_HOURS (pagal nutylėjimą 24), ir backup-offsite,
aprašytą toliau.
docker compose -f docker/compose.yaml --profile backup up -d
Užšifruotos kopijos už sistemos ribų
Abu tomai yra toje pačioje sistemoje kaip ir duomenų bazė, o atsarginė kopija
toje mašinoje, kuri sugedo, nėra atsarginė kopija. backup-offsite nukopijuoja
kiekvieną bazinę atsatinę kopiją ir kiekvieną archyvuotą WAL segmentą į
atskirą saugyklą per saugyklos prievadą, užšifruotą, ir laiko jas ten su
saugojimo trukme:
Šifravimas. AES-256-GCM su QUIRE_BACKUP_ENCRYPTION_KEY (arba failu,
kurį nurodo QUIRE_BACKUP_ENCRYPTION_KEY_FILE): 32 baitai, gaunami su
openssl rand -hex 32. Kiekvienas failas turi savo nonces ir autentifikacijos
žymą, todėl kopija be rakto neperskaitoma ir bet koks jos pakeitimas
aptinkamas. Rakto laikykite kartu su QUIRE_MASTER_KEY, toli nuo šios
sistemos ir toli nuo atsarginių kopijų saugyklos. Be rakto atkūrimo nėra.
Kur.QUIRE_BACKUP_STORAGE_DRIVER yra s3, azure arba local
(prijungtas nuotolinis diskas ties QUIRE_BACKUP_STORAGE_ROOT). Nustatymai
yra failų saugyklos nustatymai su priešdėliu QUIRE_BACKUP_:
QUIRE_BACKUP_S3_ENDPOINT, QUIRE_BACKUP_S3_BUCKET,
QUIRE_BACKUP_S3_ACCESS_KEY_ID ir t. t. Naudokite kitą kibirą ir, pageidautina,
kitą paskyrą nei failams, su prisijungimo duomenimis, kurie gali rašyti, bet
nešalinti, jei teikėjas tai leidžia.
Saugojimo trukmė. Naujausios QUIRE_BACKUP_OFFSITE_KEEP bazinės
atsarginės kopijos (pagal nutylėjimą QUIRE_BACKUP_KEEP, kitaip 7) ir WAL,
kurio reikia seniausiai iš jų; senesni rinkiniai ir segmentai iš saugyklos
ištrinami.
Kada. Kas QUIRE_BACKUP_SHIP_INTERVAL_SECONDS (pagal nutylėjimą 300).
Siuntimas yra idempotentiškas: kas jau išsaugota, praleidžiama, o bazinė
atsarginė kopija laikoma išsaugota tik tada, kai paskutinis įrašomas jos
manifestas.
Ta pati komanda paleidžiama ranka:
docker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts shipdocker compose -f docker/compose.yaml run --rm backup-offsite bun apps/worker/src/backups/main.ts verify
Kad atkurtumėte naujoje sistemoje, pirmiausia parsisiųskite rinkinį, tada
vykdykite toliau nurodytus žingsnius, naudodami parsisiųstą katalogą vietoj
pgbackup tomo ir parsisiųstą wal-archive vietoj pgwal:
bun apps/worker/src/backups/main.ts fetch base-20260924T021500Z /srv/restore
Atkūrimas į laiko tašką
Naudokite tai praradus duomenis: blogas importas, ištrintas kursas,
sutraukimo migracija, kurią reikia atšaukti. Ji pakeičia gyvą duomenų bazę,
todėl pirmiausia išbandykite ją toliau nurodytomis pratybomis.
Pasirinkite tikslinį laiką, UTC, tik prieš žalą:
2026-09-24 09:30:00+00. Audito žurnalas (/admin/audit) paprastai
parodo tą momentą.
Sustabdykite viską, kas rašo:
docker compose -f docker/compose.yaml stop web content worker scheduler collab
Palikite sugadintą klasterį, kol atkūrimas nepatikrintas:
docker compose -f docker/compose.yaml stop postgresdocker run --rm -v quire_postgres18-data:/from -v quire_postgres-damaged:/to alpine cp -a /from/. /to/
Išpakuokite naujausią bazinę atsatinę kopiją, senesnę už tikslinį laiką,
į duomenų tomą ir paprašykite tikslingo atkūrimo:
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'
Atkurkite: paleiskite Postgres vieną kartą su atkūrimo nustatymais, iš
Compose perrašymo failo, kad įprastinis failas liktų nepaliestas:
Perrašymo failas pakeičia visą komandą, todėl jis pakartoja tuos du
nustatymus, nuo kurių priklauso atkūrimas: max_connections ne mažesnis
už pirminio (kitaip atkūrimas nutrūksta su “insufficient parameter
settings”) ir prijungtą pg_hba.conf.
docker compose -f docker/compose.yaml -f docker/compose.recover.yaml up -d postgresdocker compose -f docker/compose.yaml logs -f postgres # wait for "database system is ready"
Patikrinkite prieš įleisdami ką nors: audito grandinę
(docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts)
ir tai, kad prarasti duomenys sugrįžo.
Grįžkite į įprastą būseną: docker compose -f docker/compose.yaml up -d.
Tai paleidžia Postgres su įjungtu archyvavimu ir prasideda nauja WAL
laiko juosta. Iš karto padarykite naują bazinę atsatinę kopiją.
Projekto pavadinimas quire priešdėliu žymi kiekvieną tomą; tikslius
pavadinimus parodo docker volume ls.
Suplanuota patikros pratyba
backup-offsite taip pat paleidžia pratybą kas
QUIRE_BACKUP_DRILL_INTERVAL_HOURS (pagal nutylėjimą 168, kas savaitę) ir
dar kartą kitu prasėjimu po to, kai viena nepavyksta. Ji parsisiunčia
naujausią už sistemos ribų bazinę atsatinę kopiją ir kiekvieną WAL segmentą
po jos, iššifruoja kiekvieną (kas įrodo, kad raktas jas vis dar atveria ir
nieko nebuvo pakeista), palygina kiekvieną failą su jo manifestu, patikrina,
ar archyvas yra Postgres duomenų katalogas, ir patikrina, ar WAL nuo atsarginės
kopijos neturi skylių. Ataskaita įrašoma į saugyklą kaip
reports/drill-<time>.json ir į paslaugos žurnalą; nepavykus pratybai
įvardijamas failas arba pirmas trūkstamas segmentas.
Atkūrimo pratybos
Atsarginė kopija, kuri niekados nebuvo atkurta, nėra atsarginė kopija. Pratyba
atkuria naujausią pilną bazinę atsatinę kopiją, padarytą prieš tikslinį laiką,
plius WAL archyvą, į laikiną Postgres, niekuo nesidalijančią su gyvąja, ir
įrodo rezultatą:
docker/scripts/restore-drill.sh # to ninety minutes agodocker/scripts/restore-drill.sh --target "2026-09-24 09:30:00+00"
Tikslinis laikas yra UTC būtent tokia forma. Jam reikia bazinės atsarginės
kopijos, senesnės už jį, ir archyvuoto WAL už jį: naujai įdiegtoje sistemoje
padarykite bazinę atsatinę kopiją ir palaukite kito archyvuoto segmento
(daugiausia minutė, jei kas nors rašoma), prieš rinkdamiesi laiką po atsatinės
kopijos. Pratybai reikia Docker ir bash sistemoje, nieko daugiau.
Kiekvienas žingsnis pratybą paverčia nesėkminga:
Atkuriamumas: laikinas klasteris atkūrimu pasiekia tikslinį laiką ir
atsidaro.
Pilnumas: kiekvienos lentelės eilučių skaičius prieš gyvą duomenų bazę
(tooling/restore-drill). Gyva duomenų bazė nuo to laiko pajudėjo, todėl
lentelė gali skirtis tiek, kiek didesnis iš 500 eilučių ir dešimties
procentų jos dydžio, bet kuria kryptimi (rašymai ją atsilieka, trynimai
daro, kad atkurta turi daugiau); po tikslinio laiko sukurta skiltis nėra
prarasta lentelė. Plėskite leidimą užimtesnėje sistemoje su
QUIRE_DRILL_MAX_BEHIND ir QUIRE_DRILL_MAX_DRIFT_RATIO. Trūkstama arba
ištuštinta lentelė priverčia pratybą nepavykti.
Privalumas: programų vaidmuo skaito per eilučių lygio apsaugą.
Laikas: nuo pradžios iki žalios, prieš QUIRE_DRILL_RTO_SECONDS
(pagal nutylėjimą 3600).
Ji niekada nerašo į gyvą duomenų bazę arba jos tomus: atsatinės kopijos ir
WAL tomai prijungiami tik skaitymui, o laikinas klasteris pašalinamas pabaigoje,
pavykus arba nepavykus.
Nustatykite QUIRE_DRILL_REPORT į kelią, kad būtų įrašytas JSON ataskaitas,
pavykus arba nepavykus, ir paleiskite ją pagal tvarkaraštį iš Docker sistemos:
Paleiskite ją kas mėnesį ir prieš kiekvieną atnaujinimą. Nepavykusios pratybos
blokuoja atnaujinimą. Kartą per ketvirtį leiskite kas, kas nerašė šio veiksmų
vadovo, atlikti tikrą atkūrimą taške atsarginėje sistemoje, naudojantis tik
šiuo dokumentu.
Failai
Vietiniai failai gyvena files tome. Atsarginę kopiją darykite kartu su
duomenų baze, tuo pačiu metu, ir abu atkurkite kartu:
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 .
Naudojant objektų saugyklą, įjunkite kibiro versijavimą ir laikykite 35 dienas
neaktualių versijų; tada failų atkūrimas taške yra paties kibiro.