შიგთავსზე გადასვლა

სარეზერვო ასლი, დროში აღდგენა და აღდგენის წვრთნა

Quire-ის სარეზერვო ასლის გაკეთება, მისი დროში აღდგენა და ამის დამტკიცება აღდგენის წვრთნით.

Markdown-ად ნახვა

დიზაინია docs/architecture/23-ops.md მე-8 სექცია. ეს სამოქმედო სახელმძღვანელოა Docker Compose პროდუქტისთვის. ის დაწერილია იმისთვის, რომ მიჰყვეს ვიღაცა, ვინც ის არ დაწერა; თუ ნაბიჯი გაუგებარია, ეს ამ დოკუმენტის დეფექტია.

რა არის დაცული და როგორ

აქტივი როგორ სად
მონაცემთა ბაზა WAL არქივდება განუწყვეტლივ, მაქსიმუმ ყოველ 60 წამში, პირველი ჩართვიდან pgwal ტომი
მონაცემთა ბაზა საბაზო სარეზერვო ასლები pg_basebackup-ით, ნაგულისხმევად ყოველდღიური (backup-scheduler) pgbackup ტომი
მონაცემთა ბაზა საბაზო სარეზერვო ასლებისა და WAL-ის დაშიფრული ასლები, ყოველ ხუთ წუთში (backup-offsite) ცალკე საცავი, რომელსაც თქვენ ასახელებთ
ფაილები files ტომი. დააკოპირეთ ის თქვენი ჰოსტის სარეზერვო ინსტრუმენტით ან გამოიყენეთ ვერსირებული ობიექტების საცავი files ტომი
საიდუმლოებები docker/.env, უპირველესად QUIRE_MASTER_KEY (და ნებისმიერი ჯერ გამოყენებადი QUIRE_MASTER_KEY_RETIRED), QUIRE_BACKUP_ENCRYPTION_KEY და docker/secrets/audit-signing-key.pem დაიტოვეთ ასლი ამ ჰოსტის გარეთ
ძიების ინდექსები, ქეშები, რენდერები სარეზერვო არ კეთდება; ხელახლა აგებულია

სამიზნეები: აღდგენის წერტილი ჩავარდნიდან 60 წამის ფარგლებში და აღდგენა 60 წუთში 500 GB მონაცემთა ბაზისთვის.

ორი შეცდომა ხშირია. მონაცემთა ბაზა, აღდგენილი ფაილების გარეშე, გატეხილ გვერდებს აჩვენებს. მონაცემთა ბაზა, აღდგენილი QUIRE_MASTER_KEY-ის გარეშე, ვერ გაშიფრავს SSO, ვებჰუკისა და ინტეგრაციის რწმუნებათა სიგელებს, რომლებსაც ის ინახავს; სანამ მასტერ გასაღების როტაცია დასრულდება მოუგვარებლის გარეშე (key-rotation.md), ეს საპენსიო გასაღებებსაც მოიცავს. ორივე სარეზერვო ასლის ნაწილია.

სარეზერვო ასლების გაკეთება

მთელი კლასტერის საბაზო სარეზერვო ასლი:

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

ის ინახავს უახლეს QUIRE_BACKUP_KEEP საბაზო სარეზერვო ასლებს (ნაგულისხმევი 5) და ჭრის WAL-ს, რომელიც უძველესს აღარ სჭირდება, ასე რომ არქივი უსაზღვროდ ვერ იზრდება. დაგეგმეთ ის ყოველდღიურად cron-ით ან systemd ტაიმერით ჰოსტზე:

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

ან სტეკმა დაგეგმოს ის: backup პროფილი აწარმოებს backup-scheduler-ს, რომელიც საბაზო სარეზერვო ასლს იღებს ყოველ QUIRE_BACKUP_INTERVAL_HOURS-ში (ნაგულისხმევი 24), და backup-offsite-ს, აღწერილს შემდეგ.

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

დაშიფრული ჰოსტგარეთა ასლები

ორივე ტომი იმავე ჰოსტზე ცხოვრობს, სადაც მონაცემთა ბაზა, ხოლო სარეზერვო ასლი მანქანაზე, რომელიც ჩავარდა, სარეზერვო ასლი არ არის. backup-offsite აკოპირებს ყოველ საბაზო სარეზერვო ასლს და ყოველ დაარქივებულ WAL სეგმენტს ცალკე საცავში საცავის პორტით, დაშიფრულად, და ინახავს მათ იქ შენახვის ვადით:

  • დაშიფვრა. AES-256-GCM QUIRE_BACKUP_ENCRYPTION_KEY-ით (ან ფაილით, დასახელებული QUIRE_BACKUP_ENCRYPTION_KEY_FILE-ით): 32 ბაიტი, openssl rand -hex 32-დან. თითოეულ ფაილს აქვს საკუთარი ნონსი და ავთენტიფიკაციის ტეგი, ასე რომ ასლი გასაღების გარეშე წაუკითხავია და მასში ნებისმიერი ცვლილება აღმოჩენილია. დაიტოვეთ გასაღები 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 და ასე შემდეგ. გამოიყენეთ სხვა ბაკეტი და, იდეალურად, სხვა ანგარიში, ვიდრე ფაილები, რწმუნებათა სიგელებით, რომლებსაც ჩაწერა შეუძლიათ, მაგრამ წაშლა არა, თუ პროვაიდერი ამას უშვებს.
  • შენახვა. უახლესი QUIRE_BACKUP_OFFSITE_KEEP საბაზო სარეზერვო ასლები (ნაგულისხმევი QUIRE_BACKUP_KEEP, სხვაგვარად 7) და WAL, რომელიც მათგან უძველესს სჭირდება; ძველი ნაკრებები და სეგმენტები საცავიდან იშლება.
  • როდის. ყოველ QUIRE_BACKUP_SHIP_INTERVAL_SECONDS-ში (ნაგულისხმევი 300). გაგზავნა იდემპოტენტურია: ის, რაც უკვე შენახულია, გამოტოვებულია, ხოლო საბაზო სარეზერვო ასლი შენახულად ითვლება მხოლოდ მას შემდეგ, რაც მისი მანიფესტი დაიწერება, ბოლოს.

იგივე ბრძანება ხელით მუშაობს:

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

ახალ ჰოსტზე აღსადგენად ჯერ ნაკრები უკან მოიტანეთ, შემდეგ მიჰყევით ქვემოთ ნაბიჯებს მოტანილი დირექტორიით pgbackup ტომის ნაცვლად და მოტანილი wal-archive-ით pgwal-ის ნაცვლად:

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

დროში აღდგენა

გამოიყენეთ ეს მონაცემთა დაკარგვის შემდეგ: ცუდი იმპორტი, წაშლილი კურსი, შეკუმშვის მიგრაცია, რომლის გაუქმებაც გჭირდებათ. ის ცვლის ცოცხალ მონაცემთა ბაზას, ამიტომ ჯერ ქვემოთ წვრთნით ივარჯიშეთ.

  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. დაიტოვეთ დაზიანებული კლასტერი, სანამ აღდგენა დასტურდება:
    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. გახსენით სამიზნეზე ძველი უახლესი საბაზო სარეზერვო ასლი მონაცემთა ტომში და მოითხოვეთ მიზნობრივი აღდგენა:
    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 ერთხელ აღდგენის პარამეტრებით, Compose გადაფარვიდან, რათა ჩვეულებრივი ფაილი ხელუხლებელი იყოს:
    # 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 არანაკლები პირველადისა (სხვაგვარად აღდგენა წყდება „არასაკმარისი პარამეტრის პარამეტრებით“-ით) და დამონტაჟებული 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. შეამოწმეთ ის, სანამ ვინმეს შეუშვებთ: აუდიტის ჯაჭვი (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 წინ უძღვის თითოეულ ტომს; docker volume ls აჩვენებს ზუსტ სახელებს.

დაგეგმილი ვერიფიკაციის წვრთნა

backup-offsite ასევე აწარმოებს წვრთნას ყოველ QUIRE_BACKUP_DRILL_INTERVAL_HOURS-ში (ნაგულისხმევი 168, ყოველკვირეული), და ისევ შემდეგ გავლაზე წარუმატებლობის შემდეგ. ის მოტანს უახლეს ჰოსტგარეთა საბაზო სარეზერვო ასლს და მის შემდეგ ყოველ WAL სეგმენტს, შიფრავს თითოეულს (რაც ამტკიცებს, რომ გასაღები კვლავ ხსნის მათ და არაფერი შეცვლილა), ადარებს თითოეულ ფაილს მის მანიფესტთან, ამოწმებს, რომ არქივი Postgres მონაცემთა დირექტორიაა, და ამოწმებს, რომ WAL-ს სარეზერვო ასლიდან მოყოლებული წყვეტა არ აქვს. ანგარიში იწერება საცავში, როგორც reports/drill-<time>.json, და სერვისის ჟურნალში; წარუმატებელი წვრთნა ასახელებს ფაილს ან პირველ დაკარგულ სეგმენტს.

აღდგენის წვრთნა

სარეზერვო ასლი, რომელიც არასდროს აღდგენილა, სარეზერვო ასლი არ არის. წვრთნა აღადგენს სამიზნემდე აღებულ უახლეს სრულ საბაზო სარეზერვო ასლს პლუს 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 მის მიღმა: ახალ ინსტალაციაზე გააკეთეთ საბაზო სარეზერვო ასლი და დაელოდეთ შემდეგ დაარქივებულ სეგმენტს (მაქსიმუმ წუთი ჩაწერებით), სანამ სამიზნეს სარეზერვო ასლის შემდეგ აირჩევთ. წვრთნას სჭირდება Docker და bash ჰოსტზე და მეტი არაფერი.

თითოეული ნაბიჯი აგდებს წვრთნას:

  1. აღდგენადობა: ნულოვანი კლასტერი სამიზნემდე უკრავს და იხსნება.
  2. სისრულე: ყოველი ცხრილის სტრიქონების რაოდენობა ცოცხალი მონაცემთა ბაზის წინააღმდეგ (tooling/restore-drill). ცოცხალი მონაცემთა ბაზა სამიზნის შემდეგ წინ წავიდა, ამიტომ ცხრილი შეიძლება განსხვავდებოდეს 500 სტრიქონსა და მისი ზომის მეათედს შორის უფრო დიდით, ორივე მიმართულებით (ჩაწერები მას აყოვნებენ, წაშლები აღდგენას მეტს უტოვებენ); სამიზნის შემდეგ შექმნილი დანაყოფი დაკარგული ცხრილი არ არის. გააფართოვეთ დაშვება უფრო დატვირთულ ინსტალაციაზე QUIRE_DRILL_MAX_BEHIND-ითა და QUIRE_DRILL_MAX_DRIFT_RATIO-ით. დაკარგული ან დაცარიელებული ცხრილი აგდებს.
  3. მთლიანობა: აუდიტის ჰეშის ჯაჭვი დასტურდება აღდგენილ ასლზე.
  4. გამოყენებადობა: აპლიკაციის როლი კითხულობს სტრიქონის დონის უსაფრთხოების მეშვეობით.
  5. დრო: დაწყებიდან მწვანემდე, QUIRE_DRILL_RTO_SECONDS-ის წინააღმდეგ (ნაგულისხმევი 3600).

ის არასდროს წერს ცოცხალ მონაცემთა ბაზაში ან მის ტომებში: სარეზერვო ასლისა და WAL ტომები დამონტაჟებულია მხოლოდ წაკითხვით, ხოლო ნულოვანი კლასტერი ბოლოს იშლება, გავლით თუ ჩავარდნით.

დააყენეთ 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

გაუშვით ის ყოველთვიურად და ყოველი განახლების წინ. წარუმატებელი წვრთნა ბლოკავს განახლებას. კვარტალში ერთხელ ვიღაცამ, ვინც ეს სამოქმედო სახელმძღვანელო არ დაწერა, შეასრულოს ნამდვილი დროში აღდგენა სათადარიგო ჰოსტზე, მხოლოდ ამ დოკუმენტის გამოყენებით.

ფაილები

ლოკალური ფაილები 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 .

ობიექტების საცავთან ჩართეთ ბაკეტის ვერსირება და დაიტოვეთ 35 დღის არაამჟამინდელი ვერსიები; ფაილების დროში აღდგენა მაშინ ბაკეტის საკუთარია.

ნავიგაცია

ძიებისთვის აკრიფეთ…

↑↓ ნავიგაცია↵ არჩევაEsc დახურვა