容器的SR-IOV

Basics

SR-IOV的全称是Single Root I/O Virtualization

虚拟机中的网卡看起来是真实的硬件,实际则是宿主机虚拟化出来的设备,也就是运行的软件程序;这也意味着,虚拟设备是需要CPU去运行的,这样设备的性能会随着宿主机性能而改变,可能会产生额外的延时。

VT-D技术可以将物理机的PCIe设备直接分配给虚拟机,让虚拟机直接控制硬件,来避免上述问题。但是虚拟机会独占直通的PCIe设备,如果一台宿主机上有多个虚拟机,那就要求对应数量的物理网卡,这显然不现实。

为解决这样的问题,Intel提出来SR-IOV技术,该技术最初应用在网卡上。简单来讲,就是将一个物理网卡虚拟出多个轻量化的PCIe物理设备,再分配给虚拟机使用。

启用SR-IOV技术,可以大大减轻宿主机的CPU负荷,提高网络性能、降低网络延时,也避免了PCI-passthrough下各个物理网卡被各个容器独占的问题。

SR-IOV

根据Oracle Solaris的文档描述,SR-IOV规范定义了新的标准,创建的新设备能将虚拟机直接连接到I/O设备,并获得于本机性能媲美的I/O性能。具体规范由PCI-SIG定义和维护。

Functions

SR-IOV包含2种新功能类型:

  • 物理功能(Physical Function, PF):用于支持SR-IOV功能的PCI功能。
  • 虚拟功能(Virtual Function, VF):与物理功能关联的一种功能。

更具体来说,PF是包含完全的PCIe功能,可以像其他任何PCIe设备一样进行发现、管理和处理;PF拥有完全配置资源,可用于配置或控制PCIe设备。

VF是一种轻量级的PCIe功能,可以与物理功能及同一物理功能关联的其他VF共享一个或多个物理资源,且仅允许拥有用于其自身行为的配置资源。

每个SR-IOV设备都可以有一个PF,每个PF最多可以关联64,000个VF。PF可以通过专用寄存器创建VF。

一旦在PF中启用了SR-IOV,就可以通过PF的总线、设备和路由ID访问各个VF的PCI配置空间。每个VF具有一个映射了其寄存器集的PCI内存空间,VF设备驱动程序对寄存器集进行操作以启用其功能,并显示为实际存在的PCI设备。
创建VF后,可以直接将其指定给IO guest域的各个应用程序。

此功能试的虚拟功能可以在没有CPU和VMM开销的情况下执行I/O。
缺省状态下,SR-IOV功能处于禁用状态,PF充当传统PCIe设备。

SR-IOV技术也有其局限性,比如同一PF可关联的VF数量是有限的,而且是硬件绑定的,不支持容器迁移。

Docker with SR-IOV

SR-IOV在虚拟化中应用广泛,理所当然地可以被用在容器上。

Intel官方就维护了一个SR-IOV的CNI插件,Intel/sriov-cni。除此之外,其开发的clearcontainers项目下也有一个SR-IOV的network plugin,支持docker指定容器运行时为runc或clearcontainer时使用,见clearcontainers/sriov

更多有关Docker Plugin的信息,请参考Docker Engine managed plugin system

下面我们采用clearcontainers提供的SR-IOV Docker Plugin进行安装。

Install Clear Containers SRIOV Docker Plugin

环境要求:

  • 物理机(VM中可能找不到支持SR-IOV的PF)
  • Golang
  • Docker Engine

克隆源码。

git clone https://github.com/clearcontainers/sriov.git
cd sriov

用Go Mod或者Dep等工具初始化go pkg依赖,配置docker/libnetwork项目到较新版本(旧版本会导致编译失败)。

$ go mod init
$ go mod vendor
$ cat go.mod

module github.com/clearcontainers/sriov

go 1.13

require (
        github.com/01org/ciao v0.0.0-20180108144400-194264adc583
        github.com/boltdb/bolt v1.3.1
        github.com/docker/libnetwork v0.7.2-rc.1
        github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
        github.com/gorilla/mux v1.7.3
        github.com/vishvananda/netlink v1.1.0
)

编译源码。

go build

在host上安装SR-IOV插件,并在后台启动插件。

sudo cp sriov.json /etc/docker/plugins
sudo ./sriov &

以Docker默认的runc容器运行时来测试插件功能。

# Create a virtual network on physical network b2b with vlanid 100
sudo docker network create -d sriov --internal --opt pf_iface=eth0 --opt vlanid vfnet

# Create container on the network vfnet
docker run --net=vfnet -itd busybox top

# Check that container is running
docker ps

# Cleanup
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
sudo docker network rm vfnet

Kata with SR-IOV

Kata是一个使用轻量虚拟机(VM)来替代传统的容器运行时(runc等),以达到容器间或Pod间的内核隔离的安全容器方案。其技术上的设计细节可以参考Architecture - kata-containers/documentation

由于Kata默认采用QEMU作为hypervisor,而QEMU不支持veth,所以网络上的默认方案是采用TAP来为VM内外打通网络。前面我们了解到SR-IOV是不需要采用传统的veth的,在CPU、内核功能上支持SR-IOV的前提下,就可以采用SR-IOV来提升Kata容器的网络性能。

Setup to Use SR-IOV with Kata

遵循Setup to use SR-IOV with Kata Containers and Docker一文,首先准备好clearcontainer的SR-IOV Docker Plugin。

Requirement

  • Host支持Intel VT-d
  • NIC支持SR-IOV
  • Host的内核支持IOMMU和VFIO

更新以下配置前,请最好确保你已经有当前host的足够新的备份

检查你的NIC是否支持SR-IOV:

# get the pci class code of your NIC
$ lspci | fgrep -i ethernet
01:00.0 Ethernet controller: Intel Corporation Ethernet Controller 10-Gigabit X540-AT2 (rev 03)
...

