---
title: "ការបម្រុងទុក ការស្តារចំពោះពេលវេលា និងលំហាត់ស្តារឡើងវិញ"
description: "បម្រុងទុក Quire ស្តារវាទៅចំណុចមួយក្នុងពេលវេលា ហើយបញ្ជាក់វាជាមួយលំហាត់ស្តារឡើងវិញ។"
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/km/llms.txt
> Use this file to discover all available pages before exploring further.

# ការបម្រុងទុក ការស្តារចំពោះពេលវេលា និងលំហាត់ស្តារឡើងវិញ

<span id="backup-point-in-time-recovery-and-the-restore-drill"></span>

រចនាសម្ព័ន្ធស្ថិតនៅ `docs/architecture/23-ops.md` ផ្នែក 8។ នេះជា runbook សម្រាប់ផលិតផល Docker Compose។ វាត្រូវបានសរសេរដើម្បីឱ្យនរណាម្នាក់ធ្វើតាមដែលមិនបានសរសេរវា។ បើជំហានណាមួយមិនច្បាស់ នោះជាកំហុសក្នុងឯកសារនេះ។

## អ្វីដែលត្រូវបានការពារ និងដោយរបៀបណា <!--quire:what-is-protected-and-how-->

| ទ្រព្យសម្បត្តិ | របៀប | កន្លែង |
| --- | --- | --- |
| មូលដ្ឋានទិន្នន័យ | WAL ទុកក្នុងបណ្ណសារឥតឈប់ឈរ យ៉ាងច្រើនរៀងរាល់ 60 វិនាទី ចាប់ពីការចាប់ផ្តើមដំបូង | volume `pgwal` |
| មូលដ្ឋានទិន្នន័យ | ការបម្រុងទុកគោលជាមួយ `pg_basebackup` ប្រចាំថ្ងៃតាមលំនាំដើម (`backup-scheduler`) | volume `pgbackup` |
| មូលដ្ឋានទិន្នន័យ | ច្បាប់ចម្លងដែលបានឌិគ្រីបនៃការបម្រុងទុកគោល និង WAL រៀងរាល់ប្រាំនាទី (`backup-offsite`) | ឃ្លាំងផ្សេងដែលអ្នកដាក់ឈ្មោះ |
| ឯកសារ | volume `files`។ ចម្លងវាជាមួយឧបករណ៍បម្រុងទុកនៃម៉ាស៊ីនរបស់អ្នក ឬប្រើការផ្ទុកវត្ថុដែលមានកំណែ | volume `files` |
| សំណង់ | `docker/.env` ជាងគេ `QUIRE_MASTER_KEY` (និង `QUIRE_MASTER_KEY_RETIRED` ណាដែលនៅតែប្រើ) `QUIRE_BACKUP_ENCRYPTION_KEY` និង `docker/secrets/audit-signing-key.pem` | រក្សាច្បាប់ចម្លងចេញពីម៉ាស៊ីននេះ |
| index ស្វែងរក cache rendition | មិនបម្រុងទុក។ សង់ឡើងវិញ | |

គោលដៅ៖ ចំណុចស្តារក្នុង 60 វិនាទីនៃការដួល ហើយការស្តារក្នុង 60 នាទីសម្រាប់មូលដ្ឋានទិន្នន័យ 500 GB។

កំហុសពីរគឺធម្មតា។ មូលដ្ឋានទិន្នន័យដែលបានស្តារ**ដោយគ្មានឯកសាររបស់វា**បង្ហាញទំព័រខូច។ មូលដ្ឋានទិន្នន័យដែលបានស្តារ**ដោយគ្មាន `QUIRE_MASTER_KEY`**មិនអាចឌិគ្រីបលិខិតសម្គាល់ SSO webhook និងការរួមបញ្ចូលដែលវាកាន់បានទេ។ រហូតដល់ការបង្វិលសោ master បញ្ចប់ដោយគ្មានអ្វីដែលមិនដោះស្រាយ ([key-rotation.md](/km/ops/key-rotation/)) នោះរួមបញ្ចូលសោដែលបានដក។ ទាំងពីរជាផ្នែកនៃការបម្រុងទុក។

