Làm ngay trong 5 phút: Bộ cấu hình tối ưu cơ bản
Nếu bạn đang có một server CentOS Stream 9 đang chạy và muốn cải thiện hiệu năng ngay, chạy các lệnh sau:
# Xem giá trị hiện tại của một tham số bất kỳ
sysctl net.core.somaxconn
# Apply ngay lập tức (không cần reboot, nhưng mất sau khi restart)
sudo sysctl -w net.core.somaxconn=65535
sudo sysctl -w net.ipv4.tcp_fin_timeout=30
sudo sysctl -w vm.swappiness=10
# Kiểm tra đã áp dụng chưa
sysctl net.core.somaxconn vm.swappiness net.ipv4.tcp_fin_timeout
Lưu ý: sysctl -w chỉ áp dụng cho session hiện tại. Để lưu vĩnh viễn, cần tạo file config — mình sẽ hướng dẫn ở phần tiếp theo.
Tại sao cần chỉnh sysctl trên production?
sysctl là công cụ đọc và ghi trực tiếp các tham số kernel thông qua filesystem ảo /proc/sys/. Mỗi tham số ánh xạ đến một hành vi cụ thể — net.core.somaxconn quyết định bao nhiêu kết nối TCP được xếp hàng chờ accept, vm.swappiness quyết định kernel dùng swap sớm hay muộn.
Vấn đề là CentOS Stream 9 đi kèm các giá trị mặc định thiết kế cho server đa dụng, không phải workload production. Chạy Nginx xử lý 1.000 req/s với somaxconn=128? Kết nối bắt đầu bị drop ngay khi có traffic spike nhỏ.
Hồi CentOS 8 EOL, mình migrate gấp 5 server sang Rocky Linux trong 1 tuần. Lúc đó mới giật mình: tất cả đang chạy với cấu hình TCP mặc định từ cả chục năm trước. Sau khi apply đúng bộ tham số, latency trung bình giảm rõ rệt — đặc biệt với các kết nối ngắn hạn trong môi trường microservice.
Ba nhóm tham số ảnh hưởng nhiều nhất đến production server:
- net.* — TCP/IP stack, hàng đợi kết nối, port range
- vm.* — Bộ nhớ ảo, swap behavior, dirty page
- fs.* — File descriptor, inotify limits
Giải thích chi tiết từng nhóm tham số
TCP/IP Stack: Tối ưu cho web server và reverse proxy
Tạo file cấu hình riêng trong /etc/sysctl.d/ — không chỉnh thẳng /etc/sysctl.conf vì dễ bị overwrite và khó track:
sudo nano /etc/sysctl.d/99-production.conf
Paste nội dung sau vào file:
# --- TCP/IP Stack ---
# Tăng backlog queue cho socket (mặc định 128 — quá thấp cho web server)
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
# Tái sử dụng socket ở trạng thái TIME_WAIT (giảm port exhaustion)
net.ipv4.tcp_tw_reuse = 1
# Rút ngắn thời gian chờ đóng kết nối (mặc định 60s)
net.ipv4.tcp_fin_timeout = 30
# Tăng range port cho outbound connections (quan trọng với reverse proxy)
net.ipv4.ip_local_port_range = 1024 65535
# Buffer size cho socket — tăng throughput khi latency cao
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# SYN backlog — chống SYN flood cơ bản
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_syncookies = 1
somaxconn = 65535: Số kết nối tối đa trong hàng đợi chờ accept(). Với giá trị mặc định là 128, một web server nhận 500 req/s sẽ bắt đầu drop kết nối ngay khi có spike nhỏ.
tcp_tw_reuse = 1: Cho phép kernel tái dùng socket đang ở trạng thái TIME_WAIT cho kết nối mới. Với microservices tạo hàng nghìn kết nối ngắn hạn, đây là một trong những tham số có tác động rõ nhất.
ip_local_port_range: Range port cho outbound connection. Nếu Nginx kết nối đến 10 backend upstream, mỗi request tốn một port. Mặc định chỉ có khoảng 28.000 port — với 300 req/s thì hết trong dưới 2 phút nếu TIME_WAIT kéo dài.
Bộ nhớ ảo (vm.*): Tránh OOM killer đánh vào lúc cao điểm
# --- Virtual Memory ---
# Giảm xu hướng dùng swap (10 = chỉ swap khi thực sự cần)
vm.swappiness = 10
# Áp lực reclaim page cache khi RAM thấp
vm.vfs_cache_pressure = 50
# Flush dirty page ra disk thường xuyên hơn (tránh spike I/O đột ngột)
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.swappiness = 10: Với giá trị mặc định là 60, kernel khá tích cực trong việc swap — ngay cả khi RAM chưa cạn. Điều này gây latency spike đáng kể vì đọc/ghi disk chậm hơn RAM hàng trăm đến hàng nghìn lần (tùy HDD hay NVMe). Set về 10 giữ ứng dụng trong RAM lâu hơn, chỉ swap khi thực sự thiếu.
Nếu bạn chạy MySQL hoặc PostgreSQL, nhiều DBA khuyên set vm.swappiness = 1 thay vì 0. Set 0 không tắt hoàn toàn swap mà khiến kernel ngần ngại hơn trong một số edge case; set 1 ít gây side effect hơn trên kernel mới.
vm.dirty_ratio = 15: Phần trăm RAM tối đa chứa dirty page trước khi kernel buộc flush ra disk. Giảm từ 20 (mặc định) xuống 15 giúp I/O phân bổ đều hơn, tránh tình trạng server bỗng nhiên bị I/O wait cao đột ngột do flush một lúc quá nhiều.
File Descriptor Limits: Cần thiết cho Nginx, Node.js và Elasticsearch
Giới hạn file descriptor hay bị bỏ qua cho đến khi log xuất hiện lỗi Too many open files — thường là lúc server đang chịu tải. CentOS Stream 9 mặc định giới hạn 1024 file/process — quá thấp cho bất kỳ production service nào nghiêm túc.
# --- File System ---
# Số file descriptor tối đa toàn hệ thống
fs.file-max = 2097152
# Inotify watches — cần cho các tool monitor file (Webpack, Vite, Elasticsearch)
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
Ngoài sysctl, cần chỉnh thêm per-process limit qua PAM:
sudo nano /etc/security/limits.d/99-nofile.conf
# Áp dụng cho tất cả user
* soft nofile 65536
* hard nofile 65536
# Riêng cho user chạy Nginx
nginx soft nofile 200000
nginx hard nofile 200000
Với systemd service, còn cần thêm vào file unit để override limit:
sudo systemctl edit nginx
[Service]
LimitNOFILE=200000
Nâng cao: Apply và kiểm tra cấu hình
# Apply tất cả file trong /etc/sysctl.d/ — không cần reboot
sudo sysctl --system
# Kiểm tra một tham số cụ thể
sysctl net.core.somaxconn
# Tìm kiếm tham số (hữu ích khi không nhớ tên đầy đủ)
sysctl -a 2>/dev/null | grep tcp_fin
# Kiểm tra file descriptor limit của process đang chạy
cat /proc/$(pgrep nginx | head -1)/limits | grep "open files"
# Đếm số socket TIME_WAIT — giảm sau khi bật tcp_tw_reuse
ss -ant | grep TIME-WAIT | wc -l
Elasticsearch có một tham số bắt buộc riêng — thiếu nó, service từ chối khởi động và báo lỗi ngay:
# Bắt buộc cho Elasticsearch
echo 'vm.max_map_count = 262144' | sudo tee /etc/sysctl.d/99-elasticsearch.conf
sudo sysctl --system
Tips thực tế
1. Benchmark trước và sau khi thay đổi
Dùng ss -s để xem thống kê socket trước và sau khi thay đổi. Mình hay chụp output lại để so sánh sau 24 giờ production traffic:
# Snapshot thống kê kết nối TCP
ss -s
# Xem chi tiết theo state
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn
2. Đừng copy-paste mù quáng — đọc giá trị hiện tại trước
Trước khi thay đổi bất kỳ tham số nào, kiểm tra giá trị hiện tại và đọc kernel docs. Trên kernel 5.x trở lên, nhiều tham số TCP buffer đã được auto-tune — override thủ công đôi khi làm chậm hơn chứ không giúp gì.
3. Test trên staging trước khi áp dụng production
Hầu hết tham số an toàn để thử nghiệm, nhưng vm.overcommit_memory = 2 là ngoại lệ — nó có thể làm OOM killer hoạt động tích cực hơn và kill process nếu tính sai tổng RAM. Mình đã thấy một server MySQL bị kill ngay sau khi apply — may mắn là staging, không phải production.
4. Kiểm tra sau mỗi kernel update
File trong /etc/sysctl.d/ tự động load lúc boot — nhưng kernel update đôi khi kéo theo thay đổi default. Giá trị bạn override hôm nay có thể không còn cần thiết sau vài bản kernel. Chạy lệnh sau để verify:
# So sánh giá trị đang chạy với file config
sysctl -a 2>/dev/null | grep -E 'somaxconn|swappiness|file-max|tcp_fin_timeout'
# Xem tất cả file sysctl đang được load
sysctl --system --dry-run 2>&1 | head -20
Sau khi apply tham số mạng, chạy thêm dmesg | tail -20 để xem kernel có log warning gì không — một số tham số vượt giới hạn hardware sẽ xuất hiện ở đây trước khi gây ra vấn đề thực sự.

