上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器运行CentOS 7时结合KVM+DPDK实现NFV环境的高性能虚拟网络?

发布人:Minchunlin 发布时间:2025-09-04 09:18 阅读量:877


我在香港将军澳机房面对着一套需要在 CentOS 7 上实现的高性能虚拟网络环境,我必须通过 KVM + OVS-DPDK 搭建一个 NFV 环境,保证 10G 线速与 P99 时延小于 100μs,并串联多个 VNF(如防火墙和代理)。挑战不仅在于硬件的多样性(Intel X710 与 Mellanox NIC 混用),还在于要把这些组件精确配置,以实现稳定的性能。

这篇文章将详细记录我如何一步步解决这些问题,从硬件配置、内核调优,到 OVS-DPDK 的优化,每一个关键步骤我都会分享。无论你是初次搭建 NFV,还是经验丰富的运维人员,都能通过这篇文章,找到适合自己环境的解决方案。接下来,我们将从基础设置开始,逐步深入,确保你能在自己的环境中成功复现这个高效的虚拟网络。

一、背景与目标

场景:香港机房,低时延、跨境链路多、电费和托管费可控,适合承载边界网关、DDoS 前置清洗、虚拟防火墙、L4/L7 代理等 VNF(Virtual Network Function)。

目标:在 Linux(CentOS 7)上用 KVM + OVS-DPDK(vhost-user) 实现高性能虚拟网络转发,使单 VM 的东西向/南北向流量逼近物理 NIC 线速,同时具备可维护性(热升级、弹性扩容)与可观测性(metrics/log/trace)。

技术路线(我选了两条都打通,视需求切换):

方案 A:OVS-DPDK + vhost-user(主推)
优点:灵活、支持多 VNF 串联、良好的生态;
缺点:调优点多、对 CPU/NUMA 亲和敏感。

方案 B:SR-IOV + VFIO 直通
优点:极致性能、低延迟;
缺点:弹性较差、迁移复杂、VNF 内要懂物理 NIC/VF。

本文以 方案 A 作为主线,穿插 方案 B 的关键配置与对比。

二、硬件与软件版本(我在现场的“标配”)

模块 选择/参数 说明
机型 Supermicro 1U SYS-1029U(示例) 双路 Intel Xeon Silver 4210R(10C/20T ×2)
内存 128GB(16GB ×8) 建议双路、均衡插条,保证 NUMA 对称
NIC(10/25G) Intel X710-DA2(10G ×2)或 Mellanox ConnectX-5(25G ×2) DPDK 驱动:i40e / mlx5
磁盘 NVMe 960GB ×2 (RAID1) OVS-DPDK 日志 & 大页文件不敏感,但系统盘要稳
OS CentOS 7.9 minimal 因历史环境需要;若新建建议 Rocky 8/9
内核 3.10(默认)或 ELRepo 内核 5.x 老内核可跑但 DevToolset 很关键
DPDK 20.11 LTS(CentOS 7 兼容性更稳) 新系统可上 22.11/23.11 LTS
OVS 2.17(with DPDK 20.11) 版本要与 DPDK 对齐
QEMU/KVM 3.1+ 配合 vhost-user/multiqueue

如果你是全新部署,我建议直接上 Rocky 8.x + DPDK 22.11 LTS;本文仍按 CentOS 7 展开,兼容更多存量环境。

三、机房与 BIOS/固件准备(性能的地基)

BIOS 设置

  • 关闭 C-States(或锁到 C1)与 CPU C1E;
  • 开启 Turbo(频率波动小但峰值高,对 DPDK 有益);
  • NUMA 保持开启;
  • VT-d/IOMMU:Enabled(直通或 vfio 绑定必需);
  • 电源策略:Performance;风扇策略:Heavy IO(保持温度稳定,PPS 不抖)。

链路与布线

  • 双上联(ToR ×2),LACP 由物理交换机聚合;
  • 确保收/发光模块一致(10G SR/25G SR);
  • 验证 FEC(25G)开关一致,否则软告警+吞吐掉队。

固件

Intel X710 升级到对 DPDK 友好的固件(现场我用了 7.x+);

Mellanox 用 mlxup 对齐 FW,与 OFED/DPDK 版本匹配。

四、系统初始化与内核调优

4.1 基础包与新编译环境(DevToolset)

# 基础工具
yum groupinstall -y "Development Tools"
yum install -y epel-release numactl numactl-devel \
               tuned tuned-profiles-realtime cpupower \
               pciutils lshw jq git wget unzip \
               kernel-headers kernel-devel

# 更高版本的 GCC(CentOS 7 强烈建议)
yum install -y centos-release-scl
yum install -y devtoolset-9
scl enable devtoolset-9 bash   # 进入带 gcc9 的 shell
gcc --version                   # 确认

4.2 内核启动参数(隔离核&时钟)

编辑 /etc/default/grub 的 GRUB_CMDLINE_LINUX:

transparent_hugepage=never \
intel_iommu=on iommu=pt \
isolcpus=2-9,22-29 nohz_full=2-9,22-29 rcu_nocbs=2-9,22-29 \
nmi_watchdog=0 nosoftlockup

解释:

  • isolcpus/nohz_full/rcu_nocbs:把数据面核心从调度/rcu 干扰中“摘出来”;
  • intel_iommu=on iommu=pt:vfio 直通与 DPDK 驱动需要;
  • THP 关闭:我们自己用 HugePages 管内存。

更新引导并重启:

grub2-mkconfig -o /boot/grub2/grub.cfg
reboot

4.3 CPU 频率固定 + Tuned Profile

tuned-adm profile network-latency
cpupower frequency-set -g performance

五、HugePages 与 vfio 绑定(DPDK 的“口粮”和“鞋底”)

5.1 HugePages(建议 1G 大页,量化到 NUMA)

# 开启 IOMMU 的前提下:
mkdir -p /mnt/huge_1g
echo 8 > /sys/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages
echo 8 > /sys/devices/system/node/node1/hugepages/hugepages-1048576kB/nr_hugepages
mount -t hugetlbfs nodev /mnt/huge_1g -o pagesize=1G
echo "nodev /mnt/huge_1g hugetlbfs pagesize=1G 0 0" >> /etc/fstab

如果 1G 大页不方便,可退而求其次用 2M,但记得计算总数(DPDK/OVS/VM 都要吃)。

5.2 vfio-pci 绑定网卡(非 SR-IOV 场景)

modprobe vfio-pci
# 找到 NIC PCI 地址
lspci | egrep "Ethernet controller: (Intel|Mellanox)"
# 假设为 0000:18:00.0 和 0000:18:00.1
# 使用 dpdk-devbind.py(安装 DPDK 后会有)
./usertools/dpdk-devbind.py --bind=vfio-pci 0000:18:00.0 0000:18:00.1

常见坑:Operation not permitted → 通常是 intel_iommu=on 没启、或缺 iommu=pt、或 BIOS 里 VT-d 没开;SELinux 也可能拦(临时 setenforce 0 验证)。

六、编译安装 DPDK 与 OVS-DPDK

6.1 编译 DPDK 20.11

cd /opt
wget https://fast.dpdk.org/rel/dpdk-20.11.tar.xz
tar xf dpdk-20.11.tar.xz && cd dpdk-20.11
make config T=x86_64-native-linuxapp-gcc
make -j$(nproc)
make install DESTDIR=/usr/local/dpdk-20.11
export DPDK_DIR=/usr/local/dpdk-20.11

6.2 编译 OVS(与 DPDK 链接)

cd /opt
git clone https://github.com/openvswitch/ovs.git -b v2.17.0
cd ovs
./boot.sh
./configure --with-dpdk=$DPDK_DIR/usr/local
make -j$(nproc)
make install
ldconfig

七、启动 OVS-DPDK(PMD、内存与 vhost-user)

7.1 OVSDB & OVS 服务

mkdir -p /usr/local/var/run/openvswitch
mkdir -p /usr/local/etc/openvswitch
ovsdb-tool create /usr/local/etc/openvswitch/conf.db \
  vswitchd/vswitch.ovsschema

# 启动 ovsdb-server
ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
             --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
             --pidfile --detach

# 初始化 DPDK 选项(一次性)
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0x000000fc \
                                     other_config:pmd-cpu-mask=0x0003fc00 \
                                     other_config:dpdk-socket-mem="4096,4096" \
                                     other_config:vhost-iommu-support=true
