1.\tSR-IOV介绍

SR-IOV 技术是一种基于硬件的虚拟化解决方案,可提高网络性能和可伸缩性。SR-IOV 标准允许在虚拟机之间高效共享 PCIe(Peripheral Component Interconnect Express,快速外设组件互连)设备,并且它是在硬件中实现的,可以获得能够与本机性能媲美的 I/O 性能。SR-IOV 规范定义了新的标准,根据该标准,创建的新设备可允许将虚拟机直接连接到 I/O 设备。

单个 I/O 资源可由许多虚拟机共享。共享的设备将提供专用的资源,并且还使用共享的通用资源。这样,每个虚拟机都可访问唯一的资源。因此,启用了 SR-IOV 并且具有适当的硬件和 OS 支持的 PCIe 设备(例如以太网端口)可以显示为多个单独的物理设备,每个都具有自己的 PCIe 配置空间。

下图展示了针对 PCIe 硬件的 SR-IOV 技术体系示意图。

\"image\"

图1 SR-IOV技术体系架构

支持SR-IOV的网卡主要功能模块分为:

  • 物理功能 (Physical Function, PF)
    支持SR-IOV 的管理。可以创建VF,对于网卡,理论上可以创建256个VF。一般情况下, 千兆网卡能支持达7个VF, 万兆网卡能支持达63个VF。

  • 虚拟功能 (Virtual Function, VF)
    VF可以理解为一个虚拟网卡,拥有独立内存空间、中断和Direct Memory Access (DMA)流。VF是PF虚拟出的一个实例,以一个独立的网卡形式展现。VF 是一种轻量级 PCIe 功能,可以与PF以及其创建的所有VF 共享一个或多个物理资源。

  • 交换功能(Layer 2 Classifier/Sorter switch)
    这个交换机其实是物理网卡内置的。流量进入物理网卡后,经过这个交换机然后分发到不同的VF上。

使用SR-IOV技术的主要优点是:

  • 提高虚拟机包转发效率

  • 减少报文在主机内部的传输延时以及延时抖动

由于绕过了主机的协议栈以及VMM(Virtual Machine Manager),减少了主机在网络报文收发中的资源消耗
同时,由于SR-IOV技术使虚拟机通过PCI-passthrough的方式直接使用VF,绕过了主机的内核协议栈以及VMM,故存在以下局限:

  • Host无法监控VF的状态

  • 安全组无法应用

  • 虚拟机热迁移无法实现,迁移的支持能力不够灵活

  • 对HA的支持不太高

  • sriov不支持vxlan

2.\tSR-IOV性能

本测试对比测试万兆网络中SRIOV虚拟机和OVS虚拟机在网络负载较高情况下的虚拟网络表现,总结分析,同时为以后SRIOV虚拟机和其它类型的虚拟机做对比给出参考。

2.1\t测试性能指标

主要对以下几个网络性能指标进行测试:

  1. 负载(OfferedLoad):网络流量负荷百分比,当前流量占端口速率的比例(百分比)

  2. 包速(Frame Per-second):数据包每秒的收/发个数

  3. 平均时延(Average Latency):数据包传输的平均延迟(毫秒,ms)

2.2\t测试拓扑图

SR-IOV测试环境网络拓扑:

\"image\"

Openvswitch测试环境网络拓扑:

\"image\"

2.3\t测试结果

\"image\"
\"image\"
\"image\"
\"image\"
\"image\"
\"image\"
\"image\"
\"image\"
可以看出SR-IOV各方面性能都要优于Openvswitch。

3.\tSR-IOV高可用方案

因为采用SR-IOV,虚拟机流量不经过宿主机操作系统,所以不能在宿主机操作系统层面做高可用,必须在虚拟机内部做高可用。SR-IOV高可用方案如下图:

\"image\"

(1)\tvm挂载不同pf的vf,在vm内部配置网卡bond,可以根据需求选择balance-rr、active-backup或者balance-xor策略。

(2)\tvm挂载的vf的mac地址必须设置成一样。这样在虚拟机配置网卡bond的情况下,仍然可以使用vf的mac spoofing check功能。

(3)\tpf配置lacp bond,交换机侧配置动态链路聚合,实现聚合带宽的动态调整。

4.\tOpenStack创建SR-IOV虚拟机过程

Openstack默认不支持SR-IOV的高可用,neutron中一个逻辑的port对应一个vf口。所以我们需要对openstack进行改造,总体思路是,通过在调用Neutron Create Port的api时候,对Port打上bond标签,Nova 在创建虚机的时候通过读取到对应port的bond标签,为该port分配两个VF口,并且在分配VF口的pci资源的时候,默认的把两个vf分配在不同的pf上。对应在port的binding:profile上需要展示使用的两个vf口的pci信息。

4.1\t调用neutron api创建sriov的port

调用neutron接口创建sriov port,在profile中对Port打上bond标签,这里无需赘述。命令如下:

neutron port-create --binding:vnic_type=direct --binding:profile type=dict bond=true
4.2\t调用nova api创建虚拟机

入口为nova/api/openstack/compute/servers的create方法,检查一系列参数和policy,然后调用compute_api的create方法:

\"image\"

compute_api是nova/compute/api.py模块,在该文件中找到create方法,该方法接着调用_create_instance方法,在_create_instance方法中调用_validate_and_build_base_options方法对所有的输入参数和策略做检查,并且封装pci请求。封装pci请求的方法是nova/network/neutronv2/api.py的create_pci_requests_for_sriov_ports方法,该方法会先调用neutron api获取port的vnic_type、phynet_name、bond属性,根据bond属性确定vf的数量(bond为true,vf的数量是2,否则vf数量是1)。