## ការយកការបម្រុងទុក <!--quire:taking-backups-->

ការបម្រុងទុកគោលនៃពេញ kluster៖

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

វារក្សាការបម្រុងទុកគោលថ្មីបំផុត `QUIRE_BACKUP_KEEP` (លំនាំដើម 5) ហើយកាត់ WAL ដែលចាស់បំផុតមិនត្រូវការទៀត ដូច្នេះបណ្ណសារមិនអាចធំឡើងឥតដែនកំណត់។ កំណត់កាលវិភាគប្រចាំថ្ងៃជាមួយ cron ឬ timer មួយ systemd លើម៉ាស៊ីន៖

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

ឬអនុញ្ញាតឱ្យស្តាខកំណត់កាលវិភាគវា៖ profile `backup` ដំណើរការ `backup-scheduler` ដែលយកការបម្រុងទុកគោលរៀងរាល់ `QUIRE_BACKUP_INTERVAL_HOURS` (លំនាំដើម 24) ហើយ `backup-offsite` ដែលបានពិពណ៌នាខាងក្រោម។

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

## ច្បាប់ចម្លងឌិគ្រីបចេញពីម៉ាស៊ីន <!--quire:encrypted-off-host-copies-->

volume ទាំងពីរស្ថិតលើម៉ាស៊ីនតែមួយជាមួយមូលដ្ឋានទិន្នន័យ ហើយការបម្រុងទុកលើម៉ាស៊ីនដែលបានដួលមិនមែនជាការបម្រុងទុកទេ។ `backup-offsite` ចម្លងការបម្រុងទុកគោលនីមួយៗ និង segment WAL ដែលបានទុកក្នុងបណ្ណសារនីមួយៗទៅឃ្លាំងផ្សែងតាមច្រកការផ្ទុក ដែលបានឌិគ្រីប ហើយរក្សាពួកវានៅទីនោះក្រោមការរក្សាទុក៖

- **ការឌិគ្រីប។** AES-256-GCM ជាមួយ `QUIRE_BACKUP_ENCRYPTION_KEY` (ឬឯកសារដែល `QUIRE_BACKUP_ENCRYPTION_KEY_FILE` ដាក់ឈ្មោះ)៖ 32 បៃ ពី `openssl rand -hex 32`។ ឯកសារនីមួយៗមាន nonce ផ្ទាល់ និង authentication tag ដូច្នេះច្បាប់ចម្លងអានមិនបានដោយគ្មានសោ ហើយការផ្លាស់ប្តូរណាមួយចំពោះវាត្រូវបានរកឃើញ។ រក្សាសោជាមួយ `QUIRE_MASTER_KEY` ឆ្ងាយពីម៉ាស៊ីននេះ ហើយឆ្ងាយពីឃ្លាំងបម្រុងទុក។ គ្មានសោគ្មានការស្តារទេ។
- **កន្លែង។** `QUIRE_BACKUP_STORAGE_DRIVER` ជា `s3`, `azure` ឬ `local` (ថាសឆ្ងាយដែលបានតោងនៅ `QUIRE_BACKUP_STORAGE_ROOT`)។ ការកំណត់ជាការកំណត់ឯកសារផ្ទុកដែលមានបូក `QUIRE_BACKUP_` ខាងមុខ៖ `QUIRE_BACKUP_S3_ENDPOINT`, `QUIRE_BACKUP_S3_BUCKET`, `QUIRE_BACKUP_S3_ACCESS_KEY_ID` និងផ្សេងៗ។ ប្រើ bucket ខុសគ្នា ហើយល្អបំផុតគណនីខុសគ្នាពីឯកសារ ជាមួយលិខិតសម្គាល់ដែលអាចសរសេរបានប៉ុន្តែមិនអាចលុបបាន បើអ្នកផ្តល់អនុញ្ញាត។
- **ការរក្សាទុក។** ការបម្រុងទុកគោលថ្មីបំផុត `QUIRE_BACKUP_OFFSITE_KEEP` (លំនាំដើម `QUIRE_BACKUP_KEEP` ឬ 7) និង WAL ដែលចាស់បំផុតក្នុងចំណោមពួកវាត្រូវការ។ បញ្ជី និង segment ចាស់ៗត្រូវបានលុបចេញពីឃ្លាំង។
- **ពេល។** រៀងរាល់ `QUIRE_BACKUP_SHIP_INTERVAL_SECONDS` (លំនាំដើម 300)។ ការដឹកជញ្ជូនស្ថិតនិរន្តរ៍៖ អ្វីដែលបានផ្ទុករួចហើយត្រូវបានរំលង ហើយការបម្រុងទុកគោលរាប់ថាបានផ្ទុកតែនៅពេល manifest របស់វាត្រូវបានសរសេរ ចុងក្រោយ។

