រចនាសម្ព័ន្ធស្ថិតនៅ docs/architecture/23-ops.md ផ្នែក 8។ នេះជា runbook សម្រាប់ផលិតផល Docker Compose។ វាត្រូវបានសរសេរដើម្បីឱ្យនរណាម្នាក់ធ្វើតាមដែលមិនបានសរសេរវា។ បើជំហានណាមួយមិនច្បាស់ នោះជាកំហុសក្នុងឯកសារនេះ។
អ្វីដែលត្រូវបានការពារ និងដោយរបៀបណា
| ទ្រព្យសម្បត្តិ | របៀប | កន្លែង |
|---|---|---|
| មូលដ្ឋានទិន្នន័យ | 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) នោះរួមបញ្ចូលសោដែលបានដក។ ទាំងពីរជាផ្នែកនៃការបម្រុងទុក។
ការយកការបម្រុងទុក
ការបម្រុងទុកគោលនៃពេញ kluster៖
docker compose -f docker/compose.yaml --profile backup run --rm backupវារក្សាការបម្រុងទុកគោលថ្មីបំផុត QUIRE_BACKUP_KEEP (លំនាំដើម 5) ហើយកាត់ WAL ដែលចាស់បំផុតមិនត្រូវការទៀត ដូច្នេះបណ្ណសារមិនអាចធំឡើងឥតដែនកំណត់។ កំណត់កាលវិភាគប្រចាំថ្ងៃជាមួយ cron ឬ timer មួយ systemd លើម៉ាស៊ីន៖
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 ដែលបានពិពណ៌នាខាងក្រោម។
docker compose -f docker/compose.yaml --profile backup up -dច្បាប់ចម្លងឌិគ្រីបចេញពីម៉ាស៊ីន
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 របស់វាត្រូវបានសរសេរ ចុងក្រោយ។
ពាក្យបញ្ជាដដែលដំណើរការដោយដៃ៖
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៖
bun apps/worker/src/backups/main.ts fetch base-20260924T021500Z /srv/restoreការស្តារចំពោះពេលវេលា
ប្រើនេះបន្ទាប់ពីការបាត់បង់ទិន្នន័យ៖ ការនាំចូលខូច វគ្គសិក្សាដែលបានលុប ការផ្លាស់ប្តូរ contract ដែលអ្នកត្រូវលុប។ វាជំនួសមូលដ្ឋានទិន្នន័យផ្ទាល់ ដូច្នេះហាត់ប្រាណជាមួយលំហាត់ខាងក្រោមជាមុនសិន។
- ជ្រើសពេលគោលដៅ ជា UTC ទាន់មុនការខូច៖
2026-09-24 09:30:00+00។ កំណត់ត្រាសវនកម្ម (/admin/audit) ជាទូទៅបង្ហាញពេលនោះ។ - ឈប់អ្វីៗដែលសរសេរ៖
docker compose -f docker/compose.yaml stop web content worker scheduler collab - រក្សា kluster ដែលខូច រហូតដល់ការស្តារត្រូវបានផ្ទៀងផ្ទាត់៖
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/ - បើកការបម្រុងទុកគោលថ្មីបំផុតដែលចាស់ជាងពេលគោលដៅចូលក្នុង volume ទិន្នន័យ ហើយសុំការស្តារគោលដៅ៖
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' - ស្តារ៖ ចាប់ផ្តើម Postgres មួយដងជាមួយការកំណត់ស្តារ ពី override មួយ Compose ដូច្នេះឯកសារធម្មតាមិនប៉ះ៖
override ជំនួសពាក្យបញ្ជាទាំងមូល ដូច្នេះវាបញ្ជូនការកំណត់ពីរដែលការស្តារពឹងផ្អែកលើ៖# 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]max_connectionsមិនទាបជាងនៃ primary (បើអត់ការស្តារឈប់ជាមួយ “insufficient parameter settings”) និង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" - ពិនិត្យវា មុននឹងអនុញ្ញាតឱ្យអ្នកណាចូល៖ ខ្សែសវនកម្ម (
docker compose -f docker/compose.yaml run --rm worker bun tooling/audit-verify/run.ts) ហើយថាទិន្នន័យដែលបានបាត់ត្រឡប់មកវិញ។ - ត្រឡប់ទៅធម្មតា៖
docker compose -f docker/compose.yaml up -d។ នេះចាប់ផ្តើមឡើងវិញ Postgres ជាមួយការទុកក្នុងបណ្ណសារបើក ហើយពេលវេលា WAL ថ្មីមួយចាប់ផ្តើម។ យកការបម្រុងទុកគោលថ្មីភ្លាមៗ។
ឈ្មោះគម្រោង quire ដាក់បូកខាងមុខ volume នីមួយៗ។ docker volume ls បង្ហាញឈ្មោះពិត។
លំហាត់ផ្ទៀងផ្ទាត់ដែលបានកំណត់កាលវិភាគ
backup-offsite ក៏ដំណើរការលំហាត់មួយរៀងរាល់ QUIRE_BACKUP_DRILL_INTERVAL_HOURS (លំនាំដើម 168 ប្រចាំសប្តាហ៍) ហើយម្តងទៀតនៅការឆ្លងកាត់បន្ទាប់បន្ទាប់ពីមួយបរាជ័យ។ វាទាញយកការបម្រុងទុកគោលចេញពីម៉ាស៊ីនថ្មីបំផុត និង segment WAL ទាំងអស់បន្ទាប់ពីវា ឌិគ្រីបនីមួយៗ (ដែលបញ្ជាក់ថាសោនៅតែបើកពួកវា ហើយគ្មានអ្វីបានកែ) ប្រៀបធៀបឯកសារនីមួយៗជាមួយ manifest របស់វា ពិនិត្យថាបណ្ណសារជាថតទិន្នន័យ Postgres ហើយពិនិត្យថាពីការបម្រុងទុកតទៅ WAL គ្មានគម្លាត។ របាយការណ៍ត្រូវបានសរសេរទៅឃ្លាំងជា reports/drill-<time>.json ហើយទៅកំណត់ត្រានៃសេវា។ លំហាត់ដែលបរាជ័យដាក់ឈ្មោះឯកសារ ឬ segment ដែលបាត់ដំបូង។
លំហាត់ស្តារឡើងវិញ
ការបម្រុងទុកដែលមិនដែលបានស្តារមិនមែនជាការបម្រុងទុកទេ។ លំហាត់ស្តារការបម្រុងទុកគោលពេញលេញថ្មីបំផុតដែលបានយកមុនពេលគោលដៅ បន្ថែមបណ្ណសារ WAL ចូល Postgres សាកល្បងដែលមិនចែករំលែកអ្វីជាមួយផ្ទាល់ ហើយបញ្ជាក់លទ្ធផល៖
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 លើម៉ាស៊ីន គ្មានអ្វីផ្សេង។
ជំហាននីមួយៗបរាជ័យលំហាត់៖
- សមត្ថភាពស្តារ៖ kluster សាកល្បងបញ្ជូនឡើងវិញទៅពេលគោលដៅ ហើយបើក។
- ភាពពេញលេញ៖ ចំនួនជួររបស់តារាងនីមួយៗប្រឆាំងនឹងមូលដ្ឋានទិន្នន័យផ្ទាល់ (
tooling/restore-drill)។ មូលដ្ឋានទិន្នន័យផ្ទាល់បានផ្លាស់ទីបន្ទាប់ពីពេលគោលដៅ ដូច្នេះតារាងមួយអាចខុសគ្នាដោយធំជាងនៃ 500 ជួរ និងមួយទាក់នៃទំហំរបស់វា ក្នុងទិសណាមួយ (ការសរសេរធ្វើឱ្យវា យឺត ការលុបធ្វើឱ្យការស្តារកាន់ច្រើន)។ ផ្នែកដែលបានបង្កើតបន្ទាប់ពីពេលគោលដៅមិនមែនជាតារាងដែលបានបាត់ទេ។ ពង្រីកការអនុញ្ញាតលើការដំឡើងដែលមមាញឹកជាងជាមួយQUIRE_DRILL_MAX_BEHINDនិងQUIRE_DRILL_MAX_DRIFT_RATIO។ តារាងដែលបាត់ ឬទទេបរាជ័យ។ - ភាពពេញលេញនៃទិន្នន័យ៖ ខ្សែ hash សវនកម្មផ្ទៀងផ្ទាត់លើច្បាប់ចម្លងដែលបានស្តារ។
- ភាពអាចប្រើបាន៖ តួនាទីកម្មវិធីអានតាមសុវត្ថិភាពជួរដេក។
- ពេលវេលា៖ ពីការចាប់ផ្តើមដល់សុខភាពល្អ ប្រឆាំងនឹង
QUIRE_DRILL_RTO_SECONDS(លំនាំដើម 3600)។
វាមិនដែលសរសេរទៅមូលដ្ឋានទិន្នន័យផ្ទាល់ ឬ volume របស់វាទេ៖ volume បម្រុងទុក និង WAL ត្រូវបានតោងតែអាន ហើយ kluster សាកល្បងត្រូវបានយកចេញនៅចុងបញ្ចប់ ជោគជ័យ ឬបរាជ័យ។
កំណត់ QUIRE_DRILL_REPORT ទៅផ្លូវមួយដើម្បីឱ្យរបាយការណ៍ JSON ត្រូវបានសរសេរ ជោគជ័យ ឬបរាជ័យ ហើយដំណើរការវាតាមកាលវិភាគពីម៉ាស៊ីន 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ដំណើរការវាប្រចាំខែ ហើយមុនរាល់ការធ្វើឱ្យប្រសើរ។ លំហាត់ដែលបរាជ័យរារាំងការធ្វើឱ្យប្រសើរ។ មួយត្រីមាសមួយ ឱ្យនរណាម្នាក់ដែលមិនបានសរសេរ runbook នេះធ្វើការស្តារចំពោះពេលវេលាពិតលើម៉ាស៊ីនសម្រាប់បម្រុង ដោយប្រើតែឯកសារនេះ។
ឯកសារ
ឯកសារក្នុងស្រុកស្ថិតក្នុង volume files។ បម្រុងទុកវាជាមួយមូលដ្ឋានទិន្នន័យ ក្នុងពេលតែមួយ ហើយស្តារទាំងពីរជាមួយគ្នា៖
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 ផ្ទាល់។