بلاگ/رفع خطای No Space Left on Device در لینوکس: دیسک پر نشده، پس مشکل چیست؟
به‌روز شده

رفع خطای No Space Left on Device در لینوکس: دیسک پر نشده، پس مشکل چیست؟

1405/08/050 بازدید
رفع خطای No Space Left on Device در لینوکس: دیسک پر نشده، پس مشکل چیست؟

رفع خطای No Space Left on Device در لینوکس: دیسک پر نشده، پس مشکل چیست؟

یک برنامه روی سرور با خطای No space left on device از کار می‌افتد، شما df -h را می‌زنید، و فضای خالی زیادی می‌بینید. این خطا همیشه به معنای تمام شدن بایت‌های دیسک نیست. لینوکس به همان اندازه که به فضای خام نیاز دارد، به تعداد inode هم نیاز دارد، و گاهی فایلی که حذف شده هنوز جایی را اشغال کرده.

خطای No Space Left on Device با وجود فضای خالی در df

در ادامه دو دلیل اصلی این خطا را با دستورهای واقعی بررسی می‌کنیم: اتمام inodeها، و فایل حذف‌شده‌ای که یک پردازش هنوز آن را باز نگه داشته.

چرا df -h فضای خالی نشان می‌دهد ولی خطا می‌گیرید؟

df -h فقط فضای بایتی دیسک را گزارش می‌دهد. اما هر فایل‌سیستم مثل ext4 یا xfs برای هر فایل و پوشه یک رکورد به اسم inode می‌سازد، و تعداد این رکوردها از ابتدای فرمت فایل‌سیستم محدود است. اگر تعداد فایل‌ها، حتی فایل‌های خالی و خیلی کوچک، از این سقف بگذرد، سیستم دیگر نمی‌تواند فایل جدید بسازد، هرچند صدها گیگابایت فضای خام خالی باشد.

دلیل دوم کمتر شناخته‌شده است: وقتی یک پردازش فایلی را باز نگه داشته، حذف آن فایل با rm فضای دیسک را آزاد نمی‌کند. فضا تا وقتی آزاد می‌شود که همان پردازش هم فایل را ببندد یا از نو راه‌اندازی شود.

دلیل اول: تمام شدن inodeها

برای چک کردن این حالت، به‌جای df -h این دستور را بزنید:

df -i

ستون IUse% را ببینید، نه Use%. اگر IUse% نزدیک صد درصد است، مشکل همین‌جاست.

df -hdf -i
چه چیزی را می‌شماردفضای بایتیتعداد inode
چه زمانی پر می‌شودفایل‌های بزرگ زیاد شوندفایل‌های کوچک و زیاد ساخته شوند
نمونه‌ی رایجیک فایل لاگ چندگیگابایتیصدها هزار فایل سشن یا کش
دستور بررسیdf -hdf -i

بررسی تعداد inode با دستور df -i در لینوکس

جایی که معمولا تعداد فایل‌ها زیاد می‌شود:

  • پوشه session یا cache فریم‌ورک‌های PHP و پایتون، مثل /var/lib/php/sessions
  • صف mail سرور، /var/spool/postfix یا /var/spool/mqueue
  • کش پکیج‌منیجرها (apt، npm، pip) که با گذشت زمان میلیون‌ها فایل کوچک جمع می‌کنند
  • جورنال systemd، وقتی journalctl محدودیت اندازه نداشته باشد
  • پوشه overlay2 داکر، وقتی ایمیج‌ها و لایه‌های قدیمی پاک نشوند

برای پیدا کردن پوشه‌ای که بیشترین فایل را دارد:

for d in /var/* /home/* /tmp; do echo -n "$d: "; find "$d" -xdev | wc -l; done

راه‌حل معمول پاک کردن فایل‌های اضافه در همان پوشه است، نه ساختن فضای بیشتر. برای journald و داکر این دو دستور معمولا کافی است:

journalctl --vacuum-size=200M
docker system prune

اگر مشکل از ساختار خود فایل‌سیستم باشد، مثلا یک پارتیشن کوچک که از اول با تعداد inode کم فرمت شده، تنها راه فرمت دوباره با mkfs و پارامتر -N برای تعیین تعداد inode بیشتر است. این کار داده‌های پارتیشن را پاک می‌کند، پس باید قبلش بکاپ گرفته شود.

دلیل دوم: فایلی که حذف شده ولی هنوز باز است

این حالت معمولا با لاگ‌فایل‌ها اتفاق می‌افتد. یک سرویس مثل nginx یا mysql لاگ می‌نویسد، فضا تمام می‌شود، کسی با دست rm access.log را می‌زند، اما df -h همچنان همان عدد قبلی را نشان می‌دهد. چون پردازش هنوز فایل را باز نگه داشته و فضا آزاد نمی‌شود تا پردازش بسته شود.

برای پیدا کردن این فایل‌ها:

lsof +L1

این دستور فایل‌هایی را نشان می‌دهد که link count آن‌ها کمتر از یک است؛ یعنی از مسیر فایل‌سیستم حذف شده‌اند ولی یک file descriptor هنوز باز است. اگر خروجی چیزی مثل این را دید:

nginx   1423  www-data  12w  REG  8,1  5368709120  /var/log/nginx/access.log (deleted)

یعنی همان فایل پنج‌گیگابایتی که انتظار داشتید حذف شود، هنوز جا گرفته. دو راه برای پس گرفتن فضا بدون قطع سرویس هست. اول:

> /proc/1423/fd/12

این دستور محتوای فایل را صفر می‌کند بدون این‌که پردازش را ببندد. اگر این راه جواب نداد، ریستارت همان سرویس (systemctl restart nginx) کافی است تا فایل‌دسکریپتور قدیمی بسته شود و فضا برگردد.

یک مسیر سریع برای پیدا کردن ریشه‌ی مشکل

وقتی خطا می‌گیرید و نمی‌دانید کدام یک از این دو حالت است، این ترتیب را دنبال کنید:

  1. df -h برای دیدن این‌که کدام پارتیشن پر است.
  2. df -i روی همان پارتیشن، برای رد کردن یا تایید مشکل inode.
  3. lsof +L1 برای فایل‌های حذف‌شده‌ی باز.
  4. du -hsx /* 2>/dev/null | sort -rh | head برای پیدا کردن پوشه‌ای که بیشترین فضا را گرفته.

اگر du و df روی یک پارتیشن عدد خیلی متفاوت نشان دادند، تقریبا همیشه یعنی یک فایل حذف‌شده هنوز باز است. du فقط فایل‌های موجود در مسیر فایل‌سیستم را می‌شمارد، در حالی که df فضای واقعی بلوک‌های گرفته‌شده را گزارش می‌دهد.

جلوگیری از تکرار این خطا

برای inode: پوشه‌هایی که فایل موقت زیاد می‌سازند را با logrotate یا یک cron job ساده پاک‌سازی کنید، و کش پکیج‌منیجرها را به‌صورت دوره‌ای خالی کنید.

برای فایل‌های حذف‌شده‌ی باز: لاگ‌ها را با logrotate مدیریت کنید تا هیچ فایلی بدون محدودیت اندازه رشد نکند. تنظیمات postrotate را طوری بچینید که سیگنال reopen به سرویس بفرستد، نه این‌که فقط فایل قدیمی را rename کند و بگذارد سرویس همچنان روی همان file descriptor بنویسد.

یک کار دیگر که کمک می‌کند، آلارم‌گذاری روی درصد مصرف دیسک و inode است؛ با Zabbix، Prometheus node_exporter یا حتی یک cron ساده که هر شب df -h و df -i را چک کند و در صورت عبور از آستانه ایمیل بفرستد.

دیسک پر، نوع دیگری از خطاهای کمبود منبع در لینوکس است؛ اگر سرویس‌ها به دلیل کمبود RAM هم از کار می‌افتند، سراغ خطای OOM Killer را بگیرید که دلایل و رفع مشابهی دارد.

اگر این مشکل روی یک سرور با منابع محدود دائما تکرار می‌شود و وقت زیادی صرف پاک‌سازی دستی می‌کنید، شاید وقتش باشد که به یک پلن با فضای دیسک بیشتر مهاجرت کنید. سرور مجازی ابری آریانت فضای NVMe بیشتری می‌دهد و پلن را هر وقت لازم شد از همان پنل بالا می‌برید، بدون این‌که منتظر قرارداد جدید بمانید.

سؤالات متداول

چرا du و df گاهی عدد متفاوت نشان می‌دهند؟ du فقط فایل‌هایی را می‌شمارد که در مسیر فایل‌سیستم قابل دیدن هستند. df فضای واقعی گرفته‌شده را می‌شمارد، حتی اگر فایلی حذف شده باشد ولی هنوز باز باشد. اختلاف زیاد بین این دو عدد نشانه‌ی همین حالت دوم است.

آیا بزرگ کردن دیسک مشکل تمام شدن inode را هم حل می‌کند؟ نه به‌تنهایی. تعداد inode در زمان فرمت فایل‌سیستم تعیین می‌شود، نه در زمان افزایش فضا. باید فایل‌سیستم را با تعداد inode بیشتر دوباره فرمت کنید یا فایل‌های اضافه را پاک کنید.

چطور بفهمم کدام پردازش فایل حذف‌شده را باز نگه داشته؟ با lsof +L1 لیست فایل‌های حذف‌شده‌ی باز و شناسه‌ی پردازش هر کدام را می‌بینید. ستون COMMAND نام برنامه را نشان می‌دهد و ستون FD شماره‌ی file descriptor را.

خطای No space left on device فقط برای فایل‌های معمولی پیش می‌آید؟ نه، همین خطا وقتی سیستم نتواند inode جدید برای socket، pipe یا حتی یک دایرکتوری خالی بسازد هم ظاهر می‌شود. ربطی به حجم فایل ندارد.