ពាក្យបញ្ជាដដែលដំណើរការដោយដៃ៖

```sh
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
```

ដើម្បីស្តារលើម៉ាស៊ីនថ្មី នាំបញ្ជីមួយត្រឡប់មុនសិន រួចធ្វើតាមជំហានខាងក្រោមជាមួយថតដែលបានទាញយកជំនួស volume `pgbackup` ហើយ `wal-archive` ដែលបានទាញយកជំនួស `pgwal`៖

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

## ការស្តារចំពោះពេលវេលា <!--quire:restoring-to-a-point-in-time-->

ប្រើនេះបន្ទាប់ពីការបាត់បង់ទិន្នន័យ៖ ការនាំចូលខូច វគ្គសិក្សាដែលបានលុប ការផ្លាស់ប្តូរ contract ដែលអ្នកត្រូវលុប។ វាជំនួសមូលដ្ឋានទិន្នន័យផ្ទាល់ ដូច្នេះហាត់ប្រាណជាមួយលំហាត់ខាងក្រោមជាមុនសិន។

1. **ជ្រើសពេលគោលដៅ** ជា UTC ទាន់មុនការខូច៖ `2026-09-24 09:30:00+00`។ កំណត់ត្រាសវនកម្ម (`/admin/audit`) ជាទូទៅបង្ហាញពេលនោះ។
2. **ឈប់អ្វីៗដែលសរសេរ**៖ `docker compose -f docker/compose.yaml stop web content worker scheduler collab`
3. **រក្សា kluster ដែលខូច** រហូតដល់ការស្តារត្រូវបានផ្ទៀងផ្ទាត់៖
   ```sh
   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. **បើកការបម្រុងទុកគោលថ្មីបំផុតដែលចាស់ជាងពេលគោលដៅ**ចូលក្នុង volume ទិន្នន័យ ហើយសុំការស្តារគោលដៅ៖
   ```sh
   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. **ស្តារ**៖ ចាប់ផ្តើម Postgres មួយដងជាមួយការកំណត់ស្តារ ពី override មួយ Compose ដូច្នេះឯកសារធម្មតាមិនប៉ះ៖
   ```yaml
   # 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]
   ```
   override ជំនួសពាក្យបញ្ជាទាំងមូល ដូច្នេះវាបញ្ជូនការកំណត់ពីរដែលការស្តារពឹងផ្អែកលើ៖ `max_connections` មិនទាបជាងនៃ primary (បើអត់ការស្តារឈប់ជាមួយ "insufficient parameter settings") និង `pg_hba.conf` ដែលបានតោង។
   ```sh
   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. **ពិនិត្យវា** មុននឹងអនុញ្ញាតឱ្យអ្នកណាចូល៖ ខ្សែសវនកម្ម (`docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts`) ហើយថាទិន្នន័យដែលបានបាត់ត្រឡប់មកវិញ។
7. **ត្រឡប់ទៅធម្មតា**៖ `docker compose -f docker/compose.yaml up -d`។ នេះចាប់ផ្តើមឡើងវិញ Postgres ជាមួយការទុកក្នុងបណ្ណសារបើក ហើយពេលវេលា WAL ថ្មីមួយចាប់ផ្តើម។ យកការបម្រុងទុកគោលថ្មីភ្លាមៗ។

ឈ្មោះគម្រោង `quire` ដាក់បូកខាងមុខ volume នីមួយៗ។ `docker volume ls` បង្ហាញឈ្មោះពិត។

## លំហាត់ផ្ទៀងផ្ទាត់ដែលបានកំណត់កាលវិភាគ <!--quire:the-scheduled-verification-drill-->

`backup-offsite` ក៏ដំណើរការលំហាត់មួយរៀងរាល់ `QUIRE_BACKUP_DRILL_INTERVAL_HOURS` (លំនាំដើម 168 ប្រចាំសប្តាហ៍) ហើយម្តងទៀតនៅការឆ្លងកាត់បន្ទាប់បន្ទាប់ពីមួយបរាជ័យ។ វាទាញយកការបម្រុងទុកគោលចេញពីម៉ាស៊ីនថ្មីបំផុត និង segment WAL ទាំងអស់បន្ទាប់ពីវា ឌិគ្រីបនីមួយៗ (ដែលបញ្ជាក់ថាសោនៅតែបើកពួកវា ហើយគ្មានអ្វីបានកែ) ប្រៀបធៀបឯកសារនីមួយៗជាមួយ manifest របស់វា ពិនិត្យថាបណ្ណសារជាថតទិន្នន័យ Postgres ហើយពិនិត្យថាពីការបម្រុងទុកតទៅ WAL គ្មានគម្លាត។ របាយការណ៍ត្រូវបានសរសេរទៅឃ្លាំងជា `reports/drill-<time>.json` ហើយទៅកំណត់ត្រានៃសេវា។ លំហាត់ដែលបរាជ័យដាក់ឈ្មោះឯកសារ ឬ segment ដែលបាត់ដំបូង។

## លំហាត់ស្តារឡើងវិញ <!--quire:the-restore-drill-->

ការបម្រុងទុកដែលមិនដែលបានស្តារមិនមែនជាការបម្រុងទុកទេ។ លំហាត់ស្តារការបម្រុងទុកគោលពេញលេញថ្មីបំផុតដែលបានយកមុនពេលគោលដៅ បន្ថែមបណ្ណសារ WAL ចូល Postgres សាកល្បងដែលមិនចែករំលែកអ្វីជាមួយផ្ទាល់ ហើយបញ្ជាក់លទ្ធផល៖

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

ពេលគោលដៅជា UTC តាមទម្រង់នោះពិត។ វាត្រូវការការបម្រុងទុកគោលចាស់ជាងវា ហើយ WAL ដែលបានទុកក្នុងបណ្ណសារលើសពីវា។ លើការដំឡើងថ្មី យកការបម្រុងទុកគោល ហើយរង់ចាំ segment ដែលបានទុកក្នុងបណ្ណសារបន្ទាប់ (យ៉ាងច្រើនមួយនាទីជាមួយការសរសេរ) មុននឹងជ្រើសពេលគោលដៅបន្ទាប់ពីការបម្រុងទុក។ លំហាត់ត្រូវការ Docker និង bash លើម៉ាស៊ីន គ្មានអ្វីផ្សេង។

ជំហាននីមួយៗបរាជ័យលំហាត់៖

1. **សមត្ថភាពស្តារ**៖ kluster សាកល្បងបញ្ជូនឡើងវិញទៅពេលគោលដៅ ហើយបើក។
2. **ភាពពេញលេញ**៖ ចំនួនជួររបស់តារាងនីមួយៗប្រឆាំងនឹងមូលដ្ឋានទិន្នន័យផ្ទាល់ (`tooling/restore-drill`)។ មូលដ្ឋានទិន្នន័យផ្ទាល់បានផ្លាស់ទីបន្ទាប់ពីពេលគោលដៅ ដូច្នេះតារាងមួយអាចខុសគ្នាដោយធំជាងនៃ 500 ជួរ និងមួយទាក់នៃទំហំរបស់វា ក្នុងទិសណាមួយ (ការសរសេរធ្វើឱ្យវា យឺត ការលុបធ្វើឱ្យការស្តារកាន់ច្រើន)។ ផ្នែកដែលបានបង្កើតបន្ទាប់ពីពេលគោលដៅមិនមែនជាតារាងដែលបានបាត់ទេ។ ពង្រីកការអនុញ្ញាតលើការដំឡើងដែលមមាញឹកជាងជាមួយ `QUIRE_DRILL_MAX_BEHIND` និង `QUIRE_DRILL_MAX_DRIFT_RATIO`។ តារាងដែលបាត់ ឬទទេបរាជ័យ។
3. **ភាពពេញលេញនៃទិន្នន័យ**៖ ខ្សែ hash សវនកម្មផ្ទៀងផ្ទាត់លើច្បាប់ចម្លងដែលបានស្តារ។
4. **ភាពអាចប្រើបាន**៖ តួនាទីកម្មវិធីអានតាមសុវត្ថិភាពជួរដេក។
5. **ពេលវេលា**៖ ពីការចាប់ផ្តើមដល់សុខភាពល្អ ប្រឆាំងនឹង `QUIRE_DRILL_RTO_SECONDS` (លំនាំដើម 3600)។

វាមិនដែលសរសេរទៅមូលដ្ឋានទិន្នន័យផ្ទាល់ ឬ volume របស់វាទេ៖ volume បម្រុងទុក និង WAL ត្រូវបានតោងតែអាន ហើយ kluster សាកល្បងត្រូវបានយកចេញនៅចុងបញ្ចប់ ជោគជ័យ ឬបរាជ័យ។

កំណត់ `QUIRE_DRILL_REPORT` ទៅផ្លូវមួយដើម្បីឱ្យរបាយការណ៍ JSON ត្រូវបានសរសេរ ជោគជ័យ ឬបរាជ័យ ហើយដំណើរការវាតាមកាលវិភាគពីម៉ាស៊ីន Docker៖

```cron
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
```

ដំណើរការវាប្រចាំខែ ហើយមុនរាល់ការធ្វើឱ្យប្រសើរ។ លំហាត់ដែលបរាជ័យរារាំងការធ្វើឱ្យប្រសើរ។ មួយត្រីមាសមួយ ឱ្យនរណាម្នាក់ដែលមិនបានសរសេរ runbook នេះធ្វើការស្តារចំពោះពេលវេលាពិតលើម៉ាស៊ីនសម្រាប់បម្រុង ដោយប្រើតែឯកសារនេះ។

## ឯកសារ <!--quire:files-->

ឯកសារក្នុងស្រុកស្ថិតក្នុង volume `files`។ បម្រុងទុកវាជាមួយមូលដ្ឋានទិន្នន័យ ក្នុងពេលតែមួយ ហើយស្តារទាំងពីរជាមួយគ្នា៖

```sh
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 .
```

ជាមួយការផ្ទុកវត្ថុ បើក bucket versioning ហើយរក្សា 35 ថ្ងៃនៃកំណែដែលមិនមែនបច្ចុប្បន្ន។ ការស្តារចំពោះពេលវេលាសម្រាប់ឯកសារបន្ទាប់មកជារបស់ bucket ផ្ទាល់។

Source: https://docs.quirelms.com/km/ops/backup-restore/index.mdx
