SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-28

แก้ปัญหา df บอกดิสก์เต็มแต่ du เช็คแล้วพื้นที่ยังเหลือ

พบปัญหา df รายงานดิสก์เต็มแต่ du ไม่พบไฟล์ ให้ตรวจสอบไฟล์ที่ถูกลบแต่ process ยังเปิดค้างอยู่ด้วย lsof พร้อมวิธีคืนพื้นที่ว่างโดยไม่ต้องรีบูตเซิร์ฟเวอร์บน Linux

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, August 15, 2026.

เหตุใด df จึงรายงานว่าดิสก์เต็มในขณะที่ du รายงานว่าไม่เต็ม

df รายงานว่าดิสก์เต็มในขณะที่ du ไม่พบพื้นที่ที่ถูกใช้งาน เนื่องจากมีกระบวนการ (process) หนึ่งยังคงเปิดไฟล์ที่ถูกลบไปแล้วค้างไว้ การลบไฟล์เป็นการนำชื่อไฟล์ออกจากไดเรกทอรีเท่านั้น แต่บล็อกข้อมูลจะถูกปล่อยคืนก็ต่อเมื่อ file descriptor สุดท้ายที่ชี้ไปยัง inode นั้นถูกปิดลง du ทำงานโดยการไล่ตามชื่อไฟล์ จึงไม่นับรวมไฟล์ดังกล่าว ส่วน df สอบถามไปยังระบบไฟล์ว่ามีการจัดสรรบล็อกไปเท่าใด จึงยังคงนับรวมไฟล์ที่ไม่มีชื่อปรากฏอยู่แล้ว

คู่มือนี้จะจำลองสถานการณ์ดังกล่าวบน Ubuntu VPS ทั่วไปโดยใช้เครื่องมือที่มีติดตั้งอยู่แล้ว พร้อมทั้งค้นหากระบวนการที่ถือครองไฟล์ผ่าน /proc และคืนพื้นที่ว่างโดยไม่ต้องรีบูตเครื่อง นอกจากนี้ยังมีสาเหตุอื่นที่ทำให้เกิดอาการเดียวกัน ได้แก่ ตาราง inode ที่ไม่มีช่องว่างเหลืออยู่, ไฟล์ที่ถูกซ่อนอยู่ภายใต้จุด mount, และบล็อกที่ถูกสำรองไว้สำหรับ root

ให้รันคำสั่งแต่ละคำสั่งและอ่านผลลัพธ์ที่ปรากฏ ค่าที่ได้จะขึ้นอยู่กับดิสก์ของคุณ ดังนั้นให้เปรียบเทียบค่าก่อนและหลังดำเนินการบนเครื่องของคุณเอง แทนการเปรียบเทียบกับตัวเลขที่ระบุไว้ในคู่มือ

ความแตกต่างระหว่างสิ่งที่ df และ du นับ

df (disk free) จะสอบถามระบบไฟล์ที่ mount อยู่แต่ละแห่งว่ามีสถานะการใช้งานอย่างไร ได้แก่ มีบล็อกทั้งหมดเท่าใด ถูกจัดสรรไปแล้วเท่าใด และเหลือว่างเท่าใด โดยคำสั่งนี้จะไม่เปิดอ่านไดเรกทอรีใดๆ เลย ผลลัพธ์ที่ได้จึงครอบคลุมทุกบล็อกที่ถูกจัดสรรไว้ รวมถึงบล็อกที่อยู่ในไฟล์ซึ่งไม่มีรายการในไดเรกทอรีใดชี้ไปถึง

du (disk usage) ทำงานในทางตรงกันข้าม โดยจะเริ่มจาก path ที่คุณระบุ จากนั้นจะอ่านไดเรกทอรี ทำการ stat ทุกรายการที่พบ และรวมผลลัพธ์ของบล็อกทั้งหมดเข้าด้วยกัน ไฟล์ที่ไม่มีชื่อจึงเป็นสิ่งที่คำสั่งนี้มองไม่เห็น เช่นเดียวกับไดเรกทอรีใดก็ตามที่คำสั่งนี้ไม่มีสิทธิ์อ่าน ซึ่งเป็นเหตุผลว่าทำไมผู้ใช้ทั่วไปจึงได้ผลรวมน้อยกว่า root ควรเรียกใช้ du ภายใต้สิทธิ์ sudo ก่อนที่คุณจะสรุปผลใดๆ จากการเปรียบเทียบ

มีสองตัวเลือกที่สำคัญทุกครั้งที่คุณเปรียบเทียบคำสั่งทั้งสอง:

  • -x จะจำกัดการทำงานของ du ให้อยู่ภายในระบบไฟล์เดียว หากไม่มีตัวเลือกนี้ du / จะเข้าไปยังทุกระบบไฟล์ที่ถูก mount อยู่ภายใต้ / และสร้างผลรวมที่ df / ไม่เคยตรวจวัดมาก่อน
  • -s จะแสดงผลสรุปเพียงบรรทัดเดียวต่อหนึ่งอาร์กิวเมนต์ แทนที่จะแสดงผลทีละบรรทัดต่อหนึ่งไดเรกทอรี

สิ่งนี้ทำให้คุณสามารถเรียกใช้คำสั่งทั้งคู่ควบคู่กันบนระบบไฟล์ที่คุณต้องการตรวจสอบได้

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

df จะให้คำตอบได้ทันที ในขณะที่ du อาจใช้เวลาหลายนาทีบนระบบไฟล์ขนาดใหญ่ เพราะต้องทำการ stat ไฟล์ทุกไฟล์ระหว่างการทำงาน เมื่อผลรวมทั้งสองห่างกันมาก และ du ถูกเรียกใช้ในฐานะ root พร้อมกับ -x แสดงว่าพื้นที่ที่หายไปนั้นถูกจัดสรรให้กับสิ่งที่ไม่มีชื่อเรียกนั่นเอง

จำลองสถานการณ์ความไม่สอดคล้องของข้อมูลโดยเจตนา

ให้ดำเนินการบน 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 .

$(...) คือการทำ command substitution: shell จะรันคำสั่งที่อยู่ภายใน และนำผลลัพธ์ที่ได้มาเป็นค่าของ free หากคุณยังไม่คุ้นเคยกับไวยากรณ์นี้ command substitution ใน 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

การ redirect คือหัวใจสำคัญของวิธีนี้ sleep infinity < ghost.bin & จะเริ่มกระบวนการเบื้องหลัง (background process) ที่มี standard input เป็นไฟล์นั้น โดย shell จะเปิดไฟล์และส่ง descriptor ให้กับ sleep ซึ่งจะเปิดไฟล์นั้นค้างไว้ $! คือการเก็บ process ID ของงานเบื้องหลังนั้น จากนั้น rm จะทำการลบชื่อไฟล์ออกในขณะที่ descriptor ยังคงเปิดอยู่

อ่านผลลัพธ์ที่ได้ ls จะไม่พบไฟล์ดังกล่าวเนื่องจากชื่อไฟล์ถูกลบไปแล้ว du จะกลับไปใกล้เคียงกับค่าเริ่มต้นเนื่องจากมันใช้วิธีไล่ดูตามชื่อไฟล์ ส่วน df จะไม่มีการเปลี่ยนแปลงเนื่องจากบล็อกข้อมูลยังคงถูกจองไว้ ระบบไฟล์และโครงสร้างไดเรกทอรีจึงไม่สอดคล้องกัน และช่องว่างที่เกิดขึ้นระหว่างทั้งสองส่วนก็คือไฟล์ที่คุณเพิ่งลบไปนั่นเอง

การค้นหาโพรเซสที่ถือครองไฟล์ที่ถูกลบไปแล้ว

File descriptor ทุกตัวที่เปิดอยู่จะปรากฏภายใต้ /proc/<pid>/fd/ ในรูปแบบ symbolic link ที่ชี้ไปยังไฟล์นั้นๆ เมื่อไฟล์ถูกลบ (unlink) เคอร์เนลจะทำเครื่องหมายที่ปลายทางของลิงก์นั้นว่าถูกลบไปแล้ว ดังนั้นการค้นหาโพรเซสที่ถือครองไฟล์ดังกล่าวจึงหมายถึงการค้นหาลิงก์ที่มีปลายทางติดเครื่องหมายนั้นไว้

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

-lname จะจับคู่กับปลายทางของ symbolic link แทนที่จะเป็นชื่อของมัน ส่วน %p จะแสดง path ของ descriptor และ %l จะแสดงสิ่งที่ลิงก์นั้นชี้ไป โดยเลขโพรเซส (PID) คือองค์ประกอบที่สองของ path ที่แสดงออกมา ให้รันคำสั่งด้วย sudo เพราะหากไม่ทำเช่นนั้น คุณจะสามารถอ่านได้เพียง /proc/<pid>/fd ของโพรเซสที่เป็นของคุณเองเท่านั้น การ redirect 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

