هذا المقال للمستوى المبتدئ. هتطلع منه بسكربت Backup يومي لـ PostgreSQL على S3 في 40 سطر، مع خطة استرجاع جاهزة تشتغل في أقل من 4 دقايق على DB حجمها 8GB.
Backup يومي لـ PostgreSQL على S3 — السكربت اللي بينقذك يوم ما السيرفر يقع
لو عندك DB إنتاج من غير backup أوتوماتيكي في موقع منفصل، أنت ماشي على حبل. يوم ما القرص يفسد أو حد ينفّذ DROP TABLE بالغلط، الخسارة مش بس بيانات، الخسارة وقت توقف وثقة عميل. الحل في 40 سطر bash و 5 دقايق إعداد.
المشكلة باختصار
كتير من الفرق الصغيرة بتعتمد على snapshot من مزوّد الاستضافة وبس. ده backup مش استراتيجية. Snapshot على نفس المزوّد بيموت لو الحساب اتقفل أو منطقة الـ availability zone وقعت ساعة كاملة. الطريقة دي بتفشل في الحالة اللي محتاج فيها الـ backup فعلًا — اللحظة اللي الكارثة فيها واصلة لكل حاجة في نفس الحساب.
تخيّل الموضوع كده — مثال للمبتدئ
تخيّل إنك بتمسك دفتر حسابات شركة. كل يوم بتاخد صورة من الدفتر وتحطها في خزنة في بنك تاني في حي تاني. لو حصل حريق في مكتبك، الدفتر اللي في البنك بيرجّعك تشتغل بكرة الصبح. لكن لو حطيت الصورة في دُرج جنب الدفتر الأصلي، النار هتاكلهم الاتنين.
الـ snapshot من مزوّد الاستضافة = الدُرج جنب الدفتر. الـ backup على S3 في حساب AWS مختلف = البنك في الحي التاني.
التعريف الدقيق — الفرق بين Logical و Physical Backup
Logical backup (زي اللي pg_dump بيعمله): بيقرا الجداول صف صف ويولّد ملف SQL فيه أوامر CREATE TABLE و INSERT. مرن جدًا، بيشتغل بين إصدارات مختلفة من PostgreSQL، وحجم الملف صغير بعد الضغط.
Physical backup (زي pg_basebackup): بيناخد نسخة بايت-بـ-بايت من ملفات الـ data directory. أسرع في الاسترجاع، لكنه مرتبط بإصدار PostgreSQL ومعمارية السيرفر.
الافتراض هنا: الـ DB بتاعتك أقل من 50GB، وبتستحمل قفل قراءة لمدة دقايق. فوق كده، اقرا قسم "متى لا تستخدم" في الآخر.
الأدوات اللي محتاجها
- pg_dump: أداة رسمية شغّالة مع PostgreSQL منذ 1996، موثّقة في توثيق PostgreSQL الرسمي.
- aws-cli v2: بترفع الملف على S3 بـ multipart upload أوتوماتيكي للملفات الكبيرة.
- systemd timer: بديل أنظف من cron في الجدولة، logs مرتبة في
journalctl. - bucket S3 في حساب AWS مختلف: ده الجزء المهم — مش نفس الحساب اللي السيرفر فيه.
السكربت في 40 سطر
#!/usr/bin/env bash
set -euo pipefail
DB_NAME="${DB_NAME:-app_production}"
DB_USER="${DB_USER:-postgres}"
S3_BUCKET="${S3_BUCKET:-my-db-backups}"
RETENTION_DAYS="${RETENTION_DAYS:-30}"
TIMESTAMP=$(date -u +%Y%m%d_%H%M%S)
DUMP_FILE="/tmp/${DB_NAME}_${TIMESTAMP}.sql.gz"
S3_KEY="postgres/${DB_NAME}/${TIMESTAMP}.sql.gz"
echo "[$(date -Iseconds)] Starting backup of ${DB_NAME}"
# 1) Dump مع ضغط مباشر
pg_dump --no-owner --no-acl \
-U "${DB_USER}" -h localhost "${DB_NAME}" \
| gzip -9 > "${DUMP_FILE}"
# 2) فحص حجم بسيط — لو طلع أقل من 1KB يبقى فشل صامت
SIZE=$(stat -c%s "${DUMP_FILE}")
if [ "${SIZE}" -lt 1024 ]; then
echo "ERROR: dump too small (${SIZE} bytes)" >&2
exit 1
fi
# 3) رفع على S3 بـ Standard-IA لتوفير 45% من تكلفة التخزين
aws s3 cp "${DUMP_FILE}" "s3://${S3_BUCKET}/${S3_KEY}" \
--storage-class STANDARD_IA
rm -f "${DUMP_FILE}"
# 4) مسح أي backup أقدم من RETENTION_DAYS
CUTOFF=$(date -u -d "-${RETENTION_DAYS} days" +%Y%m%d)
aws s3 ls "s3://${S3_BUCKET}/postgres/${DB_NAME}/" \
| awk -v d="${CUTOFF}" '$4 ~ /sql.gz$/ && substr($4,1,8) < d {print $4}' \
| xargs -r -I{} aws s3 rm "s3://${S3_BUCKET}/postgres/${DB_NAME}/{}"
echo "[$(date -Iseconds)] Backup OK: ${S3_KEY} (${SIZE} bytes)"