Cấu hình Network Bridge và TAP Interface cho KVM trên Linux — Thực chiến từ Homelab

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

Vấn đề thực tế khi chạy KVM mà không có bridge

Mình chạy homelab với Proxmox VE quản lý 12 VM và container — playground để test mọi thứ trước khi đưa lên production. Trước khi chuyển sang Proxmox, mình dùng KVM thuần trên Ubuntu Server 22.04. Cái đau đầu đầu tiên: VM tạo xong, virt-manager báo đang dùng NAT, nhưng các VM không ping được nhau và không SSH được từ ngoài vào.

Nguyên nhân: KVM mặc định dùng NAT mode qua bridge ảo virbr0 do libvirt tạo — mặc định là dải 192.168.122.0/24, hoàn toàn tách biệt khỏi LAN của bạn. Tiện cho lab cá nhân, nhưng VM bị ẩn sau NAT. Muốn VM có IP thật trên LAN, ta cần tạo network bridge gắn vào card mạng vật lý và để VM dùng TAP interface kết nối vào bridge đó.

Bridge và TAP — Giải thích thẳng vào vấn đề

Tài liệu KVM thường giải thích hai khái niệm này riêng lẻ nên khi gặp lỗi rất khó biết debug ở tầng nào. Gom lại đây trước khi vào config.

Network Bridge (br0)

Bridge hoạt động như switch ảo Layer 2. Thêm card mạng vật lý (enp3s0) vào bridge, IP lúc này nằm trên br0 — không còn trên enp3s0 nữa. Bridge nhận traffic từ cả mạng vật lý lẫn các interface ảo gắn vào nó. VM kết nối vào bridge nhận IP từ DHCP router thật — giống hệt một máy tính cắm cáp mạng vào switch.

TAP Interface

TAP là virtual network interface hoạt động ở Layer 2 (Ethernet frames). Khi QEMU/KVM khởi động VM, nó tạo TAP interface (tap0, vnet0…) trên host. Một đầu TAP gắn vào bridge, đầu kia là card mạng ảo mà guest OS nhìn thấy.

Luồng dữ liệu: Guest NIC → TAP → Bridge → NIC vật lý → Router/LAN.

Tại sao không dùng TUN? TUN hoạt động Layer 3 (IP packets), còn VM cần Layer 2 để xử lý ARP, broadcast, và VLAN tagging. TAP mới đáp ứng được yêu cầu đó.

Cấu hình thực tế — Từng bước một

Bước 1: Xác định card mạng vật lý

ip link show
# Tìm tên interface đang có kết nối, ví dụ: enp3s0 hoặc eth0

ip addr show enp3s0
# Ghi lại IP hiện tại, subnet mask, gateway

Cảnh báo trước khi làm: sau khi thêm enp3s0 vào bridge, interface này mất IP ngay lập tức — IP chuyển sang br0. Nếu đang SSH qua enp3s0, kết nối đứt ngay khi chạy lệnh. Cách an toàn nhất là làm trực tiếp trên console, hoặc viết script gom toàn bộ lệnh rồi chạy một lần qua nohup sh bridge-setup.sh & — xong rồi SSH lại bằng IP mới trên br0.

Bước 2: Tạo bridge qua Netplan

Ubuntu Server dùng Netplan mặc định — đây là cách persistent, không mất cấu hình sau reboot. Backup trước:

sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak

Cấu hình DHCP:

# /etc/netplan/00-installer-config.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    enp3s0:
      dhcp4: no        # Tắt DHCP trên interface vật lý
      dhcp6: no
  bridges:
    br0:
      interfaces: [enp3s0]   # Gắn enp3s0 vào bridge
      dhcp4: yes             # Bridge nhận IP từ DHCP
      parameters:
        stp: false           # Tắt Spanning Tree (lab thường không cần)
        forward-delay: 0

Hoặc IP tĩnh nếu router không có DHCP hoặc cần IP cố định:

  bridges:
    br0:
      interfaces: [enp3s0]
      addresses: [192.168.1.100/24]
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [1.1.1.1, 8.8.8.8]
      parameters:
        stp: false
        forward-delay: 0

Áp dụng cấu hình:

sudo netplan apply

# Kiểm tra bridge đã tạo chưa
ip addr show br0
bridge link show

Bước 3: Tạo TAP interface thủ công (để hiểu cơ chế)

