سایت بالا نمیآید، MySQL خطای Disk full میدهد، یا SSH اجازهٔ ورود نمیدهد چون نمیتواند فایل session بنویسد. اول از همه این را بپذیر: تا وقتی ندانید کدام مسیر فضا را خورده، هر حذفی قمار است. یک rm -rf اشتباه در مسیر اشتباه، هم سایت را میخواباند و هم بکاپ را. پس ترتیب کار مهم است.
اول df، بعد du؛ نه برعکس
با df -h شروع کنید تا ببینید کدام mount پر شده. اگر پارتیشن / پر است ولی /home سالم است، جستوجو را در همان درخت محدود کنید. یک نکته که خیلیها از دست میدهند: df -i را هم بزنید. ممکن است فضای دیسک آزاد باشد اما inode تمام شده باشد.
df -h
df -i
du -xhd1 / 2>/dev/null | sort -h
فلگ -x در du باعث میشود از مرز فایلسیستم عبور نکند؛ بدون آن، اگر یک NFS یا bind mount داخل مسیر باشد، اعداد بیمعنی میشوند. -d1 عمق یک سطح میدهد و sort -h بزرگترینها را آخر لیست میآورد. بعد از پیدا کردن پوشهٔ مشکوک، همان دستور را روی آن تکرار کنید تا به فایل برسید.
ncdu سریعتر از du جواب میدهد
روی سرورهای پرترافیک، du روی میلیونها فایل چند دقیقه طول میکشد و I/O دیسک را میخورد. ncdu یک بار درخت را ایندکس میکند و بعد با کلیدهای جهتدار میچرخید. اگر نصب نیست، از مخزن توزیع نصبش کنید:
ncdu -x /var
در خروجی، با d فایل حذف میکنید و با g بین درصد و حجم خام جابهجا میشوید. یک هشدار عملی: ncdu فقط چیزی را میبیند که در فایلسیستم دیده میشود. فایلهای حذفشدهٔ باز و فایلهای مخفی داخل مسیرهای عمیق را باید جداگانه شکار کنید.
فایل حذف شدهای که هنوز فضا میخورد
این کلاسیکترین تله است. لاگ را با rm پاک میکنید، df هنوز پر است، و هیچ فایلی در du پیدا نمیشود. دلیلش ساده است: پروسهای مثل nginx یا PHP-FPM هنوز file descriptor آن فایل را باز نگه داشته و کرنل تا بسته نشدن آن، بلوکها را آزاد نمیکند. با lsof پیدایش کنید:
lsof +L1
lsof -nP | grep deleted
خروجی lsof +L1 فایلهایی را نشان میدهد که link count آنها صفر شده. ستون آخر حجم آزادشده را میدهد. راهحل، حذف فایل نیست؛ ریاستارت همان سرویس است. اگر nginx است، systemctl reload nginx کافی نیست چون مستر پروسه فایل را باز نگه داشته؛ systemctl restart nginx لازم است. قبل از ریاستارت مطمئن شوید کانفیگ سالم است، وگرنه سرویس بالا نمیآید و حالا دو مشکل دارید.
اینجا اشتباه میکنند
رایجترین اشتباهی که در تیکتها میبینم این است: کسی access.log را با rm پاک میکند، df تغییری نمیکند، و نتیجه میگیرد «دیسک خراب شده». در واقع پروسهٔ وبسرور فایل را باز نگه داشته. علامتش دقیقاً همین است: du روی پوشهٔ لاگ عدد کوچکی نشان میدهد ولی df همان درصد قبلی را میدهد. راه درست، truncate کردن است نه حذف:
truncate -s 0 /var/log/nginx/access.log
این دستور فایل را سر جایش نگه میدارد، inode را حفظ میکند و پروسه بدون ریاستارت به نوشتن ادامه میدهد. برای لاگهای چرخشی، logrotate را با copytruncate تنظیم کنید تا همین مشکل تکرار نشود.
وقتی inode تمام میشود، نه فضا
اگر df -h میگوید ۴۰٪ آزاد است ولی df -i روی ۱۰۰٪ است، مشکل جای دیگری است. این حالت وقتی پیش میآید که میلیونها فایل کوچک دارید: sessionهای PHP، فایلهای cache، صف ایمیل، یا spam انبارشده در /var/spool. فضای دیسک بیفایده است چون inode تمام شده. با این دستور مقصر را پیدا کنید:
for d in /var/* /home/*; do echo "$(find $d -xdev 2>/dev/null | wc -l) $d"; done | sort -n
پاک کردن sessionهای منقضی معمولاً چند میلیون inode آزاد میکند. اگر تعداد فایلهای کوچک بهطور ساختاری بالاست، راهحل موقت پاکسازی دورهای است و راهحل واقعی، انتقال آن داده به یک فایلسیستم با inode بیشتر یا استفاده از Redis برای session.
مقایسهٔ ابزارها؛ کدام را کِی بزنیم
| ابزار | برای چه کاری | محدودیت |
|---|---|---|
df -h | دیدن فضای آزاد هر mount | فایل حذفشدهٔ باز را نمیبیند |
df -i | تشخیص اتمام inode | فقط تعداد، نه حجم |
du -xhd1 | پیدا کردن پوشهٔ بزرگ | روی میلیونها فایل کند است |
ncdu | کاوش تعاملی و حذف سریع | نیاز به نصب و ایندکس اولیه |
lsof +L1 | فایل حذفشدهٔ باز | نیاز به root |
ترتیب منطقی این است: df -h و df -i برای تشخیص سطح بالا، بعد ncdu برای پیدا کردن مسیر، و lsof +L1 وقتی اعداد با هم نمیخوانند. اگر بعد از این سه مرحله هنوز فضا پیدا نشد، احتمالاً مشکل در لایهٔ دیگری است و باید سراغ بررسی سرویسهای در حال نوشتن بروید.
جلوگیری از تکرار؛ نه فقط آزادسازی
آزاد کردن فضا نیمی از کار است. اگر ریشه را نبندید، دو هفته بعد همان تیکت را میزنید. سه کار عملی:
- برای لاگها
logrotateباmaxsizeوrotateمشخص تنظیم کنید، نه فقط چرخش روزانه. - روی پوشهٔ uploads وردپرس و پوشهٔ بکاپ، هشدار
dfبگذارید؛ مثلاً یک cron که بالای ۸۵٪ به شما ایمیل بزند. - بکاپهای قدیمی را روی همان سرور نگه ندارید. بکاپ روی همان دیسکی که پر میشود، بکاپ نیست.
اگر سایت وردپرسی دارید، بخش زیادی از این فایلهای کوچک از افزونهها و کش میآید. یک ممیزی افزونه وردپرس اغلب چند گیگابایت و صدها هزار فایل اضافه را حذف میکند، بدون آنکه چیزی از دست برود. برای سایتهای پربازدید، جدا کردن لاگها و session روی یک پارتیشن مستقل، جلوی خوابیدن کل سرور را میگیرد. اگر هنوز روی یک سرور مشترک با پارتیشن واحد کار میکنید، هاست لینوکس با پارتیشنبندی جداگانه این ریسک را کم میکند.
برای دستورهای بیشتر و تنظیمات سرویسها، مستندات و پایگاه دانش سرورنت مرجع خوبی است. و اگر میخواهید همین بررسی را روی سایت خودتان انجام دهید، ابزارهای رایگان نقطهٔ شروع سریعی هستند.
پرسشهای پرتکرار
چرا بعد از حذف فایل، df هنوز فضای آزاد نشان نمیدهد؟
چون پروسهای آن فایل را باز نگه داشته و کرنل تا بسته نشدن file descriptor، بلوکها را آزاد نمیکند. با lsof +L1 پروسه را پیدا کنید و همان سرویس را ریاستارت کنید. اگر میخواهید فایل لاگ را خالی کنید، بهجای rm از truncate -s 0 استفاده کنید.
تفاوت df -h و df -i چیست و کدام مهمتر است؟
df -h حجم فضای مصرفشده را نشان میدهد و df -i تعداد inode مصرفشده. هر دو مهماند. اگر حجم آزاد است ولی inode روی ۱۰۰٪ است، نمیتوانید فایل جدید بسازید و خطاهایی مثل No space left on device میگیرید، درست مثل وقتی دیسک واقعاً پر است.
چطور بفهمم کدام پوشه دیسک را پر کرده؟
با du -xhd1 / از ریشه شروع کنید و بزرگترین پوشه را انتخاب کنید، بعد همان دستور را داخل آن تکرار کنید تا به فایل برسید. اگر تعداد فایلها زیاد است، ncdu -x /var سریعتر و راحتتر است چون یک بار ایندکس میکند.
آیا حذف لاگها با rm امن است؟
نه، اگر سرویس در حال نوشتن در آن فایل باشد. حذف باعث میشود فایل از دید du ناپدید شود ولی فضا آزاد نشود و سرویس به نوشتن در فایل بینام ادامه دهد. راه درست، truncate کردن فایل یا استفاده از logrotate با گزینهٔ copytruncate است.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!