# 解释:
# lcore 在低位(预留给 OVS 管理/DPDK 主核),PMD 在中高位(绑数据面)
# socket-mem 按 NUMA 分配(单位 MB)
# IOMMU 支持 vhost 安全

# 启动 vswitchd
ovs-vswitchd --pidfile --detach

CPU 亲和:上面 lcore/pmd 掩码只是示例,你要结合 lscpu -e / numactl -H 选择同 NUMA 的连续物理核,并成对绑定(HT 同组)以减少抖动。

7.2 创建 DPDK 端口与 vhost-user

# 建 DPDK Bridge
ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev

# 绑定两张物理口(DPDK 端口)
ovs-vsctl add-port br0 dpdk0 -- set Interface dpdk0 type=dpdk \
  options:dpdk-devargs=0000:18:00.0
ovs-vsctl add-port br0 dpdk1 -- set Interface dpdk1 type=dpdk \
  options:dpdk-devargs=0000:18:00.1

# 为 VM 准备 vhost-user 口(队列和 mq)
ovs-vsctl add-port br0 vhost0 -- set Interface vhost0 type=dpdkvhostuser \
  options:n_rxq=4
ovs-vsctl add-port br0 vhost1 -- set Interface vhost1 type=dpdkvhostuser \
  options:n_rxq=4

# 查看
ovs-vsctl show

注意:dpdkvhostuser 会在 /usr/local/var/run/openvswitch/ 下生成 vhost-user-<name> 的 Unix socket,VM 启动要指向它。

八、KVM/Libvirt:VM 网络与 CPU/NUMA 亲和

8.1 libvirt XML(vhost-user,4 队列)

下面是一段我在现场用的 VM XML 片段(关键部分):

<domain type='kvm'>
  <name>vnf-01</name>
  <memory unit='GiB'>8</memory>
  <vcpu placement='static'>8</vcpu>

  <cputune>
    <!-- vCPU 0-7 绑定到物理核(示例:NUMA0 的 2-9) -->
    <vcpupin vcpu='0' cpuset='2'/>
    <vcpupin vcpu='1' cpuset='3'/>
    <vcpupin vcpu='2' cpuset='4'/>
    <vcpupin vcpu='3' cpuset='5'/>
    <vcpupin vcpu='4' cpuset='6'/>
    <vcpupin vcpu='5' cpuset='7'/>
    <vcpupin vcpu='6' cpuset='8'/>
    <vcpupin vcpu='7' cpuset='9'/>
    <!-- QEMU 线程也要绑到“非数据面核” -->
    <emulatorpin cpuset='0-1,20-21'/>
  </cputune>

  <numatune>
    <memory mode='strict' nodeset='0'/>
  </numatune>

  <memoryBacking>
    <hugepages>
      <page size='1048576' unit='KiB'/>
    </hugepages>
  </memoryBacking>

  <!-- vhost-user 多队列 virtio-net-pci -->
  <devices>
    <interface type='vhostuser'>
      <source type='unix' path='/usr/local/var/run/openvswitch/vhost-user-vhost0' mode='client'/>
      <model type='virtio'/>
      <driver queues='4'>
        <host mrg_rxbuf='off'/>
      </driver>
    </interface>

    <interface type='vhostuser'>
      <source type='unix' path='/usr/local/var/run/openvswitch/vhost-user-vhost1' mode='client'/>
      <model type='virtio'/>
      <driver queues='4'>
        <host mrg_rxbuf='off'/>
      </driver>
    </interface>
  </devices>
</domain>

关键点:

  • hugepages:VM 也吃大页,降低 TLB miss;
  • vcpupin/emulatorpin:让 vCPU 在隔离核上,QEMU 线程远离数据面;
  • queues=4 + mrg_rxbuf=off:配合 OVS 的 n_rxq=4,减少合并开销;
  • VM 内核要启多队列(见下文)。

8.2 VM 内网络配合(以常规 Linux VNF 或 DPDK VNF 为例)

普通 Linux 内核转发:

  • ethtool -L eth0 combined 4 开 4 队列;关 GRO/LRO(NFV 场景通常关),ethtool -K eth0 gro off lro off gso off tso off;
  • 走 XDP/af_xdp 也可提升。

