Tối ưu Kernel & TCP Stack: Bí quyết tăng tốc máy chủ web của InitOps

Khi traffic tăng đột biến, đa số máy chủ web không chết vì thiếu CPU hay RAM — mà vì TCP stack bị nghẽn, kernel không đủ “cửa” để xử lý kết nối, hoặc thuật toán kiểm soát tắc nghẽn quá thận trọng. Trong bài này, mình phân tích chi tiết cách InitOps tinh chỉnh kernel Linux để đẩy throughput mạng lên cao nhất, giữ kết nối ổn định và phản hồi nhanh hơn — tất cả chỉ qua một bộ cấu hình kernel toàn diện.

Tối ưu Kernel & TCP Stack: Bí quyết tăng tốc máy chủ web của InitOps

1. TCP BBR — Game Changer Với Thuật Toán Kiểm Soát Tắc Nghẽn

Mặc định, Linux dùng thuật toán Cubic để kiểm soát tắc nghẽn TCP. Cubic hoạt động dựa trên mô hình “phát hiện mất gói tin” — nghĩa là nó cứ gửi nhanh cho đến khi gói tin bị mất, rồi mới giảm tốc. Cách này tốt cho mạng ổn định, nhưng tệ hại trên mạng có độ trễ cao hoặc băng thông biến động (ví dụ: CDN quốc tế, mobile network).

BBR (Bottleneck Bandwidth and Round-trip propagation time) của Google hoạt động khác biệt hoàn toàn. Thay vì chờ mất gói tin, BBR đo lường thực tế băng thông cổ chai và thời gian khứ hồi (RTT), sau đó điều chỉnh tốc độ gửi ngay lập tức. Kết quả:

  • Giảm độ trễ đáng kể — đặc biệt trên kết nối quốc tế hoặc mạng chậm.
  • Tăng tốc độ tải trang — BBR có thể tận dụng 100% băng thông sẵn có mà không gây nghẽn.
  • Ổn định hơn Cubic — không bị “sawtooth” (tăng-giảm đột ngột) khi mạng biến động.

InitOps tự động bật BBR, biến máy chủ của bạn từ “chạy bằng cảm tính” sang “chạy bằng dữ liệu thực tế”.

2. File Limits — Mở Rộng Giới Hạn File Descriptor Cho Nginx & PHP-FPM

Mỗi kết nối TCP, mỗi file log, mỗi socket Unix — tất cả đều tiêu tốn một file descriptor trong kernel. Khi traffic cao, Nginx và PHP-FPM có thể mở hàng chục nghìn descriptor cùng lúc. Nếu giới hạn hệ thống quá thấp, bạn sẽ thấy lỗi Too many open files và dịch vụ đột ngột từ chối kết nối mới.

InitOps điều chỉnh hai tham số then chốt:

fs.file-max = 2.000.000

Đây là giới hạn tổng số file descriptor toàn hệ thống có thể mở cùng lúc. Mặc định Linux thường để khoảng 500.000–1.000.000. InitOps đẩy lên 2 triệu, đảm bảo ngay cả khi bạn chạy nhiều instance Nginx/PHP-FPM, hệ thống vẫn còn “room” cho các tiến trình khác (database, cache, monitoring).

fs.inotify.max_user_watches = 524.288

Tham số này kiểm soát số lượng file/directory mà một user có thể “theo dõi” qua inotify — cơ chế kernel dùng để thông báo khi file thay đổi. Các tool như systemd, file watcher, hoặc PHP-FPM với opcache đều dùng inotify. Giới hạn mặc định (thường 8.192) rất dễ bị vượt qua trên server có nhiều file source code. 524.288 watches cho phép theo dõi toàn bộ codebase lớn mà không bị ENOSPC error.

3. Connection Backlog — Chống Đỡ Traffic Đột Biến & SYN Flood

Khi client gửi request đến server, kết nối không được xử lý ngay lập tức mà phải “xếp hàng” trong một hàng đợi gọi là backlog. Nếu hàng đợi quá ngắn, các kết nối mới sẽ bị từ chối ngay cả khi server còn dư tài nguyên CPU/RAM.

InitOps đẩy đồng loạt ba tham số backlog lên 65.535:

net.core.somaxconn

Giới hạn tối đa của hàng đợi socket listening. Khi Nginx gọi listen(), số lượng kết nối chờ accept không thể vượt quá giá trị này. 65.535 cho phép Nginx “tích trữ” hàng chục nghìn kết nối trong hàng đợi, xử lý dần theo khả năng của worker process.

tcp_max_syn_backlog

Hàng đợi cho các gói tin SYN — giai đoạn đầu của bắt tay 3 bước TCP. Khi traffic đột biến, hàng nghìn client gửi SYN cùng lúc. Nếu backlog SYN quá nhỏ, kernel sẽ lặng lẽ drop các SYN mới, client sẽ timeout và retry — gây cảm giác “server chậm” dù thực ra server chưa kịp phản hồi.

netdev_max_backlog

Hàng đợi ở tầng network device — nơi gói tin đến từ NIC (Network Interface Card) trước khi được kernel xử lý. Giá trị cao giúp NIC không bị “nghẽn” khi CPU đang bận xử lý các gói tin trước đó.

tcp_syncookies = 1 — Tấm Khiên Chống SYN Flood

