Chuyện gì đã xảy ra
Bạn đang chạy một script, khởi động dịch vụ, hoặc SSH vào máy chủ — và đột nhiên mọi thứ đứng hình với thông báo này:
bash: fork: retry: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
Không thể khởi tạo tiến trình mới. Kết nối SSH bị timeout. Cron job âm thầm thất bại. RAM ổn, CPU đang rảnh, máy chủ rõ ràng vẫn chạy — nhưng không có gì fork được. Bạn đã chạm ngưỡng giới hạn max user processes (nproc).
Linux theo dõi số tiến trình mà mỗi người dùng sở hữu tại bất kỳ thời điểm nào. Chạm trần đó thì fork() trả về EAGAIN — đó chính xác là những gì bạn đang thấy.
Chẩn đoán trước
Kiểm tra giới hạn hiện tại
# Xem giới hạn cho phiên shell hiện tại
ulimit -a
# Cụ thể là max user processes
ulimit -u
Trên các hệ thống cũ, giá trị mặc định thấp đến đáng thương:
max user processes (-u) 1024
Các bản phân phối systemd hiện đại thường có giá trị mặc định cao hơn (63503 trên Ubuntu 22.04 chẳng hạn), nhưng các dịch vụ chạy dưới user riêng của chúng thường kế thừa giới hạn PAM thấp hơn nhiều — đôi khi vẫn là 1024.
Đếm số tiến trình mà user bị ảnh hưởng đang chạy
# Thay 'www-data' bằng user thực tế
ps -u www-data --no-header | wc -l
# Hoặc xem tất cả user được sắp xếp theo số tiến trình
ps aux | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Nếu con số đó bằng hoặc gần với giá trị ulimit, bạn đã xác nhận được vấn đề.
Kiểm tra giới hạn được cấu hình qua PAM (trần thực sự)
grep -r nproc /etc/security/limits.conf /etc/security/limits.d/
Bạn có thể thấy nội dung như sau:
* soft nproc 1024
* hard nproc 4096
Giới hạn soft là giá trị các tiến trình bắt đầu với. Giới hạn hard là mức tối đa mà một tiến trình có thể tự nâng lên. Cả hai đều thấp chính là thủ phạm của bạn.
Kiểm tra những gì systemd áp đặt (cho các dịch vụ)
Đây là một điểm bẫy: nếu tiến trình bị ảnh hưởng là một dịch vụ systemd, /etc/security/limits.conf thường không áp dụng — systemd quản lý giới hạn của riêng nó một cách độc lập.
# Kiểm tra giới hạn thực tế của một dịch vụ đang chạy (ví dụ nginx)
systemctl show nginx | grep -i task
# Hoặc kiểm tra /proc với một PID cụ thể
cat /proc/$(pgrep -o nginx)/limits | grep processes
Cách khắc phục
Tùy chọn 1: Tăng giới hạn cho phiên hiện tại (tạm thời)
Phù hợp để kiểm tra nhanh hoặc chạy script một lần. Nó sẽ đặt lại ngay khi shell đóng.
# Tăng soft limit lên 8192
ulimit -u 8192
# Kiểm tra lại
ulimit -u
Bạn chỉ có thể tăng đến giới hạn hard. Tăng giới hạn hard yêu cầu quyền root.
Tùy chọn 2: Đặt giới hạn vĩnh viễn qua PAM (cho login user và daemon)
Thêm file mới trong /etc/security/limits.d/ — cách này gọn hơn là chỉnh sửa trực tiếp limits.conf:
sudo nano /etc/security/limits.d/99-nproc.conf
Thêm các dòng sau (điều chỉnh giá trị theo khối lượng công việc thực tế của bạn):
# Tăng nproc cho tất cả user
* soft nproc 65536
* hard nproc 65536
# Hoặc chỉ định một user cụ thể
www-data soft nproc 32768
www-data hard nproc 32768
# root thường bị loại khỏi ký tự đại diện * — đặt rõ ràng nếu cần
root soft nproc unlimited
root hard nproc unlimited
Lưu ý quan trọng: Giới hạn PAM chỉ áp dụng cho các phiên đăng nhập mới. Các tiến trình hiện có vẫn giữ giới hạn cũ. Khởi động lại dịch vụ hoặc đăng xuất rồi đăng nhập lại để áp dụng thay đổi.
Tùy chọn 3: Sửa giới hạn cho dịch vụ systemd
Giới hạn PAM không áp dụng cho các dịch vụ được khởi động bởi systemd. Ghi đè theo từng dịch vụ bằng drop-in file:
sudo systemctl edit nginx
Thêm vào:
[Service]
TasksMax=infinity
LimitNPROC=65536
Sau đó reload và restart:
sudo systemctl daemon-reload
sudo systemctl restart nginx
Để tăng giá trị mặc định toàn cục cho tất cả dịch vụ systemd, chỉnh sửa /etc/systemd/system.conf:
sudo nano /etc/systemd/system.conf
[Manager]
DefaultTasksMax=infinity
sudo systemctl daemon-reload
Tùy chọn 4: Kiểm tra rò rỉ tiến trình (khắc phục nguyên nhân gốc rễ)
Tăng giới hạn chỉ là chữa triệu chứng. Nếu tiến trình zombie hoặc vòng lặp spawn không kiểm soát mới là vấn đề thực sự, không có giới hạn nào cứu được bạn lâu dài.
# Liệt kê các tiến trình zombie
ps aux | awk '$8 == "Z"'
# Theo dõi xem số tiến trình của một user cụ thể có tiếp tục tăng không
watch -n 2 "ps -u myuser --no-header | wc -l"
# Tìm tiến trình cha đang spawn quá nhiều tiến trình con
ps -eo pid,ppid,user,comm | awk '{print $2}' | sort | uniq -c | sort -rn | head -5
Số đếm liên tục tăng mà không bao giờ giảm? Đó là rò rỉ. Hãy sửa ứng dụng.
Xác nhận bản sửa đã có tác dụng
# Sau khi áp dụng thay đổi limits.conf, mở phiên mới và kiểm tra:
ulimit -u
# Với dịch vụ systemd, xác nhận giới hạn mới đã được áp dụng:
cat /proc/$(pgrep -o nginx)/limits | grep processes
# Kiểm tra tải — spawn 100 tiến trình nền:
for i in $(seq 1 100); do sleep 1 & done
# Nên hoàn tất mà không có lỗi 'Resource temporarily unavailable'
jobs | wc -l
kill $(jobs -p)
Tham khảo nhanh: giá trị mặc định phổ biến theo bản phân phối
- CentOS 6 / RHEL 6: nproc mặc định = 1024 cho non-root user — ứng dụng Java và Ruby on Rails liên tục vượt ngưỡng này
- CentOS 7 / RHEL 7+: đi kèm
/etc/security/limits.d/20-nproc.confvới giá trị 4096 — kiểm tra và ghi đè file cụ thể này, không phảilimits.conf - Ubuntu 20.04+: được quản lý bởi systemd, TasksMax mặc định bằng 15% giới hạn tối đa của hệ thống (khoảng 4915 trên VPS thông thường) — các dịch vụ có độ đồng thời cao vẫn chạm ngưỡng
- Docker containers: kế thừa ulimit của máy chủ theo mặc định; truyền
--ulimit nproc=65535:65535khi chạy container với nhiều tiến trình đồng thời
Thủ phạm trong thực tế: CI runner
Nơi phổ biến nhất tôi gặp vấn đề này: một instance Jenkins hoặc GitLab Runner đang chạy 20 build song song, mỗi job fork một shell, một trình biên dịch và một bộ test. Đó là hơn 60 tiến trình mỗi job — nhân với 20 job, bạn vượt ngưỡng 1024 trong vài giây.
Với bất kỳ user nào chạy dịch vụ, hãy đặt nproc ít nhất là 65536. Không có chi phí nào — đây chỉ là giới hạn kế toán của kernel, không phải đặt trước bộ nhớ.
Một điều nữa: trên các hệ thống có cấu hình PAM nhưng systemd quản lý các dịch vụ, chỉ sửa limits.conf thôi sẽ không có tác dụng. Bạn cần cả hai.