stat -L จะติดตามลิงก์ไปยัง inode โดยตรง ดังนั้น %s จะรายงานขนาดของไฟล์ที่ไม่มีชื่อเรียกอีกต่อไป การเรียงลำดับด้วยตัวเลขนี้จะทำให้ไฟล์ที่มีขนาดใหญ่ที่สุดแสดงขึ้นมาก่อน

จากนั้นให้ระบุโพรเซสที่อยู่เบื้องหลัง descriptor ที่พบ โดย path ที่อยู่ด้านบนสุดของรายการจะมีตัวเลขทั้งสองที่คุณต้องการอยู่ ให้กำหนดค่าเหล่านั้นลงในตัวแปร โดยแทนที่ PID และ N ด้วยค่าที่คำสั่งของคุณแสดงออกมา

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

ps จะระบุชื่อโปรแกรมและแสดงระยะเวลาที่โพรเซสนั้นทำงานอยู่ ส่วน stat -L จะแสดงขนาดและจำนวนบล็อกที่ถูกจัดสรรไว้ของ inode ที่ถูกลบไป ข้อมูลเหล่านี้จะช่วยตอบคำถามสำคัญว่าบริการใดที่กำลังทำให้ไฟล์นี้ยังคงค้างอยู่ในระบบ

หากเครื่องมี lsof ติดตั้งอยู่แล้ว sudo lsof +L1 จะแสดงรายการไฟล์ที่เปิดอยู่ซึ่งมีค่า link count ลดลงเหลือศูนย์และแสดงขนาดไฟล์ไว้ในตารางเดียว อย่างไรก็ตาม เครื่องมือนี้อาจไม่มีใน Ubuntu รุ่น minimal และการติดตั้งแพ็กเกจบนระบบไฟล์ที่ไม่มีพื้นที่ว่างอาจล้มเหลวได้ ดังนั้นการใช้ /proc จึงเป็นวิธีที่ใช้งานได้เสมอในทุกสถานการณ์

การคืนพื้นที่โดยไม่ต้องรีบูต

การรีบูตอาจแก้ปัญหาได้ แต่ไม่ใช่ขั้นตอนแรกที่ควรทำ เพราะจะทำให้บริการหยุดทำงานและทำลายหลักฐานที่จำเป็น มีทางเลือกที่นุ่มนวลกว่า 4 วิธี โดยควรลองทำตามลำดับดังนี้

ขั้นแรก ให้คัดลอกข้อมูลออกมาหากคุณยังต้องการใช้งาน การอ่านจาก path ของ descriptor คือการอ่านจาก inode ที่ยังทำงานอยู่

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

นี่เป็นกรณีเดียวที่ไฟล์ที่ถูกลบไปแล้วสามารถกู้คืนได้ง่าย ซึ่งเป็นเหตุผลว่าทำไม การกู้คืนไฟล์ที่ถูกลบด้วย rm -rf จึงเริ่มต้นด้วยการตรวจสอบว่ายังมี process ใดเปิดไฟล์นั้นค้างไว้อยู่หรือไม่ เมื่อ descriptor สุดท้ายถูกปิด เส้นทางนั้นจะหายไปทันที

ขั้นที่สอง ให้ล้างเนื้อหาไฟล์ผ่าน descriptor โดย path /proc จะชี้ไปยัง inode เดียวกัน ดังนั้นการสั่ง truncate จะเป็นการคืน block พื้นที่ว่างในขณะที่ process ยังคงทำงานอยู่

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

วิธีนี้จะได้ผลดีเมื่อโปรแกรมเขียนไฟล์ในโหมด append เพราะการเขียนทุกครั้งจะไปต่อท้ายไฟล์ปัจจุบันเสมอ หากไม่ใช่โหมดดังกล่าว process จะยังคงใช้ write offset เดิม ทำให้การเขียนครั้งถัดไปกระโดดไปไกลและสร้างไฟล์ใหม่ที่มีช่องว่าง (hole) อยู่ด้านหน้า ช่องว่างเหล่านี้ไม่ได้ถูกจองพื้นที่ไว้ ดังนั้น block จึงยังคงว่างอยู่และ df จะยังคงแสดงพื้นที่ที่เพิ่งคืนกลับมา สิ่งที่เพิ่มขึ้นมาคือขนาดไฟล์เท่านั้น หากคุณรัน sudo stat -L "/proc/$pid/fd/$n" อีกครั้งหลังจาก process เขียนข้อมูล มันจะรายงานขนาดเดิมควบคู่ไปกับจำนวน block ที่ไม่สัมพันธ์กัน หากต้องการให้ขนาดไฟล์เริ่มนับจากศูนย์ คุณต้องรีสตาร์ท process นั้น

