使用 SR-IOV 将虚拟功能(VF)直通到容器

解读

在国内金融、运营商及 AI 训练场景中,低延迟、高吞吐、内核旁路是刚需。Docker 默认 veth-pair 方案经协议栈转发,延迟普遍在 50 µs 以上,无法满足 5G UPF 或高频交易 <5 µs 的 SLA。SR-IOV 把一张物理网卡(PF)切分为多个独立 VF,每个 VF 在 PCI 层呈现为独立设备,可直接透传进容器,实现零拷贝、零上下文切换,性能接近裸机。面试官问“怎么做”只是敲门砖,更关注你对PCI 设备隔离、驱动选型、热迁移限制、Kubernetes 设备调度的系统性认知,能否在真实产线落地并兜底风险。

知识点

  1. SR-IOV 核心概念:PF(Physical Function)、VF(Virtual Function)、VF MAC/VLAN 预配、PCIe 地址(Domain:Bus:Device.Function)。
  2. Linux 内核开启 IOMMU(Intel VT-d/AMD-Vi),grub 参数 intel_iommu=on iommu=pt,否则 VF 无法安全直通。
  3. 驱动选型:
    • 内核原生 ixgbevf/i40evf/mlx5_core 适用于裸机/容器;
    • 用户态 DPDK 驱动 vfio-pci/uio_pci_generic 需额外大页、NUMA 绑定。
  4. Docker 端两种挂载方式:
    • --device=/dev/vfio/XX(vfio 模式,安全、支持热插拔,推荐);
    • --device=/sys/bus/pci/devices/0000:xx:xx.x(直接 PCI 绑定,需特权,风险高)。
  5. 多租户隔离:
    • 利用 macvlan/ipvlan 子接口 做 VLAN tag 剥离,防止 VF 跨租户广播;
    • Kubernetes 设备插件(k8s-device-plugin、sriov-network-device-plugin) 实现 VF 作为扩展资源调度,避免人工写 PCI 地址。
  6. 生命周期管理:
    • sriov-cni 负责把 VF 注入 Pod 网络命名空间;
    • Multus 做多网卡编排;
    • sriov-network-operator 自动为节点打 label、同步 VF 数量。
  7. 故障排查:
    • dmesg | grep -i vfio 查看 IOMMU 分组;
    • lspci -vvv -s <bdf> 确认驱动是否被 vfio-pci 认领;
    • ip link show <pf> 看 VF 数量与 MAC 是否生成;
    • 容器内 ethtool -i eth1 核对 driver 与 firmware。
  8. 限制与兜底:
    • 容器热迁移不支持,需业务层无状态或采用 Blue/Green 发布
    • VF 数量上限受 NIC 固件与 PCIe ARI 扩展限制(常见 128 VF/口);
    • 安全合规:VF 直通后宿主机 netfilter 失效,需在硬件交换机 ACL 或容器侧 eBPF/XDP 做微隔离。

答案

步骤以 CentOS 7.9 + Intel X710 + Docker 20.10 为例,全程非 root 不运行容器,符合国内等保三级要求:

  1. 宿主机开启 IOMMU
    编辑 /etc/default/grub 追加:
    GRUB_CMDLINE_LINUX="... intel_iommu=on iommu=pt"
    grub2-mkconfig -o /boot/grub2/grub.cfg && reboot

  2. 加载 VF 驱动并生成 VF
    echo 8 > /sys/class/net/pf0/device/sriov_numvfs
    查看 VF:ip link show pf0 | grep vf

  3. 安装并配置 sriov-network-device-plugin
    ConfigMap 示例:

    resourceList:
      - resourceName: "intel_sriov_vf"
        selectors:
          vendors: ["8086"]
          devices: ["154c"]   # X710 VF
          drivers: ["vfio-pci"]
    

    插件以 DaemonSet 运行,节点自动上报 intel.com/intel_sriov_vf: 8

  4. 构建最小安全镜像
    Dockerfile:

    FROM alpine:3.18
    RUN apk add --no-cache dpdk-tools
    USER 1001:1001
    

    不安装 sshd、sudo,镜像 <10 MB,减少攻击面。

  5. 运行容器

    docker run -it --rm \
      --device /dev/vfio/vfio \
      --device /dev/vfio/24 \   # 对应 VF 24
      --cap-add IPC_LOCK \
      -v /dev/hugepages:/dev/hugepages \
      mydpdk:1.0
    

    容器内 testpmd -l 1-2 -w 0000:05:00.2 -- --forward-mode=macswap -i 验证双向 64 byte 小包 14.88 Mpps,延迟 1.8 µs,达到产线 KPI。

  6. 纳入 CI/CD
    GitLab-CI 阶段:

    • build:docker buildx 多阶段编译;
    • security:trivy 镜像扫描,Critical 漏洞=0 才准入
    • deploy:Ansible 调用 sriov-network-operator CRD 动态扩容 VF,零人工 PCI 地址

拓展思考

  1. 双栈场景:同一 PF 既切 VF 又保留部分端口做 RoCE v2 RDMA,需 PCIe ACS 隔离 防止 VF 与 PF 间互相嗅探;如何在不重启节点前提下,在线调整 PF/VF 带宽分配
  2. 云原生混部:Kubernetes 中 SR-IOV Pod 与常规 Calico Pod 共存,拓扑管理器(Topology Manager) 如何确保 VF 与 CPU 在同一 NUMA node,避免跨 node 内存访问导致 5% 性能衰减?
  3. 安全合规:等保 2.0 要求 “最小权限 + 三权分立”,但 VF 直通后容器可发任意以太网帧;如何结合 eBPF 程序挂载 tc egress 实现 MAC/IP/端口白名单,并对接审计系统留存 6 个月日志?
  4. 业务连续性:若宿主机 NIC 固件升级,VF 瞬时掉线 3s,对 5G 用户面会造成断链;如何设计 双 PF 主备 + BGP Anycast 在 50 ms 内完成路径切换,并保证容器内会话表不丢?