SSD Nodes Learn 🎉 VPS $5.50/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor

df ডিস্ক ফুল দেখাচ্ছে কিন্তু du খালি? সমাধান জানুন

আপনার VPS ডিস্ক ফুল দেখালেও du কমান্ডে জায়গা খালি দেখাচ্ছে? ফাইল ডিলিট করার পরেও প্রসেস ফাইল ধরে রাখলে এমন হয়। lsof কমান্ড ব্যবহার করে কীভাবে ফাইল খুঁজে রিলিজ করবেন তা দেখুন।

কেন df ডিস্ক পূর্ণ দেখায় কিন্তু du ভিন্ন তথ্য দেয়

df ডিস্ক পূর্ণ বলে রিপোর্ট করে, কিন্তু du সেই জায়গা খুঁজে পায় না কারণ কোনো একটি প্রসেস এমন একটি ফাইল ধরে রেখেছে যা ডিলিট করা হয়েছে। একটি ফাইল ডিলিট করলে ডিরেক্টরি থেকে শুধু তার নাম মুছে যায়। ডেটা ব্লকগুলো তখনই মুক্ত হয় যখন সেই inode-কে নির্দেশ করা সর্বশেষ open file descriptor বন্ধ করা হয়। du ফাইলের নাম ধরে খোঁজে, তাই এটি সেই ফাইলটিকে গণনা করে না। df ফাইলসিস্টেমকে জিজ্ঞাসা করে কতগুলো ব্লক বরাদ্দ করা আছে, তাই এটি সেই ফাইলটিকেও গণনা করে যার এখন আর কোনো নাম নেই।

এই নির্দেশিকাটি একটি সাধারণ Ubuntu VPS-এ আগে থেকেই ইনস্টল করা টুল ব্যবহার করে এই পরিস্থিতি তৈরি করে, /proc-এর মাধ্যমে ফাইল ধরে রাখা প্রসেসটিকে খুঁজে বের করে এবং রিবুট ছাড়াই জায়গা খালি করে। একই লক্ষণের অন্যান্য কারণগুলো হলো: কোনো খালি এন্ট্রি না থাকা একটি inode table, মাউন্ট পয়েন্টের নিচে লুকিয়ে থাকা ফাইল এবং root-এর জন্য সংরক্ষিত ব্লক।

প্রতিটি কমান্ড চালান এবং আপনার নিজের আউটপুট পড়ুন। মানগুলো আপনার ডিস্কের ওপর নির্ভর করে, তাই গাইডে দেওয়া কোনো সংখ্যার সাথে তুলনা না করে আপনার নিজের মেশিনে কমান্ড চালানোর আগের ও পরের অবস্থার তুলনা করুন।

What df counts and what du counts

df (disk free) asks each mounted filesystem for its own accounting: how many blocks exist, how many are allocated, how many are free. It never opens a directory. The answer covers every allocated block, including blocks belonging to a file that no directory entry points at.

du (disk usage) does the opposite. It starts at a path you give it, reads directories, stats every entry it finds, and adds up the blocks. A file with no name is invisible to it. So is any directory it is not allowed to read, which is why an ordinary user gets a smaller total than root does. Run du under sudo before you conclude anything from the comparison.

Two options matter every time you compare the two.

  • -x keeps du on one filesystem. Without it, du / walks into every filesystem mounted below / and produces a total that df / was never measuring.
  • -s prints one summary line per argument instead of one line per directory.

That gives the pair to run side by side on the filesystem you care about.

df -h /
sudo du -xhs / 2>/dev/null

df answers immediately. du takes minutes on a large filesystem, because it stats every file on the way. When the two totals are far apart, and du ran as root with -x, the missing space is allocated to something that has no name.

ইচ্ছাকৃতভাবে অমিল তৈরি করা

এটি একটি টেস্ট VPS-এ করুন। নিচে সব কমান্ড bash এবং coreutils-এর, তাই আলাদা করে কিছু ইনস্টল করার প্রয়োজন নেই।

যে ফাইলসিস্টেমটি /var/tmp ধারণ করে তার শুরুর অবস্থা রেকর্ড করুন।

cd /var/tmp
df -h .
df --output=used -B1 .

দ্বিতীয় কমান্ডটি কোনো রাউন্ডিং ছাড়াই ব্যবহৃত বাইট সংখ্যা দেখায়, যা শেষের চেকটিকে নির্ভুল করে তোলে।

এখন একটি ফাইল তৈরি করুন। এর আকার মেশিনটি যে পরিমাণ খালি জায়গা রিপোর্ট করছে তার ওপর ভিত্তি করে নির্ধারিত হয়, ফলে আপনার ডিস্কের আকার যাই হোক না কেন এই ডেমোটি কাজ করবে।