ขั้นที่สาม ให้สั่งให้ service เปิดไฟล์ log ใหม่ daemon ที่ถูกลบไฟล์ log ทิ้งไปในขณะที่ยังทำงานอยู่เป็นปัญหาที่พบบ่อย daemon หลายตัวจะเปิดไฟล์ log ใหม่เมื่อได้รับสัญญาณ (signal) เช่น nginx ใช้ SIGUSR1 และ rsyslog ใช้ SIGHUP ให้ตรวจสอบเอกสารประกอบของ daemon นั้นๆ แทนการเดา เพราะการส่งสัญญาณผิดประเภทไปยัง daemon อาจทำให้มันหยุดทำงานได้

sudo systemctl kill -s USR1 nginx

คำสั่งดังกล่าวจะส่งสัญญาณไปยัง process ที่ systemd บันทึกไว้ว่าเป็น process หลักของ unit ดังนั้น unit ที่ระบุ Type= ไม่ถูกต้องตามวิธีการเริ่มทำงานจริงของ daemon อาจส่งสัญญาณไปยัง process ที่ไม่ได้ถือไฟล์ที่ถูกลบไว้ ทำให้พื้นที่ยังคงไม่ถูกคืนกลับมา

ขั้นที่สี่ ให้รีสตาร์ท unit นั้น sudo systemctl restart <unit> จะปิดทุก descriptor ที่ process เก่าถือครองไว้ ทำให้ block พื้นที่ว่างถูกคืนกลับมาอย่างแน่นอน สำหรับตัวอย่างข้างต้น ผู้ถือครองไฟล์คือ sleep ที่คุณเริ่มขึ้นเอง ดังนั้นการยุติการทำงานของมันจึงเพียงพอแล้ว

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

เปรียบเทียบจำนวน byte ที่ถูกใช้งานกับค่าที่คุณบันทึกไว้ก่อนสร้างไฟล์ ทั้งสองค่าจะกลับมาตรงกันอีกครั้ง และ find จะไม่แสดง descriptor ของคุณอีกต่อไป การตรวจสอบด้วยคำสั่งเดิมที่ใช้ค้นหาปัญหาเป็นนิสัยที่ดีที่ควรทำ

การเฝ้าดูค่าที่เปลี่ยนแปลงนั้นง่ายกว่าการรัน df ด้วยตนเองซ้ำๆ watch จะทำซ้ำคำสั่งตามช่วงเวลาที่กำหนด และแสดงผลลัพธ์ในตำแหน่งเดิม ดังนั้น watch df -h / จะแสดงให้เห็นว่าคอลัมน์ที่ระบุการใช้งานเปลี่ยนแปลงไปอย่างไรเมื่อพื้นที่ถูกคืนกลับมา

เมื่อผลรวมตรงกันแต่ดิสก์ยังคงเต็ม

หาก df และ root du -x แสดงผลตรงกัน แสดงว่าไม่มีไฟล์ที่ถูกลบตกค้างอยู่ สาเหตุที่เหลือจะมีลักษณะแตกต่างออกไปและต้องตรวจสอบแยกกันในแต่ละกรณี

Inode หมด แต่พื้นที่จัดเก็บยังเหลือ

Inode ทำหน้าที่เก็บข้อมูลเมตา (metadata) ของไฟล์หนึ่งไฟล์ ระบบไฟล์ ext4 จะกำหนดจำนวน inode ไว้คงที่ตั้งแต่ตอนสร้างระบบไฟล์ ดังนั้นระบบไฟล์อาจเกิดอาการ inode หมดในขณะที่ยังมีบล็อกว่างเหลืออยู่ ส่งผลให้ไม่สามารถสร้างไฟล์ใหม่ได้แม้ว่า df -h จะแสดงว่ายังมีพื้นที่ว่างเหลืออยู่ก็ตาม

df -h /
df -i /

