بکاپ خودکار سرور و دیتابیس روی Object Storage با Restic
بیشتر سرورهایی که بررسی میکنیم یکی از این دو وضعیت را دارند: یا بکاپی ندارند، یا بکاپ روی همان سرور نگهداری میشود. هر دو یعنی با یک خرابی دیسک، حملهی باجافزار یا یک اشتباه ساده، همهچیز از دست میرود. Restic ابزاری متنباز و تکفایلی است که بکاپها را رمزنگاریشده، افزایشی و با حذف دادههای تکراری (Deduplication) روی مقصدهای مختلف، از جمله هر Object Storage سازگار با S3، ذخیره میکند. در این آموزش یک سیستم بکاپ کامل برای فایلها و دیتابیسها میسازیم.
چرا Restic و Object Storage؟
- رمزنگاری سمت کلاینت: داده قبل از خروج از سرور رمز میشود. حتی کسی که به Bucket دسترسی دارد، بدون رمز Restic چیزی نمیبیند.
- افزایشی و بدون تکرار: فقط تغییرات ارسال میشود، پس بکاپ روزانه سریع است و فضای کمی میگیرد.
- هر Snapshot کامل است: برای بازیابی لازم نیست زنجیرهای از بکاپهای افزایشی را دستی کنار هم بگذارید.
- Object Storage جدا از سرور: بکاپ در جایی است که خرابی سرور اصلی به آن نمیرسد.
به یک Bucket روی یک Object Storage سازگار با S3 نیاز دارید؛ چه سرویس ابری، چه اختصاصی (مثلاً با MinIO یا گزینههای مقالهی مقایسهی MinIO، Ceph و Garage). برای هر سرور یک کاربر S3 جداگانه بسازید که فقط به Bucket بکاپ همان سرور دسترسی داشته باشد.
قدم ۱: نصب Restic
sudo apt update
sudo apt install -y restic
restic version
نسخهی مخزن اوبونتو کمی قدیمیتر از آخرین نسخه است. برای قابلیتهای جدیدتر (مثل --stdin-from-command که در ادامه میبینیم و از نسخهی 0.17 اضافه شده) میتوانید فایل اجرایی رسمی را از صفحهی Releases پروژه در GitHub دریافت کنید.
قدم ۲: تنظیمات و رمز مخزن
یک پوشهی امن برای تنظیمات بسازید:
sudo mkdir -p /etc/restic
sudo chmod 700 /etc/restic
openssl rand -base64 32 | sudo tee /etc/restic/password > /dev/null
sudo chmod 600 /etc/restic/password
فایل /etc/restic/env (بدون export، تا systemd هم بتواند آن را بخواند):
AWS_ACCESS_KEY_ID=backup-web01
AWS_SECRET_ACCESS_KEY=کلید-مخفی-کاربر-بکاپ
RESTIC_REPOSITORY=s3:https://s3.example.com/backup-web01
RESTIC_PASSWORD_FILE=/etc/restic/password
sudo chmod 600 /etc/restic/env
هشدار دربارهی رمز مخزن
اگر رمز Restic را گم کنید، بکاپها برای همیشه غیرقابل بازیابیاند. هیچ راه بازگشتی وجود ندارد. رمز را همین حالا در یک Password Manager سازمانی یا یک نسخهی آفلاین و امن، بیرون از همین سرور، ذخیره کنید. اگر سرور کامل از بین برود و رمز فقط روی خود سرور باشد، بکاپ عملاً بیفایده است.
قدم ۳: ساخت مخزن
برای اجرای دستی دستورات، متغیرها را در Shell بارگذاری کنید:
sudo -i
set -a; . /etc/restic/env; set +a
restic init
اگر Object Storage شما فقط آدرسدهی Path-style را پشتیبانی میکند و خطای DNS یا Bucket دیدید، این گزینه را اضافه کنید:
restic -o s3.bucket-lookup=path init
قدم ۴: بکاپ فایلها
یک فایل برای مسیرهایی که نباید بکاپ گرفته شوند بسازید، /etc/restic/excludes:
/var/www/*/cache
/var/www/*/storage/framework/cache
/var/www/*/node_modules
*.tmp
*.log.gz
اولین بکاپ:
restic backup /etc /var/www /home /root \
--exclude-file=/etc/restic/excludes \
--tag files
restic snapshots
اولین اجرا کل داده را منتقل میکند و ممکن است طول بکشد. اجراهای بعدی فقط تغییرات را میفرستند.
قدم ۵: بکاپ دیتابیسها از طریق stdin
کپی کردن فایلهای خام دیتابیسِ در حال کار، بکاپ قابلاعتمادی نمیسازد. روش درست، گرفتن Dump است. Restic میتواند خروجی Dump را مستقیم از stdin بگیرد، بدون اینکه فایل موقتی روی دیسک ساخته شود.
MySQL / MariaDB
set -o pipefail
mysqldump --single-transaction --routines --triggers --events --all-databases \
| restic backup --stdin --stdin-filename mysql-all.sql --tag mysql
--single-transaction برای جدولهای InnoDB یک Dump سازگار بدون قفل کردن جدولها میگیرد. اطلاعات ورود را در /root/.my.cnf با دسترسی 600 بگذارید، نه در خط فرمان. set -o pipefail هم مهم است: بدون آن اگر mysqldump وسط کار خطا بدهد، ممکن است یک Snapshot ناقص بیصدا ذخیره شود.
در Restic نسخهی 0.17 به بعد، روش امنتر این است که خود Restic دستور را اجرا کند؛ اگر دستور با خطا تمام شود، Snapshot ساخته نمیشود:
restic backup --tag mysql --stdin-filename mysql-all.sql \
--stdin-from-command -- mysqldump --single-transaction --routines --triggers --all-databases
PostgreSQL
set -o pipefail
sudo -u postgres pg_dumpall \
| restic backup --stdin --stdin-filename postgres-all.sql --tag postgres
برای دیتابیسهای بزرگ، Dump جداگانهی هر دیتابیس با فرمت custom (pg_dump -Fc dbname) بازیابی انتخابی را سادهتر میکند.
قدم ۶: سیاست نگهداری
بدون سیاست نگهداری، مخزن تا ابد بزرگ میشود. این دستور ۷ بکاپ روزانه، ۴ هفتگی و ۶ ماهانه را نگه میدارد و بقیه را حذف میکند:
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Restic بهطور پیشفرض Snapshotها را بر اساس نام سرور و مسیرها گروهبندی میکند، پس بکاپ فایلها و هر Dump دیتابیس جداگانه شمرده میشوند. برای اطمینان از سلامت مخزن، دورهای بخشی از داده را واقعاً بخوانید و بررسی کنید:
restic check --read-data-subset=5%
قدم ۷: خودکارسازی با systemd timer
اسکریپت /usr/local/sbin/restic-backup.sh:
#!/bin/bash
set -euo pipefail
set -a; . /etc/restic/env; set +a
restic backup /etc /var/www /home /root \
--exclude-file=/etc/restic/excludes --tag files
mysqldump --single-transaction --routines --triggers --events --all-databases \
| restic backup --stdin --stdin-filename mysql-all.sql --tag mysql
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
sudo chmod 700 /usr/local/sbin/restic-backup.sh
فایل /etc/systemd/system/restic-backup.service:
[Unit]
Description=Restic backup to object storage
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
فایل /etc/systemd/system/restic-backup.timer:
[Unit]
Description=Daily restic backup
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15min
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
sudo systemctl start restic-backup.service # یک اجرای آزمایشی
journalctl -u restic-backup.service -n 50
Persistent=true باعث میشود اگر سرور در ساعت بکاپ خاموش بود، بکاپ بعد از روشن شدن اجرا شود. برای شکست بکاپ هم هشدار بگذارید؛ بکاپی که هفتهها بیصدا شکست خورده، از نبود بکاپ خطرناکتر است چون حس امنیت کاذب میدهد. یک راه ساده، ارسال وضعیت به سیستم مانیتورینگ با OnFailure= در systemd است (مقالهی مانیتورینگ با Prometheus و Grafana).
قدم ۸: تست بازیابی (حذفنشدنی!)
بکاپی که بازیابیاش تست نشده، بکاپ نیست. حداقل ماهی یک بار این تستها را انجام دهید:
# فهرست Snapshotها
restic snapshots
# بازیابی بخشی از فایلها در مسیر جداگانه
restic restore latest --tag files --target /tmp/restore-test --include /etc/nginx
ls -la /tmp/restore-test/etc/nginx
# بیرون کشیدن Dump دیتابیس
restic dump --tag mysql latest /mysql-all.sql > /tmp/mysql-all.sql
head -n 20 /tmp/mysql-all.sql
تست کامل یعنی وارد کردن Dump در یک سرور یا کانتینر جداگانه و اطمینان از اینکه اپلیکیشن با آن بالا میآید. بهتر است این تست را روی سروری غیر از سرور اصلی انجام دهید تا مطمئن شوید رمز و کلیدهای دسترسی بیرون از سرور هم در دسترساند.
امنیت بکاپ در برابر باجافزار
کلیدهای S3 روی سرور ذخیره شدهاند؛ پس مهاجمی که سرور را تصاحب کند، میتواند بکاپها را هم حذف کند. چند لایهی دفاعی:
- برای هر سرور کاربر S3 جداگانه، فقط با دسترسی به Bucket خودش.
- فعال کردن Versioning روی Bucket (در Object Storageهایی که پشتیبانی میکنند) همراه با قانون Lifecycle برای حذف نسخههای قدیمی بعد از چند روز؛ به این ترتیب Objectهای حذفشده تا مدتی قابل بازگشتاند.
- یک نسخهی دوم در مکان دیگر با
restic copyیا یک فرایند Pull از سرور جداگانه، طبق قاعدهی ۳-۲-۱: سه نسخه، روی دو نوع رسانه، یکی خارج از محل.
جمعبندی
با چند فایل تنظیمات و یک systemd timer، سروری دارید که هر شب فایلها و دیتابیسهایش را رمزنگاریشده و افزایشی روی Object Storage میفرستد و نسخههای قدیمی را خودکار مدیریت میکند. فقط دو چیز را فراموش نکنید: رمز مخزن را جای امنی بیرون از سرور نگه دارید و بازیابی را منظم تست کنید.
نمیخواهید این کار را خودتان انجام دهید؟ تیم مروانت بکاپ خودکار، مانیتورینگ و تست بازیابی سرورهای شما را راهاندازی و نگهداری میکند. صفحهی مدیریت و پشتیبانی سرور را ببینید. اگر مقصد بکاپ مطمئن و اختصاصی میخواهید، راهاندازی Object Storage اختصاصی را هم ببینید.
