Lỗi
Lỗi này thường xảy ra khi lưu lượng truy cập tăng đột biến: máy chủ Nginx của bạn ngừng chấp nhận kết nối, hoặc một microservice Java đột ngột bị treo. Nhật ký (logs) sẽ hiển thị một thông báo ngắn gọn: Too many open files. Điều này xảy ra vì tiến trình đã cố gắng mở nhiều tài nguyên hơn mức hệ điều hành cho phép. Linux coi hầu hết mọi tài nguyên I/O—bao gồm socket mạng, kết nối cơ sở dữ liệu và đường ống (pipes)—là một tệp tin. Mỗi tài nguyên này tiêu thụ một "File Descriptor" (FD).
Too many open files
Tại sao lỗi này xảy ra
Linux áp đặt các giới hạn an toàn để ngăn một tiến trình bị lỗi ngốn hết bộ nhớ hệ thống. Theo mặc định, nhiều bản phân phối thiết lập giới hạn chỉ 1.024 tệp cho mỗi tiến trình. Con số này là quá thấp đối với một máy chủ web hoặc cơ sở dữ liệu hiện đại. Có hai lớp giới hạn này:
- Soft Limit (Giới hạn mềm): Đây là giới hạn hiện tại được kernel thực thi. Một tiến trình có thể tự tăng giới hạn mềm của nó lên đến giá trị của giới hạn cứng.
- Hard Limit (Giới hạn cứng): Đóng vai trò như một mức trần vật lý. Chỉ người dùng có quyền root mới có thể tăng giới hạn cứng.
Bước 1: Kiểm tra các giới hạn hiện tại
Bắt đầu bằng cách xác định những gì hệ thống của bạn hiện đang cho phép. Chạy các lệnh này với tư cách là người dùng cụ thể đang chạy ứng dụng của bạn (ví dụ: www-data hoặc mysql):
# Xem giới hạn mềm
ulimit -Sn
# Xem giới hạn cứng
ulimit -Hn
Để xem số lượng tệp tối đa mà toàn bộ hệ điều hành có thể xử lý trên tất cả các tiến trình, hãy chạy:
cat /proc/sys/fs/file-max
Bước 2: Tăng giới hạn cho người dùng (Vĩnh viễn)
Nếu bạn chạy ứng dụng thủ công hoặc qua cron, bạn cần chỉnh sửa tệp /etc/security/limits.conf. Thay đổi này là vĩnh viễn và vẫn được duy trì sau khi khởi động lại hệ thống.
sudo nano /etc/security/limits.conf
Thêm các dòng sau vào cuối tệp. Chúng tôi khuyên dùng giá trị 65535 cho hầu hết các môi trường production. Thay thế * bằng tên người dùng cụ thể nếu bạn muốn giới hạn thay đổi này:
* soft nofile 65535
* hard nofile 65535
Quan trọng: Bạn phải đăng xuất và đăng nhập lại để các thay đổi này có hiệu lực cho phiên làm việc của người dùng.
Bước 3: Khắc phục cho các dịch vụ Systemd
Các tệp cấu hình tiêu chuẩn như limits.conf thường không áp dụng cho các dịch vụ được quản lý bởi systemd (như Nginx, MySQL hoặc các ứng dụng Node.js). Đối với những trường hợp này, bạn phải chỉ định giới hạn trong cấu hình riêng của dịch vụ.
Tạo một tệp ghi đè (override) cho dịch vụ cụ thể của bạn:
sudo systemctl edit myservice.service
Trong trình soạn thảo, hãy chèn các dòng sau:
[Service]
LimitNOFILE=65535
Lưu tệp và thoát. Bây giờ, yêu cầu systemd tải lại cấu hình và khởi động lại ứng dụng của bạn:
sudo systemctl daemon-reload
sudo systemctl restart myservice.service
Bước 4: Tăng giới hạn trên toàn hệ thống
Các máy chủ có độ tải cao xử lý hàng triệu yêu cầu có thể chạm tới giới hạn toàn cục của kernel. Nếu giá trị /proc/sys/fs/file-max thấp, bạn có thể tăng nó bằng cách chỉnh sửa /etc/sysctl.conf:
sudo nano /etc/sysctl.conf
Thêm hoặc cập nhật tham số sau để cho phép mở 2 triệu tệp:
fs.file-max = 2097152
Áp dụng các cài đặt kernel mới ngay lập tức mà không cần khởi động lại:
sudo sysctl -p
Cách xác minh kết quả
Đừng bao giờ mặc định rằng thay đổi cấu hình đã hoạt động mà không kiểm tra tiến trình đang chạy. Tìm Process ID (PID) của ứng dụng và kiểm tra các giới hạn thực tế khi thực thi của nó:
# Tìm PID (ví dụ: cho Nginx)
pgrep nginx
# Kiểm tra giới hạn cho PID cụ thể đó
cat /proc/<PID>/limits | grep "Max open files"
Kết quả đầu ra bây giờ sẽ phản ánh giới hạn mới của bạn (ví dụ: 65535).
Mẹo xử lý sự cố
Xác định rò rỉ File Descriptor
Nếu lỗi quay trở lại sau vài ngày mặc dù đã đặt giới hạn cao, mã nguồn của bạn có khả năng bị rò rỉ (leak). Điều này xảy ra khi một ứng dụng mở các kết nối cơ sở dữ liệu hoặc tệp tin nhưng quên đóng chúng. Sử dụng lsof để điều tra xem tiến trình đang giữ những gì:
# Đếm chính xác số lượng tệp mà một PID đang mở hiện tại
sudo lsof -p <PID> | wc -l
# Xem các tệp hoặc socket cụ thể đang được giữ
sudo lsof -p <PID>
Yêu cầu về PAM
Nếu các thay đổi trong limits.conf không hoạt động, hãy đảm bảo hệ thống của bạn thực sự đang tải mô-đun giới hạn. Kiểm tra /etc/pam.d/common-session và xác minh dòng này không bị vô hiệu hóa (commented out):
session required pam_limits.so