คำสั่งแรกใช้สำหรับนับจำนวนบล็อก และคำสั่งที่สองใช้สำหรับนับจำนวน inode ให้เปรียบเทียบค่าในคอลัมน์การใช้งานของทั้งสองคำสั่ง หากการใช้งานบล็อกอยู่ในระดับต่ำแต่การใช้งาน inode เต็มขีดจำกัด แสดงว่าปัญหาเกิดจากการมีไฟล์ขนาดเล็กจำนวนมหาศาล

df ไม่รองรับการใช้ -i และ --output พร้อมกันในคำสั่งเดียว ดังนั้นหากคุณต้องการค่าจำนวนนับดิบเพื่อนำไปอ่านหรือส่งต่อให้คำสั่งอื่น ให้เลือกเฉพาะฟิลด์ inode โดยระบุชื่อ และไม่ต้องใส่ -i

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

คอลัมน์เหล่านั้นแสดงข้อมูลการนับแบบเดียวกับที่ df -i แสดงผล ซึ่งอยู่ในรูปแบบที่คุณสามารถนำไปประมวลผลต่อได้

ค้นหาไฟล์เหล่านั้นโดยการนับจำนวนรายการแทนการนับจำนวนไบต์

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

ทำซ้ำคำสั่งเดิมในระดับไดเรกทอรีที่อยู่ถัดลงไป โดยเลือกไดเรกทอรีที่พบไฟล์จำนวนมากที่สุด จนกว่าจะพบโครงสร้างต้นไม้ (tree) ที่เป็นแหล่งกำเนิดไฟล์เหล่านั้น หาก du ของคุณไม่รองรับ --inodes คุณสามารถใช้ sudo find /var -xdev -type f | wc -l เพื่อช่วยนับจำนวนไฟล์ในไดเรกทอรีย่อยได้แม้จะทำงานช้ากว่า

วิธีแก้ไขคือการลบหรือย้ายไฟล์เหล่านั้นออกไป คุณไม่สามารถเพิ่มจำนวน inode ให้กับระบบไฟล์ ext4 ที่มีอยู่เดิมได้ เนื่องจากจำนวนดังกล่าวถูกกำหนดไว้คงที่ตั้งแต่ตอน mkfs ดังนั้นการเพิ่มจำนวน inode จึงทำได้เพียงการสร้างระบบไฟล์ใหม่และกู้คืนข้อมูลจากสำรองเท่านั้น ในขณะที่ XFS จะจัดสรร inode ตามความจำเป็น จึงไม่พบข้อจำกัดในลักษณะเดียวกัน เครื่องที่รันคอนเทนเนอร์มักจะถึงขีดจำกัดทั้งสองอย่างเร็วกว่าเครื่องทั่วไป เนื่องจากเลเยอร์ของอิมเมจประกอบด้วยไฟล์ขนาดเล็กจำนวนมาก สำหรับเครื่องประเภทนี้ การล้างข้อมูลการใช้งานดิสก์ของ Docker บน VPS คือวิธีแก้ไขที่ตรงจุดที่สุด และจะช่วยคืนพื้นที่ได้มากกว่าการกวาดล้างไฟล์ทั่วไปในระบบไฟล์

พื้นที่ที่ถูกบดบังอยู่ใต้จุด mount

ไดเรกทอรีสามารถเก็บไฟล์ได้ก่อนที่จะมีการ mount อุปกรณ์ใดๆ ลงไป หากคุณ mount ระบบไฟล์ทับไดเรกทอรีนั้น ไฟล์ที่อยู่ข้างใต้จะยังคงอยู่ที่เดิม: ยังคงถูกจัดสรรพื้นที่ไว้ ยังคงถูกนับโดย df และไม่สามารถเข้าถึงได้ผ่านชื่อไฟล์อีกต่อไป du จะไม่สามารถมองเห็นไฟล์เหล่านั้นได้เนื่องจากจุด mount ได้บดบังไว้

สาธิตด้วย tmpfs ซึ่งไม่จำเป็นต้องใช้พื้นที่ดิสก์สำรอง ส่วนนี้จำเป็นต้องใช้เครื่องที่คุณมีสิทธิ์ในการ mount ดังนั้นจึงสามารถทำบน 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 ตรงกลางแสดงไดเรกทอรีที่ว่างเปล่า การคัดลอกไฟล์ไม่ได้หายไปไหน: มันยังคงอยู่บนระบบไฟล์ root และจะปรากฏกลับมาทันทีที่คุณ unmount ลองจินตนาการถึงบริการที่เขียน log ลงใน path นั้นมาเป็นเวลาหนึ่งเดือนก่อนที่จะมีคน mount volume ทับลงไป

