Vấn Đề: Bạn Không Thể Theo Dõi Từng File Bằng Tay
Hình dung thế này: một deployment script đẩy file config vào /etc/app/, nhưng service không reload. Hoặc người dùng upload file lên /var/uploads/ và không có gì xảy ra cho đến khi ai đó thủ công chạy job xử lý. Bạn phải chọn giữa hai lựa chọn tệ: dùng cron job polling mỗi phút — vừa lãng phí tài nguyên vừa chậm trễ — hoặc tự viết code daemon phức tạp từ đầu.
Sau hơn ba năm quản lý 10+ VPS Linux, tôi nhận ra rằng automation phản ứng theo sự kiện — hành động ngay lúc file thay đổi — đáng tin cậy hơn nhiều so với polling theo lịch. Kernel Linux đã theo dõi mọi sự kiện filesystem sẵn rồi. Bạn chỉ cần công cụ phù hợp để khai thác nó.
Đó là lúc inotify, inotifywait, và incron phát huy tác dụng. Phản ứng với sự kiện file ngay lập tức, không cần viết code daemon.
Khái Niệm Cốt Lõi: Cách Linux Theo Dõi Sự Kiện File
Kernel Subsystem inotify
Ra đời từ Linux kernel 2.6.13, inotify (inode notify) cho phép các tiến trình theo dõi file và thư mục để phát hiện các sự kiện cụ thể — không polling, không timer. Kernel đẩy thông báo ngay khi có thay đổi.
Các sự kiện được hỗ trợ bao gồm:
IN_CREATE— một file hoặc thư mục được tạoIN_MODIFY— một file được ghi vàoIN_DELETE— một file hoặc thư mục bị xóaIN_MOVED_FROM/IN_MOVED_TO— file bị đổi tên hoặc di chuyểnIN_CLOSE_WRITE— file mở để ghi đã được đóng lại (rất hữu ích để phát hiện upload hoàn tất)IN_ATTRIB— metadata thay đổi (quyền, ownership, timestamp)
inotifywait: Truy Cập inotify Từ Command Line
inotifywait là một phần của gói inotify-tools. Nó chặn cho đến khi có sự kiện xảy ra, in ra thông tin sự kiện rồi thoát — hoặc tiếp tục lặp nếu dùng flag -m. Kết hợp với vòng lặp while, bạn có ngay một file watcher đơn giản, dễ dùng trong script.
incron: inotify + cron = incron
Hãy nghĩ incron như cron, nhưng được kích hoạt bởi sự kiện filesystem thay vì thời gian. Bạn định nghĩa các rule trong file incrontab, và incrond tự động chạy lệnh khi sự kiện xảy ra. Không cần vòng lặp shell, không cần khởi động lại thủ công — nó chạy như một daemon nền.
Thực Hành
Bước 1: Cài Đặt Công Cụ
Trên Debian/Ubuntu:
sudo apt update
sudo apt install inotify-tools incron
Trên RHEL/AlmaLinux/Rocky:
sudo dnf install inotify-tools incron
Bước 2: Kiểm Tra Nhanh với inotifywait
Trước khi thiết lập bất kỳ automation nào, hãy luôn xác minh sự kiện thủ công. Mở hai terminal.
Terminal 1 — bắt đầu theo dõi:
inotifywait -m -r -e create,modify,delete /tmp/watch-test/
Terminal 2 — kích hoạt một số sự kiện:
mkdir -p /tmp/watch-test
touch /tmp/watch-test/hello.txt
echo "data" >> /tmp/watch-test/hello.txt
rm /tmp/watch-test/hello.txt
Bạn sẽ thấy output như sau:
/tmp/watch-test/ CREATE hello.txt
/tmp/watch-test/ MODIFY hello.txt
/tmp/watch-test/ DELETE hello.txt
Điều này xác nhận kernel đang báo cáo sự kiện đúng cho thư mục đó. Tôi từng bị dính bẫy khi một symlink trỏ đến nơi inotify thực sự không theo dõi — bước kiểm tra nhanh này giúp phát hiện vấn đề trước khi gây lỗi trên môi trường production.
Bước 3: Viết Shell Loop Watcher với inotifywait
Với automation nhẹ, dùng một lần, shell script là cách nhanh nhất:
#!/bin/bash
# watch-uploads.sh — xử lý file ngay khi chúng được upload lên
WATCH_DIR="/var/uploads"
PROCESS_SCRIPT="/opt/scripts/process-file.sh"
inotifywait -m -e close_write --format '%f' "$WATCH_DIR" | while read FILENAME; do
echo "[$(date)] Phát hiện: $FILENAME"
"$PROCESS_SCRIPT" "$WATCH_DIR/$FILENAME"
done
Mẹo quan trọng: dùng close_write thay vì modify cho file upload. Sự kiện modify kích hoạt mỗi lần có syscall ghi — copy file 500MB sẽ tạo ra hàng trăm sự kiện giữa chừng. close_write chỉ kích hoạt đúng một lần khi file handle được đóng sau khi ghi xong. Sạch sẽ hơn nhiều.
Chạy script này như một systemd service để nó tồn tại sau khi reboot:
# /etc/systemd/system/watch-uploads.service
[Unit]
Description=Theo dõi /var/uploads để phát hiện file mới
After=network.target
[Service]
ExecStart=/opt/scripts/watch-uploads.sh
Restart=on-failure
User=www-data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now watch-uploads
Bước 4: Thiết Lập incron cho Các File Trigger Cấp Hệ Thống
incron tỏa sáng với các rule cố định được quản lý bởi daemon — reload config, kích hoạt log rotation, hoặc môi trường nhiều user khi mỗi người quản lý incrontab riêng.
Đầu tiên, cho phép user của bạn (hoặc root) sử dụng incron:
echo "root" | sudo tee -a /etc/incron.allow
Chỉnh sửa incrontab cho root:
sudo incrontab -e
Cú pháp: <đường_dẫn> <sự_kiện> <lệnh>
# Reload nginx khi config thay đổi
/etc/nginx/conf.d IN_CLOSE_WRITE systemctl reload nginx
# Nén file log mới được đưa vào /var/log/archive/
/var/log/archive IN_CREATE gzip $@/$#
# Cảnh báo khi file bị xóa khỏi thư mục nhạy cảm
/etc/cron.d IN_DELETE /opt/scripts/alert-cron-delete.sh $#
Các biến đặc biệt của incron:
$@— đường dẫn thư mục đang theo dõi$#— tên file đã kích hoạt sự kiện$$— ký tự$thực sự$%— các flag sự kiện dưới dạng chuỗi
Kích hoạt và khởi động incrond:
sudo systemctl enable --now incrond
sudo systemctl status incrond
Bước 5: Theo Dõi Đệ Quy và Lọc Sự Kiện
Một lưu ý phổ biến: incron mặc định không theo dõi các thư mục con. Để theo dõi đệ quy, inotifywait -r là lựa chọn tốt hơn. Với incron, bạn phải thêm rule cho từng thư mục con hoặc viết wrapper tự động đăng ký watch mới.
Lọc theo tên file trong inotifywait rất dễ với grep:
inotifywait -m -e close_write --format '%f' /var/uploads | \
grep --line-buffered '\.csv$' | \
while read FILENAME; do
/opt/scripts/import-csv.sh "/var/uploads/$FILENAME"
done
Đừng quên --line-buffered cho grep. Nếu không có nó, grep sẽ buffer output thành từng khối và script của bạn sẽ có vẻ bị treo. Lỗi này hầu như ai cũng mắc phải lần đầu khi pipe output của inotifywait qua grep.
Bước 6: Debounce Các Sự Kiện Liên Tiếp
Một số editor và công cụ ghi file theo nhiều bước — ghi file tạm, đổi tên, xóa bản gốc. Một lần lưu logic đơn giản có thể tạo ra một loạt sự kiện. Đây là cách debounce đơn giản bằng bash:
#!/bin/bash
WATCH_DIR="/etc/app"
DEBOUNCE=2 # số giây chờ sau sự kiện cuối cùng
inotifywait -m -e close_write,moved_to --format '%f' "$WATCH_DIR" | \
while read FILENAME; do
# Hủy lần reload đang chờ nếu có
[ -n "$DEBOUNCE_PID" ] && kill "$DEBOUNCE_PID" 2>/dev/null
(
sleep $DEBOUNCE
echo "[$(date)] Đang reload do thay đổi: $FILENAME"
systemctl reload myapp
) &
DEBOUNCE_PID=$!
done
Script này chờ 2 giây sau sự kiện cuối cùng trước khi kích hoạt hành động. Trong quá trình rolling deployment ghi đè config file 4–5 lần liên tiếp, bạn chỉ nhận được đúng một lần reload thay vì năm lần.
Mẹo Thực Tế: Kiểm Tra Giới Hạn inotify Watch
Mỗi inotify watch tiêu thụ một tài nguyên kernel. Giới hạn mặc định là 8.192 watch mỗi user. Trên server có nhiều thư mục được theo dõi, bạn có thể chạm ngưỡng đó rất nhanh:
# Kiểm tra giới hạn hiện tại
cat /proc/sys/fs/inotify/max_user_watches
# Tăng tạm thời
sudo sysctl fs.inotify.max_user_watches=524288
# Thiết lập vĩnh viễn
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.d/99-inotify.conf
sudo sysctl -p /etc/sysctl.d/99-inotify.conf
Giới hạn này rất dễ bị đánh giá thấp. Trên một VPS chạy cả ứng dụng Node.js (vốn dùng inotify nhiều cho hot-reload) lẫn các rule incron, chúng tôi đã âm thầm chạm ngưỡng. incron ngừng kích hoạt mà không có bất kỳ output lỗi nào — chỉ im lặng hoàn toàn. Chạy dmesg | grep inotify đã lộ ra nguyên nhân ngay lập tức. Hãy luôn kiểm tra điều này trước khi deploy lên production.
Khi Nào Dùng Công Cụ Nào
- inotifywait trong vòng lặp: Script nhanh, automation trong quá trình phát triển, watcher chuyên dụng. Dễ kiểm tra tương tác.
- incron: Các rule cấp hệ thống muốn quản lý như daemon. Lý tưởng cho các tác vụ vận hành như tự động reload service hoặc nén log. Toàn bộ rule nằm trong một incrontab dễ kiểm tra.
- Thư viện watchdog của Python: Theo dõi file đa nền tảng bên trong ứng dụng Python. Nó bọc inotify với API sạch sẽ và xử lý sự khác biệt giữa các nền tảng một cách trong suốt.
Đưa inotify Vào Thực Tế
Chuyển từ cron polling mỗi phút sang theo dõi file theo sự kiện giúp giảm độ trễ từ tối đa 60 giây xuống còn mili giây. Nó cũng loại bỏ các lần CPU thức dậy không cần thiết — tiến trình ngủ cho đến khi kernel báo hiệu.
Bắt đầu nhỏ. Chọn một thư mục, theo dõi nó với inotifywait -m trong một ngày và quan sát các sự kiện. Bạn sẽ nhanh chóng nhận ra cơ hội automation — các file config cần reload, upload đang chờ job xử lý, các đường dẫn nhạy cảm không được thay đổi âm thầm.
Kết hợp inotifywait cho scripting và incron cho các rule quản lý bởi daemon, bạn có thể xử lý 90% nhu cầu giám sát file trong thực tế mà không cần viết một dòng C hay triển khai monitoring agent nặng nề.