#sudo required below to read the card capabilities
$ sudo lspci -s 01:00.0 -v | grep SR-IOV
        Capabilities: [160] Single Root I/O Virtualization (SR-IOV)

检查host上的IOMMU groups设置

find /sys/kernel/iommu_groups/ -type l

如果没有输出,就说明你需要按下面步骤更新内核的配置。

Update Host Kernel Configuration

开启intel-iommu,手动编辑/etc/default/grub或者下面的sed命令都应该可以;编辑好配置文件后,使用update-grub更新grub配置并重启:

# edit /etc/default/grub and add intel_iommu=on to cmdline:
$ sudo sed -i -e 's/^kernel_params = "\(.*\)"/GRUB_CMDLINE_LINUX="\1 intel_iommu=on"/g' /etc/default/grub
$ sudo update-grub
$ sudo reboot -f

重启后,检查启动命令行(也就是上面更改的CMDLINE_LINUX)是否更新成功:

$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4... root=UUID=4b... ... intel_iommu=on

使用dmesg命令查看系统启动信息,以检查Intel VT-d是否已初始化:

$ dmesg | grep --color "Virtualization"
# output can be
DMAR: Intel(R) Virtualization Technology for Directed I/O
# or
PCI-DMA: Intel(R) Virtualization Technology for Directed I/O

加入vfio-pci模块:

sudo modprobe vfio-pci

再次检查iommu group,确保你的NIC支持PCIE-ACS,即它的class id出现在以下find指令输出中,并且其对应的group id不包括其他设备:

$ find /sys/kernel/iommu_groups/ -type l | grep "01:00"
/sys/kernel/iommu_groups/35/devices/0000:01:00.1
/sys/kernel/iommu_groups/34/devices/0000:01:00.0
$ find /sys/kernel/iommu_groups/ -type l | grep "35"
/sys/kernel/iommu_groups/35/devices/0000:01:00.1

Set Up the SR-IOV Device

Remark: 前面小节的操作都是为了让host支持SR-IOV做的,只需要设置一次就行。下面的操作需要在每次系统启动后执行,以促进物理设备的VFs的设置。

加入vfio-pci设备驱动(上一节加过的可以不再做),该驱动将用于VF的保留:

sudo modprobe vfio-pci

查看你的NICs可以创建多少个VFs:

# use the pci class id of your NICs
$ cat /sys/bus/pci/devices/0000\:01\:00.0/sriov_totalvfs
63
$ cat /sys/bus/pci/devices/0000\:01\:00.1/sriov_totalvfs
63

修改sriov_numvfs的值来创建VFs;注意每个NIC都会对应这样一个文件,每个sriov_numvfs的值默认为0,修改为2或者更多,恰好对应每个PF创建的VF,最多不超过上面的totalvfs值:

vim /sys/bus/pci/devices/0000\:01\:00.0/sriov_numvfs

这里我们修改第一张NIC的numvfs为2,确认VF信息:

$ sudo lspci | grep Ethernet | grep Virtual
02:10.0 Ethernet controller: Intel Corporation X540 Ethernet Controller Virtual Function (rev 01)
02:10.1 Ethernet controller: Intel Corporation X540 Ethernet Controller Virtual Function (rev 01)
$ ls /sys/class/net/eth0/device/virtfn0/net
enp59s0f2
$ ls /sys/class/net/eth0/device/virtfn1/net
enp59s0f3
# the path for other NICs' VF can be "/sys/class/net/eth2/device/virtfn1/net"

目前的VF是没有MAC地址的,为它们设置MAC地址来保证host和VM中的地址一致性,下面以第一个VF为例:

$ ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1550 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether b8:59:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    vf 0 MAC 00:00:00:00:00:00, spoof checking off, link-state auto, trust off, query_rss off
    vf 1 MAC 00:00:00:00:00:00, spoof checking off, link-state auto, trust off, query_rss off
$ ip link show dev enp59s0f2
13: enp59s0f2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 4a:b3:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
$ ip link set eth0 vf 0 mac 4a:b3:xx:xx:xx:xx
# also set MAC for other VFs
$ ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1550 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether b8:59:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    vf 0 MAC 4a:b3:xx:xx:xx:xx, spoof checking off, link-state auto, trust off, query_rss off
    vf 1 MAC ee:20:xx:xx:xx:xx, spoof checking off, link-state auto, trust off, query_rss off

Launch a Kata Container using SR-IOV

首先,按照Install Clear Containers SRIOV Docker Plugin中的步骤,启用sriov的Docker Plugin。

然后,创建docker network:

$ docker network create -d sriov --internal --opt pf_iface=eth0 --opt vlanid=100 --subnet=192.168.0.0/24 vfnet
E0212 15:07:55.427057 1187089 plugin.go:298] Numvfs and Totalvfs are not same on the PF - Initialize numvfs to totalvfs
ee2e5a594f9e4d3796eda972f3b46e52342aea04cbae8e5eac9b2dd6ff37b067

使用新建的network来创建kata容器:

$ docker run --net=vfnet --runtime=kata-runtime --cap-add SYS_ADMIN --ip=192.168.0.10 -it busybox ls /
bin   dev   etc   home  proc  root  sys   tmp   usr   var

清除动作与使用runc的方式类似。

参考资料

  1. SR-IOV是什么?性能能好到什么程度?. 钱乎. 知乎专栏
  2. SR-IOV简介 - 编写驱动程序. Oracle
  3. Setup to use SR-IOV with Kata Containers and Docker. kata-containers. GitHub
全部评论

相关推荐

投递美团等公司10个岗位
点赞 评论 收藏
转发
点赞 2 评论
分享
牛客网
牛客企业服务