لو قاعدة بياناتك وقعت دلوقتي، إنت خسرت كام ساعة شغل؟ السكربت اللي قدامك بياخد نسخة كاملة من PostgreSQL، يضغطها، يشفّرها، ويرفعها على S3 كل يوم الساعة 2 صباحًا. 40 سطر bash.
أتمتة نسخ احتياطي PostgreSQL إلى S3 يوميًا
المشكلة باختصار
أغلب الفرق الصغيرة بتعتمد على snapshot من مزوّد السيرفر (DigitalOcean, Hetzner, AWS) وبيفتكروا ده كفاية. مش كفاية. الـ snapshot بيتاخد على مستوى الـ volume مش على مستوى الـ transaction، فلو قاعدة البيانات كانت في نص عملية كتابة، النسخة ممكن ترجع corrupted. غير كده، لو محتاج تسترجع جدول واحد بس، الـ snapshot هيجبرك ترجع السيرفر كله.
الحل بسيط: pg_dump + gzip + gpg + aws s3 cp، مجدولين بـ cron يوميًا. التكلفة: أقل من دولارين شهريًا لقاعدة بيانات ≤ 5GB مع احتفاظ 30 يوم.
ليه pg_dump أفضل من snapshot الـ volume
خلّيني أوضّحها بمثال بسيط الأول. تخيّل إنك بتصوّر مكتب وفيه موظف بيكتب في ورقة. لو صوّرت الورقة وهو في نص الكتابة، هتلاقي كلمة ناقصة. ده اللي بيحصل في snapshot الـ volume: بيتاخد وقاعدة البيانات بتكتب، ساعات بتلاقي بلوك ناقص.
دلوقتي بالشرح الدقيق: pg_dump بيشتغل على الـ logical layer لـ PostgreSQL، مش على البلوكات على القرص. بيفتح transaction بـ isolation level REPEATABLE READ ويستفيد من الـ MVCC (Multi-Version Concurrency Control) علشان يقرأ snapshot consistent من غير ما يقفل الجداول. النتيجة: نسخة متناسقة (consistent) حتى وقاعدة البيانات شغالة تحت حمل كتابة.
الافتراض هنا: قاعدة بياناتك ≤ 50GB. لو أكبر، زمن الـ dump هيطوّل ويستهلك CPU وIO كتير. في الحالة دي لازم تفكّر في pg_basebackup مع WAL archiving بدل pg_dump.
السكربت الكامل
#!/usr/bin/env bash
set -euo pipefail
# === Config (خلّيها في متغيرات بيئة، مش hardcoded) ===
PG_HOST="${PG_HOST:-localhost}"
PG_USER="${PG_USER:-postgres}"
PG_DB="${PG_DB:-app_prod}"
S3_BUCKET="${S3_BUCKET:-my-app-backups}"
GPG_RECIPIENT="${GPG_RECIPIENT:-ops@example.com}"
RETENTION_DAYS="${RETENTION_DAYS:-30}"
TS=$(date -u +%Y%m%d_%H%M%S)
FILE="${PG_DB}_${TS}.dump.gz.gpg"
LOCAL="/tmp/${FILE}"
# === Dump + compress + encrypt في pipeline واحد ===
PGPASSWORD="${PG_PASSWORD}" pg_dump \
-h "$PG_HOST" -U "$PG_USER" -d "$PG_DB" \
--format=custom --no-owner --no-privileges \
| gzip -9 \
| gpg --batch --yes --encrypt --recipient "$GPG_RECIPIENT" \
> "$LOCAL"
# === Upload على S3 بـ storage class أرخص ===
aws s3 cp "$LOCAL" "s3://${S3_BUCKET}/daily/${FILE}" \
--storage-class STANDARD_IA
# === تحقّق إن الملف وصل فعلاً قبل ما تمسح المحلي ===
aws s3api head-object --bucket "$S3_BUCKET" --key "daily/${FILE}" > /dev/null
# === نضّف المحلي + النسخ الأقدم من retention ===
rm -f "$LOCAL"
CUTOFF=$(date -u -d "-${RETENTION_DAYS} days" +%Y-%m-%d)
aws s3 ls "s3://${S3_BUCKET}/daily/" \
| awk -v d="$CUTOFF" '$1 < d {print $4}' \
| xargs -I {} aws s3 rm "s3://${S3_BUCKET}/daily/{}"
echo "✓ backup ${FILE} uploaded, retention ${RETENTION_DAYS}d"
لاحظ حاجة مهمة: الـ pipeline ماشي في الذاكرة (stream). يعني مش بنكتب ملف مؤقت كبير قبل الضغط، وده بيوفّر disk IO على سيرفرات صغيرة.
جدولة cron والتحقق إن كله شغّال
- حط السكربت في
/usr/local/bin/pg_backup.shوخلّيه executable بصلاحية محدودة:chmod 700 /usr/local/bin/pg_backup.sh - حط المتغيرات الحساسة في ملف
/etc/pg_backup.envبصلاحية600، وحمّله من الـ cron. - ضيف الجدولة في crontab:
0 2 * * * . /etc/pg_backup.env && /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1 - شغّله يدويًا مرة:
. /etc/pg_backup.env && /usr/local/bin/pg_backup.shوتأكد من المخرج. - افتح S3 console، دخل على
daily/، لازم تلاقي الملف. - كل شهر، اعمل restore drill فعلي على سيرفر staging. مش كفاية إن الملف موجود، لازم ترجع منه DB شغالة. ده اللي بيحصل فعلاً: 60% من النسخ الاحتياطية مش بتشتغل يوم الكارثة لأن محدش جرّبها.
Trade-offs لازم تنتبه لها
بتكسب: نسخة يومية معزولة على S3، مشفّرة بـ GPG، ومضغوطة بـ gzip -9. حتى لو مفتاح AWS اتسرّب، النسخ مش هتتقرا من غير المفتاح الخاص لـ GPG. ده بيحميك من سيناريو الـ ransomware اللي بيمسح النسخ الاحتياطية.
بتخسر: CPU وقت الـ dump (دقيقتين لـ 5 دقائق حسب الحجم). خلّي الجدولة بعد ساعات الـ peak. التكلفة الفعلية: S3 STANDARD_IA حوالي 0.0125$ لكل GB شهريًا — قاعدة 5GB مضغوطة لـ 1GB × 30 نسخة = ~0.37$ شهريًا، زائد تكلفة الـ PUT requests ≈ 0.5$. إجمالي أقل من دولار.
الـ trade-off الأكبر: استعادة جدول واحد. مع custom format تقدر تعمل pg_restore -t users backup.dump، لكن لأن الـ pipeline عدّى على gpg، لازم تفك التشفير كله الأول (gpg --decrypt) قبل ما تقدر تختار جدول. مش مشكلة كبيرة، بس خلّيها في بالك.
متى لا تستخدم هذه الطريقة
لو الـ RPO (Recovery Point Objective) عندك أقل من ساعة، pg_dump يومي مش كفاية. الـ RPO يعني "أد إيه بيانات مقبول تخسرها". لو تطبيقك معاملات مالية، خسارة 24 ساعة كارثة. في الحالة دي استخدم WAL archiving + pg_basebackup وارفع الـ WAL segments على S3 كل 5 دقائق.
كذلك لو قاعدة البيانات فوق 50GB، زمن الـ dump هيأثر على الإنتاج. ولو عندك compliance requirements صارمة (HIPAA مثلاً)، لازم تضيف audit logs وتتأكد إن S3 bucket عليه Object Lock في وضع Compliance.
الخطوة التالية
شغّل السكربت يدويًا دلوقتي. تأكد إن الملف ظهر على S3 وإن head-object رجع success. بعد 24 ساعة راجع /var/log/pg_backup.log للتأكد إن cron اشتغل تلقائيًا. لو الـ log فاضي، راجع حالة cron service بـ systemctl status cron وتأكد إن الـ user عنده صلاحيات aws CLI. بعد أسبوع، احجز ساعة في التقويم لأول restore drill.