free=$(df --output=avail -B1 . | tail -n 1)
fallocate -l $((free / 10)) ghost.bin
ls -l ghost.bin
df -h .

$(...) হলো কমান্ড সাবস্টিটিউশন: শেল এর ভেতরের কমান্ডটি চালায় এবং আউটপুটটিকে free-এর মান হিসেবে ব্যবহার করে। যদি এই সিনট্যাক্স আপনার কাছে নতুন মনে হয়, তবে bash-এ কমান্ড সাবস্টিটিউশন বিষয়টি বিস্তারিত দেখুন। fallocate কোনো কিছু না লিখে সরাসরি ব্লক রিজার্ভ করে, তাই এটি তাৎক্ষণিকভাবে শেষ হয়। যে ফাইলসিস্টেমে এটি সাপোর্ট করে না সেখানে কমান্ডটি ব্যর্থ হবে, সেক্ষেত্রে head -c $((free / 10)) /dev/zero > ghost.bin ব্যবহার করে বাইটগুলো লিখে একই কাজ করা যাবে।

এই df -h .-এর সাথে আপনার রেকর্ড করা মানটি তুলনা করুন। ব্যবহৃত (used) কলামটি বেড়েছে এবং খালি (available) কলামটি কমেছে।

এখন অন্য একটি প্রসেস থেকে ফাইলটিকে ওপেন রাখুন এবং তারপর সেটিকে ডিলিট করুন।

sleep infinity < ghost.bin &
holder=$!
rm ghost.bin
ls -l ghost.bin
df -h .
sudo du -xhs . 2>/dev/null

রিডাইরেকশনই হলো এই কৌশলের মূল বিষয়। sleep infinity < ghost.bin & একটি ব্যাকগ্রাউন্ড প্রসেস শুরু করে যার স্ট্যান্ডার্ড ইনপুট হলো ওই ফাইলটি, তাই শেল ফাইলটি ওপেন করে এবং এর ডেসক্রিপটর sleep-এর কাছে হস্তান্তর করে, যা এটিকে ওপেন রাখে। $! ওই ব্যাকগ্রাউন্ড জবের প্রসেস আইডি (PID) ধরে রাখে। এরপর rm ফাইলটির নাম মুছে ফেলে, যদিও ডেসক্রিপটরটি তখনও ওপেন থাকে।

আউটপুটটি পড়ুন। ls ফাইলটি খুঁজে পাবে না, কারণ এর নাম মুছে ফেলা হয়েছে। du প্রায় আগের অবস্থানে ফিরে এসেছে, কারণ এটি ফাইলের নাম ধরে হিসাব করে। df-এর কোনো পরিবর্তন হয়নি, কারণ ব্লকগুলো তখনও বরাদ্দ করা আছে। ফাইলসিস্টেম এবং ডিরেক্টরি ট্রি এখন আর একমত নয়, এবং এদের মধ্যকার এই পার্থক্যই হলো সেই ফাইলটি যা আপনি এইমাত্র ডিলিট করেছেন।

মুছে ফেলা ফাইলটি যে প্রসেস ধরে রেখেছে তা খুঁজে বের করা

প্রতিটি ওপেন ফাইল ডেসক্রিপ্টর /proc/<pid>/fd/-এর অধীনে একটি সিম্বলিক লিঙ্ক হিসেবে থাকে যা মূল ফাইলটিকে নির্দেশ করে। যখন ফাইলটি আনলিঙ্ক করা হয়, কার্নেল সেই লিঙ্কের লক্ষ্যবস্তুকে মুছে ফেলা হিসেবে চিহ্নিত করে। তাই ফাইলটি কোন প্রসেস ধরে রেখেছে তা বের করার অর্থ হলো এমন একটি লিঙ্ক খুঁজে বের করা যার লক্ষ্যবস্তুতে এই মার্কারটি রয়েছে।

sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

sudo lsof +L1

-lname সিম্বলিক লিঙ্কের নামের পরিবর্তে তার লক্ষ্যবস্তুর সাথে মিল খোঁজে, %p ডেসক্রিপ্টর পাথ প্রিন্ট করে এবং %l সেটি কী নির্দেশ করছে তা দেখায়। প্রিন্ট করা পাথের দ্বিতীয় অংশটি হলো প্রসেস আইডি। এটি sudo দিয়ে চালান, অন্যথায় আপনি কেবল নিজের প্রসেসগুলোর জন্য /proc/<pid>/fd পড়তে পারবেন। stderr রিডাইরেক্ট সেই প্রসেসগুলোর নয়েজ বাদ দেয় যেগুলো find চলার সময় বন্ধ হয়ে যায়।