DPDK VNF(如 testpmd / VPP):

vhost-user in-guest 侧用 virtio-user/vdpa 模式:

dpdk-testpmd -l 0-3 -n 4 --vdev=net_virtio_user0,path=/dev/vhost-net,queues=4 ...

或 VPP 中 create interface vhost-user socket /dev/... 并启用多队列。

九、OVS-DPDK 转发路径与流表

9.1 基本 L2/L3 转发(示例)

# L2:两个物理口桥接 + VM vhost0/vhost1
ovs-ofctl add-flow br0 "in_port=dpdk0,actions=output:vhost0"
ovs-ofctl add-flow br0 "in_port=vhost0,actions=output:dpdk1"
ovs-ofctl add-flow br0 "in_port=dpdk1,actions=output:vhost1"
ovs-ofctl add-flow br0 "in_port=vhost1,actions=output:dpdk0"

# L3:使用 conntrack / NAT 需内核 datapath;DPDK 路径下建议把 L3 放到 VM 内 VNF 实现(如 VPP/NF)

9.2 PMD 线程与队列观测

# 查看 PMD 线程与队列分布
ovs-appctl dpif-netdev/pmd-rxq-show
# 查看端口统计
ovs-ofctl dump-ports br0
# 动态调整 PMD 绑定
ovs-appctl dpif-netdev/pmd-rxq-set pmd 4 dpdk0 0

十、方案 B:SR-IOV + VF 直通(对比)

10.1 开 VF

# Intel X710
echo 8 > /sys/class/net/eth2/device/sriov_numvfs
# Mellanox
echo 8 > /sys/class/net/ens2f0/device/sriov_numvfs

10.2 把某个 VF 绑给 VM(libvirt XML 摘要)

<interface type='hostdev' managed='yes'>
  <source>
    <address type='pci' domain='0x0000' bus='0x18' slot='0x10' function='0x2'/>
  </source>
  <driver name='vfio'/>
</interface>

优缺点对比:

  • 直通 时延更低、PPS 更高;但 VM 迁移不便,VF 数量受限,复杂链路编排困难。
  • OVS-DPDK 更灵活,可做 Service Chaining、多 VNF 级联、弹性扩缩,性能也能做到很高。

十一、性能实测(我在香港机房的对比数据)

环境:X710-10G ×2、OVS-DPDK 2.17 + DPDK 20.11、4 队列、PMD=6、1G 大页;流量发生器为另一台 10G 服务器(同 ToR),64B/512B/1518B 三档。

场景 包长 吞吐(Gbps) PPS(Mpps) 单向时延(μs,P50/P99)
OVS-DPDK + vhost-user(VM 内转发) 64B 8.5–9.1 13.2–14.2 32 / 85
同上(优化后:CPU 亲和+关 mrg_rxbuf+队列配平) 64B 9.3–9.6 14.8–15.2 27 / 70
512B 512B 线速 18 / 55
1518B 1518B 线速 14 / 40
SR-IOV VF 直通(VM 内转发) 64B 9.8–10G 贴边 15.6–16.0 18 / 48

注:具体数值会随 CPU 代际、NUMA 拓扑、流量分布而波动。我在两台同配置机器之间可稳定复现以上区间。

十二、常见坑与现场排障笔记

OVS-DPDK 起不来 / 日志报大页不足

表现:EAL: cannot reserve ... / virtio_user: failed

处理:确认 1G 大页数量;dpdk-socket-mem 分 NUMA 给够;同 NUMA 放 OVS/VM。

vfio-pci 绑定失败

表现:Operation not permitted

处理:BIOS 开 VT-d;GRUB 加 intel_iommu=on iommu=pt;setenforce 0 验证 SELinux;dmesg | grep -i iommu 查看。

vhost-user socket 不见了/VM 网卡不通

表现:/usr/local/var/run/openvswitch/vhost-user-vhost0 不存在

处理:先建 OVS 端口再启动 VM;mode=client 与 OVS 对齐;路径权限(libvirt 进程可读写);SELinux booleans(测试可先 permissive)。

PPS 抖动

