Skip to content
JKX'home|金开心的博客
Go back

VPP连接虚机实验

Updated:

本章将介绍如何将 FD.io VPP 与虚拟机一起使用。我们描述如何使用 VPP 创建 Vhost 端口以及如何将它们连接到 VPP。我们还将讨论 Vhost 的一些限制。这里默认已经安装好了可以配置的VPP。

拓扑

image.png

创建虚拟接口

注意,现在没有任何虚拟接口。我们只用一个已启动的物理接口。该物理接口连接到正在运行的另一个linux系统。我们将使用该系统通过 ping 验证与虚拟机的连接。

jkx@duck:~/vpp$ make run
perfmon               [warn  ]: skipping source 'intel-uncore' - intel_uncore_init: no uncore units found
vat-plug/load         [error ]: vat_plugin_register: oddbuf plugin not loaded...
vat-plug/load         [error ]: vat_plugin_register: idpf plugin not loaded...
    _______    _        _   _____  ___ 
 __/ __/ _ \  (_)__    | | / / _ \/ _ \
 _/ _// // / / / _ \   | |/ / ___/ ___/
 /_/ /____(_)_/\___/   |___/_/  /_/    

DBGvpp# 
DBGvpp# sh int
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
eth1                              1     down         9000/0/0/0     
eth2                              2     down         9000/0/0/0     
eth3                              3     down         9000/0/0/0     
eth4                              4     down         9000/0/0/0     
local0                            0     down          0/0/0/0       

接下来使用 create vhost user 命令创建虚拟端口。此命令将在 VPP 中创建虚拟端口,创建 VM 将用于连接到 VPP 的 Linux 套接字。可以使用 VPP 作为套接字服务器或客户端来创建端口。 创建 VPP 端口:

DBGvpp# create vhost socket /tmp/vm00.sock
VirtualEthernet0/0/0
DBGvpp# sh int
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
VirtualEthernet0/0/0              5     down         9000/0/0/0     
eth1                              1     down         9000/0/0/0     
eth2                              2     down         9000/0/0/0     
eth3                              3     down         9000/0/0/0     
eth4                              4     down         9000/0/0/0     
local0                            0     down          0/0/0/0       
DBGvpp# 

在此示例中,我们创建虚拟接口作为客户端。我们可以使用 show vhost 命令获取有关虚拟主机连接的更多详细信息。

DBGvpp# sh vhost
Virtio vhost-user interfaces
Global:
  coalesce frames 32 time 5e-1
  Number of rx virtqueues in interrupt mode: 0
  Number of GSO interfaces: 0
  Thread 0: Polling queue count 0
  Thread 1: Polling queue count 1
Interface: VirtualEthernet0/0/0 (ifindex 5)
  Number of qids 2
virtio_net_hdr_sz 0
 features mask (0xfffffffbdfffa27c): 
 features (0x0): 
  protocol features (0x0)

 socket filename /tmp/vm00.sock type client errno "No such file or directory"

 rx placement: 
   thread 1 on vring 1, polling
 tx placement

 Memory regions (total 0)

注意 “No such file or directory”。这是因为虚拟机尚未创建。

创建虚拟机

使用xml文件来创建虚拟机(xml文件见附件)

jkx@duck:~$ virsh create ./iperf3-vm.xml
Domain 'iperf-server' created from ./iperf3-vm.xml

jkx@duck:~$ virsh list
 Id   Name           State
------------------------------
 1    iperf-server   running

在虚拟机成功创建并运行之后,你可以通过 virsh dumpxml 命令,反向导出该虚拟机当前完整的 XML 配置文件。

jkx@duck:~$ # 将屏幕上显示的配置信息保存为一个新的文件备份
virsh dumpxml iperf-server > backup-iperf.xml

virsh create my-vm.xml 启动了虚拟机,这个虚拟机是“瞬时”的(关机即消失)。如果你想把它变成“持久”的,就需要把 dumpxml 导出的内容保存下来。

创建虚拟机后,请注意套接字文件名显示成功并且存在内存区域。此时VM和FD.io VPP已连接。另请注意 qsz 1024。256 的队列大小将影响虚拟主机吞吐量。 qsz 应为 1024。在 QEMU 2.10.0 和 libvirt 3.7.0 及更高版本中,这是在 xml 文件中使用上面和示例中显示的行 指定的。

jkx@duck:/tmp$ ls -la
total 84
drwxrwxrwt 19 root         root 4096 Apr 17 14:48 .
drwxr-xr-x 23 root         root 4096 Feb  6 17:02 ..
srwxrwxr-x  1 libvirt-qemu kvm     0 Apr 17 14:33 vm00.sock
DBGvpp# sh vhost
Virtio vhost-user interfaces
Global:
  coalesce frames 32 time 5e-1
  Number of rx virtqueues in interrupt mode: 0
  Number of GSO interfaces: 0
  Thread 0: Polling queue count 0
  Thread 1: Polling queue count 1