একটি ব্যস্ত সার্ভারে যেকোনো মুহূর্তে বেশ কিছু মুছে ফেলা ফাইল ওপেন থাকতে পারে এবং সেগুলোর বেশিরভাগই ছোট ও ক্ষতিকর নয়। সেগুলোকে সাইজ অনুযায়ী সাজান যাতে কেবল গুরুত্বপূর্ণ ফাইলগুলো উপরে থাকে।

sudo bash -c 'for fd in /proc/[0-9]*/fd/*; do
  target=$(readlink "$fd" 2>/dev/null) || continue
  case "$target" in
    *"(deleted)") echo "$(stat -Lc %s "$fd" 2>/dev/null) $fd $target" ;;
  esac
done' | sort -rn | head

sudo lsof +L1 | sort -k7 -n

stat -L সরাসরি ইনোড (inode)-এর লিঙ্ক অনুসরণ করে, তাই %s সেই ফাইলের সাইজ রিপোর্ট করে যার এখন আর কোনো নাম নেই। এই সংখ্যা অনুযায়ী সাজালে সবচেয়ে বড় ফাইলটি সবার উপরে থাকে।

এরপর বিজয়ী ডেসক্রিপ্টরের পেছনের প্রসেসটি শনাক্ত করুন। তালিকার উপরের পাথে আপনার প্রয়োজনীয় দুটি সংখ্যাই থাকে, তাই সেগুলোকে প্রথমে ভেরিয়েবলে রাখুন এবং PID ও N-এর জায়গায় আপনার নিজের লিস্টে প্রাপ্ত মানগুলো বসান।

pid=PID
n=N
ps -o pid,user,etime,args -p "$pid"
sudo stat -L "/proc/$pid/fd/$n"

PID=1234; N=5; ps -p $PID -o comm=; ls -l /proc/$PID/fd/$N

ps প্রোগ্রামের নাম এবং সেটি কতক্ষণ ধরে চলছে তা দেখায়। stat -L মুছে ফেলা ইনোডের সাইজ এবং বরাদ্দকৃত ব্লকের সংখ্যা প্রিন্ট করে। এগুলো একত্রে সেই গুরুত্বপূর্ণ প্রশ্নের উত্তর দেয়: কোন সার্ভিসটি এই ফাইলটিকে জীবিত রেখেছে।

যদি মেশিনে আগে থেকেই lsof থাকে, তবে sudo lsof +L1 সেই সব ওপেন ফাইল তালিকাভুক্ত করে যাদের লিঙ্ক কাউন্ট শূন্যে নেমে গেছে এবং একটি টেবিলে সেগুলোর সাইজ দেখায়। এটি একটি মিনিমাল Ubuntu ইমেজে থাকে না, এবং যেখানে ডিস্ক স্পেস খালি নেই সেখানে নতুন প্যাকেজ ইনস্টল করা নিজেই ব্যর্থ হতে পারে, তাই /proc ব্যবহার করে ফাইল খোঁজার পদ্ধতিটিই সবসময় কার্যকর।

রিবুট না করেই জায়গা খালি করা

রিবুট করলে সমস্যার সমাধান হয় ঠিকই, কিন্তু এটি প্রথম পদক্ষেপ হিসেবে সঠিক নয়: এটি সার্ভিসটিকে বন্ধ করে দেয় এবং প্রমাণের চিহ্ন মুছে ফেলে। চারটি সহজতর উপায় রয়েছে, যা ক্রমানুসারে চেষ্টা করা উচিত।

প্রথমত, যদি ডেটাটি আপনার প্রয়োজন হয় তবে তা কপি করে নিন। ডেসক্রিপ্টর পাথ থেকে রিড করলে তা সরাসরি লাইভ ইনোড (inode) থেকে ডেটা পড়ে।

sudo cp /proc/<pid>/fd/<n> /root/recovered.log

এটিই একমাত্র ক্ষেত্র যেখানে ডিলিট করা ফাইল ফিরে পাওয়া সহজ, আর এই কারণেই rm -rf দিয়ে ডিলিট করা ফাইল পুনরুদ্ধার করার সময় প্রথমেই জিজ্ঞাসা করা হয় যে কোনো প্রসেস ফাইলটিকে ওপেন করে রেখেছে কি না। একবার শেষ ডেসক্রিপ্টরটি বন্ধ হয়ে গেলে, সেই পথটি আর খোলা থাকে না।

দ্বিতীয়ত, ডেসক্রিপ্টরের মাধ্যমে ফাইলটি খালি করুন। /proc পাথটি একই ইনোডে নির্দেশ করে, তাই ফাইলটি ট্রাঙ্কেট (truncate) করলে প্রসেসটি চালু থাকা অবস্থাতেই ব্লকগুলো মুক্ত হয়ে যায়।