ในการค้นหาไฟล์ที่แท้จริงบนเซิร์ฟเวอร์ที่กำลังทำงานอยู่ ให้ mount ระบบไฟล์ root เป็นครั้งที่สองในตำแหน่งอื่น การทำ bind mount จะแสดงระบบไฟล์หนึ่งโดยไม่รวมระบบไฟล์ที่ถูก 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

สิ่งใดก็ตามที่ปรากฏในรายการนั้นแต่ไม่ปรากฏภายใต้ path ปกติ แสดงว่าไฟล์เหล่านั้นถูกฝังอยู่ใต้จุด mount ให้ unmount bind mount เมื่อคุณดำเนินการเสร็จสิ้น มิฉะนั้นการเรียกใช้ du ในภายหลังโดยไม่มีแฟล็ก -x จะนับไฟล์ชุดเดียวกันซ้ำสองครั้ง

บล็อกที่สงวนไว้สำหรับ root

ระบบไฟล์ ext4 จะกันพื้นที่บล็อกจำนวนหนึ่งไว้สำหรับผู้ใช้ root เพื่อป้องกันไม่ให้ดิสก์เต็มจน root ไม่สามารถล็อกอินเข้าสู่ระบบเพื่อซ่อมแซมเครื่องได้ กระบวนการที่รันด้วยสิทธิ์ผู้ใช้ทั่วไปจะพบกับข้อจำกัดนี้ก่อน ในขณะที่ df ยังคงแสดงพื้นที่ว่างเหลืออยู่เล็กน้อย คุณควรตรวจสอบการตั้งค่าบนระบบไฟล์ของคุณเองแทนการคาดเดาจากค่าเริ่มต้น

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

คำสั่งดังกล่าวจะแสดงจำนวนบล็อกทั้งหมดและจำนวนบล็อกที่ถูกสงวนไว้ในหน่วยเดียวกัน ทำให้สามารถคำนวณอัตราส่วนระหว่างกันได้โดยตรง df จะรายงานคอลัมน์ available เป็นพื้นที่ที่ผู้ใช้ทั่วไปสามารถใช้งานได้จริง ซึ่งเป็นเหตุผลว่าทำไมผลรวมของพื้นที่ที่ใช้ไปกับพื้นที่ว่างจึงน้อยกว่าขนาดความจุรวม พื้นที่ส่วนต่างที่หายไปคือส่วนที่ถูกสงวนไว้

คุณสามารถปรับเปลี่ยนค่านี้ได้ด้วย sudo tune2fs -m <percent> "$dev" การเปลี่ยนแปลงจะมีผลทันทีโดยไม่จำเป็นต้อง remount ระบบไฟล์ การลดพื้นที่สำรองบนระบบไฟล์ข้อมูลแยกต่างหากถือเป็นเรื่องที่สมเหตุสมผล แต่สำหรับระบบไฟล์ root ควรเหลือพื้นที่ไว้เพียงพอเพื่อให้ root ยังคงเขียนข้อมูลได้ เนื่องจากระบบไฟล์ root ที่ไม่มีพื้นที่ว่างเหลือเลยจะซ่อมแซมได้ยากกว่ามาก นอกจากนี้ พื้นที่ส่วนนี้ยังเป็นปราการด่านสุดท้ายที่ป้องกันไม่ให้คุณถูกล็อกออกจากระบบ หากมีการเพิ่ม key ลงใน authorized_keys บนระบบไฟล์ที่ไม่มีพื้นที่เหลือ ข้อมูลอาจถูกเขียนไม่ครบหรือเขียนไม่ได้เลย และการล็อกอินครั้งถัดไปจะตอบกลับด้วย Permission denied (publickey) ซึ่งเป็นปัญหาที่ไม่ได้เกิดจากตัว key เอง tune2fs สามารถใช้งานได้กับ ext2, ext3 และ ext4 ส่วน XFS ไม่มีการตั้งค่าในลักษณะเดียวกันนี้

จุดที่ du อาจทำให้คุณเข้าใจผิด

