انتقال با scp وسط کار قطع شده و حالا نمیدانید کدام فایلها رسیدهاند و کدام نرسیدهاند. یا با cp -r یک پوشهٔ ۴۰ گیگابایتی را کپی کردهاید و هر بار از صفر شروع میشود. مشکل اینجاست که ابزار اشتباهی را انتخاب کردهاید. rsync برای همین لحظه ساخته شده: انتقال افزایشی، ادامهپذیر، و قابل تکرار بدون کپی دوبارهٔ چیزهایی که از قبل سر جایشان هستند.
در این راهنما فقط چیزهایی را مینویسم که روی سرور واقعی به آنها برخوردهام؛ از تفاوت یک اسلش پایانی که کل نتیجه را عوض میکند تا پرچمهایی که اگر اشتباه بزنید، فایلهای سرور مقصد را پاک میکنند.
rsync دقیقاً چه چیزی را منتقل میکند و چه چیزی را نه
rsync قبل از هر انتقال، فهرست فایلهای مبدأ و مقصد را میسازد و مقایسه میکند. معیار پیشفرض مقایسه، ترکیب اندازهٔ فایل و زمان تغییر (mtime) است، نه هش محتوا. یعنی اگر فایلی هماندازه باشد و mtime آن هم یکی باشد، rsync فرض میکند یکسان است و از آن رد میشود. این تصمیم برای ۹۹ درصد کارها درست است چون سرعت را چند برابر میکند، اما یک استثنای مهم دارد: وقتی محتوای فایل عوض شده ولی اندازه و زمان دستنخورده ماندهاند. مثلاً یک اسکریپت که timestamp را دستی بازنشانی میکند. در آن حالت باید --checksum بزنید تا rsync هش MD5 بگیرد. هزینهاش این است که روی چند صد هزار فایل کوچک، اسکن اولیه چند دقیقه طول میکشد و I/O دیسک بالا میرود.
برای انتقال روی شبکه، rsync معمولاً از SSH بهعنوان تونل استفاده میکند. اگر SSH را روی پورت غیرپیشفرض گذاشتهاید، باید صریح بگویید:
rsync -avz -e "ssh -p 2222" /var/www/html/ deploy@203.0.113.10:/var/www/html/
پرچم -z داده را قبل از ارسال فشرده میکند. روی فایلهای متنی و کد، صرفه دارد. روی فایلهای از قبل فشرده مثل jpg، zip یا mp4 فقط CPU مصرف میکند و چیزی به دست نمیآورید. اگر بکاپ روی شبکهٔ داخلی میگیرید، -z را بردارید.
تفاوت اسلش پایانی؛ جایی که بیشترین اشتباه رخ میدهد
اینجا اشتباه میکنند: اسلش آخر مسیر مبدأ را میبینند و فکر میکنند تزئینی است. نیست. این دو دستور نتیجهٔ کاملاً متفاوتی میدهند:
rsync -av /var/www/html/ user@host:/backup/
rsync -av /var/www/html user@host:/backup/
در خط اول، محتوای پوشهٔ html داخل backup ریخته میشود. در خط دوم، خود پوشهٔ html بهعنوان یک زیرپوشه داخل backup ساخته میشود. اگر این را اشتباه بزنید، بعد از چند روز بکاپگیری متوجه میشوید ساختار پوشهها تودرتو شده و مسیر بازیابی بههم ریخته. نشانهٔ عملیاش این است که در مقصد پوشهای به اسم html/html/html میبینید.
قاعدهٔ حفظکردنی: اسلش پایانی یعنی «داخل این پوشه را بریز». بدون اسلش یعنی «خود این پوشه را ببر». برای مسیر مقصد این تفاوت وجود ندارد؛ آنجا اسلش پایانی بیاثر است.
گزینههایی که واقعاً به کار میآیند
فهرست کامل man rsync بیش از صد پرچم دارد. اینها آنهایی هستند که در عمل روی سرورهای وب لازم میشوند:
| پرچم | کارکرد | هزینه یا نکته |
|---|---|---|
-a | حالت آرشیو: بازگشتی، حفظ مجوزها، مالکیت، لینکها و زمانها | مالکیت را فقط root میتواند روی مقصد اعمال کند |
-v | نمایش فایلهای منتقلشده | روی میلیونها فایل، لاگ را سنگین میکند |
-n | اجرای آزمایشی؛ هیچ تغییری اعمال نمیشود | قبل از هر --delete اجباری است |
--delete | فایلهایی که در مبدأ نیستند را از مقصد پاک میکند | خطرناک؛ با یک اسلش اشتباه، کل مقصد خالی میشود |
--exclude | حذف الگو از انتقال | ترتیب الگوها مهم است |
--partial | فایل نیمهکاره را نگه میدارد | بدون آن، فایل ناقص پاک میشود |
--bwlimit | محدودکردن پهنای باند به کیلوبایت بر ثانیه | مقدار ۵۰۰۰ یعنی حدود ۵ مگابایت بر ثانیه |
ترکیبی که من برای بکاپ روزانهٔ یک سایت وردپرسی استفاده میکنم این است:
rsync -avz --partial --bwlimit=8000 \
--exclude='wp-content/cache/' \
--exclude='*.log' \
/var/www/html/ backup@10.0.0.5:/srv/backups/site1/
محدودیت پهنای باند را دستکم نگیرید. اگر بکاپ روی همان لینک شبکهای باشد که ترافیک کاربران از آن میآید، یک rsync بدون محدودیت میتواند TTFB سایت را برای دقایقی بالا ببرد. عدد ۸۰۰۰ کیلوبایت بر ثانیه روی یک لینک ۱۰۰ مگابیتی حدود ۶۰ درصد ظرفیت را میگیرد و بقیه برای ترافیک زنده میماند.
ادامهٔ انتقال قطعشده بدون شروع از صفر
اگر انتقال یک فایل ۲۰ گیگابایتی وسط کار قطع شود، پیشفرض rsync فایل ناقص مقصد را پاک میکند و دفعهٔ بعد از ابتدا شروع میکند. با --partial فایل نیمهکاره سر جایش میماند و اجرای بعدی از همانجا ادامه میدهد. برای فایلهای بزرگ، --append-verify هم گزینه است: فرض میکند بخش منتقلشده سالم است و فقط دنباله را میفرستد. اما اگر آن بخش اول خراب شده باشد، فایل نهایی خراب میماند. من برای بکاپهای حساس --partial را ترجیح میدهم و بعد از اتمام، یک بار rsync -avnc میزنم تا با checksum تأیید شود که همهچیز یکسان است.
برای انتقالهای طولانی، اجرای rsync را زیر screen یا tmux بگذارید. اگر SSH قطع شود و rsync بهعنوان فرزند آن بمیرد، همهٔ آن ساعتها هدر میرود.
حذف امن: چطور با --delete خودتان را نابود نکنید
--delete همان چیزی است که rsync را از یک کپیگیر ساده به یک ابزار همگامسازی واقعی تبدیل میکند. بدون آن، فایلهایی که از مبدأ حذف شدهاند تا ابد در مقصد میمانند و بکاپ شما بهتدریج بیارزش میشود. با آن، یک اشتباه در مسیر مبدأ یعنی پاکشدن داده روی مقصد.
روش امن این است که همیشه اول با -n اجرا کنید و خروجی را بخوانید:
rsync -avn --delete /var/www/html/ backup@10.0.0.5:/srv/backups/site1/
خطوطی که با deleting شروع میشوند، فایلهایی هستند که قرار است پاک شوند. اگر تعدادشان غیرمنتظره زیاد بود، مسیر را اشتباه نوشتهاید. یک لایهٔ محافظ دیگر --max-delete=100 است: اگر rsync بخواهد بیشتر از این تعداد فایل پاک کند، متوقف میشود. این پرچم جلوی فاجعهای مثل پاکشدن کل مقصد بهخاطر یک اسلش جاافتاده را میگیرد.
نکتهٔ دوم این است که --delete با --exclude تعامل دارد. فایلهای مستثناشده روی مقصد از دید rsync «محافظتشده» هستند و پاک نمیشوند، مگر اینکه --delete-excluded بزنید. اگر روی مقصد فایلهایی دارید که فقط آنجا باید بمانند (مثل کش محلی)، این رفتار دقیقاً همان چیزی است که میخواهید.
وقتی rsync جواب درست نمیدهد
سه نشانهٔ رایج که هر کدام علت متفاوتی دارند:
- «Permission denied (publickey)» در ابتدای کار: کلید SSH روی کاربر مقصد نصب نیست. rsync از همان مکانیزم SSH استفاده میکند، پس اول باید
ssh user@hostبدون رمز کار کند. - «rsync: mkstemp ... failed: Permission denied» وسط انتقال: کاربر مقصد روی پوشهٔ هدف اجازهٔ نوشتن ندارد. rsync برای هر فایل یک فایل موقت میسازد و بعد جابهجا میکند؛ اگر پوشه قابل نوشتن نباشد، همین خطا را میبینید.
- انتقال هر بار کل فایلها را میفرستد: احتمالاً mtime حفظ نمیشود. اگر
-aرا نزده باشید یا فایلسیستم مقصد مثل FAT زمانها را با دقت دو ثانیه ذخیره کند، rsync همیشه فکر میکند فایل عوض شده.-aرا اضافه کنید.
اگر سرور مقصدتان یک هاست اشتراکی است و دسترسی SSH کامل ندارید، rsync گزینه نیست و باید سراغ روشهای دیگر انتقال بروید. برای سرورهایی که کنترل کامل دارند، هاست لینوکس با دسترسی SSH و rsync از پیش نصبشده نقطهٔ شروع منطقی است.
اسکریپت بکاپ روزانه با کرون
یک اسکریپت کوتاه که هم لاگ میگیرد و هم در صورت خطا ایمیل میزند، از یک دستور طولانی که هر بار دستی میزنید بهتر است:
#!/bin/bash
LOG=/var/log/rsync-backup.log
rsync -az --partial --delete --max-delete=200 \
--exclude='wp-content/cache/' \
/var/www/html/ backup@10.0.0.5:/srv/backups/site1/ \
>> "$LOG" 2>&1
echo "$(date '+%F %T') exit=$?" >> "$LOG"
خط آخر را حذف نکنید. بدون ثبت کد خروج، اگر rsync شکست بخورد و کرون فقط سکوت کند، هفتهها بعد متوجه میشوید بکاپها ناقص بودهاند. کد خروج صفر یعنی موفقیت؛ هر عدد دیگری یعنی باید لاگ را باز کنید.
اگر روی همان سرور وردپرس اجرا میکنید، حواستان به زمانبندی باشد. اگر کرون بکاپ را دقیقاً وقتی اجرا کنید که wp-cron هم در حال اجراست، هر دو روی دیسک و CPU فشار میآورند. دربارهٔ این تداخل و راهحلش در wp-cron وردپرس و جایگزینی با کرون واقعی نوشتهام.
پرسشهای پرتکرار
تفاوت rsync و scp چیست و کدام را انتخاب کنم؟
scp کل فایل را هر بار از ابتدا منتقل میکند و امکان ادامهٔ انتقال قطعشده یا حذف فایلهای اضافی مقصد را ندارد. rsync فقط تفاوتها را میفرستد و با --delete میتواند مقصد را دقیقاً آینهٔ مبدأ کند. برای انتقال یکبارهٔ یک فایل کوچک، scp سادهتر است؛ برای هر چیزی که قرار است تکرار شود، rsync انتخاب درست است.
چرا rsync هر بار همهٔ فایلها را دوباره منتقل میکند؟
در بیشتر موارد چون زمان تغییر فایلها حفظ نمیشود. پرچم -a را فراموش کردهاید، یا فایلسیستم مقصد از نوعی است که mtime را با دقت پایین ذخیره میکند. با rsync -avni اجرا کنید و ببینید کدام فایلها در فهرست انتقال هستند؛ اگر همهشان آنجا بودند، مشکل زمان است نه محتوا.
آیا rsync میتواند فایلها را روی سرور مقصد رمزنگاری کند؟
خود rsync رمزنگاری محتوا ندارد. انتقال از طریق SSH رمزنگاریشده است، اما فایل روی دیسک مقصد به همان شکل اصلی ذخیره میشود. اگر بکاپ رمزنگاریشده میخواهید، باید بعد از انتقال با ابزاری مثل gpg یا openssl enc فایلها را رمز کنید، یا از یک ابزار بکاپ که این کار را داخلی انجام میدهد استفاده کنید.
چطور فقط فایلهای جدیدتر از یک تاریخ مشخص را منتقل کنم؟
از --newer استفاده کنید: rsync -av --newer='2024-01-01' /src/ user@host:/dst/. این پرچم فقط فایلهایی را میفرستد که mtime آنها بعد از تاریخ دادهشده باشد. برای بازهٔ زمانی، --newer و --older را با هم ترکیب کنید. توجه کنید که این فیلتر روی mtime عمل میکند، نه روی تاریخ ایجاد فایل.
اگر میخواهید قبل از راهاندازی بکاپ خودکار، ساختار مسیرها و مجوزها را یک بار درست بچینید، مستندات و پایگاه دانش سرورنت نقطهٔ شروع خوبی است. و اگر فقط میخواهید بدانید سرعت واقعی انتقال بین دو سرورتان چقدر است، ابزارهای رایگان برای همین اندازهگیری ساخته شدهاند.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!