sudo truncate -s 0 "/proc/$pid/fd/$n"
df -h /

এটি তখনই কার্যকর হয় যখন রাইটার ফাইলটিকে অ্যাপেন্ড মোডে ওপেন করে, কারণ প্রতিটি রাইট তখন ফাইলের বর্তমান শেষে যুক্ত হয়। যদি তা না হয়, তবে প্রসেসটি তার পুরনো রাইট অফসেট বজায় রাখে, ফলে পরবর্তী রাইটটি ফাইলের অনেক ভেতরে গিয়ে পড়ে এবং শুরুতে একটি গর্ত (hole) তৈরি করে। গর্তটি অ্যালোকেটেড নয়, তাই ব্লকগুলো মুক্তই থাকে এবং df তার ফিরে পাওয়া জায়গা ধরে রাখে। যা ফিরে আসে তা কেবল ফাইলের সাইজ: প্রসেসটি রাইট করার পর পুনরায় sudo stat -L "/proc/$pid/fd/$n" চালান, দেখবেন এটি পুরনো সাইজ দেখাচ্ছে কিন্তু ব্লক কাউন্ট তার সাথে মিলছে না। সাইজ শূন্য থেকে শুরু করতে চাইলে প্রসেসটি রিস্টার্ট করুন।

তৃতীয়ত, সার্ভিসটিকে তার লগ ফাইল পুনরায় ওপেন করতে বলুন। যে ডেমনের লগ ফাইল তার অজান্তেই ডিলিট হয়ে গেছে, এটি সেই সমস্যার একটি সাধারণ বাস্তব রূপ। অনেক ডেমোন সিগন্যাল পাওয়ার পর তাদের লগ ফাইল পুনরায় ওপেন করে: Nginx এর জন্য SIGUSR1 এবং rsyslog এর জন্য SIGHUP ব্যবহৃত হয়। আন্দাজে কাজ না করে আপনার সামনে থাকা ডেমোনের ডকুমেন্টেশন দেখুন, কারণ ভুল ডেমোনে ভুল সিগন্যাল পাঠালে তা বন্ধ হয়ে যেতে পারে।

sudo systemctl kill -s USR1 nginx

চতুর্থত, ইউনিটটি রিস্টার্ট করুন। sudo systemctl restart <unit> পুরনো প্রসেসের ধরে রাখা প্রতিটি ডেসক্রিপ্টর বন্ধ করে দেয়, ফলে ব্লকগুলো নিশ্চিতভাবে ফিরে আসে। উপরের প্রদর্শনের ক্ষেত্রে হোল্ডারটি হলো একটি sleep যা আপনি নিজেই চালু করেছিলেন, তাই সেটিকে বন্ধ করাই যথেষ্ট।

kill $holder
df -h .
df --output=used -B1 .
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

ফাইলটি তৈরি করার আগে যে ভ্যালু রেকর্ড করেছিলেন তার সাথে ব্যবহৃত বাইটগুলো তুলনা করুন। সেগুলো আবার মিলে যাবে এবং find আর আপনার ডেসক্রিপ্টরটি দেখাবে না। যে কমান্ড দিয়ে সমস্যাটি খুঁজে পেয়েছিলেন, তা দিয়েই যাচাই করার অভ্যাস বজায় রাখা ভালো।

বারবার হাতে df চালানোর চেয়ে ভ্যালুটির পরিবর্তন পর্যবেক্ষণ করা সহজ। watch একটি কমান্ড নির্দিষ্ট বিরতিতে পুনরাবৃত্তি করে এবং আউটপুটটি একই জায়গায় প্রিন্ট করে, তাই watch df -h / ব্যবহার করলে জায়গা ফিরে আসার সাথে সাথে ব্যবহৃত কলামের পরিবর্তন দেখতে পাবেন।

যখন মোট পরিমাণ মিলে যায় কিন্তু ডিস্ক এখনও পূর্ণ থাকে

যদি df এবং রুট du -x একে অপরের সাথে মিলে যায়, তবে কোনো ডিলিট করা ফাইল এর কারণ নয়। অবশিষ্ট কারণগুলো ভিন্ন প্রকৃতির এবং প্রতিটির জন্য আলাদা যাচাইকরণ পদ্ধতি রয়েছে।

ব্লক নয়, ইনোড (inode) শেষ হয়ে যাওয়া

একটি ইনোড একটি ফাইলের মেটাডেটা ধারণ করে। ফাইলসিস্টেম তৈরির সময় ext4 একটি নির্দিষ্ট সংখ্যক ইনোড তৈরি করে, তাই ফাইলসিস্টেমে খালি ব্লক থাকা সত্ত্বেও ইনোড শেষ হয়ে যেতে পারে। তখন নতুন ফাইল তৈরি করা সম্ভব হয় না, যদিও df -h অনুযায়ী জায়গা খালি দেখায়।

