Cuộc gọi đánh thức lúc 2 giờ sáng từ PagerDuty
Điện thoại tôi reo vang lúc 2 giờ sáng. Dashboard giám sát gateway chính của chúng tôi đang hiển thị màu đỏ rực. Người dùng không thể truy cập ứng dụng, ping bị mất đến 80% gói tin và các phiên SSH liên tục bị treo. Khi cuối cùng tôi cũng thiết lập được một phiên terminal ổn định, kernel logs đã vạch trần thủ phạm. Màn hình tràn ngập một thông báo lặp đi lặp lại:
[84321.123456] net_ratelimit: đã chặn 542 lượt gọi lại
[84321.123457] tràn bảng neighbor!
[84321.123458] tràn bảng neighbor!
Đây là một lỗi kinh điển trong các mạng Layer 2 lớn, chẳng hạn như các cụm ảo hóa mật độ cao mà chúng tôi đang vận hành. Máy chủ đã chạm đến giới hạn cứng mà nó chưa bao giờ được cấu hình để xử lý. Về cơ bản, Linux kernel đang “quên” các láng giềng của mình vì “danh bạ” đã hết chỗ chứa.
Neighbor Table hoạt động như thế nào
Hãy coi Neighbor Table (ARP Cache trong IPv4) như một bản đồ ánh xạ từ địa chỉ IP sang địa chỉ MAC. Để giao tiếp với bất kỳ thiết bị nào trong mạng cục bộ, máy chủ của bạn cần địa chỉ MAC của thiết bị đó. Nó sẽ phát một bản tin ARP request, nhận về phản hồi và lưu lại ánh xạ đó để không phải hỏi lại lần sau.
Các bản phân phối Linux tiêu chuẩn như Ubuntu 22.04 hay Debian thường được cấu hình cho các môi trường phổ thông. Chúng chỉ dự kiến có tối đa vài trăm “láng giềng”. Tuy nhiên, các mô hình hiện đại — như mạng Kubernetes phẳng hoặc các VLAN với hơn 2.000 node — sẽ dễ dàng phá vỡ các thiết lập mặc định này. Khi số lượng thiết bị vượt quá giới hạn nội bộ của kernel, nó sẽ ngừng chấp nhận các mục nhập mới. Đó là lúc kết nối mạng của bạn bị gián đoạn.
Kiểm tra giới hạn của bạn
Để chẩn đoán điểm nghẽn, hãy bắt đầu bằng cách đếm các mục nhập neighbor hiện tại:
ip neighbor show | wc -l
Tiếp theo, hãy xem xét ba ngưỡng sysctl điều phối trình thu gom rác (garbage collector – GC) của Giao thức phân giải địa chỉ (ARP):
sysctl -a | grep gc_thresh
Bạn sẽ tìm thấy ba giá trị cụ thể cho net.ipv4.neigh.default.gc_thresh:
- gc_thresh1 (Mặc định 128): Mức sàn. Kernel sẽ không tính đến việc dọn dẹp cache nếu bạn có ít hơn 128 mục nhập.
- gc_thresh2 (Mặc định 512): Ngưỡng mềm. Nếu cache duy trì trên mức 512 trong hơn 5 giây, trình thu gom rác sẽ bắt đầu xóa bỏ các mục.
- gc_thresh3 (Mặc định 1024): Ngưỡng cứng. Nếu bạn chạm mức 1.024, kernel sẽ ngay lập tức cố gắng giải phóng các mục nhập. Nếu thất bại, bạn sẽ gặp lỗi “overflow” và các kết nối mới sẽ bị từ chối.
Trong trường hợp của chúng tôi, mạng đã tăng vọt lên 2.400 thiết bị đang hoạt động. Vì gc_thresh3 vẫn kẹt ở mức mặc định 1024, máy chủ đang cố gắng nhồi nhét một đám đông vào một căn phòng nhỏ xíu. Nó đã không thể vận hành bình thường.
Hai cách để khắc phục
1. “Sửa lỗi nhanh” (Thận trọng khi sử dụng)
Nếu bạn đang gặp sự cố hệ thống nghiêm trọng, bạn có thể cân nhắc việc xóa sạch bảng ARP:
sudo ip -s -s neigh flush all
Rủi ro: Điều này buộc máy chủ phải thực hiện lại ARP cho mọi kết nối cùng một lúc. Trên một gateway có lưu lượng truy cập cao, việc này tạo ra một “cơn bão ARP” có thể làm nghẽn đường truyền mạng của bạn trong vài phút. Chỉ sử dụng cách này như là giải pháp cuối cùng.
2. Cấu hình Sysctl vĩnh viễn
Cách tốt hơn là báo cho kernel biết rằng một “khu phố đông đúc” là chuyện bình thường. Đối với hầu hết các trung tâm dữ liệu hiện đại, tôi khuyên bạn nên tăng gấp bốn lần các giá trị mặc định. Ngay cả với 8.000 mục nhập, dung lượng bộ nhớ tiêu tốn là không đáng kể. Mỗi mục ARP chiếm khoảng 256 byte; 8.192 mục chỉ tốn khoảng 2MB RAM. Đó là cái giá quá nhỏ để đổi lấy sự ổn định của mạng.
Cấu hình sẵn sàng cho môi trường Production
Đừng sửa trực tiếp tệp /etc/sysctl.conf. Thay vào đó, hãy tạo một tệp riêng trong /etc/sysctl.d/ để quản lý tốt hơn.
Áp dụng các cài đặt sau để xử lý các môi trường mật độ cao. Tôi đã sử dụng cấu hình cụ thể này trên các cụm hơn 5.000 node mà không gặp bất kỳ vấn đề nào.
# Lưu vào /etc/sysctl.d/20-neighbor-limits.conf
# Tối ưu hóa Neighbor Table cho IPv4
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
# Tối ưu hóa Neighbor Table cho IPv6
net.ipv6.neigh.default.gc_thresh1 = 1024
net.ipv6.neigh.default.gc_thresh2 = 4096
net.ipv6.neigh.default.gc_thresh3 = 8192
# Giảm lưu lượng ARP bằng cách giữ các mục nhập trong cache 1 giờ
net.ipv4.neigh.default.gc_stale_time = 3600
net.ipv6.neigh.default.gc_stale_time = 3600
Tải các thiết lập mới ngay lập tức mà không cần khởi động lại:
sudo sysctl -p /etc/sysctl.d/20-neighbor-limits.conf
Tại sao lại là các giá trị này?
Việc đặt gc_thresh1 thành 1.024 giúp kernel không lãng phí chu kỳ CPU cho việc thu gom rác ở các bảng nhỏ. Giới hạn 8.192 cho gc_thresh3 cung cấp một vùng đệm khổng lồ cho sự gia tăng đột ngột của các container, máy ảo hoặc các lượt quét mạng mới mà không gây ra tình trạng hoảng loạn (panic).
Kiểm chứng kết quả
Kiểm tra kernel logs (dmesg) sau khi áp dụng thay đổi. Các thông báo “overflow” sẽ biến mất ngay lập tức. Để theo dõi bảng ARP tăng trưởng tự nhiên, hãy sử dụng lệnh watch:
watch -n 1 "ip neigh show | wc -l"
Số lượng bây giờ sẽ tăng dần qua mức giới hạn 1.024 cũ một cách thoải mái và ổn định ở số lượng thiết bị thực tế của bạn. Nếu nó tiếp tục tăng vô hạn mà không dừng lại, có thể bạn đang đối mặt với vòng lặp mạng (network loop) hoặc một đợt quét ARP từ đối tượng xấu.
Tổng kết
Quản trị mạng ở quy mô lớn đòi hỏi chúng ta phải vượt qua các thiết lập “mặc định”. ‘Neighbor Table Overflow’ là một ví dụ điển hình về việc một giá trị mặc định nhỏ có thể gây ra rắc rối lớn như thế nào. Nếu bạn quản lý máy chủ trong một trung tâm dữ liệu hiện đại hoặc VLAN lớn, đừng đợi đến khi nhận được cảnh báo lúc 2 giờ sáng. Hãy kiểm tra số lượng neighbor của bạn ngay hôm nay và điều chỉnh các giới hạn trước khi bảng ARP chạm trần.