Interface: VirtualEthernet0/0/0 (ifindex 5)
  Number of qids 2
virtio_net_hdr_sz 12
 features mask (0xfffffffbdfffa27c): 
 features (0x150208000): 
   VIRTIO_NET_F_MRG_RXBUF (15)
   VIRTIO_NET_F_GUEST_ANNOUNCE (21)
   VIRTIO_RING_F_INDIRECT_DESC (28)
   VHOST_USER_F_PROTOCOL_FEATURES (30)
   VIRTIO_F_VERSION_1 (32)
  protocol features (0x3)
   VHOST_USER_PROTOCOL_F_MQ (0)
   VHOST_USER_PROTOCOL_F_LOG_SHMFD (1)

 socket filename /tmp/vm00.sock type client errno "Success"

 rx placement: 
   thread 1 on vring 1, polling
 tx placement
   threads 0-1 on vring 0: spin-lock

 Memory regions (total 1)
 region fd    guest_phys_addr    memory_size        userspace_addr     mmap_offset        mmap_addr
 ====== ===== ================== ================== ================== ================== ==================
  0     43    0x0000000000000000 0x0000000040000000 0x000077e580000000 0x0000000000000000 0x0000746600000000

 Virtqueue 0 (TX)
  global TX queue index 8
  qsz 1024 last_avail_idx 0 last_used_idx 0 last_kick 0
  avail.flags 0 avail event idx 0 avail.idx 0 used.flags 1 used event idx 0 used.idx 0
  kickfd 44 callfd 45 errfd 40

 Virtqueue 1 (RX)
  global RX queue index 4
  qsz 1024 last_avail_idx 0 last_used_idx 0 last_kick 0
  avail.flags 0 avail event idx 0 avail.idx 0 used.flags 1 used event idx 0 used.idx 0
  kickfd 39 callfd 46 errfd 42

桥接接口

将两接口(连接外部linux的eth1接口、刚刚创建的vhost接口)桥接到二层桥上面

DBGvpp# sh int
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
VirtualEthernet0/0/0              5     down         9000/0/0/0     
eth1                              1     down         9000/0/0/0     
eth2                              2     down         9000/0/0/0     
eth3                              3     down         9000/0/0/0     
eth4                              4     down         9000/0/0/0     
local0                            0     down          0/0/0/0       
DBGvpp# set interface l2 bridge VirtualEthernet0/0/0 100
DBGvpp# set interface l2 bridge eth1 100                
DBGvpp# sh bridge-domain 
  BD-ID   Index   BSN  Age(min)  Learning  U-Forwrd   UU-Flood   Flooding  ARP-Term  arp-ufwd Learn-co Learn-li   BVI-Intf 
   100      1      0     off        on        on       flood        on       off       off        0    16777216     N/A    

DBGvpp# sh bridge-domain 100 detail
  BD-ID   Index   BSN  Age(min)  Learning  U-Forwrd   UU-Flood   Flooding  ARP-Term  arp-ufwd Learn-co Learn-li   BVI-Intf 
   100      1      0     off        on        on       flood        on       off       off        0    16777216     N/A    
span-l2-input l2-input-classify l2-input-feat-arc l2-policer-classify l2-input-acl vpath-input-l2 l2-ip-qos-record l2-input-vtr l2-learn l2-rw l2-fwd l2-flood l2-flood l2-output 

           Interface           If-idx ISN  SHG  BVI  TxFlood        VLAN-Tag-Rewrite       
     VirtualEthernet0/0/0        5     1    0    -      *                 none             
             eth1                1     1    0    -      *                 none             
DBGvpp# 

启动并配置IP

启动vpp上桥接的接口:

DBGvpp# set interface state VirtualEthernet0/0/0 up
DBGvpp# set interface state eth1 up                
DBGvpp# sh int
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
VirtualEthernet0/0/0              5      up          9000/0/0/0     
eth1                              1      up          9000/0/0/0     

配置虚拟机和外部linux的接口地址

duck内部的虚拟机:

jkx@duck:/tmp$ virsh console iperf-server 
Connected to domain 'iperf-server'
Escape character is ^] (Ctrl + ])

root@ubuntu:~# ip a
---
2: enp1s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 52:54:00:4c:47:f2 brd ff:ff:ff:ff:ff:ff
root@ubuntu:~# sudo ip addr add 192.168.150.10/24 dev enp1s0
root@ubuntu:~# sudo ip link set enp1s0 up
root@ubuntu:~# ip a
---
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:4c:47:f2 brd ff:ff:ff:ff:ff:ff
    inet 192.168.150.10/24 scope global enp1s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe4c:47f2/64 scope link 
       valid_lft forever preferred_lft forever

外部的linux主机

(base) test@test-Default-string:~$ sudo ip addr add 192.168.150.15/24 dev enp6s0
(base) test@test-Default-string:~$ sudo ip link set enp6s0 up
(base) test@test-Default-string:~$ ifconfig
enp6s0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.150.15  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 88:06:5b:50:ac:d3  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device memory 0xb1100000-b11fffff  