df -h /
df -i /

প্রথম কমান্ডটি ব্লক গণনা করে এবং দ্বিতীয়টি ইনোড গণনা করে। প্রতিটি আউটপুটের use কলামটি তুলনা করুন। ব্লকের ব্যবহার কম কিন্তু ইনোডের ব্যবহার সর্বোচ্চ সীমায় থাকলে বুঝতে হবে যে খুব ছোট আকারের অনেক বেশি ফাইল তৈরি হয়েছে।

df একই ইনভোকেশনে -i এবং --output গ্রহণ করে না, তাই যখন আপনি raw count পড়তে চান বা অন্য কোনো কমান্ডে পাঠাতে চান, তখন ইনোড ফিল্ডগুলো নাম ধরে নির্বাচন করুন এবং -i বাদ দিন।

df --output=itotal,iused,iavail,ipcent /

এই কলামগুলো df -i-এর প্রিন্ট করা একই হিসাব বহন করে, যা আপনি সহজেই আলাদা করতে পারবেন।

বাইট গণনা না করে এন্ট্রি গণনা করে ফাইলগুলো খুঁজে বের করুন।

sudo du --inodes -x -d 1 / 2>/dev/null | sort -rn | head

যে ডিরেক্টরিটিতে সবচেয়ে বেশি ফাইল পাওয়া গেছে, সেটিতে গিয়ে একই কমান্ডটি পুনরায় চালান। এভাবে ফাইল তৈরি করা ডিরেক্টরি ট্রি-টি খুঁজে না পাওয়া পর্যন্ত এগিয়ে যান। যদি আপনার du-এ --inodes সাপোর্ট না থাকে, তবে sudo find /var -xdev -type f | wc -l ব্যবহার করে ধীরগতিতে সাবট্রি গণনা করুন।

এর সমাধান হলো ফাইলগুলো মুছে ফেলা বা সরিয়ে নেওয়া। বিদ্যমান ext4 ফাইলসিস্টেমে আপনি ইনোড যোগ করতে পারবেন না, কারণ এর সংখ্যা mkfs-এর সময় নির্ধারিত হয়ে যায়। তাই ইনোড বাড়াতে হলে ফাইলসিস্টেম নতুন করে তৈরি করতে হবে এবং ব্যাকআপ থেকে ডেটা রিস্টোর করতে হবে। XFS প্রয়োজন অনুযায়ী ইনোড বরাদ্দ করে, তাই এটি একই ধরনের নির্দিষ্ট সীমার সম্মুখীন হয় না। কন্টেইনার চালানো মেশিনগুলোতে খুব দ্রুত এই দুই সীমার যেকোনো একটি পূর্ণ হয়ে যায়, কারণ ইমেজ লেয়ারগুলোতে অনেক ছোট ছোট ফাইল থাকে। সেই মেশিনের ক্ষেত্রে VPS-এ Docker-এর ডিস্ক ব্যবহার কমানো হলো নির্দিষ্ট সমাধান, যা সাধারণ ফাইলসিস্টেম ক্লিনআপের চেয়ে অনেক বেশি জায়গা পুনরুদ্ধার করে।

মাউন্ট পয়েন্টের নিচে লুকিয়ে থাকা জায়গা

কোনো ডিরেক্টরিতে কিছু মাউন্ট করার আগেই সেখানে ফাইল থাকতে পারে। সেই ডিরেক্টরির ওপর একটি ফাইলসিস্টেম মাউন্ট করলে নিচের ফাইলগুলো ঠিক আগের জায়গাতেই থেকে যায়: সেগুলো তখনও বরাদ্দ থাকে, df দ্বারা গণনা করা হয়, কিন্তু নাম দিয়ে আর খুঁজে পাওয়া যায় না। মাউন্ট করার ফলে সেগুলো ঢাকা পড়ে যায় বলে du সেগুলোকে দেখতে পায় না।

tmpfs ব্যবহার করে এটি প্রদর্শন করা যাক, যার জন্য কোনো অতিরিক্ত ডিস্কের প্রয়োজন নেই। এই অংশটির জন্য এমন একটি মেশিন প্রয়োজন যেখানে আপনার মাউন্ট করার অনুমতি আছে, তাই এটি KVM VPS-এ কাজ করবে।

sudo mkdir -p /srv/covered
sudo cp /etc/services /srv/covered/
ls /srv/covered
sudo mount -t tmpfs tmpfs /srv/covered
ls /srv/covered
sudo umount /srv/covered
ls /srv/covered

মাঝখানের ls একটি খালি ডিরেক্টরি দেখাচ্ছে। কপিটি কোথাও যায়নি: এটি এখনও রুট ফাইলসিস্টেমে আছে এবং আনমাউন্ট করার সাথে সাথেই তা আবার ফিরে আসবে। এখন এমন একটি সার্ভিসের কথা চিন্তা করুন যা কোনো ভলিউম মাউন্ট করার আগে এক মাস ধরে সেই পাথে লগ ফাইল লিখেছে।

চলমান সার্ভারে আসল ফাইলগুলো খুঁজে পেতে, রুট ফাইলসিস্টেমটিকে অন্য কোথাও দ্বিতীয়বার মাউন্ট করুন। একটি বাইন্ড মাউন্ট (bind mount) এমন একটি ফাইলসিস্টেম দেখায় যার ভেতরে অন্য কোনো ফাইলসিস্টেম মাউন্ট করা নেই।

sudo mkdir -p /mnt/rootcheck
sudo mount --bind / /mnt/rootcheck
sudo du -xhs /mnt/rootcheck/* 2>/dev/null | sort -h
sudo umount /mnt/rootcheck

যে ফাইলগুলো এই তালিকায় দেখা যাচ্ছে কিন্তু স্বাভাবিক পাথে নেই, সেগুলো মাউন্ট পয়েন্টের নিচে চাপা পড়ে আছে। কাজ শেষ হলে বাইন্ড মাউন্টটি আনমাউন্ট করুন, অন্যথায় -x ছাড়া পরবর্তী du একই ফাইল দুইবার গণনা করবে।

root-এর জন্য সংরক্ষিত ব্লক

ext4 ফাইলসিস্টেম তার কিছু ব্লক root ব্যবহারকারীর জন্য আলাদা করে রাখে, যাতে ডিস্ক পূর্ণ হয়ে গেলেও root ব্যবহারকারী লগইন করতে এবং মেশিন মেরামত করতে পারেন। সাধারণ ব্যবহারকারীর অধীনে চলা কোনো প্রসেস আগে এই সীমাবদ্ধতায় পড়ে, যদিও df কমান্ডে তখনো কিছুটা জায়গা খালি দেখায়। ডিফল্ট সেটিংসের ওপর নির্ভর না করে আপনার ফাইলসিস্টেমের বর্তমান সেটিং যাচাই করে নিন।

dev=$(df --output=source / | tail -n 1)
sudo tune2fs -l "$dev" | grep -i 'block count'

এটি মোট ব্লকের সংখ্যা এবং সংরক্ষিত ব্লকের সংখ্যা একই ইউনিটে প্রদর্শন করে, তাই এদের অনুপাত সরাসরি নির্ণয় করা যায়। df কমান্ডে 'available' কলামে সেই জায়গাটি দেখানো হয় যা একজন সাধারণ ব্যবহারকারী ব্যবহার করতে পারেন, যে কারণে 'used' এবং 'available' যোগ করলে তা মোট সাইজের চেয়ে কম হয়। এই পার্থক্যটুকুই হলো সংরক্ষিত অংশ।

sudo tune2fs -m <percent> "$dev" কমান্ড ব্যবহার করে এটি পরিবর্তন করুন। এই পরিবর্তন তাৎক্ষণিকভাবে কার্যকর হয় এবং ফাইলসিস্টেম রিমাউন্ট করার প্রয়োজন হয় না। আলাদা কোনো ডেটা ফাইলসিস্টেমের ক্ষেত্রে সংরক্ষিত ব্লকের পরিমাণ কমিয়ে রাখা যুক্তিসঙ্গত। তবে root ফাইলসিস্টেমের ক্ষেত্রে পর্যাপ্ত জায়গা খালি রাখুন যাতে root ব্যবহারকারী লিখতে পারেন, কারণ সম্পূর্ণ পূর্ণ হয়ে যাওয়া root ফাইলসিস্টেম মেরামত করা অনেক বেশি কঠিন। tune2fs কমান্ডটি ext2, ext3 এবং ext4-এ কাজ করে। XFS-এ এই ধরনের কোনো সেটিং নেই।

যেখানে du আপনাকে বিভ্রান্ত করতে পারে

du-এর চারটি অভ্যাস এমন মোট হিসাব দেয় যা ভুল মনে হতে পারে।

  • Hard links: du একটি inode-কে একবারই গণনা করে, এমনকি যখন একাধিক নাম সেটিকে নির্দেশ করে। তাই hard link-এ পূর্ণ একটি ডিরেক্টরি ট্রির আকার তার ফাইলগুলোর যোগফলের চেয়ে কম দেখায়।
  • Sparse files: du শুধুমাত্র বরাদ্দকৃত ব্লকের হিসাব দেয়, যেখানে ls -l ফাইলের আপাত আকার (apparent size) দেখায়। অন্য সংখ্যাটি দেখতে --apparent-size যোগ করুন।
  • Permissions: সাধারণ ব্যবহারকারী হিসেবে চালালে, du যেসব ফাইল পড়তে পারে না সেগুলো এড়িয়ে যায় এবং কম হিসাব দেখায়। এটি যে ত্রুটিগুলো প্রিন্ট করে, মানুষ সাধারণত সেগুলো /dev/null-এ রিডাইরেক্ট করে এবং পড়া বন্ধ করে দেয়।
  • Filesystem boundaries: -x ছাড়া, du / প্রতিটি ফাইলসিস্টেমকে গণনা করে যা /-এর নিচে মাউন্ট করা থাকে। ফলে এর মোট হিসাব df /-এর রিপোর্টের চেয়ে বেশি হতে পারে।

df-এরও একটি অভ্যাস জেনে রাখা ভালো। এটি প্রতিটি ফাইলসিস্টেমকে আলাদাভাবে রিপোর্ট করে, তাই যে পাথে রাইট করতে ব্যর্থ হচ্ছেন, ঠিক সেই পাথের ওপর এটি চালান। কার্নেল প্যাকেজ জমা হওয়ার সাথে সাথে একটি আলাদা /boot নিজস্ব সময়সূচী অনুযায়ী পূর্ণ হয়ে যায়, এবং Ubuntu-তে পুরনো কার্নেল মুছে ফেলা বিষয়টি / থেকে জায়গা খালি করার চেয়ে ভিন্ন একটি কাজ।

বাস্তব কোনো ঘটনার ক্ষেত্রে কাজের ধারা

  1. ব্যর্থ রাইট অপারেশন যে ফাইলসিস্টেমকে লক্ষ্য করে করা হয়েছিল, সেটিতে df -h <path> এবং df -i <path> চালান; অভ্যাসবশত সরাসরি /-এ চালাবেন না।
  2. sudo du -xh -d 1 <mountpoint> 2>/dev/null | sort -h চালান, তারপর সবচেয়ে বড় ডিরেক্টরিটির ভেতরে প্রবেশ করুন।
  3. df যা ব্যবহার হয়েছে বলে রিপোর্ট করছে, যদি du তা মেলাতে না পারে, তবে /proc ব্যবহার করে এমন ডিলিট হওয়া ফাইল খুঁজুন যা এখনও ওপেন অবস্থায় আছে।
  4. যদি উভয়ই একই তথ্য দেয়, তবে ফাইলসিস্টেমটিকে অন্য কোথাও bind mount করুন এবং মাউন্ট পয়েন্টের নিচে কোনো ফাইল জমে আছে কি না তা দেখুন।
  5. যদি inode ব্যবহারের সীমা অতিক্রম করে, তবে বাইটের পরিবর্তে ফাইলের সংখ্যা গণনা করুন।

প্রতিটি ধাপেই এমন একটি কমান্ড রয়েছে যার আউটপুট আপনি পড়তে পারবেন। সমস্যাটি অনুমান করে সমাধান করা এবং সঠিকভাবে সমাধান করার মধ্যে পার্থক্য এখানেই।

FAQ

কেন df ডিস্ক পূর্ণ দেখায় যখন du অনেক কম জায়গা ব্যবহার হতে দেখে?

এর সাধারণ কারণ হলো এমন একটি ফাইল যা মুছে ফেলা হয়েছে কিন্তু কোনো প্রসেস এখনো সেটি ওপেন করে রেখেছে। ফাইলটি মুছে ফেললে এর ডিরেক্টরি এন্ট্রি চলে যায়, তাই du এর কোনো নাম খুঁজে পায় না এবং গণনা করা বন্ধ করে দেয়। ইনোড (inode) এবং এর ব্লকগুলো বরাদ্দকৃতই থাকে যতক্ষণ না শেষ ডেসক্রিপ্টরটি বন্ধ হয়, আর df বরাদ্দকৃত ব্লকগুলোই গণনা করে। /proc/<pid>/fd-এ এমন সিম্বলিক লিঙ্ক খুঁজুন যার টার্গেট মুছে ফেলা হয়েছে বলে চিহ্নিত, তাহলে আপনি ফাইলটি এবং সেটি ধরে রাখা প্রসেস উভয়ই খুঁজে পাবেন। এই তুলনার ওপর আস্থা রাখার আগে নিশ্চিত করুন যে আপনি du কমান্ডটি root হিসেবে এবং -x ফ্ল্যাগসহ চালিয়েছেন, কারণ সাধারণ ব্যবহারকারী হিসেবে চালালে যেসব ডিরেক্টরি পড়া সম্ভব নয় সেগুলো নীরবে বাদ পড়ে যায়।

lsof ছাড়া ওপেন থাকা মুছে ফেলা ফাইল কীভাবে খুঁজে পাব?

কার্নেলের নিজস্ব ওপেন ডেসক্রিপ্টর রেকর্ড ব্যবহার করুন। sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null এমন প্রতিটি ডেসক্রিপ্টর তালিকাভুক্ত করে যা কোনো নামহীন ফাইলের দিকে নির্দেশ করছে, এবং এর প্রিন্ট করা পাথের ভেতরেই প্রসেস আইডি থাকে। সেই ডেসক্রিপ্টর পাথগুলোর একটিতে sudo stat -Lc %s চালালে এর আকার জানা যায়, ফলে আপনি সেগুলোকে সাজিয়ে গুরুত্বপূর্ণ ফাইলটি বেছে নিতে পারেন। এর জন্য কোনো প্যাকেজ ইনস্টল করার প্রয়োজন নেই, যা গুরুত্বপূর্ণ কারণ ডিস্কে জায়গা না থাকলে নতুন প্যাকেজ ইনস্টল করা ব্যর্থ হতে পারে।

প্রসেস বন্ধ না করেই কি জায়গা খালি করা সম্ভব?

কখনো কখনো সম্ভব। sudo truncate -s 0 /proc/<pid>/fd/<n> ডেসক্রিপ্টরের মাধ্যমে একই ইনোডে পৌঁছায় এবং প্রসেসটি চালু থাকা অবস্থাতেই এর ব্লকগুলো মুক্ত করে দেয়। এটি সবচেয়ে কার্যকর হয় যদি প্রসেসটি ফাইলটিকে append মোডে ওপেন করে থাকে, কারণ তখন এর রাইট অপারেশন সবসময় বর্তমান শেষের দিকেই হয়। যদি তা না হয়, তবে রাইট অফসেট আগের জায়গাতেই থেকে যায় এবং পরবর্তী রাইট অপারেশনে ফাইলের শুরুতে একটি গর্ত (hole) তৈরি করে ফাইলটি পুনরায় তৈরি হয়, ফলে রিপোর্ট করা আকার আবার বেড়ে যায় যদিও গর্তের নিচের ব্লকগুলো খালি থাকে। ইউনিটটি রিস্টার্ট করা অথবা এর ডকুমেন্টেশনে উল্লেখিত সিগন্যাল পাঠিয়ে লগ ফাইল পুনরায় ওপেন করতে বলা হলো সঠিক সমাধান, যাতে কোনো sparse ফাইল অবশিষ্ট না থাকে।

df খালি জায়গা দেখাচ্ছে কিন্তু রাইট অপারেশন ব্যর্থ হচ্ছে। আর কী কারণ হতে পারে?

একই পাথে df -i চালিয়ে ইনোড চেক করুন, কারণ খালি ব্লক থাকলেও ইনোড খালি না থাকলে ফাইল সিস্টেম নতুন ফাইল গ্রহণ করে না। চেক করুন রাইট অপারেশনটি non-root ব্যবহারকারী হিসেবে এমন কোনো ext4 ফাইল সিস্টেমে চালানো হচ্ছে কি না যেখানে কেবল সংরক্ষিত ব্লকগুলোই অবশিষ্ট আছে, যা ডিভাইসের ওপর sudo tune2fs -l চালালে দেখা যাবে। নিশ্চিত করুন যে আপনি সেই ফাইল সিস্টেমটিই চেক করছেন যেখানে রাইট অপারেশনটি হচ্ছে, কারণ একটি আলাদা /boot বা /var মাউন্ট পয়েন্ট / থেকে স্বাধীনভাবে পূর্ণ হতে পারে।

কেন du, df-এর তুলনায় বেশি মোট জায়গা রিপোর্ট করে?

-x ছাড়া du আপনার দেওয়া পাথের অধীনে মাউন্ট করা প্রতিটি ফাইল সিস্টেমে প্রবেশ করে, তাই এটি একাধিক ফাইল সিস্টেমের যোগফল দেখায় যেখানে df কেবল একটির বর্ণনা দেয়। বাইন্ড মাউন্ট (bind mounts) থাকলে সমস্যা আরও বাড়ে, কারণ একই ফাইল প্রতিটি পাথে একবার করে গণনা করা হয়। du-কে একটি একক ফাইল সিস্টেমে সীমাবদ্ধ রাখতে -x যোগ করুন এবং df-কে একই পাথ দিন, যাতে উভয় কমান্ড একই বিষয়ের বর্ণনা দেয়।