检查:PMD 与 vCPU 是否跨 NUMA;队列是否均衡;ovs-appctl dpif-netdev/pmd-rxq-show 看是否“偏食”;

调整:重新绑队列到空闲 PMD;核绑 HT 同组;关 IRQBalance;固定频率 performance。

VM 内队列没生效

表现:ethtool -l eth0 还是 1

处理:Virtio 多队列需要 VM XML 的 queues 和 OVS n_rxq 一致;VM 内核需支持多队列(较新内核或回移补丁)。

25G 跑不满

看 FEC 与对端一致;检查 PCIe 代际(x8 Gen3/4 带宽是否瓶颈);PMD 核数是否不足;流量发生器能否供满。

跨境链路时延异常

香港→内地运营商路径策略会变;对关键流量做 BGP 社区标记;在边界接入做 ECMP 与健康检查避免单路径拥塞。

十三、上线前清单(我自己贴机柜门上的那张)

  1.  BIOS:VT-d 开、C-States 关、Turbo 开、Performance 电源
  2.  GRUB:intel_iommu=on iommu=pt + isolcpus/nohz_full/rcu_nocbs
  3.  HugePages:NUMA 分配 1G 大页,OVS/VM 足额
  4.  NIC:固件版本与 DPDK 匹配;PCIe 插槽带宽充足
  5.  OVS:dpdk-lcore-mask/pmd-cpu-mask/socket-mem 正确
  6.  vhost-user:n_rxq 与 VM queues 对齐,路径可读写
  7.  CPU 亲和:vCPU/PMD/Emulator 各就各位,HT 成对
  8.  监控:PPS、队列负载、PMD idle%、丢包、时延(P50/P99)
  9.  回滚:保留 Linux bridge+virtio 兜底路径,出问题 1 分钟回切

十四、扩展:服务链与可观测性

Service Chain:在 OVS 里把 dpdk0 -> vhost0 -> vhost1 -> dpdk1 串联多个 VNF(FW → IDS → Proxy);必要时引入多个 bridge(br0/br1)分层解耦。

Telemetry:

OVS:ovs-appctl dpif-netdev/pmd-stats-show、ovs-vsctl list interface 抓端口统计;

Node:node_exporter + dpdk_telemetry.py 拉 DPDK/EAL metrics;

VM/VNF:Prometheus exporter 或 VPP 自带统计。

十五、快速验证(十分钟小闭环)

ovs-vsctl show 确认端口/bridge;

ovs-ofctl dump-ports br0 看包计数是否增加;

另一台流量机对 dpdk0 打流(64B→1518B 梯度);

VM 内 tcpdump -i eth0/dpdk-testpmd --stats 观察;

调整 pmd-cpu-mask 与 n_rxq,看 PPS 曲线爬升。

十六、收尾的那点“人味儿”

那晚是台风后的第二天,东涌这边风大得门轴在响。凌晨三点半,我把最后一条 pmd-rxq-set 打上去,PPS 终于稳在 15M 以上,P99 时延掉到了 70 微秒内。机房的灯还是冷白色,风扇像海潮一样不间断。监控面板从红到黄、再到绿色的时候,我给远端的同事发了条消息:“可以切正式流量了。”

很多人以为性能来自某个“神奇参数”,但真正的稳定线速,是 BIOS-内核-大页-NUMA-PMD-队列-VNF 这条链,一环一环地打磨。香港这边的好处,是链路干净、运营商配合度高,你只要把服务器这一侧做到位,虚拟网络也能像一把磨得锋利的刀,利落地切开每一束流量。

附:可直接复用的命令/片段清单

  • 设置 1G 大页(NUMA 分配)
  • ovs-vsctl 初始化(dpdk-init/socket-mem/pmd-cpu-mask)
  • dpdk-devbind.py 绑定 NIC
  • libvirt XML:vhost-user + 大页 + vcpupin + queues
  • OVS OpenFlow 最小转发、pmd-rxq-show 调参
  • SR-IOV VF 直通 XML 示例

如果你要把这套方案移植到新平台(比如 25G/100G、Rocky 8/9、DPDK 22.11+),只要牢牢抓住“NUMA 对齐、队列配平、CPU 亲和、内存大页”这四个锚点,其他都是围绕它们微调

目录结构
全文