libvirt tự làm bước này khi khởi động VM. Nhưng làm thủ công một lần giúp hiểu rõ cơ chế — và debug nhanh hơn khi VM báo lỗi network:

# Tạo TAP interface
sudo ip tuntap add tap0 mode tap

# Bring up interface
sudo ip link set tap0 up

# Gắn TAP vào bridge
sudo ip link set tap0 master br0

# Kiểm tra — tap0 phải xuất hiện trong danh sách
bridge link show br0

Xóa sau khi test:

sudo ip link set tap0 nomaster
sudo ip tuntap del tap0 mode tap

Bước 4: Cấu hình libvirt để VM dùng bridge

Đây là cách mình dùng thường xuyên nhất — để libvirt quản lý TAP tự động:

# Tạo file XML định nghĩa network
cat << 'EOF' > /tmp/br0-network.xml
<network>
  <name>br0-network</name>
  <forward mode="bridge"/>
  <bridge name="br0"/>
</network>
EOF

# Đăng ký network với libvirt
virsh net-define /tmp/br0-network.xml
virsh net-start br0-network
virsh net-autostart br0-network

# Kiểm tra
virsh net-list --all

Gắn NIC của VM vào bridge. Cách thứ nhất — chỉnh qua virsh edit:

virsh edit vm-name

# Tìm phần <interface type='network'> và đổi thành:
# <interface type='bridge'>
#   <source bridge='br0'/>
#   <model type='virtio'/>
# </interface>

Hoặc gắn trực tiếp vào VM đang chạy:

virsh attach-interface --domain vm-name \
  --type bridge \
  --source br0 \
  --model virtio \
  --config --live

Bước 5: Khởi động VM và kiểm tra

virsh start vm-name
virsh console vm-name   # Hoặc dùng virt-viewer

# Trong guest OS:
ip addr show     # Phải thấy IP trên dải 192.168.1.x
ping 192.168.1.1 # Ping gateway

# Từ máy khác trong LAN:
ping <IP của VM>       # Phải ping được
ssh user@<IP của VM>   # SSH trực tiếp không qua NAT

Debug khi bridge không hoạt động

Bốn lỗi mình hay gặp nhất — và cách fix nhanh:

  • VM không nhận IP qua DHCP: Chạy bridge fdb show br0 — TAP interface phải xuất hiện. Không thấy thì gắn thủ công: ip link set vnetX master br0. libvirt đôi khi tạo TAP nhưng quên gắn vào bridge sau khi restart host.
  • Mất SSH sau khi chạy netplan apply: IP đã chuyển sang br0. Lấy IP mới bằng ip addr show br0 trực tiếp trên console rồi SSH lại.
  • Packet bị drop dù bridge đúng: libvirt thêm rule iptables FORWARD. Kiểm tra iptables -L FORWARD -n -v. Nếu policy DROP, thêm rule: iptables -I FORWARD -i br0 -j ACCEPT.
  • Bridge không forward packet dù rule iptables đúng: Kiểm tra sysctl net.bridge.bridge-nf-call-iptables. Giá trị 1 nghĩa là bridge gọi iptables cho mọi packet — thử set về 0: sysctl -w net.bridge.bridge-nf-call-iptables=0.

Tổng kết

Bridge + TAP là kiến trúc mạng nền tảng của hầu hết hypervisor phổ biến hiện nay — Proxmox VE, OpenStack Nova, hay libvirt trên Debian đều dùng nguyên lý này bên dưới, chỉ khác ở mức độ tự động hóa phần cấu hình.

Từ khi hiểu rõ luồng Guest NIC → TAP → Bridge → Physical NIC, mình giải quyết hầu hết ticket mạng VM trong vòng 10 phút. Lỗi thường chỉ nằm ở một trong ba điểm: TAP chưa gắn vào bridge, iptables block FORWARD, hoặc sysctl bridge-nf can thiệp.

Muốn đi sâu hơn, hai hướng đáng học tiếp: macvtap — VM dùng thẳng MAC layer của NIC vật lý, bỏ qua bridge, latency thấp hơn; hoặc SR-IOV — NIC vật lý tạo Virtual Functions cấp thẳng cho VM, performance gần bằng bare-metal. Với homelab thông thường thì bridge + TAP là đủ và dễ debug nhất.

Share: