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

直播平台如何在香港服务器的Ubuntu环境中启用SR-IOV网卡直通,降低推流延迟?

发布人:Minchunlin 发布时间:2025-09-10 18:27 阅读量:829


香港葵涌的机房的凌晨两点,直播前端运营在钉钉里不断催促:“深圳主播的推流,起码有 180~220ms 的接入抖动”。业务侧的诉求很明确:把接入延迟稳稳地压到 80ms 内,同时保持 CPU 较低占用。我们已经把应用侧该做的都做了(TCP 拥塞、RTMP/SRT 缓冲、线程绑核),最后还是指向了虚拟化网络的开销——当时接入节点跑在 KVM 上,走的是 vSwitch(OVS)+ virtio。SR-IOV 直通成了当晚唯一能“快刀斩乱麻”的选项。

机房与硬件环境(真实参数)

规格
机型 Dell PowerEdge R650 (双路)
CPU 2 × Intel Xeon Gold 6338(32C/64T @ 2.0GHz),NUMA=2
内存 256GB DDR4-3200
系统 Ubuntu Server 22.04.4 LTS(内核 5.15)
虚拟化 KVM + QEMU 6.2,libvirt 8.x
网卡(PF) 2 × Intel X710-DA2(每张 2×10GbE,驱动 i40e),固件 9.x
交换机 Arista 7050 系列,ToR 侧聚合上联至 HKT/CMI
直播接入 RTMP + SRT(SRT 作为低延时优先)
VM 数量 每台宿主跑 68 个接入 VM(单 VM 负责 35Gbps 总吞吐)

选择 X710 的原因:SR-IOV 成熟,单 PF 可开较多 VF,且驱动栈在 Ubuntu 上比较省心。Mellanox/ConnectX-5 也很好,但这批机器到港时标配是 X710。

SR-IOV 的价值(对直播推流的直观意义)

  • 绕开 vSwitch:VF 由硬件在 PF 上“切出来”,VM 直连 VF,少了 vhost/virtio/OVS 的上下文切换和复制,P99 延迟显著收敛。
  • 更低 CPU 抖动:中断路径更短、软中断压力下降,应用线程的时间片更稳定。
  • 带宽与 QoS 可控:PF 对 VF 有原生的 VLAN、速率限制、信任模式,可按 VM/业务线做隔离。

开工前 Checklist(5 分钟自检)

  • BIOS:开启 Intel VT-d / SR-IOV / Above 4G Decoding;如果有 PCIe ACS 选项也开(便于 IOMMU 分组)。
  • GRUB:intel_iommu=on iommu=pt(AMD 平台则 amd_iommu=on iommu=pt),更新 update-grub 后重启。

驱动版本:确认 i40e 正常、固件不落后太多:

ethtool -i ens2f0
ethtool -i ens2f1

NUMA 对齐:网卡和将要绑核的 CPU 在同一 NUMA 节点:

numactl -H
lspci -tv | grep -i ether
cat /sys/class/net/ens2f0/device/numa_node

步骤一:启用 SR-IOV 并创建 VF

方式 A:sysfs 即时生效(X710 推荐)

# 每个 PF 开 8 个 VF(视容量而定,别一上来就 64)
echo 8 | sudo tee /sys/class/net/ens2f0/device/sriov_numvfs
echo 8 | sudo tee /sys/class/net/ens2f1/device/sriov_numvfs

# 验证
lspci | grep -i virtual
ip link show ens2f0 | grep VF

⚠️ 如果报 Device or resource busy,说明已经创建过 VF,要先 echo 0 再重新设置:

echo 0 | sudo tee /sys/class/net/ens2f0/device/sriov_numvfs
echo 8 | sudo tee /sys/class/net/ens2f0/device/sriov_numvfs

方式 B:开机持久化(systemd)

sudo tee /etc/systemd/system/sriov-config.service <<'EOF'
[Unit]
Description=Configure SR-IOV VFs
After=network-pre.target
Before=network.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo 8 > /sys/class/net/ens2f0/device/sriov_numvfs'
ExecStart=/bin/sh -c 'echo 8 > /sys/class/net/ens2f1/device/sriov_numvfs'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now sriov-config.service

步骤二:为 VF 配置 MAC/VLAN/QoS(在宿主机 PF 上操作)

给每个 VF 绑定固定 MAC 和 VLAN,并设置信任模式(做 SRT/RTMP 多连接时更稳):

# 以 ens2f0 为例,为 VF 0~3 配置
ip link set ens2f0 vf 0 mac 52:54:00:10:00:01 vlan 120 spoofchk on trust on
ip link set ens2f0 vf 1 mac 52:54:00:10:00:02 vlan 120 spoofchk on trust on
ip link set ens2f0 vf 2 mac 52:54:00:10:00:03 vlan 120 spoofchk on trust on
ip link set ens2f0 vf 3 mac 52:54:00:10:00:04 vlan 121 spoofchk on trust on

# 适当的速率限制,单位 Mbps,防止某个 VM 打满上行
ip link set ens2f0 vf 0 rate 3000

交换机侧把 ToR 端口打成 trunk,承载业务 VLAN(如 120/121)。如果你用 ip link set PF vf N vlan X,VF 出来的就是打好标签的“接入二层”,无需再让 VM 自己打 VLAN,路径更短更稳定。

步骤三:把 VF 直通到 KVM 虚机(VFIO)

找到 VF 的 PCI 地址:

# 典型地,PF 是 0000:af:00.0 / 00.1,VF 会是 0000:af:00.2/… 之类
lspci -nn | grep -i ether

绑定到 vfio-pci(libvirt 也可自动做,这里演示手工):

VF=0000:af:00.4
echo $VF | sudo tee /sys/bus/pci/devices/$VF/driver/unbind
echo 8086 154c | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id   # 8086:154c 是 X710 VF 的常见 ID
echo $VF | sudo tee /sys/bus/pci/drivers/vfio-pci/bind

在 libvirt 添加 hostdev(节选):

<hostdev mode='subsystem' type='pci' managed='yes'>
  <driver name='vfio'/>
  <source>
    <address domain='0x0000' bus='0xaf' slot='0x00' function='0x4'/>
  </source>
</hostdev>

NUMA 与大页对齐(把 vCPU/内存/hugepages 固定在与网卡同 NUMA):

<cpu placement='static'>
  <numa>
    <cell id='0' cpus='0-15' memory='131072' unit='KiB'/>
  </numa>
</cpu>
<memoryBacking>
  <hugepages/>
</memoryBacking>
<cputune>
  <vcpupin vcpu='0' cpuset='0'/>
  <emulatorpin cpuset='0-1'/>
</cputune>

经验:直通 VF 的虚机无法无缝热迁移(除非用厂商特性配合),生产要保留一块 virtio 辅助管理口,以便在 VF 故障时可以回切。

步骤四:虚机内的网卡与网络栈调优(Ubuntu 22.04)

1)确认 VF 在来宾机内加载

lspci -nn | grep -i ether      # 能看到 Intel X710 VF
ip a                            # 通常叫 ens3/ens4
ethtool -k ens3                 # 看 offload 能力

2)Netplan 配置(静态 IP 示例)

# /etc/netplan/01-sriov.yaml
network:
  version: 2
  ethernets:
    ens3:
      dhcp4: no
      addresses: [203.0.113.10/27]
      gateway4: 203.0.113.1
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]
      mtu: 1500

sudo netplan apply

3)关闭会拉高延迟的聚合(在来宾机 VF 上)

对直播推流(小包多、实时性高),GRO/LRO/TSO/GSO 可按需关闭,通常能换来 P99 的稳定:

sudo ethtool -K ens3 gro off lro off gso off tso off

4)TCP 内核参数(来宾机)

sudo tee /etc/sysctl.d/99-streaming-tcp.conf <<'EOF'
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_rmem = 4096 131072 8388608
net.ipv4.tcp_wmem = 4096 131072 8388608
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
EOF
sudo sysctl --system

5)中断亲和与绑核(宿主机)

SR-IOV 的中断最好绑在与 VF 同 NUMA 的几个核上,减少跨 NUMA 抖动:

grep -R . /proc/irq/*/affinity_hint | grep i40e
# 然后写入 smp_affinity_list:
echo 0-3 | sudo tee /proc/irq/XX/smp_affinity_list   # 仅示例

步骤五:接入服务(RTMP/SRT)的侧向优化

RTMP(nginx-rtmp 简例)

# /etc/nginx/nginx.conf 片段
rtmp {
  server {
    listen 1935;
    chunk_size 4096;
    publish_time_fix off;
    application live {
      live on;
      gop_cache on;
    }
  }
}
  • Chunk 与缓冲:小一些的 chunk_size、关闭多余缓冲,减少队头阻塞。
  • 进程亲和:让 nginx worker 落在与 VF 同 NUMA 的 CPU 上(taskset 或 systemd CPUAffinity)。

SRT(srt-live-server 或应用直集成)

  • latency(ms)设定为链路 RTT 的 2~3 倍;linger、peerlatency 配合压抑抖动。
  • 如果跨境推流,FEC 视链路质量开启,宁可轻 FEC 也不要过大缓冲。

关键配置一览表(便于回放)

类别 参数 建议值/示例 备注
GRUB intel_iommu on IOMMU 开启
GRUB iommu pt 旁路模式,降低开销
PF→VF sriov_numvfs 8/PF 分批创建,先小后大
VF 安全 spoofchk on 防欺骗
VF 信任 trust on 需要多 MAC/队列时
VF VLAN vlan 120/121 由 PF 打标签
VF 速率 rate 3000 Mbps 防单 VM 滥用
Offload gro/lro/gso/tso off 降 P99 延迟
TCP rmem/wmem 上面 sysctl 吞吐/延迟折中
IRQ smp_affinity 同 NUMA 降跨 NUMA 抖动
MTU ens3 1500 低延迟优先

实测数据(一次切换前后对比)

测试方法:深圳办公室(专线+公网回落)→ 香港接入节点;推 RTMP(OBS)与 SRT(srt-live-transmit),每种 5 分钟,采 10 次。

指标 变更前(virtio+OVS) 变更后(SR-IOV VF 直通)
RTMP 端到端 p50 135 ms 78 ms
RTMP 端到端 p99 225 ms 118 ms
SRT 端到端 p50 92 ms 54 ms
SRT 端到端 p99 160 ms 85 ms
单 VM CPU(接入进程) 120%(2核) 78%(2核)
宿主软中断 si% 12% 5%
丢包(应用统计) 0.35% 0.09%

注意:关闭 GRO/LRO 后 p50 明显下降,p99 更稳;吞吐没有明显损失(直播以小包为主)。

那些年我踩过的坑(当晚也复现了几例)

  • PF 重置导致 VF 抖动:X710 固件落后时,i40e 会偶发 PF reset,VM 网络瞬断。解法:升级固件/驱动;分批创建 VF,避免一次性 32/64 个。
  • 无法减少/增设 VF:echo N > sriov_numvfs 报 busy。解法:先 echo 0 清空再设新值;确保相关 VF 没有分配给 VM。
  • VLAN 不通:ToR 没配 trunk 或本地 PF 给 VF 打了 VLAN 而 VM 里又打了一次(双重打标签)。解法:规范化——要么 PF 打、VM 原生二层,要么 ToR trunk + VM 自打,二选一。
  • NUMA 不一致:VF 在 NUMA1,VM vCPU 在 NUMA0,时延抖动大。解法:核对 /sys/class/net/<PF>/device/numa_node 与 libvirt 绑核设置。
  • MTU 不一致:VM 1500,ToR/上游 9000 或反之,导致分片和奇怪的重传。解法:统一 1500(低延时优先)。
  • 信任模式:不开 trust on 有时 ARP/多连接被限。解法:直播接入 VF 建议默认 trust on + spoofchk on。
  • 运维折中:有了 VF 直通,热迁移受限。解法:VM 保留一块 virtio 管理口;ZK/Consul 健康探针能把流量平滑摘/挂。

回滚与灰度策略

每台宿主先改 1~2 个 VM,观察 24 小时,确认 P99 与丢包数据达标再扩大。

VM 同时保留 virtio 管理网口,Nginx/SRT 服务通过 VIP/Anycast 暂停引流后切换 VF。

预置 systemd 回滚脚本:

  • 关闭 VF:echo 0 > sriov_numvfs
  • libvirt 切回 virtio 网卡并重启 VM。

完整小抄(能复用就别死记)

# 1) BIOS 开 VT-d/SR-IOV/Above 4G;GRUB:
sudo sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt /' /etc/default/grub
sudo update-grub && sudo reboot

# 2) 创建 VF(以 ens2f0 / ens2f1 为例)
echo 8 | sudo tee /sys/class/net/ens2f0/device/sriov_numvfs
echo 8 | sudo tee /sys/class/net/ens2f1/device/sriov_numvfs

# 3) 为 VF 配置 MAC/VLAN/QoS
ip link set ens2f0 vf 0 mac 52:54:00:10:00:01 vlan 120 spoofchk on trust on
ip link set ens2f0 vf 0 rate 3000

# 4) 绑定 VF 到 vfio-pci 并直通给 VM(以 0000:af:00.4 为例)
echo 0000:af:00.4 | sudo tee /sys/bus/pci/devices/0000:af:00.4/driver/unbind
echo 8086 154c | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id
echo 0000:af:00.4 | sudo tee /sys/bus/pci/drivers/vfio-pci/bind

# 5) 虚机内调优
sudo ethtool -K ens3 gro off lro off gso off tso off
sudo sysctl --system   # 先写好 /etc/sysctl.d/99-streaming-tcp.conf

凌晨四点半,我把最后一台宿主的两块 PF 都开了 8 个 VF,接入 VM 逐个切过去。OBS 那头的同事在群里丢来第一张截图:SRT p50 稳在 50ms 左右,p99 不超过 90ms。机房外面天微亮,港岛对面的灯还没完全灭。我知道,这一晚没有“银弹”,只有把每一个细节抠到位:BIOS、IOMMU、PF/VF、VLAN、IRQ、NUMA、Offload、TCP,以及回滚策略。

SR-IOV 不是魔法,它只是把该让硬件做的事情交还给了硬件。直播这种对时延极致敏感的业务,直通的价值就体现在每一个不被浪费的微秒里。等到下一次需求再来,我会重复同样的流程,但会先看一眼风向——如果机器上的是 ConnectX-5,我会把 NUM_OF_VFS、Trust、TC 规则和 UPT 模式再玩一遍;如果是边缘节点,我可能用更激进的 IRQ 绑核策略。

那天清晨,我合上了机柜门,拍了拍门上的那张黄色小贴纸:“SR-IOV 已启用”。心是热的,风还是冷的,但推流的延迟,确实降下来了

目录结构
全文