Khi quản trị một máy chủ Linux, một trong những tình huống gây bối rối nhất cho người vận hành là website bỗng nhiên sập, cơ sở dữ liệu từ chối ghi dữ liệu mới hoặc lệnh tạo file trả về lỗi dứt khoát: No space left on device. Tuy nhiên, khi bạn đăng nhập vào kiểm tra bằng lệnh df -h, dung lượng ổ cứng vẫn hiển thị còn trống từ vài chục đến hàng trăm GB, tỷ lệ sử dụng chỉ mới 30% đến 40%.
Hiện tượng này không phải lỗi hiển thị của hệ điều hành. Trên các hệ thống tệp tin Linux như ext4 hay xfs, tài nguyên lưu trữ được chia làm hai thành phần quản lý hoàn toàn độc lập: không gian khối dữ liệu (Data Blocks) dùng để chứa nội dung của file, và bảng chỉ mục (Inode Table) dùng để lưu trữ thông tin siêu dữ liệu (metadata).
Dù phân vùng đĩa của bạn còn hàng trăm GB dung lượng trống, nhưng nếu số lượng file nhỏ tăng vọt đến mức vét cạn toàn bộ bảng chỉ mục inode, hệ thống sẽ lập tức từ chối tạo thêm bất kỳ tệp tin, thư mục hay socket mới nào. (Lưu ý: Nếu đĩa cứng báo đầy nhưng sau khi kiểm tra inode không thấy cạn kiệt, bạn có thể đang gặp tình huống xóa file nhưng dung lượng đĩa không giảm do tiến trình còn giữ file descriptor).
Kiểm tra song song dung lượng khối (df -h) và bảng chỉ mục (df -i) để xác định đúng nguyên nhân gốc rễ.
Inode trên Linux hoạt động như thế nào?
Inode (Index Node) là một cấu trúc dữ liệu trên đĩa cứng lưu trữ toàn bộ thông tin về một tệp tin hoặc thư mục theo đặc tả cấu trúc tệp của Linux kernel inode manual, ngoại trừ tên file và nội dung dữ liệu thực tế. Mỗi inode lưu giữ:
- Loại tệp (file thông thường, thư mục, symbolic link, socket, pipe).
- Quyền hạn truy cập (permissions) và chủ sở hữu (UID, GID).
- Kích thước tệp tin.
- Các mốc thời gian: thời điểm truy cập (atime), chỉnh sửa (mtime) và thay đổi trạng thái (ctime).
- Danh sách các con trỏ trỏ tới các khối dữ liệu (data blocks) thực tế trên ổ đĩa.
Khi một phân vùng ext4 được định dạng (format), tổng số lượng inode được cấp phát cố định dựa trên dung lượng phân vùng và không thể tự động co giãn trong quá trình hoạt động.
Điều này dẫn đến quy tắc cốt lõi: Bất kể một file có dung lượng 0 byte, vài byte hay 10GB, nó đều tiêu tốn chính xác 1 inode. Nếu ứng dụng của bạn tạo ra lượng lớn file siêu nhỏ (chẳng hạn 2 đến 3 triệu file session hoặc cache, mỗi file chỉ vài chục byte), dung lượng đĩa tiêu tốn chỉ vài chục megabyte nhưng sẽ vét cạn sạch toàn bộ inode của phân vùng.
Bước 1: Xác nhận tình trạng cạn kiệt Inode
Khi gặp lỗi không thể ghi file, bước đầu tiên là chạy song song hai lệnh kiểm tra tài nguyên lưu trữ:
# Kiểm tra dung lượng khối (tính theo MB, GB)
df -h /
# Kiểm tra số lượng inode (tính theo số lượng tệp)
df -i /Quan sát đầu ra của lệnh df -i:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 3276800 3276800 0 100% /Nếu cột IUse% hiển thị 100% và IFree bằng 0, bạn có thể kết luận chắc chắn hệ thống đang tê liệt vì hết inode.
Mọi tiến trình trên máy chủ lúc này sẽ bắt đầu đổ vỡ theo dây chuyền:
- Nginx không thể ghi access log/error log hoặc tạo tệp đệm proxy, trả về mã lỗi 502/500 cho người dùng; xem thêm cách khoanh vùng tại bài Lỗi 502 Bad Gateway: kiểm tra Nginx và upstream.
- PHP không thể khởi tạo tệp phiên làm việc (session), khiến người dùng bị đẩy văng khỏi trang đăng nhập.
- MySQL hoặc PostgreSQL không thể tạo tệp tạm (temporary file) khi thực thi câu truy vấn, dẫn đến crash dịch vụ.
- Bạn không thể dùng phím
Tabđể tự động hoàn thành lệnh trong Bash vì hệ thống không thể tạo tệp tạm trong/tmp.
Nếu sự cố đang làm gián đoạn toàn bộ website trên môi trường production, hãy giữ bình tĩnh và ghi lại các thông số trước khi can thiệp, tuân thủ nguyên tắc khoanh vùng tại Quy trình 30 phút đầu khi website production gặp sự cố.
Bước 2: Khoanh vùng thư mục đang chiếm dụng nhiều Inode nhất
Một sai lầm rất phổ biến là dùng lệnh du -sh * để tìm kiếm. Lệnh du tính toán dung lượng theo byte; thư mục chứa 2 triệu file rỗng 0 byte sẽ chỉ báo dung lượng vài megabyte và hoàn toàn bị bạn bỏ qua.
Để tìm ra thư mục đang giữ lượng inode khổng lồ, bạn cần duyệt qua các cây thư mục và đếm số lượng tệp tin.
Lệnh quét nhanh các thư mục nghi vấn cấp 1
Chạy lệnh kiểm tra số lượng file tại các phân vùng chính:
for dir in /var /var/lib /var/spool /var/log /tmp /home; do
if [ -d "$dir" ]; then
echo -n "$dir: "
sudo find "$dir" -xdev -type f 2>/dev/null | wc -l
fi
doneLệnh truy tìm top 10 thư mục chứa nhiều file nhất
Nếu muốn đào sâu vào thư mục con cụ thể, dùng lệnh sau tại thư mục nghi vấn (ví dụ trong /var):
sudo find /var -xdev -type d -exec bash -c 'echo -e "$(find "$1" -maxdepth 1 -type f 2>/dev/null | wc -l)\t$1"' _ {} \; | sort -rn | head -n 10Lệnh trên sẽ in ra danh sách 10 thư mục có số lượng file ở cấp trực tiếp lớn nhất kèm đường dẫn cụ thể.
4 thủ phạm phổ biến nhất làm cạn Inode trên VPS
Dưới đây là 4 nguồn phát sinh lượng lớn file nhỏ thường gặp nhất trong thực tế vận hành máy chủ Linux:
1. File session của PHP tích tụ lâu ngày
Đây là thủ phạm đứng đầu trên các VPS chạy PHP (WordPress, Laravel, WooCommerce, Magento), thường tích tụ tại thư mục /var/lib/php/sessions.
Theo mặc định, PHP-FPM trên nhiều bản phân phối Linux thiết lập session.gc_probability = 0 trong file php.ini để tránh việc tiến trình xử lý request của người dùng phải kiêm luôn việc dọn rác (Garbage Collection). Thay vào đó, hệ thống dựa vào một cronjob định kỳ của hệ điều hành (thường nằm tại /etc/cron.d/php) để xóa các session cũ.
Nếu cronjob này vô tình bị vô hiệu hóa, dịch vụ cron bị dừng, hoặc ứng dụng sử dụng một thư mục session riêng mà không có tiến trình dọn dẹp đi kèm, mỗi lượt truy cập từ người dùng và bot quét sẽ tạo ra một file sess_*. Sau 3 đến 6 tháng, con số này có thể đạt 3 triệu đến 5 triệu file nhỏ, chiếm trọn toàn bộ inode của VPS.
2. Email thông báo cronjob bị nghẽn trong hàng đợi Postfix
Khi bạn cài đặt cronjob trên máy chủ, theo mặc định mọi kết quả đầu ra (stdout và stderr) của lệnh cron đều được hệ thống chuyển tiếp cho dịch vụ bưu chính nội bộ (sendmail hoặc postfix) để gửi email báo cáo cho tài khoản root.
Nếu VPS chưa được cấu hình SMTP relay ra ngoài Internet hoặc dịch vụ Postfix gặp lỗi, các email thông báo này sẽ không thể gửi đi và bị dồn ứ lại thành từng file nhỏ trong /var/spool/postfix/maildrop, /var/spool/postfix/deferred hoặc /var/mail/root. Một lệnh cron chạy mỗi phút tạo ra 1.440 file mỗi ngày; sau vài tháng, hàng đợi này sẽ chứa hàng trăm nghìn tệp tin bị nghẽn.
3. Docker build cache và anonymous volumes rác
Trên các máy chủ triển khai ứng dụng bằng container, các lệnh build lặp đi lặp lại hoặc container tạo file tạm thời không được giải phóng có thể tích tụ lượng lớn inode trong /var/lib/docker/overlay2/.
Các lớp layer cũ, tệp tin sinh ra trong quá trình npm install hoặc composer install khi build image nếu không được dọn dẹp qua lifecycle policy sẽ âm thầm vét cạn bảng inode của hệ thống; xem thêm hướng dẫn kiểm soát tại bài Giới hạn và xoay vòng log Docker để tránh đầy ổ đĩa.
4. Thư mục cache file của ứng dụng (Framework Cache)
Nhiều framework lưu cache dạng file chia nhỏ thành cây thư mục nhiều cấp (ví dụ Laravel lưu tại storage/framework/cache/data/ với hàng chục nghìn thư mục con dạng /0a/1b/0a1b2c...). Nếu ứng dụng không có cơ chế invalidate cache hoặc cronjob dọn cache tự động bị lỗi, lượng file cache này sẽ tăng liên tục theo thời gian.
Bước 3: Quy trình xóa file an toàn không gây nghẽn I/O
Sau khi xác định được thư mục đang chiếm phần lớn inode, một thao tác quen thuộc nhưng tiềm ẩn rủi ro lớn là dùng lệnh xóa hàng loạt bằng ký tự đại diện:
# LỆNH NGUY HIỂM - TUYỆT ĐỐI KHÔNG CHẠY
cd /var/lib/php/sessions
sudo rm -rf *Dưới đây là 3 phương pháp xóa an toàn đã được kiểm chứng thực tế:
Cách 1: Giải phóng khẩn cấp bằng kỹ thuật Move & Recreate (Khuyên dùng khi website đang sập)
Nếu website đang gián đoạn và bạn cần khôi phục dịch vụ trong vòng 30 giây mà không thể chờ lệnh xóa chạy xong:
# 1. Đổi tên thư mục chiếm nhiều inode sang tên tạm
sudo mv /var/lib/php/sessions /var/lib/php/sessions_old
# 2. Tạo ngay thư mục mới với quyền hạn chính xác
sudo mkdir -m 1733 /var/lib/php/sessions
sudo chown root:root /var/lib/php/sessions
# 3. Reload lại dịch vụ PHP-FPM để website lập tức có inode hoạt động trở lại
sudo systemctl reload php8.2-fpmSau bước này, website của bạn đã có thư mục session mới hoàn toàn sạch sẽ, hệ thống đã có đủ inode để phục hồi kinh doanh. Lúc này bạn có thể thong thả dọn dẹp thư mục sessions_old ở chế độ nền mà không làm ảnh hưởng tới người dùng.
Cách 2: Xóa cuốn chiếu bằng lệnh find kết hợp delete
Lệnh find đọc từng inode trực tiếp từ hệ thống tệp và xóa cuốn chiếu mà không cần nạp toàn bộ danh sách file vào bộ nhớ:
# Xóa toàn bộ file session cũ hơn 7 ngày
sudo find /var/lib/php/sessions_old -type f -name 'sess_*' -mtime +7 -delete
# Hoặc xóa toàn bộ file bên trong thư mục cũ
sudo find /var/lib/php/sessions_old -type f -deleteSau khi toàn bộ file con đã bị xóa sạch, bạn xóa nốt thư mục rỗng:
sudo rmdir /var/lib/php/sessions_oldCách 3: Điều tiết tốc độ I/O khi xóa trên môi trường production
Nếu bắt buộc phải xóa trực tiếp trên thư mục đang hoạt động mà không muốn gây lag cho database, hãy giới hạn mức ưu tiên I/O của lệnh xóa về mức thấp nhất (Idle I/O priority):
# ionice -c 3 bảo đảm lệnh xóa chỉ tận dụng I/O khi đĩa cứng rảnh rỗi
sudo ionice -c 3 find /var/spool/postfix/maildrop -type f -print0 | xargs -0 -n 1000 rm -fTham số -n 1000 chia nhỏ danh sách thành từng cụm 1.000 file cho mỗi lần thực thi rm, vừa triệt tiêu hoàn toàn nguy cơ lỗi Argument list too long, vừa không làm nghẽn hàng đợi ghi đĩa của máy chủ.
Bước 4: Giải quyết nguyên nhân gốc rễ và phòng ngừa tái phát
Giải phóng thư mục chỉ là biện pháp khắc phục tức thời. Nếu không xử lý tận gốc, bảng inode sẽ lại đầy tràn sau một vài tuần hoặc vài tháng.
1. Sửa cơ chế dọn dẹp session của PHP
Kiểm tra xem cronjob dọn session của hệ thống có đang tồn tại và hoạt động hay không:
# Kiểm tra file cron dọn session của Debian/Ubuntu
cat /etc/cron.d/php
# Kiểm tra service systemd timer dọn session (trên các bản Ubuntu mới)
systemctl status phpsessionclean.timerNếu ứng dụng PHP của bạn có lượng truy cập lớn, giải pháp căn cơ nhất là không lưu session trên đĩa cứng. Hãy cấu hình chuyển toàn bộ PHP session sang Redis hoặc Memcached trong file php.ini (theo khuyến nghị cấu hình tại PHP Manual - Session Configuration):
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"Khi lưu trên Redis, mỗi session được quản lý trực tiếp trên RAM kèm thời gian sống (TTL) tự động hết hạn, loại bỏ hoàn toàn nguy cơ cạn kiệt inode do session trên đĩa cứng.
2. Chặn cronjob sinh email rác nội bộ
Mở crontab của root và các tài khoản dịch vụ:
sudo crontab -eThêm biến MAILTO="" vào ngay dòng đầu tiên của file crontab để yêu cầu hệ thống không gửi email khi cronjob kết thúc:
MAILTO=""
# Các dòng cronjob bên dưới...
*/5 * * * * /usr/local/bin/backup-sync.sh > /dev/null 2>&1Với các lệnh cron riêng lẻ, luôn điều hướng toàn bộ output vào /dev/null bằng cú pháp > /dev/null 2>&1.
Xóa sạch hàng đợi mail Postfix đang bị nghẽn:
# Xóa toàn bộ email đang nghẽn trong hàng đợi
sudo postsuper -d ALL3. Dọn dẹp Docker container và dangling resources
Thực hiện dọn dẹp các image không dùng và build cache định kỳ theo kế hoạch có kiểm soát:
# Dọn dẹp build cache không còn liên kết
docker builder prune -f
# Dọn dẹp các anonymous volume không gắn với container nào
docker volume prune -fThiết lập giám sát cảnh báo Inode trước khi chạm trần
Đa số các công cụ giám sát mặc định chỉ cảnh báo khi dung lượng khối (df -h) vượt ngưỡng 80% hoặc 90% mà bỏ quên chỉ số inode. Bạn cần thiết lập cảnh báo sớm cho cả hai khía cạnh lưu trữ.
Nếu bạn đang sử dụng Node Exporter, Telegraf hoặc Datadog, hãy thiết lập ngưỡng cảnh báo cụ thể:
- Ngưỡng cảnh báo (Warning): Khi
node_filesystem_files_free / node_filesystem_files * 100 < 25(tức inode đã dùng trên 75%). Ngưỡng này giúp đội ngũ vận hành có từ 1 đến 2 tuần để lên lịch kiểm tra trong giờ hành chính. - Ngưỡng khẩn cấp (Critical): Khi tỷ lệ inode sử dụng vượt quá 90%.
Tham khảo thêm nguyên tắc đặt ngưỡng cảnh báo không gây nhiễu tại bài Ngưỡng cảnh báo CPU, RAM và Disk: Thiết lập sao cho thực dụng? và tổng quan chiến lược giám sát tại bài Monitoring VPS chạy website: Cần theo dõi những gì?.
Định kỳ hằng tháng, việc kiểm tra tỷ lệ df -i cần được đưa vào danh mục rà soát thường xuyên; xem chi tiết tại Quản trị VPS gồm những việc gì trong một tháng vận hành? và VPS đầy ổ đĩa: nguyên nhân thường gặp và quy trình xử lý an toàn.



