Tại sao website bị treo: Cách khắc phục sự cố MTU và MSS trên Linux

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

Nỗi ám ảnh khi website chỉ tải được một phần

Bạn vừa triển khai ứng dụng web của mình lên một server Linux mới. Với kết nối cáp quang 1Gbps tại chỗ, nó chạy nhanh như chớp và log hoàn toàn sạch sẽ. Nhưng khi người dùng kết nối qua VPN công ty hoặc từ một chi nhánh từ xa, trải nghiệm bắt đầu tồi tệ đi. Các trang nhỏ tải ngay lập tức, nhưng bảng điều khiển chính—đầy rẫy JavaScript nặng và tài nguyên độ phân giải cao—cứ xoay vòng vô tận cho đến khi kết nối bị timeout.

Tôi đã thấy nhiều đội ngũ dành cả cuối tuần để tối ưu hóa các truy vấn SQL hoặc nâng cấp các instance load balancer để giải quyết vấn đề này. Họ giả định rằng code chậm, nhưng vấn đề thường nằm sâu hơn trong hệ thống mạng. Nếu trang web của bạn hoạt động với một số người nhưng lại bị treo với những người khác, rất có thể bạn đang gặp phải sự không tương thích về kích thước gói tin (packet size).

Hành vi này là dấu hiệu điển hình của xung đột MTU (Maximum Transmission Unit). Khi một gói tin quá lớn để một router có thể xử lý và router đó không thể phản hồi giới hạn này về server của bạn, dữ liệu sẽ đơn giản là biến mất. Đây chính là hiện tượng “PMTUD Black Hole” đáng sợ. Dưới đây là cách chẩn đoán và khắc phục.

Cơ bản: MTU và MSS

Để sửa đường ống, bạn cần hiểu nó có thể chứa được bao nhiêu nước. Dữ liệu di chuyển qua internet theo các khối (chunk) cụ thể.

MTU (Maximum Transmission Unit)

MTU là kích thước gói tin lớn nhất mà một giao diện (interface) có thể xử lý. Ethernet tiêu chuẩn sử dụng MTU là 1500 bytes. Tổng số này bao gồm IP header, TCP header và phần dữ liệu thực tế (payload) của bạn.

Vấn đề nảy sinh khi lưu lượng truy cập đi qua một đường truyền bị hạn chế. Ví dụ, WireGuard VPN thường sử dụng MTU 1420, trong khi một số kết nối DSL PPPoE giới hạn ở mức 1492. Nếu server của bạn gửi một gói tin 1500 byte vào một tunnel 1420 byte, gói tin đó phải bị phân mảnh hoặc bị hủy.

MSS (Maximum Segment Size)

MSS nằm bên trong TCP header. Nó cho bên kia biết chính xác lượng dữ liệu thực tế (payload) có thể được gửi trong một phân đoạn (segment). Khác với MTU, MSS không tính các header.

Phép tính rất đơn giản: MSS = MTU – 40 bytes (đối với IPv4 tiêu chuẩn). Trên một mạng tiêu chuẩn, MTU 1500 sẽ cho MSS là 1460. Nếu bạn đang sử dụng IPv6, các header sẽ lớn hơn, do đó MSS của bạn sẽ thấp hơn.

Truy tìm nút thắt cổ chai bằng Ping

Đừng đoán—hãy đo đạc. Bạn có thể sử dụng lệnh ping để tìm giới hạn MTU chính xác giữa server và client.

Kiểm tra với cờ “Do Not Fragment”

Chạy lệnh này từ máy cục bộ của bạn hướng tới server:

ping -s 1472 -M do [server_ip]
  • -s 1472: Thiết lập kích thước payload. (1472 payload + 8 ICMP header + 20 IP header = 1500 bytes).
  • -M do: Buộc mạng phải hủy gói tin nếu nó yêu cầu phân mảnh.

Nếu lệnh ping trả về thông báo “Frag needed”, đường truyền không thể xử lý 1500 bytes. Hãy giảm giá trị -s đi 10 đơn vị cho đến khi thành công. Nếu cuối cùng nó vượt qua ở mức 1432, thì MTU tối đa của bạn là 1460 (1432 + 28 bytes header).

Hai cách để khắc phục trên server Linux

Sau khi xác định được giới hạn, bạn có hai lựa chọn: thay đổi thiết lập giao diện phần cứng hoặc buộc quá trình bắt tay TCP (TCP handshake) sử dụng kích thước nhỏ hơn.

Cách 1: Điều chỉnh MTU của Interface

Đây là cách tiếp cận tốt nhất nếu server của bạn nằm sau một tunnel cụ thể như GRE hoặc WireGuard. Đầu tiên, hãy kiểm tra các thiết lập hiện tại:

ip link show eth0

Để thử nghiệm giới hạn thấp hơn ngay lập tức, hãy sử dụng:

sudo ip link set dev eth0 mtu 1460

Để áp dụng thay đổi này vĩnh viễn trên Ubuntu hoặc Debian, hãy cập nhật cấu hình Netplan của bạn (thường nằm trong /etc/netplan/):

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      mtu: 1460

Chạy sudo netplan apply để áp dụng các thay đổi.

Cách 2: MSS Clamping (Giải pháp thay thế phổ quát)

Bạn không thể kiểm soát MTU của mọi router trên internet. MSS Clamping giải quyết vấn đề này bằng cách can thiệp vào kết nối TCP ban đầu (gói tin SYN). Nó thay thế giá trị MSS mà client yêu cầu bằng một giá trị bạn chỉ định, đảm bảo tất cả các gói tin sau đó đủ nhỏ để đi qua.

Sử dụng iptables để tự động hóa việc này. Lệnh sau sẽ tính toán MSS tốt nhất dựa trên đường truyền hiện tại:

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Nếu bạn đang làm việc trực tiếp trên một web server thay vì một router, hãy áp dụng nó cho chuỗi OUTPUT:

sudo iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

Giá trị 1360 thường là an toàn cho hầu hết các VPN hoặc đường truyền đóng gói (encapsulated).

Xác minh kết quả

Sau khi áp dụng bản sửa lỗi, tình trạng “treo” sẽ dừng lại ngay lập tức. Bạn có thể theo dõi quá trình thương lượng diễn ra trong thời gian thực bằng tcpdump. Chạy lệnh này trên server trong khi tải lại trang web:

sudo tcpdump -i eth0 -n tcp[tcpflags] == tcp-syn

Kiểm tra giá trị mss trong kết quả đầu ra. Bây giờ nó sẽ khớp với giới hạn mới của bạn, xác nhận rằng server và client đã đồng ý sử dụng các gói tin nhỏ hơn, an toàn hơn.

Tại sao việc này không diễn ra tự động?

Internet có một cơ chế sửa lỗi tích hợp sẵn gọi là Path MTU Discovery (PMTUD). Khi một router thấy một gói tin quá lớn, nó đáng lẽ phải gửi một thông điệp ICMP “Destination Unreachable” quay lại cho người gửi. Điều này thông báo cho người gửi thử lại với kích thước nhỏ hơn.

Thật không may, nhiều quản trị viên tường lửa chặn tất cả lưu lượng ICMP vì nghĩ rằng nó giúp ngăn chặn các cuộc tấn công ping. Bằng cách đó, họ vô tình vô hiệu hóa PMTUD. Router hủy gói tin của bạn nhưng không bao giờ báo cho server biết lý do. Server của bạn cứ tiếp tục chờ đợi sự xác nhận (acknowledgment) không bao giờ tới, và người dùng thì chỉ thấy một biểu tượng xoay vòng.

Kết luận

Tình trạng treo mạng không phải lúc nào cũng do độ trễ cao hay thiếu RAM. Thường thì các đường ống chỉ đơn giản là quá nhỏ so với lượng dữ liệu bạn đang cố gắng đẩy qua chúng. Bằng cách đo đạc MTU đường truyền và sử dụng MSS clamping, bạn có thể đảm bảo server Linux của mình luôn phản hồi tốt cho mọi người dùng, bất kể loại kết nối của họ là gì. Lần tới khi một trang web bị treo đối với một người dùng cụ thể, hãy ngừng soi code và bắt đầu kiểm tra kích thước gói tin.

Share: