Vấn đề thực tế: Nghẽn cổ chai mạng 10GbE khi ảo hóa
Nhiều anh em đầu tư card mạng 10GbE như Intel X520-DA2 hay Mellanox ConnectX-3 cho dàn lab Proxmox với kỳ vọng kéo max băng thông giữa các máy ảo (VM). Cắm vào nhận đủ link 10G. Nhưng khi đo đạc thực tế, kết quả lại gây thất vọng.
Hệ thống của mình chạy 1 cụm Ceph 3 node kết hợp vài VM backend Postgres và streaming dữ liệu. Khi chạy trên cùng một switch quang Mikrotik CRS305 qua cáp DAC SFP+, mình gặp 3 vấn đề lớn:
- Độ trễ cao và trồi sụt: Ping giữa 2 VM qua Linux Bridge chuẩn dao động từ 0.3ms đến gần 1ms. Khi có tải IO nặng, jitter tăng rõ rệt.
- Ăn sạch CPU vì ngắt mềm (softirq): Đẩy tải iperf3 chạm ngưỡng 8 Gbps, 2 core CPU Xeon E5 trên host lập tức vọt lên 90–100%. Thủ phạm chính là tiến trình
ksoftirqdphải xử lý hàng triệu gói tin nhỏ mỗi giây. - Rớt gói (Packet drop): Buffer phần mềm của Linux Bridge bị tràn khi lưu lượng đột biến. Kết quả là TCP retransmission tăng, kéo throughput tụt xuống còn 5–6 Gbps.
Vì sao mô hình mạng ảo hóa mặc định bị chậm?
Card 10GbE chạy native trên bare-metal dễ dàng chạm mốc 9.8 Gbps. Vậy tại sao qua VM lại đuối sức?
Mô hình VirtIO kết hợp Linux Bridge hoặc Open vSwitch buộc gói tin phải đi qua một chuỗi xử lý trung gian:
- NIC vật lý nhận frame rồi kích hoạt phần cứng ngắt (Hardware IRQ) báo về CPU host.
- Driver mạng của kernel host gom gói tin, đẩy vào Linux Bridge.
- Bridge tra bảng MAC, copy dữ liệu sang tap interface của máy ảo.
- QEMU giả lập ngắt mạng ảo rồi chuyển dữ liệu vào memory của VM.
- Guest OS tiếp nhận gói qua driver
virtio-net.
Chuỗi thao tác này kéo theo hàng loạt context switch giữa kernel-space và user-space. Bộ nhớ RAM cũng phải copy nhiều lần. Khi lưu lượng vượt mốc 1 triệu gói mỗi giây (1 Mpps), CPU host quá tải vì đóng vai trò như một switch phần mềm.
So sánh 3 hướng xử lý nghẽn mạng trên Proxmox
Tùy theo cấu hình phần cứng và nhu cầu, ta có 3 lựa chọn chính:
1. Bật vhost-net và Multiqueue cho VirtIO
vhost-net đẩy việc xử lý gói tin thẳng vào kernel của host thay vì chạy qua tiến trình QEMU. Kết hợp với Multiqueue, tải ngắt mạng sẽ chia đều cho các vCPU.
Ưu điểm: Dễ cấu hình, không kén phần cứng, giữ nguyên tính năng Proxmox Live Migration.
Hạn chế: Hiệu năng tăng khoảng 25–30%. CPU host vẫn phải gánh tải mạng, độ trễ chưa thể tối ưu sâu.
2. PCIe Passthrough toàn bộ card mạng
Gán trực tiếp cổng PCIe vật lý vào một VM duy nhất qua VFIO.
Ưu điểm: Hiệu năng đạt 100% bare-metal. Độ trễ cực thấp, CPU host hoàn toàn rảnh rỗi.
Hạn chế: Tốn kém thiết bị. Một cổng 10G gán cho một VM đồng nghĩa các VM còn lại không còn cổng dùng.
3. Phân tách bằng SR-IOV (Single Root I/O Virtualization)
SR-IOV cho phép một card mạng vật lý tự nhân bản thành nhiều cổng mạng độc lập trực tiếp từ phần cứng:
- Physical Function (PF): Cổng vật lý gốc, do hệ điều hành host nắm quyền kiểm soát.
- Virtual Function (VF): Các cổng ảo do chip mạng tự sinh ra. Mỗi VF có hàng đợi RX/TX, bộ điều khiển DMA và thanh ghi riêng.
Mỗi VF được passthrough trực tiếp vào một VM. Máy ảo đọc ghi dữ liệu thẳng xuống phần cứng của NIC, bỏ qua toàn bộ switch ảo và kernel host.
Hướng dẫn từng bước cấu hình SR-IOV trên Proxmox VE
Bài thực hành sau áp dụng cho card Intel X520 (chip 82599ES, dùng driver ixgbe), một dòng card 10GbE rất phổ biến trong giới homelab và doanh nghiệp vừa.
Bước 1: Bật ảo hóa trong BIOS/UEFI
Vào BIOS của máy chủ, kiểm tra và kích hoạt 3 mục:
- Intel VT-d (nếu dùng chip Intel) hoặc AMD IOMMU / SVM (nếu dùng CPU AMD).
- SR-IOV Support: Chọn
Enabled. - Above 4G Decoding: Bật tính năng này nếu cắm nhiều thiết bị PCIe để tránh lỗi tràn địa chỉ nhớ.
Bước 2: Kích hoạt IOMMU trên Proxmox Host
Mở file cấu hình bootloader của Linux:
nano /etc/default/grub
Thêm tham số IOMMU vào biến GRUB_CMDLINE_LINUX_DEFAULT:
# Máy dùng chip Intel:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
# Máy dùng chip AMD:
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"
Lưu file và cập nhật bootloader rồi khởi động lại server:
update-grub
# Nếu bạn cài Proxmox ZFS dùng systemd-boot:
# proxmox-boot-tool refresh
reboot
Sau khi máy khởi động lại, kiểm tra xem IOMMU đã bật thành công chưa:
dmesg | grep -e DMAR -e IOMMU
Dòng thông báo DMAR: IOMMU enabled xuất hiện nghĩa là hệ thống đã sẵn sàng.
Bước 3: Khởi tạo Virtual Function (VF)
Dùng lệnh ip link để xác định tên cổng mạng 10G. Ở đây giả sử cổng là enp4s0f0.
Kiểm tra số lượng VF tối đa mà card hỗ trợ:
cat /sys/class/net/enp4s0f0/device/sriov_totalvfs
Intel X520 thường báo giá trị 63. Ta sẽ tạo thử 4 VF:
echo 4 > /sys/class/net/enp4s0f0/device/sriov_numvfs
Kiểm tra các VF vừa tạo bằng lspci:
lspci -nn | grep -i "Virtual Function"
Hệ thống sẽ liệt kê 4 thiết bị mới có ID dạng 04:10.0, 04:10.2,… mang chip 82599 Virtual Function.
Bước 4: Lưu cấu hình cố định khi khởi động
Số VF tạo thủ công ở bước 3 sẽ mất sau khi reboot. Để tự động tạo VF và gán MAC cố định, mở file /etc/network/interfaces:
auto enp4s0f0
iface enp4s0f0 inet manual
# Khởi tạo 4 VF khi card mạng UP
post-up echo 4 > /sys/class/net/enp4s0f0/device/sriov_numvfs
# Đặt MAC cố định và tắt spoofcheck để VM chủ động đổi IP
post-up ip link set enp4s0f0 vf 0 mac ba:ad:1a:20:00:01 spoofchk off
post-up ip link set enp4s0f0 vf 1 mac ba:ad:1a:20:00:02 spoofchk off
post-down echo 0 > /sys/class/net/enp4s0f0/device/sriov_numvfs
Bước 5: Gán VF vào máy ảo
Thao tác nhanh nhất là dùng giao diện Proxmox Web GUI:
- Chọn VM cần gán > vào thẻ Hardware.
- Nhấn Add > chọn PCI Device.
- Chọn Raw Device, tìm địa chỉ VF muốn gán (ví dụ
0000:04:10.0). - Tích chọn PCI-Express. Lưu ý: không tích chọn “All Functions” vì ta chỉ gán riêng một VF.
- Bấm Add rồi bật máy ảo.
Bước 6: Đo lường hiệu năng thực tế
Truy cập vào máy ảo, chạy lệnh lspci -k. Bạn sẽ thấy driver đang dùng là ixgbevf, chứng minh VM đã nhận diện trực tiếp card vật lý.
Tiến hành benchmark bằng iperf3 giữa 2 VM cùng dùng SR-IOV:
# VM 1 (Server):
iperf3 -s
# VM 2 (Client):
iperf3 -c 192.168.10.50 -P 8
Kết quả cho thấy sự khác biệt rõ rệt:
- Băng thông: Chạm ngưỡng 9.42 Gbps, tiệm cận mức tối đa của đường truyền vật lý.
- Tải CPU host: Giảm từ 85% xuống chỉ còn 2–4% trong suốt quá trình truyền tải.
- Ping: Thời gian phản hồi hạ xuống 0.05–0.07ms, không còn hiện tượng rớt gói khi tải cao.
Đổi lại, dùng SR-IOV sẽ khiến VM không thể Live Migration theo cách thông thường. Nếu hệ thống đòi hỏi tính sẵn sàng cao (HA), bạn có thể thiết lập bonding giữa một cổng SR-IOV và một cổng VirtIO dự phòng để duy trì khả năng di chuyển nóng.
