المستوى: متوسط. الشرح ده مبني على فرضية إن قاعدة بياناتك أقل من 50 جيجابايت وتقدر تتحمّل نسخة منطقية (logical dump) يومية. لو أكبر من كده بكتير أو محتاج استرجاع في ثواني، فيه قسم في الآخر بيقولك تعمل إيه.
أتمتة نسخ PostgreSQL الاحتياطية على S3 بـ restic
في آخر المقال هيبقى عندك سكربت واحد بيرفع نسخة مشفّرة ومضغوطة من قاعدة بياناتك على S3 كل ليلة، يمسح القديم لوحده، ويتأكد إن النسخة سليمة فعلًا. صفر تدخل يدوي.
المشكلة باختصار
معظم الفرق بتكتب سطر pg_dump في cron وتنساه. الطريقة دي بتفشل في اللحظة اللي محتاجها فعلًا. النسخة بتتخزّن على نفس الديسك بتاع القاعدة، من غير تشفير، وبتكبر لحد ما تملأ القرص، ومحدش بيجرّب يرجّعها.
سيناريو واقعي: عندك قاعدة app_production حجمها 18 جيجا على سيرفر واحد. حصل تلف في الديسك أو اتمسح الـ volume بالغلط. نسختك الاحتياطية كانت جنبها على نفس السيرفر، فراحت مع الأصل. ده بالظبط اللي قاعدة الـ 3-2-1 بتحذّر منه: 3 نسخ، على وسيطين مختلفين، واحدة منهم خارج الموقع (offsite).
ليه pg_dump لوحده مش كفاية
- نسخة واحدة على نفس القرص: أي عطل في الهاردوير بياخد الأصل والنسخة سوا.
- بدون تشفير: أي حد يوصل للملف يقرأ بيانات عملائك بالكامل.
- بتكبر بلا حدود: كل يوم dump كامل جديد، فالمساحة بتنفجر خلال أسابيع.
- مش متجرَّبة: نسخة معملتش لها restore مرة واحدة هي مجرد وهم أمان.
restic في جملة واحدة
تخيّل إنك بتصوّر كتاب 500 صفحة كل يوم عشان تحتفظ بنسخة. لو كل يوم بتصوّر الكتاب كله، ده هدر رهيب. الأذكى إنك تصوّر الصفحات اللي اتغيّرت بس، وتشاور على الباقي إنه زي إمبارح. ده بالظبط اللي restic بيعمله.
بالتفاصيل العلمية: restic بيقسّم البيانات لقطع متغيّرة الحجم بأسلوب content-defined chunking، وبيبصم كل قطعة ببصمة تشفيرية. القطعة اللي اتخزّنت قبل كده مبتتخزّنش تاني (deduplication)، وكل حاجة بتتشفّر بـ AES-256 قبل ما تسيب سيرفرك. النتيجة: مستودع (repository) على S3 فيه كل تاريخ نسخك، بحجم أقل بكتير من مجموع النسخ الكاملة.
السكربت الكامل (قابل للنسخ)
أول مرة بس، جهّز المستودع على S3:
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="********"
export RESTIC_REPOSITORY="s3:s3.eu-central-1.amazonaws.com/mycompany-db-backups"
export RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
restic initوبعدين ده سكربت النسخ اليومي /usr/local/bin/db-backup.sh:
#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY="s3:s3.eu-central-1.amazonaws.com/mycompany-db-backups"
export RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
DB_NAME="app_production"
pg_dump --format=custom --no-owner "$DB_NAME" \
| restic backup --stdin --stdin-filename "${DB_NAME}.dump" --tag daily
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check --read-data-subset=5%الفكرة إن pg_dump بيطلّع نسخة متسقة، والـ pipe بيوصّلها لـ restic من غير ما تنزل على القرص أصلًا. يعني مش محتاج مساحة مؤقتة بحجم القاعدة.
شغّلها كل ليلة بـ systemd timer
استخدم مؤقّت systemd يشتغل يوميًا الساعة الثانية صباحًا مع الخيار Persistent=true عشان يعوّض التشغيلة الفايتة لو كان السيرفر مطفيًا وقت الموعد. البديل على cron: سطر واحد في crontab يشغّل السكربت الساعة 2 صباحًا ويوجّه المخرجات لملف سجل. فضّلت systemd هنا عشان الاستمرارية والتكامل مع journalctl للتشخيص.
الأرقام اللي بتحصل فعلًا
على قاعدة 18 جيجا حقيقية بتشتغل عليها كل يوم:
- الـ dump بصيغة custom بيتضغط لحوالي 5 جيجا.
- أول لقطة على S3 بتاخد نفس الـ 5 جيجا تقريبًا.
- اللقطات اليومية بعدها بتاخد 180 إلى 260 ميجا بس، لأن restic بيخزّن القطع المتغيّرة فقط.
- بعد شهر كامل، الـ 17 لقطة المحفوظة بتشغل حوالي 8–10 جيجا إجمالًا، مش 150 جيجا زي ما النسخ الكاملة كانت هتعمل.
للاسترجاع، بتجيب أحدث نسخة وتعديها لـ pg_restore مباشرة:
restic dump latest app_production.dump | pg_restore --clean --if-exists -d app_productionالمقايضات (trade-offs)
- سهولة النسخ مقابل بطء الاسترجاع: النسخة المنطقية بتترجم لأوامر SQL، والاسترجاع بيعيد بناء الفهارس. استرجاع 18 جيجا ممكن ياخد 30–60 دقيقة. بتكسب بساطة وقابلية نقل بين إصدارات Postgres، بتخسر سرعة RTO.
- حمل على السيرفر:
pg_dumpبياكل CPU و I/O أثناء التشغيل، عشان كده حطّيناه الساعة 2 بالليل وقت الضغط المنخفض. - تكلفة الـ deduplication: restic بيصرف CPU وهو بيبصم القطع. على قاعدة صغيرة الفرق مش محسوس، على قاعدة كبيرة خطّط له.
- كتلة واحدة: نسخة
--stdinبتتخزّن كـ blob واحد، فمقدرش ترجّع جدول واحد بس منها؛ لازم ترجّع الـ dump كله.
متى لا تستخدم هذه الطريقة
ابعد عنها لو انطبق عليك أي من دول:
- قاعدة أكبر من بضع مئات جيجا، أو محتاج Point-in-Time Recovery وفقدان بيانات قريب من الصفر. هنا استخدم نسخ فيزيائية مع أرشفة WAL عبر pgBackRest أو WAL-G.
- قاعدة مُدارة زي Amazon RDS أو Cloud SQL؛ استخدم snapshots المزوّد نفسه بدل ما تسحب dump عبر الشبكة.
- محتاج استرجاع جدول واحد بسرعة بشكل متكرر؛ النسخة المنطقية ككتلة مش مناسبة للحالة دي.
الخطوة التالية
مش كفاية إنك تعمل نسخة، لازم تتأكد إنها بترجع. النهارده افتح سيرفر staging، نزّل أحدث لقطة بـ restic dump latest، ورجّعها بـ pg_restore على قاعدة فاضية، وقيس الوقت. لو الاسترجاع اشتغل والأرقام منطقية، يبقى عندك backup حقيقي مش وهم. لو وقع في أي خطوة، ده أحسن وقت تكتشفه فيه، مش وقت الكارثة.
مصادر
- توثيق restic الرسمي — النسخ والاسترجاع: restic.readthedocs.io/en/stable/040_backup.html
- restic — إعداد مستودع على Amazon S3: 030_preparing_a_new_repo
- restic — سياسة الاحتفاظ (forget و prune): 060_forget
- PostgreSQL — توثيق pg_dump: app-pgdump
- PostgreSQL — توثيق pg_restore: app-pgrestore
- قاعدة النسخ 3-2-1: backblaze.com/blog/the-3-2-1-backup-strategy