外部linux ping内部虚拟机

远程 Linux 系统的 IP 地址为“192.168.150.15”,我们现在可以从虚拟机访问它。 使用“virsh console”命令连接到虚拟机。 “ctrl-]”退出。

root@ubuntu:~# ping 192.168.150.15
PING 192.168.150.15 (192.168.150.15) 56(84) bytes of data.
64 bytes from 192.168.150.15: icmp_seq=1 ttl=64 time=1.69 ms
64 bytes from 192.168.150.15: icmp_seq=2 ttl=64 time=0.418 ms
64 bytes from 192.168.150.15: icmp_seq=3 ttl=64 time=0.428 ms
64 bytes from 192.168.150.15: icmp_seq=4 ttl=64 time=0.427 ms
64 bytes from 192.168.150.15: icmp_seq=5 ttl=64 time=0.414 ms

在 VPP 上,现在可以看到数据包计数在增加。来自虚拟机的数据包在 VirtualEthernet0/0/0 上被视为接收数据包,然后桥接到eth1,并被视为作为发送数据包离开系统。在进来的路上情况正好相反。

DBGvpp# sh int
              Name               Idx    State  MTU (L3/IP4/IP6/MPLS)     Counter          Count     
VirtualEthernet0/0/0              5      up          9000/0/0/0     rx packets                   167
                                                                    rx bytes                   15650
                                                                    tx packets                   209
                                                                    tx bytes                   24508
                                                                    drops                         11
eth1                              1      up          9000/0/0/0     rx packets                   209
                                                                    rx bytes                   24508
                                                                    tx packets                   156
                                                                    tx bytes                   14784
                                                                    tx-error                      11

删除虚拟机

关闭、启动虚机

virsh shutdown iperf-server
virsh start iperf-server

删除虚机

jkx@duck:~$ virsh list
 Id   Name           State
------------------------------
 1    iperf-server   running
virsh destroy iperf-server

删除vhost接口

vpp# delete vhost-user VirtualEthernet0/0/0
sh int
重启查看

vhost接口限制

1. 性能天花板 (Performance)

下面测试链接明确给出了一个性能基准,让你对 vHost 的吞吐量有一个心理预期:

2. 模式限制:只能当“设备” (Device Mode Only)

技术细节:

3. 不支持的功能 (Unsupported Features)

这里列出了三个在普通物理网卡上常见、但在 VPP vHost 中缺失的功能:


**一句话总结:**vHost 虽然好用,但它是为了兼容虚拟机而存在的,在性能和功能完备性上相比原生 DPDK 或 memif 都有所牺牲。

遇到的小问题

大页分配的问题给虚拟机宿主机duck分配了2个1G大小的大页,但是虚拟机中无可用的大页导致无法创建虚机。给duck新增一个大页解决问题。

jkx@duck:~$ virsh create ./iperf3-vm.xml
error: Failed to create domain from ./iperf3-vm.xml
error: internal error: QEMU unexpectedly closed the monitor (vm='iperf-server'): 2026-04-17T03:57:00.703959Z kvm: -chardev socket,id=charnet0,path=/tmp/vm00.sock,server=on: info: QEMU waiting for connection on: disconnected:unix:/tmp/vm00.sock,server=on
2026-04-17T03:57:03.134555Z kvm: unable to map backing store for guest RAM: Cannot allocate memory

附录

jkx@duck:~$ cat iperf3-vm.xml 
<domain type='kvm'>
  <name>iperf-server</name>
  <memory unit='KiB'>1048576</memory>
  <currentMemory unit='KiB'>1048576</currentMemory>
  <memoryBacking>
    <hugepages>
      <page size='1048576' unit='KiB'/>
    </hugepages>
  </memoryBacking>
  <vcpu placement='static'>1</vcpu>
  <os>
    <type arch='x86_64' machine='q35'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <cpu mode='host-model'>
    <model fallback='allow'></model>
    <numa>
      <cell id='0' cpus='0' memory='1048576' unit='KiB' memAccess='shared'/>
    </numa>
  </cpu>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
  </pm>
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/var/lib/libvirt/images/ubuntu22-vpp.img'/>
      <target dev='vda' bus='virtio'/>
    </disk>

    <controller type='pci' index='0' model='pcie-root'/>
    
    <controller type='usb' index='0' model='qemu-xhci'/>

    <controller type='virtio-serial' index='0'/>

    <interface type='vhostuser'>
      <mac address='52:54:00:4c:47:f2'/>
      <source type='unix' path='/tmp/vm00.sock' mode='server'/>
      <model type='virtio'/>
      <driver rx_queue_size='1024' tx_queue_size='1024'/>
    </interface>

    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>

    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>

    <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'>
      <listen type='address' address='127.0.0.1'/>
    </graphics>

    <memballoon model='virtio'/>
  </devices>
</domain>

Share this post on:

Next Post
lxc部署博客推荐方案