นิสัย 4 ประการของ du ทำให้ผลรวมที่ได้ดูไม่ถูกต้อง

  • Hard links: du นับ inode เพียงครั้งเดียวแม้จะมีชื่อไฟล์หลายชื่อชี้ไปที่ inode เดียวกัน ดังนั้นโครงสร้างไดเรกทอรีที่มี hard links จำนวนมากจะรายงานขนาดรวมน้อยกว่าผลรวมขนาดไฟล์จริง
  • Sparse files: du รายงานเฉพาะบล็อกที่มีการจัดสรรจริง ในขณะที่ ls -l รายงานขนาดตามที่ระบุไว้ใน metadata ให้เพิ่มแฟล็ก --apparent-size เพื่อดูตัวเลขอีกค่าหนึ่ง
  • Permissions: หากรันในฐานะผู้ใช้ทั่วไป du จะข้ามไฟล์ที่ไม่สามารถอ่านได้และรายงานขนาดต่ำกว่าความเป็นจริง ข้อผิดพลาดที่แสดงออกมามักเป็นสิ่งที่ผู้ใช้มักจะ redirect ไปยัง /dev/null แล้วเลิกอ่านไปในที่สุด
  • Filesystem boundaries: หากไม่ใช้ -x ตัว du / จะนับทุก filesystem ที่ถูก mount อยู่ภายใต้ / ทำให้ผลรวมอาจมากกว่าที่ df / รายงาน

df มีนิสัยอีกอย่างที่ควรทราบเช่นกัน คือมันจะรายงานแต่ละ filesystem แยกกัน ดังนั้นควรเรียกใช้คำสั่งกับ path ที่เกิดปัญหาการเขียนไฟล์โดยตรง เพราะ /boot แยกต่างหากอาจเต็มได้ตามรอบการทำงานเมื่อมีการสะสมของ kernel packages และการ ลบ kernel เก่าบน Ubuntu เป็นงานที่แยกต่างหากจากการเคลียร์พื้นที่บน /

ลำดับขั้นตอนการทำงานเมื่อเกิดเหตุการณ์จริง

  1. เรียกใช้ df -h <path> และ df -i <path> บนระบบไฟล์ที่การเขียนข้อมูลล้มเหลว ไม่ใช่เรียกใช้บน / ตามความเคยชิน
  2. เรียกใช้ sudo du -xh -d 1 <mountpoint> 2>/dev/null | sort -h จากนั้นไล่ตรวจสอบลงไปในไดเรกทอรีที่มีขนาดใหญ่ที่สุด
  3. หาก du ไม่สามารถระบุที่มาของพื้นที่ที่ถูกใช้งานตามที่ df รายงาน ให้ค้นหาใน /proc เพื่อหาไฟล์ที่ถูกลบไปแล้วแต่ยังคงถูกเปิดใช้งานอยู่
  4. หากทั้งสองเครื่องมือแสดงผลตรงกัน ให้ทำ bind mount ระบบไฟล์นั้นไปยังตำแหน่งอื่น แล้วตรวจสอบหาไฟล์ที่อาจซ่อนอยู่ภายใต้ mount point
  5. หากปัญหาเกิดจากการใช้งาน inode จนเต็ม ให้ใช้วิธีนับจำนวนไฟล์แทนการนับขนาดเป็นไบต์

ทุกขั้นตอนข้างต้นคือคำสั่งที่คุณสามารถอ่านผลลัพธ์ได้ นี่คือความแตกต่างระหว่างการแก้ไขปัญหาอย่างตรงจุดกับการคาดเดาไปเรื่อยๆ

FAQ

ทำไม df ถึงแสดงว่าดิสก์เต็ม ทั้งที่ du พบข้อมูลน้อยกว่ามาก?

สาเหตุทั่วไปคือไฟล์ถูกลบไปแล้วในขณะที่ยังมี process เปิดใช้งานอยู่ การลบไฟล์จะทำให้ entry ใน directory หายไป ดังนั้น du จึงไม่มีชื่อไฟล์ให้ไล่ตรวจสอบและหยุดนับไฟล์นั้น แต่ inode และ block ของไฟล์จะยังคงถูกจองไว้จนกว่า descriptor ตัวสุดท้ายจะถูกปิด ซึ่ง df จะนับรวม block ที่ถูกจองไว้ทั้งหมด ให้ค้นหาใน /proc/<pid>/fd เพื่อหา symbolic link ที่ชี้ไปยังไฟล์ที่ถูกลบไปแล้ว คุณก็จะพบทั้งตัวไฟล์และ process ที่ถือครองไฟล์นั้นอยู่ ก่อนจะเชื่อผลการเปรียบเทียบ ให้ยืนยันว่าคุณได้รัน du ในฐานะ root และใช้แฟล็ก -x แล้ว เพราะผู้ใช้ทั่วไปจะข้าม directory ที่ไม่มีสิทธิ์อ่านไปโดยไม่แจ้งเตือน

ฉันจะหาไฟล์ที่ถูกลบแต่ยังเปิดใช้งานอยู่ได้อย่างไรโดยไม่ต้องใช้ lsof?

ให้ใช้บันทึกของ kernel เกี่ยวกับ open descriptor โดยตรง sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null จะแสดงรายการ descriptor ทุกตัวที่ชี้ไปยังไฟล์ที่ไม่มีชื่อ และ ID ของ process จะปรากฏอยู่ใน path ที่แสดงออกมา การใช้ sudo stat -Lc %s บน path ของ descriptor เหล่านั้นจะรายงานขนาดไฟล์ ทำให้คุณสามารถเรียงลำดับและเลือกไฟล์ที่ต้องการได้ วิธีนี้ไม่จำเป็นต้องติดตั้งแพ็กเกจใดๆ ซึ่งสำคัญมากในกรณีที่ระบบไฟล์ไม่มีพื้นที่ว่างเหลือจนไม่สามารถติดตั้งซอฟต์แวร์เพิ่มได้

ฉันสามารถคืนพื้นที่โดยไม่ต้องสั่ง kill process ได้หรือไม่?

ทำได้ในบางกรณี sudo truncate -s 0 /proc/<pid>/fd/<n> จะเข้าถึง inode เดียวกันผ่านทาง descriptor และปล่อย block ของไฟล์นั้นในขณะที่ process ยังคงทำงานอยู่ วิธีนี้สะอาดที่สุดเมื่อ process เปิดไฟล์ในโหมด append เพราะการเขียนข้อมูลจะไปต่อท้ายไฟล์เสมอ หากไม่ใช่เช่นนั้น write offset จะยังคงอยู่ที่เดิม และการเขียนครั้งถัดไปจะสร้างไฟล์ขึ้นมาใหม่โดยมีช่องว่าง (hole) อยู่ที่ส่วนหน้า ทำให้ขนาดที่รายงานกลับมาเท่าเดิมในขณะที่ block ใต้ช่องว่างนั้นยังคงว่างอยู่ การรีสตาร์ท unit หรือส่งสัญญาณให้ process เปิดไฟล์ log ใหม่ตามที่ระบุไว้ในเอกสารประกอบของซอฟต์แวร์นั้นเป็นวิธีแก้ไขที่ปลอดภัยและไม่ทิ้งไฟล์แบบ sparse ไว้

df แสดงว่ามีพื้นที่ว่าง แต่ทำไมยังเขียนข้อมูลไม่ได้?

ให้ตรวจสอบ inode ด้วย df -i บน path เดียวกัน เนื่องจากระบบไฟล์ที่มี block ว่างแต่ไม่มี inode เหลืออยู่จะไม่สามารถสร้างไฟล์ใหม่ได้ ให้ตรวจสอบว่าการเขียนข้อมูลนั้นรันโดยผู้ใช้ที่ไม่ใช่ root บนระบบไฟล์ ext4 ที่เหลือเพียง block สำรองหรือไม่ ซึ่ง sudo tune2fs -l บนอุปกรณ์นั้นจะแสดงให้เห็น นอกจากนี้ให้ตรวจสอบว่าคุณกำลังอ่านค่าระบบไฟล์ที่การเขียนข้อมูลนั้นพุ่งเป้าไปจริงๆ หรือไม่ เพราะ /boot หรือ /var ที่แยกออกมาจะมีพื้นที่เต็มแยกจาก /

ทำไม du ถึงรายงานยอดรวมมากกว่า df?

du หากไม่มีการใช้ -x จะข้ามไปยังทุกระบบไฟล์ที่ถูก mount อยู่ภายใต้ path ที่คุณระบุ ทำให้เป็นการรวมยอดจากหลายระบบไฟล์ ในขณะที่ df จะอธิบายเพียงระบบไฟล์เดียว การทำ bind mount จะทำให้ปัญหานี้แย่ลงเพราะไฟล์ชุดเดียวกันจะถูกนับซ้ำในทุก path ที่ปรากฏ ให้เพิ่ม -x เพื่อให้ du ทำงานอยู่บนระบบไฟล์เดียว และระบุ path เดียวกันให้กับ df เพื่อให้ทั้งสองคำสั่งอธิบายข้อมูลชุดเดียวกัน