SYN flood là kiểu tấn công DDoS phổ biến: attacker gửi hàng loạt gói SYN giả mạo, làm đầy backlog SYN mà không bao giờ hoàn tất bắt tay. Khi tcp_syncookies = 1, kernel chuyển sang chế độ “bảo vệ”: thay vì lưu trạng thái half-open connection (tốn RAM), kernel gửi lại một cookie đặc biệt trong gói SYN-ACK. Client hợp lệ sẽ gửi cookie này trong gói ACK cuối cùng, kernel xác minh và thiết lập kết nối bình thường — không cần lưu trạng thái nào trong backlog. Điều này giúp server sống sót qua các đợt SYN flood nhẹ mà không cần firewall phức tạp.

4. Socket Lifecycle — Tái Sử Dụng Port & Giảm Chờ Đợi

Một kết nối TCP không “biến mất” ngay khi đóng. Nó trải qua các trạng thái TIME_WAIT, FIN_WAIT và chiếm giữ tài nguyên trong một khoảng thời gian. Nếu không tinh chỉnh, server có thể cạn kiệt port hoặc bị đầy bảng kết nối — dù CPU vẫn nhàn rỗi.

tcp_tw_reuse = 1

Khi một socket ở trạng thái TIME_WAIT (thường kéo dài 60 giây theo RFC), nó vẫn “giữ” địa chỉ IP và port, ngăn không cho kết nối mới dùng lại. tcp_tw_reuse cho phép kernel tái sử dụng các socket TIME_WAIT cho kết nối mới đến cùng địa chỉ, an toàn vì kernel vẫn kiểm tra sequence number để tránh trùng lặp gói tin cũ. Điều này cực kỳ quan trọng cho server web có tỷ lệ kết nối ngắn hạn cao (HTTP/1.1 keep-alive, API requests).

tcp_fin_timeout = 15

Giảm thời gian chờ ở trạng thái FIN_WAIT_2 từ mặc định 60 giây xuống còn 15 giây. Nếu đối tác không gửi FIN sau khi server đã gửi FIN của mình, socket sẽ bị đóng sớm hơn, giải phóng tài nguyên nhanh hơn.

Keepalive Tuning: 600s / 30s / 5 lần

Đây là bộ ba tham số tcp_keepalive_time, tcp_keepalive_intvl, và tcp_keepalive_probes:

  • 600 giây (10 phút): Khoảng thời gian “im lặng” trước khi kernel bắt đầu gửi keepalive probe. Nghĩa là nếu kết nối không có dữ liệu trong 10 phút, kernel mới “gõ cửa” kiểm tra.
  • 30 giây: Khoảng cách giữa các lần probe. Nếu không nhận phản hồi, kernel gửi probe tiếp theo sau 30 giây.
  • 5 lần: Số probe tối đa trước khi kernel kết luận kết nối đã chết và đóng socket.

Tổng thời gian phát hiện kết nối chết: 600 + (5 × 30) = 750 giây (~12.5 phút). Đây là sự cân bằng giữa “phát hiện sớm” và “không gây overhead” — quá ngắn sẽ gửi probe liên tục, tốn CPU và băng thông; quá dài sẽ giữ socket chết chiếm tài nguyên.

ip_local_port_range = 1024–65000

Mỗi kết nối TCP outbound (ví dụ: PHP-FPM kết nối đến MySQL, Nginx proxy đến backend) cần một ephemeral port nguồn. Mặc định Linux thường chỉ dùng range 32768–60999 (~28.000 port). InitOps mở rộng lên 1024–65000 (~64.000 port), gần gấp đôi số lượng kết nối outbound đồng thời. Điều này đặc biệt quan trọng cho:

  • Server đóng vai trò reverse proxy (Nginx → nhiều backend).
  • Ứng dụng có nhiều kết nối database hoặc API bên thứ ba.
  • Môi trường container/Kubernetes nơi mỗi pod có thể mở hàng nghìn kết nối outbound.

Tổng Kết: Tại Sao Những Con Số Này Quan Trọng?

Các tham số kernel không phải “càng cao càng tốt” — chúng là sự cân bằng giữa tài nguyên và hiệu năng. InitOps đã tính toán kỹ lưỡng:

Tham số Giá trị Tác dụng chính
TCP BBR Bật Tận dụng tối đa băng thông, giảm latency
fs.file-max 2.000.000 Không giới hạn descriptor cho high-traffic
fs.inotify.max_user_watches 524.288 Theo dõi file không bị gián đoạn
net.core.somaxconn 65.535 Hàng đợi kết nối đủ lớn cho traffic spike
tcp_max_syn_backlog 65.535 Chống đỡ SYN flood nhẹ
netdev_max_backlog 65.535 NIC không bị nghẽn khi CPU bận
tcp_syncookies 1 Bảo vệ SYN flood mà không tốn RAM
tcp_tw_reuse 1 Tái sử dụng port TIME_WAIT hiệu quả
tcp_fin_timeout 15 giây Giải phóng socket nhanh hơn
Keepalive 600/30/5 Phát hiện kết nối chết vừa đủ nhanh
ip_local_port_range 1024–65000 Gần gấp đôi kết nối outbound đồng thời

Kết hợp toàn bộ, InitOps biến một server Linux thông thường thành một “máy chiến đấu” sẵn sàng đón nhận hàng chục nghìn concurrent connection, phản hồi nhanh hơn, và sống sót qua các đợt traffic spike mà không cần scale phần cứng ngay lập tức. Đây chính là sức mạnh của việc tinh chỉnh kernel đúng cách — không phải bằng phần cứng đắt tiền, mà bằng kiến thức sâu về networking stack.

Bình luận


  • Không có bình luận.

Công cụ trực tuyến

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...