\"image\"

返回到nova/compute/api.py的_create_instance方法,该方法接着调用compute_task_api的schedule_and_build_instances方法:

\"image\"

compute_task_api是nova/conductor/api.py模块。这里没有执行什么操作,直接调用了conductor_compute_rpcapi的schedule_and_build_instances方法:

\"image\"

该方法远程过程调用api,即nova/conductor/rpcapi.py模块,该方法会对版本进行检查,然后调用RPC:

\"image\"

cast表示异步调用,schedule_and_build_instances是远程调用的方法,kw是传递的参数。现在nova-api任务完成,此时会响应用户请求,虚拟机状态为building。

4.3\tnova conductor

nova-api向nova-conductor发起RPC调用,进程跳到nova-conductor服务,入口为nova/conductor/manager.py的schedule_and_build_instances方法,该方法首先调用了_schedule_instances方法,在_schedule_instances方法中调用了scheduler_client的select_destinations方法:

\"image\"

这个方法最终调用到nova/scheduler/client/query.py下的select_destinations方法,执行RPC的调用。

\"image\"

RPC封装同样是在scheduler的rpcapi中实现。该方法RPC调用代码如下:

\"image\"

Call表示同步调用,此时nova-conductor并不会退出,而是堵塞等待直到nova-scheduler返回。因此当前状态为nova-conductor为blocked状态,等待nova-scheduler返回,nova-scheduler接管任务。

4.4\tnova scheduler

nova scheduler中入口为nova/scheduler/manager.py模块的select_destinations方法,该方法会调用driver的select_destinations方法,这里的driver是调度算法实现,通过filters过滤掉不满足条件的计算节点,剩下的节点通过weigh方法计算权值,最后选择权值高的作为候选计算节点返回。最后nova-scheduler返回调度结果的hosts集合,任务结束,返回到nova-conductor服务。

nova/scheduler/manager.py

\"image\"

nova/scheduler/driver.py

\"image\"

nova/scheduler/filter_scheduler.py

\"image\"

4.5\tnova condutor

回到nova-conductor的schedule_and_build_instances方法,nova-conductor等待nova-scheduler返回后,拿到调度的计算节点列表。因为可能同时启动多个虚拟机,因此循环调用了compute_rpcapi的build_and_run_instance方法。

\"image\"

Compute_rpcpai位于nova/compute/rpcapi模块,该方法向nova-compute发起RPC请求:

\"image\"

发起的是异步RPC,因此nova-conductor任务结束,进入nova-compute服务。

4.6\tnova compute

入口是nova/compute/manager.py的build_and_run_instance方法,该方法会调用_do_build_and_run_instance方法,该方法会更新虚拟机状态,然后调用_build_and_run_instance方法,该方法会预先声明占用计算节点的资源。

\"image\"

其中在instance_claim方法中会处理pci的请求,instance_claim→claim_instance(nova/pci/manager.py)→_claim_instance →consume_requests(nova/pci/stats.py),在nova/pci/stats.py中会根据不同的网卡把pci资源分为不同的pool。

\"image\"

在consume_requests方法中会具体分配pci资源也就是vf,

\"image\"

先根据剩余可用的vf的数量将pool进行排序,如果port的bond属性为false,则count数量为1,则从可用vf数量最多的pool中分配一个vf,这样使不同网卡的负载尽量均衡。如果port的bond属性为true,则count数量为2,则从两个pool中各分配一个vf,达到高可用的目的。
回到nova/compute/manager.py的build_and_run_instance方法,这个方法接下来会调用spawn_n方法,开始真正创建虚拟机。这里不同的虚拟机技术对应不同的driver,其中libvrit的driver对应nova/virt/libvirt/driver.py。从spawn方法开始,会先获取instance的磁盘、镜像、网络等信息,然后生成instance的xml文件。

\"image\"

在_get_guest_xml方法中会调用_get_guest_config方法,其中sriov网卡的部分如下:

\"image\"

这里如果port的bond为true,则会将两块网卡的信息写入xml文件中。

回到spawn方法,接下来会调用_create_domain_and_network方法,该方法会调用plug_vifs方法创建qbr和qvo,接着创建虚拟机,虚拟机状态为pause。然后等待neutron-server的消息,如果等到消息后将虚拟机状态改为running,否则超时没有等到,则将虚拟机销毁。

\"image\"

4.7\tneutron sriov agent

在neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py中daemon_loop方法会定时检测vf的状态变化,如果有vf发生变化,进入process_network_devices方法,在该方法中,会根据vf的状态是添加更改删除做不同的处理。

\"image\"

其中在treat_devices_added_updated方法中,如果port的bond为true,需要更新两个vf的状态:

\"image\"

然后会调用rpc,通知neutron-server更新状态。

\"image\"

这里是同步调用,neutron-server接收到消息后,会向nova发送更新port的消息,接着nova compute会resume状态是pause的虚拟机。

至此,带有sriov高可用网卡的虚拟机创建完成。

作者

李伟杰,苏宁云网络架构师,长期从事云计算的研发工作,在云计算虚拟网络和SDN网络方面有专业的研究,现在负责苏宁云虚拟网络产品设计及研发。

陈玮,苏宁云高级研发工程师,有多年云计算网络研发经验,现在负责苏宁云网络产品的研发。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