2月技术周 | 虚拟化技术选型指南


2月技术周 | 虚拟化技术选型指南


01
摘要


提起虚拟化技术绕不过的就是VMware,即使现在VMware在国内的份额在不断减少,但VMware的虚拟化技术仍然在业界具有影响力。而作为后期之秀的KVM虚拟化借助Linux、OpenStack社区的发展,逐步在成为虚拟化技术的标准。


02
VMware


VMware的定位是云计算的建设者,提供各种云计算的解决方案帮助别人建设和管理自己的云。VMware是云计算生态系统中的核心厂商,因为VMware在云计算的核心技术——虚拟化方面,有着独树一帜的实力,尤其是服务器的虚拟化、存储管理、排程管理等方面具有核心优势。服务器的虚拟化软件成熟度很高,尤其是传统数据中心的虚拟化方面,其产品具有很大的优势。在云计算专业能力,尤其是虚拟化的咨询与规划服务、云计算的管理服务方面,具有较强的能力;拥有众多的成功案例,其产品和解决方案经过了众多客户的检验,成熟性和稳定性好;是虚拟化领域的第一品牌,也是虚拟化领域的第一大公司。


VMware 属于商业产品,产品代表有vSphere ESXi、Workstation。特点是产品成熟稳定,企业级市场份额最大;对计算(CPU/内存)的管理调度有特别好的优化。而且VMware功能,产品线丰富,是行业的标杆,具备稳定成熟,通用,虚拟效率高的特点。但是VMware的功能较为简单,仅具备了虚拟化计算、网络、存储的能力,而且VMware不具备定制化开发的服务。


VMware需要花费一些经费购买权限,并且相对来说更加容易安装和运行,另外VMware则学习成本更低一些。VMware的软件系统是封闭的,并且软件的发展路线是完全遵循VMware自己的发展目标,用户或消费者在此方面没有任何控制权。OpenStack作为一个开源系统,没有任何一家单独的公司在控制OpenStack的发展路线。有了如此多公司的资源投入,OpenStack的发展是多元化的。 


VMware的产品相对集中在虚拟化领域,云计算所需要的软件产品线不够全面;产品在面向公共云的解决方案时,在性能和容错性方面存在一定的缺限;VMware虚拟化解决方案只能针对x86架构,对于小型机和大型机无法应用;VMware的价格过高,并且还在持续上涨;在中国服务能力稍有不足。在中国分支机构较少,有实施和服务能力的合作伙伴不多,尤其是虚拟化的专业咨询顾问与实施顾问数量不多。


03
OpenStack


OpenStack是免费使用的,但是由于其组件繁多,部署难度较大,需要大量的工程资源和领域专家来支持OpenStack在企业中的部署和落地,并且OpenStack需要做很多架构和搭建方面的工作,并且支持多种部署场景,而且有不同的安装过程。OpenStack入门门槛较高,但是随着项目规模的扩大,用户将从中受益,因为不必支付高额的版权费用。随着云应用大规模化,那么OpenStack的入门门槛就相应下降。


OpenStack虽然有些方面还不太成熟,然而它有全球大量的组织支持,大量的开发人员参与,发展迅速。国际上已经有很多使用OpenStack搭建的公有云、私有云、混合云,例如:RackspaceCloud、惠普云、MercadoLibre的IT基础设施云、AT&T的CloudArchitec、戴尔的OpenStack解决方案等等。而在国内OpenStack的热度也在逐渐升温,华胜天成、高德地图、京东、阿里巴巴、百度、中兴、华为等都对OpenStack产生了浓厚的兴趣并参与其中。OpenStack很可能在未来的基础设施即服务(IaaS)资源管理方面占据领导位置,成为公有云、私有云及混合云管理的“云操作系统”标准。


04
KVM/Xen/容器


OpenStack默认的虚拟化技术是KVM,KVM属于开源产品,产品代表有OpenStack,KVM继承了Linux系统管理内存的诸多特性,比如,分配给虚拟使用的内存可以被交换至交换空间、能够使用大内存页以实现更好的性能,以及对NUMA的支持能够让虚拟机高效访问更大的内存空间等。另外KVM同样具备社区庞大,资源丰富,免费,使用成本低,有大规模部署的实践验证的特点,并且其最大的优势是集成到LINUX内核中。


Xen属于开源产品,具有社区庞大,资源丰富的特点,并且其免费,使用成本低,有大规模部署的实践验证。但是缺点是需要深度调优,虚拟化效率才高。Xen单独使用功能有限,需要配置多个组件实现功能,开发能力要求高。目前Xen虚拟化技术的市场逐渐被KVM虚拟化所侵蚀,市场占有率不高。


这几年比较火的容器也属于开源产品,产品代表有Docker,容器解决了开发和运维之间的矛盾,在开发和运维之间搭建了一个桥梁,是实现devops的最佳解决方案容器社区相比虚拟化社区更小,但是容器技术发展很快,相比于虚拟化技术,容器技术的大规模实践验证较少一些。


05
业界主流产品分析结论


VMware软件套件是自底向上的架构,VMware的vSphere和vCloud director产品都是依赖于免费的ESX(i) 虚拟机管理器,ESX(i)虚拟机管理器提供了非常优秀的部署架构。VMware的产品由于其架构的健壮性,很多高规格用户在多数据中心规模的环境中都有使用。但是,VMware的软件系统是封闭的,并且软件的发展路线是完全遵循VMware自己的发展目标,用户或消费者在此方面没有任何控制权。 


OpenStack作为一个开源系统,没有任何一家单独的公司在控制OpenStack的发展路线,而且很多大公司都在支持OpenStack发展。有了如此多公司的资源投入,OpenStack的发展是多元化的。然而这也带来了问题,就是OpenStack部署和架构的实施和维护成本较比VMware高,与此同时,由于相对快速的版本更新速度,技术支持文档落后于产品本身。


虽然OpenStack是免费使用的,但是需要有大量工程资源和领域专家才能部署和应用于生产环境,OpenStack支持很多部署场景,并且安装过程门槛较高。VMware则需要花费经费购买权限,并且相对来说更加容易安装和运行。对于部分企业来说,如果是开发能力不强的业务型企业,而且业务量变化不是很大,对基础设施的灵活性要求不高,追求的是稳定、受支持的虚拟化环境。在预算充足的情况下,那么使用商业化VMware是比较合适的。如果客户是有开源软件的要求,并且有一定的运维能力,而且业务更改较为平凡,需要更多的裸机管理、编排、容器管理等要求,那么采用OpenStack较为妥当。


关于九州云99Cloud

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。公司成立六年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各大行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。

2月技术周 | 虚拟化技术选型指南

2月技术周 | OVS实现安全组,你需要知道这些!


防火墙



防火墙是避免网络信息基础设施免受复杂网络环境中安全攻击的必要设施。高效的防火墙则更需要实时跟踪来往于不同网络设备间的各类网络连接,即“有状态防火墙”。对于实际的硬件物理网络基础设施需要防火墙,对于虚拟网络设备,openstack在这样的云平台亦需要同样的防火墙进行网络保护。


在Openstack中,防火墙由“Security Group”和“FWaas”两大服务组成。其中Security Group在port级别提供对VM网络通信的访问控制。而Fwaas则运行在vrouter上在subnet的边界控制子网间的L3和L4流量。简而言之,“Security Group”保护port,“FWaas”保护subnet。


2月技术周 | OVS实现安全组,你需要知道这些!

图3


Openstack下的“Security Group”不仅保护租户VM,使其避免受到无价值数据流的影响,同时还限制租户VM,避免其主动发起ARP spoofing,DHCP spoofing等不安全网络行为。实际定位到底层,“Security Group”可以通过iptables和ovs 流表两种方式实现。本文将重点讲述基于OVS 流表实现的安全组(Security Group)。


OVS Conntrack 概述


无状态的防火墙只能通过静态的网络元组来过滤,阻拦,放行数据报文。这里的静态网络元组包括IP地址,端口,网络协议。无状态防火墙并不关心当前网络连接处于何种状态。相较于无状态防火墙,有状态防火墙增加了对当前网络连接状态的识别,同步使用静态的网络元祖对数据报文进行过滤,阻拦,放行。增加的识别标志在一定程度上消耗系统资源,但更加严格的规则却更能保障网络更加安全。


网络连接状态的识别通常是由CT模块(connection tracker)实现的。在linux内核中,CT是由conntrack实现。从OVS 2.5起,开始支持conntrack,并在openflow中体现相关CT状态的识别与处理。Openstack则从M版开始,使用OVS的新特性,来实现“有状态防火墙”中的“Security Group”功能。


从OVS提供的CT功能简图2来看,相对于原有流表处理,无非增加了提交连接数据包进入CT模块,标记连接状态,用于后续流表查询连接状态,匹配数据报文进行指定处理的过程。


2月技术周 | OVS实现安全组,你需要知道这些!

图2


图3列举了OVS实现的openflow 中新增的CT相关字段。(有删减,仅列举了后续流表分析时用到的字段)


2月技术周 | OVS实现安全组,你需要知道这些!

图3


这里需要重点解释下rel,inv,zone=value(ct_zone)这三条项目。


rel,即related。这里举个典型的例子描述下related数据包。当VM A ping 某外网IP地址B,发出一个ICMP Echo Request报文,当该Request报文到达路由器时,路由器判定外网IP地址不可达,回送一个ICMP Network Unreachable报文。那么这个ICMP Network Unreachable报文与先前发出的ICMP Echo Request报文就是存在related关系。因为对于ICMP Echo Request报文而言,只有ICMP Echo Reply报文是与它存在reply(rpl)关系的。


inv,即invalid。如果存在下述几种情况:linux内核中L3/L4协议处理程序不可用或者未加载;nf_conntrack_ipv4或nf_conntrack_ipv6模块没有加载;L3/L4协议判定数据包非法;数据包本身报文长度与协议本身不匹配,那么该数据包将被置位inv。例:UDP奇数字节报文,被某型网卡驱动末位padding 0,但未增加IP头部内长度信息,也未增加UDP头部内长度信息,导致该报文在OVS CT中判定inv,最终drop导致通信与预期不符。


通过查询系统连接跟踪条目,如图4所示,可知协议类型,源IP,目的IP,源端口,目的端口这5项元组共同构成了识别一条连接跟踪的索引。在openstack环境中,会有一定概率存在不同项目下的VM,使用相同网段的子网IP,且恰好被调度到同一台宿主机。在极其偶然的情况下这两台VM恰好使用同样的协议,同样的源端口去访问同一个目的IP的同一个目的端口。如果不加任何隔离处理必将导致连接跟踪条目的重叠冲突。


2月技术周 | OVS实现安全组,你需要知道这些!

图4


zone=value(ct_zone),很好的处理了这种冲突。zone可以称之为隔离连接跟踪条目的namespace。不同zone中的连接跟踪条目即使协议类型,源IP,目的IP,源端口,目的端口完全一致,也不会存在冲突。在Security Group的OVS驱动中,每条连接在提交至CT模块时,zone均被指定为该网络连接所处network在本节点上local vlan tag。(此处network为neutron下的network模型)


安全组相关特性


当一个port不添加任何安全组信息时,只有匹配该port的ARP报文,DHCP报文,和已建立的连接才会被初始化时下发的流表规则放行。当然,关闭port的port_security_enabled属性可以屏蔽anti ARP spoofing和anti DHCP spoofing流表规则的下发。Neutron中的一些特殊类型port,例如DHCP port,gateway port等被认为是security port,自然也是关闭port_security_enabled属性。如果需要在port上允许其他自定义的IP,MAC网络包的流通,也可以通过port的allowed_address_pairs添加相应信息。被添加的IP,MAC会在下发安全组规则时作为可信任的地址信息被填入流表。


当安全组A的一条ingress规则通过remote group id指向了安全组B,那么代表着所有通过安全组B的流量才能匹配这条ingress规则。也即意味着只有绑定了绑定了安全组B的port发出的流量才能匹配这条ingress规则。


OVS安全组流表分析


以“目的IP192.168.0.0/24目标端口22协议TCP出向放行”,“任意IP目标端口80协议TCP入向放行”两条安全组规则为例,分析流量具体经过的流表,如下图5所示。


2月技术周 | OVS实现安全组,你需要知道这些!

图5


OVS与iptables对比


不使用OVS情况下,Linux内核的连接跟踪模块仅限于IP协议层(L3)的Linux内核防火墙(iptables)使用。而实际VM流量从tap口流出时已经是L2层报文。因此额外添加了一层Linux bridge,配合ebtables,处理原有L2层报文后上送至iptables,从而在iptables中执行连接跟踪,所有Security Group规则也被转换成具体的iptables规则,配合连接状态,实现状态防火墙的功能。经过筛选处理后的报文再通过veth从Linux bridge流向br-int,执行外部交换与路由。


2月技术周 | OVS实现安全组,你需要知道这些!

图6


对比OVS与iptables实现的状态防火墙,多一层虚拟网络设备就多一次处理,这里必然导致多一重性能损耗。如图7所示,在Security Group 规则数量庞大的情况下,性能消耗则更加明显。


2月技术周 | OVS实现安全组,你需要知道这些!

图7


参考链接:

1.http://www.openvswitch.org/support/dist-docs/ovs-ofctl.8.txt

2.http://www.openvswitch.org//support/dist-docs/ovs-fields.7.txt

3.http://docs.openvswitch.org/en/latest/tutorials/ovs-conntrack/

4.https://redhatstackblog.redhat.com/2016/07/22/how-connection-tracking-in-open-vswitch-helps-openstack-performance/

5.http://www.openvswitch.org/support/ovscon2014/17/1030-conntrack_nat.pdf

6. https://docs.openstack.org/neutron/pike/contributor/internals/openvswitch_firewall.html

7. https://zhuanlan.zhihu.com/p/25089778


关于九州云99Cloud

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。公司成立六年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各大行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。

2月技术周 | OVS实现安全组,你需要知道这些!

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

前言

这是《OpenStack Rocky Octavia 实现与分析》系列的第三篇,主要关注 Octavia 核心资源对象的(Listener、Pool、Member、L7Policy、L7Rule、HeathMonitor)创建流程,在分析其创建流程的过程中我们可以感受到不同资源对象在 LBaaS 中所起到的作用。


listener 创建流程分析

create listener flow 的 UML 图

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

从上图可知,执行指令 openstack loadbalancer listener create--protocol HTTP--protocol-port8080lb-1 创建 Listener 时会执行到 Task:ListenersUpdate,在该任务中,AmphoraAPIClient 会调用:

  • PUT listeners/{amphora_id}/{listener_id}/haproxy:更新 haproxy 配置文件

  • PUT listeners/{listener_id}/reload 重启 haproxy 服务进程

所以,只有当为 loadbalancer 创建 listener 时才会启动 haproxy 服务进程。还有一点补充的是创建 Listener 时也会执行 Task:UpdateVIP,这是因为 Lisenter 含有的协议及端口信息都需要被更新到 VIP 的安全组规则中。

启动 haproxy 服务进程

登录 amphora 查看 haproxy 的配置文件:

  1. # file: /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg, Listener UUID: 1385d3c4-615e-4a92-aea1-c4fa51a75557


  2. # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  3. global

  4.    daemon

  5.    user nobody

  6.    log /dev/log local0

  7.    log /dev/log local1 notice

  8.    stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

  9.    maxconn 1000000


  10. defaults

  11.    log global

  12.    retries 3

  13.    option redispatch


  14. peers 1385d3c4615e4a92aea1c4fa51a75557_peers

  15.    peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

  16.    peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025



  17. frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

  18.    option httplog

  19.    maxconn 1000000

  20.    bind 172.16.1.10:8080

  21.    mode http

  22.    timeout client 50000

因为 Listener 指定了监听 HTTP 协议和端口 8080,所以 frontend section 也被渲染了的 bind172.16.1.10:8080mode http 配置项。

在 Amphora 操作系统启动的 haproxy 进程是 haproxy-1385d3c4-615e-4a92-aea1-c4fa51a75557.service(ListenerUUID:1385d3c4-615e-4a92-aea1-c4fa51a75557),查看该进程的 service 配置:

  1. # file: /usr/lib/systemd/system/haproxy-1385d3c4-615e-4a92-aea1-c4fa51a75557.service


  2. [Unit]

  3. Description=HAProxy Load Balancer

  4. After=network.target syslog.service amphora-netns.service

  5. Before=octavia-keepalived.service

  6. Wants=syslog.service

  7. Requires=amphora-netns.service


  8. [Service]

  9. # Force context as we start haproxy under "ip netns exec"

  10. SELinuxContext=system_u:system_r:haproxy_t:s0


  11. Environment="CONFIG=/var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg" "USERCONFIG=/var/lib/octavia/haproxy-default-user-group.conf" "PIDFILE=/var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/1385d3c4-615e-4a92-aea1-c4fa51a75557.pid"


  12. ExecStartPre=/usr/sbin/haproxy -f $CONFIG -f $USERCONFIG -c -q -L O08zAgUhIv9TEXhyYZf2iHdxOkA


  13. ExecReload=/usr/sbin/haproxy -c -f $CONFIG -f $USERCONFIG -L O08zAgUhIv9TEXhyYZf2iHdxOkA

  14. ExecReload=/bin/kill -USR2 $MAINPID


  15. ExecStart=/sbin/ip netns exec amphora-haproxy /usr/sbin/haproxy-systemd-wrapper -f $CONFIG -f $USERCONFIG -p $PIDFILE -L O08zAgUhIv9TEXhyYZf2iHdxOkA


  16. KillMode=mixed

  17. Restart=always

  18. LimitNOFILE=2097152


  19. [Install]

  20. WantedBy=multi-user.target

从配置内容可以看出实际启动的服务为 /usr/sbin/haproxy-systemd-wrapper,同样运行在 namespace amphora-haproxy 中,从日志可以了解到它所做的事情就是调用了 /usr/sbin/haproxy 指令而已:

  1. Nov 15 10:12:01 amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77 ip[13206]: haproxy-systemd-wrapper: executing /usr/sbin/haproxy -f /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/haproxy.cfg -f /var/lib/octavia/haproxy-default-user-group.conf -p /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557/1385d3c4-615e-4a92-aea1-c4fa51a75557.pid -L O08zAgUhIv9TEXhyYZf2iHdxOkA -Ds

除了 Listener 之外,Pool、Member、L7policy、L7rule 以及 Health Monitor 等对象的创建也会影响 haproxy 配置的变更。

pool 创建流程分析

create pool flow 的 UML 图

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

create pool flow 最关键的任务依然是 Task:ListenersUpdate,更新 haproxy 的配置文件。当执行指令 openstack loadbalancer pool create--protocol HTTP--lb-algorithm ROUND_ROBIN--listener1385d3c4-615e-4a92-aea1-c4fa51a75557 为 listener 创建一个 default pool,haproxy.cfg 就会添加一个 backend section,并且根据指令传入的参数渲染 backend mode httpbalance roundrobin 配置项。

  1. # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  2. global

  3.    daemon

  4.    user nobody

  5.    log /dev/log local0

  6.    log /dev/log local1 notice

  7.    stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

  8.    maxconn 1000000


  9. defaults

  10.    log global

  11.    retries 3

  12.    option redispatch


  13. peers 1385d3c4615e4a92aea1c4fa51a75557_peers

  14.    peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

  15.    peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025



  16. frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

  17.    option httplog

  18.    maxconn 1000000

  19.    bind 172.16.1.10:8080

  20.    mode http

  21.    default_backend 8196f752-a367-4fb4-9194-37c7eab95714       # UUID of pool

  22.    timeout client 50000


  23. backend 8196f752-a367-4fb4-9194-37c7eab95714

  24.    mode http

  25.    balance roundrobin

  26.    fullconn 1000000

  27.    option allbackups

  28.    timeout connect 5000

  29.    timeout server 50000

值得注意的是,创建 pool 时可以指定一个 listener uuid 或 loadbalancer uuid。当指定了前者时,意味着为 listener 指定了一个 default pool,listener 只能有一个 default pool,后续重复指定 default pool 则会触发异常;当指定了 loadbalancer uuid 时,则创建了一个 shared pool。shared pool 能被同属一个 loadbalancer 下的所有 listener 共享,常被用于辅助实现 l7policy 的功能。当 listener 的 l7policy 动作被设定为为「转发至另一个 pool」时,此时就可以选定一个 shared pool。shared pool 可以接受同属 loadbalancer 下所有 listener 的转发请求。执行指令创建一个 shared pool:

  1. $ openstack loadbalancer pool create --protocol HTTP --lb-algorithm ROUND_ROBIN --loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  2. +---------------------+--------------------------------------+

  3. | Field               | Value                                |

  4. +---------------------+--------------------------------------+

  5. | admin_state_up      | True                                 |

  6. | created_at          | 2018-11-20T03:35:08                  |

  7. | description         |                                      |

  8. | healthmonitor_id    |                                      |

  9. | id                  | 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 |

  10. | lb_algorithm        | ROUND_ROBIN                          |

  11. | listeners           |                                      |

  12. | loadbalancers       | 01197be7-98d5-440d-a846-cd70f52dc503 |

  13. | members             |                                      |

  14. | name                |                                      |

  15. | operating_status    | OFFLINE                              |

  16. | project_id          | 9e4fe13a6d7645269dc69579c027fde4     |

  17. | protocol            | HTTP                                 |

  18. | provisioning_status | PENDING_CREATE                       |

  19. | session_persistence | None                                 |

  20. | updated_at          | None                                 |

  21. +---------------------+--------------------------------------+

注意,单纯的创建 shared pool 不将其绑定到 listener 的话,haproxy.cfg 配置文件是不会立即更改的。

member 创建流程分析

使用下述指令创建一个 member 到 default pool,选项指定云主机所在的 subnet、ipaddress 以及接收数据转发的 protocol-port。

  1. [root@control01 ~]# openstack loadbalancer member create --subnet-id 2137f3fb-00ee-41a9-b66e-06705c724a36 --address 192.168.1.14 --protocol-port 80 8196f752-a367-4fb4-9194-37c7eab95714

  2. +---------------------+--------------------------------------+

  3. | Field               | Value                                |

  4. +---------------------+--------------------------------------+

  5. | address             | 192.168.1.14                         |

  6. | admin_state_up      | True                                 |

  7. | created_at          | 2018-11-20T06:09:58                  |

  8. | id                  | b6e464fd-dd1e-4775-90f2-4231444a0bbe |

  9. | name                |                                      |

  10. | operating_status    | NO_MONITOR                           |

  11. | project_id          | 9e4fe13a6d7645269dc69579c027fde4     |

  12. | protocol_port       | 80                                   |

  13. | provisioning_status | PENDING_CREATE                       |

  14. | subnet_id           | 2137f3fb-00ee-41a9-b66e-06705c724a36 |

  15. | updated_at          | None                                 |

  16. | weight              | 1                                    |

  17. | monitor_port        | None                                 |

  18. | monitor_address     | None                                 |

  19. | backup              | False                                |

  20. +---------------------+--------------------------------------+

在 octavia-api 层先会通过配置 CONF.networking.reserved_ips 验证该 member 的 ipaddress 是否可用,验证 member 所在的 subnet 是否存在,然后再进入 octavia-worker 的流程。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

下面展开几个关键的 TASKs。

CalculateDelta

TASK:CalculateDelta 轮询 loadbalancer 下属的 Amphorae 执行 Task:CalculateAmphoraDelta,计算 Amphora 现有的 NICs 集合与预期需要的 NICs 集合之间的 “差值”。

  1. # file: /opt/rocky/octavia/octavia/controller/worker/tasks/network_tasks.py



  2. class CalculateAmphoraDelta(BaseNetworkTask):


  3.    default_provides = constants.DELTA


  4.    def execute(self, loadbalancer, amphora):

  5.        LOG.debug("Calculating network delta for amphora id: %s", amphora.id)


  6.        # Figure out what networks we want

  7.        # seed with lb network(s)

  8.        vrrp_port = self.network_driver.get_port(amphora.vrrp_port_id)

  9.        desired_network_ids = {vrrp_port.network_id}.union(

  10.            CONF.controller_worker.amp_boot_network_list)


  11.        for pool in loadbalancer.pools:

  12.            member_networks = [

  13.                self.network_driver.get_subnet(member.subnet_id).network_id

  14.                for member in pool.members

  15.                if member.subnet_id

  16.            ]

  17.            desired_network_ids.update(member_networks)


  18.        nics = self.network_driver.get_plugged_networks(amphora.compute_id)

  19.        # assume we don't have two nics in the same network

  20.        actual_network_nics = dict((nic.network_id, nic) for nic in nics)


  21.        del_ids = set(actual_network_nics) - desired_network_ids

  22.        delete_nics = list(

  23.            actual_network_nics[net_id] for net_id in del_ids)


  24.        add_ids = desired_network_ids - set(actual_network_nics)

  25.        add_nics = list(n_data_models.Interface(

  26.            network_id=net_id) for net_id in add_ids)

  27.        delta = n_data_models.Delta(

  28.            amphora_id=amphora.id, compute_id=amphora.compute_id,

  29.            add_nics=add_nics, delete_nics=delete_nics)

  30.        return delta

简单的说,首先得到预期需要的 desirednetworkids 和已经存在的 actualnetworknics。然后计算出待删除的 deletenics 和待添加的 addnics,并最终 returns 一个 Delta data models 到 Task:HandleNetworkDeltas 执行实际的 Amphora NICs 挂载和卸载。

HandleNetworkDeltas

Task:HandleNetworkDelta 负载基于 Amphora Delta 挂载和卸载 networks。

  1. # file: /opt/rocky/octavia/octavia/controller/worker/tasks/network_tasks.py



  2. class HandleNetworkDelta(BaseNetworkTask):

  3.    """Task to plug and unplug networks


  4.    Plug or unplug networks based on delta

  5.    """


  6.    def execute(self, amphora, delta):

  7.        """Handle network plugging based off deltas."""

  8.        added_ports = {}

  9.        added_ports[amphora.id] = []

  10.        for nic in delta.add_nics:

  11.            interface = self.network_driver.plug_network(delta.compute_id,

  12.                                                         nic.network_id)

  13.            port = self.network_driver.get_port(interface.port_id)

  14.            port.network = self.network_driver.get_network(port.network_id)

  15.            for fixed_ip in port.fixed_ips:

  16.                fixed_ip.subnet = self.network_driver.get_subnet(

  17.                    fixed_ip.subnet_id)

  18.            added_ports[amphora.id].append(port)

  19.        for nic in delta.delete_nics:

  20.            try:

  21.                self.network_driver.unplug_network(delta.compute_id,

  22.                                                   nic.network_id)

  23.            except base.NetworkNotFound:

  24.                LOG.debug("Network %d not found ", nic.network_id)

  25.            except Exception:

  26.                LOG.exception("Unable to unplug network")

  27.        return added_ports

最后返回 added_port return 给后续的 TASK:AmphoraePostNetworkPlug 使用。

AmphoraePostNetworkPlug

Task:AmphoraePostNetworkPlug 负责将 member 所处网络的 port 的信息注入到 network namespace 中。需要注意区分 AmphoraePostNetworkPlug 与 AmphoraePostVIPPlug,前者在 create menber flow 中生效,用于添加连通 member tenant-net 的 interface;后者在 create lb flow 中生效,用于添加连通 vip-net 的 interface。当然了,如果说 member 与 VIP 来自同一个网络,则不再需要为 amphora 添加新的 inferface 了。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

添加 member 之后再次查看 Amphora 的网络情况:

  1. root@amphora-cd444019-ce8f-4f89-be6b-0edf76f41b77:~# ip netns exec amphora-haproxy ifconfig

  2. eth1      Link encap:Ethernet  HWaddr fa:16:3e:f4:69:4b

  3.          inet addr:172.16.1.3  Bcast:172.16.1.255  Mask:255.255.255.0

  4.          inet6 addr: fe80::f816:3eff:fef4:694b/64 Scope:Link

  5.          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1

  6.          RX packets:12705 errors:0 dropped:0 overruns:0 frame:0

  7.          TX packets:613211 errors:0 dropped:0 overruns:0 carrier:0

  8.          collisions:0 txqueuelen:1000

  9.          RX bytes:762300 (762.3 KB)  TX bytes:36792968 (36.7 MB)


  10. eth1:0    Link encap:Ethernet  HWaddr fa:16:3e:f4:69:4b

  11.          inet addr:172.16.1.10  Bcast:172.16.1.255  Mask:255.255.255.0

  12.          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1


  13. eth2      Link encap:Ethernet  HWaddr fa:16:3e:18:23:7a

  14.          inet addr:192.168.1.3  Bcast:192.168.1.255  Mask:255.255.255.0

  15.          inet6 addr: fe80::f816:3eff:fe18:237a/64 Scope:Link

  16.          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1

  17.          RX packets:8 errors:2 dropped:0 overruns:0 frame:2

  18.          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0

  19.          collisions:0 txqueuelen:1000

  20.          RX bytes:2156 (2.1 KB)  TX bytes:808 (808.0 B)

配置文件如下:

  1. # Generated by Octavia agent

  2. auto eth2

  3. iface eth2 inet static

  4. address 192.168.1.3

  5. broadcast 192.168.1.255

  6. netmask 255.255.255.0

  7. mtu 1450

  8. post-up /sbin/iptables -t nat -A POSTROUTING -p udp -o eth2 -j MASQUERADE

  9. post-down /sbin/iptables -t nat -D POSTROUTING -p udp -o eth2 -j MASQUERADE

ListenersUpdate

最后 haproxy 配置的变更依旧是由 Task:ListenersUpdate 完成。

  1. # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  2. global

  3.    daemon

  4.    user nobody

  5.    log /dev/log local0

  6.    log /dev/log local1 notice

  7.    stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

  8.    maxconn 1000000


  9. defaults

  10.    log global

  11.    retries 3

  12.    option redispatch


  13. peers 1385d3c4615e4a92aea1c4fa51a75557_peers

  14.    peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

  15.    peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025



  16. frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

  17.    option httplog

  18.    maxconn 1000000

  19.    bind 172.16.1.10:8080

  20.    mode http

  21.    default_backend 8196f752-a367-4fb4-9194-37c7eab95714

  22.    timeout client 50000


  23. backend 8196f752-a367-4fb4-9194-37c7eab95714

  24.    mode http

  25.    balance roundrobin

  26.    fullconn 1000000

  27.    option allbackups

  28.    timeout connect 5000

  29.    timeout server 50000

  30.    server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14:80 weight 1

实际上,添加 member 就是在 backend(default pool)中加入了 server<member_id>192.168.1.14:80weight1 项目,表示该云主机作为了 default pool 的一部分。

L7policy & L7rule & Health Monitor 的创建流程分析

L7policy 对象的语义是用于描述转发的动作类型(e.g. 转发至 pool、转发至 URL 或拒绝转发)以及 L7rule 的容器,下属于 Listener。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

L7Rule 对象的语义是数据转发的匹配域,描述了转发的路由关系,下属于 L7policy。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

Health Monitor 对象用于对 Pool 中 Member 的健康状态进行监控,本质就是一条数据库记录,描述了健康检查的的规则,下属于 Pool。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

为什么将三者一同分析?从上述三个 UML 图可以感受到 Create L7policy、L7rule、Health-Monitor 和 Pool 实际上是非常类似,关键都是在于 TASK:ListenersUpdate 对 haproxy 配置文件内容的更新。所以,我们主要通过一些例子来观察 haproxy 配置文件的更改规律即可。

EXAMPLE 1. 转发至 default pool

  1. $ openstack loadbalancer healthmonitor create --name healthmonitor1 --type PING --delay 5 --timeout 10 --max-retries 3 8196f752-a367-4fb4-9194-37c7eab95714


  2. $ openstack loadbalancer l7policy create --name l7p1 --action REDIRECT_TO_POOL --redirect-pool 8196f752-a367-4fb4-9194-37c7eab95714 1385d3c4-615e-4a92-aea1-c4fa51a75557


  3. $ openstack loadbalancer l7rule create --type HOST_NAME --compare-type STARTS_WITH --value "server" 87593985-e02f-4880-b80f-22a4095c05a7

haproxy.cfg:

  1. # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  2. global

  3.    daemon

  4.    user nobody

  5.    log /dev/log local0

  6.    log /dev/log local1 notice

  7.    stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

  8.    maxconn 1000000

  9.    external-check


  10. defaults

  11.    log global

  12.    retries 3

  13.    option redispatch


  14. peers 1385d3c4615e4a92aea1c4fa51a75557_peers

  15.    peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

  16.    peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025



  17. frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

  18.    option httplog

  19.    maxconn 1000000

  20.    # 前端监听 http://172.16.1.10:8080

  21.    bind 172.16.1.10:8080

  22.    mode http

  23.        # ACL 转发规则

  24.        acl 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 req.hdr(host) -i -m beg server

  25.    # if ACL 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 满足,则转发至 backend 8196f752-a367-4fb4-9194-37c7eab95714

  26.    use_backend 8196f752-a367-4fb4-9194-37c7eab95714 if 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9

  27.    # 如果没有匹配到任何 ACL 规则,则转发至默认 backend 8196f752-a367-4fb4-9194-37c7eab95714

  28.    default_backend 8196f752-a367-4fb4-9194-37c7eab95714

  29.    timeout client 50000


  30. backend 8196f752-a367-4fb4-9194-37c7eab95714

  31.    # 后端监听协议为 http

  32.    mode http

  33.    # 负载均衡算法为 RR 轮询

  34.    balance roundrobin

  35.    timeout check 10s

  36.    option external-check

  37.    # 使用脚本 ping-wrapper.sh 对 server 进行健康检查

  38.    external-check command /var/lib/octavia/ping-wrapper.sh

  39.    fullconn 1000000

  40.    option allbackups

  41.    timeout connect 5000

  42.    timeout server 50000

  43.    # 后端真实服务器(real server),服务端口为 80,监控检查规则为 inter 5s fall 3 rise 3

  44.    server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14:80 weight 1 check inter 5s fall 3 rise 3

健康检查脚本,正如我们设定的 PING 方式:

  1. #!/bin/bash

  2. if [[ $HAPROXY_SERVER_ADDR =~ ":" ]]; then

  3.    /bin/ping6 -q -n -w 1 -c 1 $HAPROXY_SERVER_ADDR > /dev/null 2>&1

  4. else

  5.    /bin/ping -q -n -w 1 -c 1 $HAPROXY_SERVER_ADDR > /dev/null 2>&1

  6. fi

EXAMPLE 2. 转发至 shared pool

  1. $ openstack loadbalancer healthmonitor create --name healthmonitor1 --type PING --delay 5 --timeout 10 --max-retries 3 822f78c3-ea2c-4770-bef0-e97f1ac2eba8


  2. $ openstack loadbalancer l7policy create --name l7p1 --action REDIRECT_TO_POOL --redirect-pool 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 1385d3c4-615e-4a92-aea1-c4fa51a75557


  3. $ openstack loadbalancer l7rule create --type HOST_NAME --compare-type STARTS_WITH --value "server" fb90a3b5-c97c-4d99-973e-118840a7a236

haproxy.cfg:

  1. # Configuration for loadbalancer 01197be7-98d5-440d-a846-cd70f52dc503

  2. global

  3.    daemon

  4.    user nobody

  5.    log /dev/log local0

  6.    log /dev/log local1 notice

  7.    stats socket /var/lib/octavia/1385d3c4-615e-4a92-aea1-c4fa51a75557.sock mode 0666 level user

  8.    maxconn 1000000

  9.    external-check


  10. defaults

  11.    log global

  12.    retries 3

  13.    option redispatch


  14. peers 1385d3c4615e4a92aea1c4fa51a75557_peers

  15.    peer l_Ustq0qE-h-_Q1dlXLXBAiWR8U 172.16.1.7:1025

  16.    peer O08zAgUhIv9TEXhyYZf2iHdxOkA 172.16.1.3:1025



  17. frontend 1385d3c4-615e-4a92-aea1-c4fa51a75557

  18.    option httplog

  19.    maxconn 1000000

  20.    bind 172.16.1.10:8080

  21.    mode http

  22.        acl 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9 req.hdr(host) -i -m beg server

  23.    use_backend 8196f752-a367-4fb4-9194-37c7eab95714 if 8d9b8b1e-83d7-44ca-a5b4-0103d5f90cb9

  24.        acl c76f36bc-92c0-4f48-8d57-a13e3b1f09e1 req.hdr(host) -i -m beg server

  25.    use_backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8 if c76f36bc-92c0-4f48-8d57-a13e3b1f09e1

  26.    default_backend 8196f752-a367-4fb4-9194-37c7eab95714

  27.    timeout client 50000


  28. backend 8196f752-a367-4fb4-9194-37c7eab95714

  29.    mode http

  30.    balance roundrobin

  31.    timeout check 10s

  32.    option external-check

  33.    external-check command /var/lib/octavia/ping-wrapper.sh

  34.    fullconn 1000000

  35.    option allbackups

  36.    timeout connect 5000

  37.    timeout server 50000

  38.    server b6e464fd-dd1e-4775-90f2-4231444a0bbe 192.168.1.14:80 weight 1 check inter 5s fall 3 rise 3


  39. backend 822f78c3-ea2c-4770-bef0-e97f1ac2eba8

  40.    mode http

  41.    balance roundrobin

  42.    timeout check 10s

  43.    option external-check

  44.    external-check command /var/lib/octavia/ping-wrapper.sh

  45.    fullconn 1000000

  46.    option allbackups

  47.    timeout connect 5000

  48.    timeout server 50000

  49.    server 7da6f176-36c6-479a-9d86-c892ecca6ae5 192.168.1.6:80 weight 1 check inter 5s fall 3 rise 3

可见,在为 listener 添加了 shared pool 之后,会在增加一个 backend section 对应 shared pool 822f78c3-ea2c-4770-bef0-e97f1ac2eba8。

最后

至此,系列三篇基本已经将 Octavia 的资源模型介绍完了,接下来主要会关注 Octavia 在高可用、通信安全、协议类型、性能上的实现细节,敬请关注!



关于九州云99Cloud

九州云成立于2012年,是中国早期从事开放基础架构服务的专业公司。公司成立六年,秉承“开源 · 赋能变革”的理念,不断夯实自身实力,先后为政府、金融、运营商、能源、制造业、商业、交通、物流、教育、医疗等各大行业的企业级客户提供高质量的开放基础架构服务。目前拥有国家电网、南方电网广东公司、中国人民银行、中国银联、中国移动、中国电信、中国联通、中国资源卫星、eBay、国际陆港集团、中国人寿、万达信息、东风汽车、诺基亚等众多重量级客户,被用户认可为最值得信赖的合作伙伴。

2月技术周 | Rocky Octavia 实现与分析 (三): 核心资源创建流程

2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈


Neutron 的经典网络实现模型

网络实现模型,即 Neutron 为了支撑上层网络资源模型(e.g. Network、Subnet、Port、Router etc.)所依赖底层物理/虚拟网元(e.g. 物理交换机、路由器、Linux Bridge、Open vSwitch、LAN Device 等 VNF)构建的网络实现。简单地说,就是下面这张图:

2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈


首先不妨分析一下基于上述网络实现模型的两个计算节点间的虚拟机通信流程  (示意图见下文):虚拟机 1 存在于计算节点 1 上,虚拟机 4 存在于计算节点 2 上,并且虚拟机 1 和虚拟机 4 属于同一个租户网络的同一个子网内,两者之间的数据通信将会经过连接到计算机点 1 与计算节点 2 的物理交换机上进行传输。当虚拟机 1 想发送一个报文给位于不同计算节点上的虚拟机 4 时, 首先会发送一个 ARP 广播报文来确定虚拟机 4 的 MAC 地址。该 ARP 广播报文会通过 Tap 设备以及 qbr 网桥,然后被计算节点 1 上的虚拟交换机 br-int 转发到所有与 br-int 相连的接口上。当广播报文经过计算节点 1 上 br-ethx 时,会被带上一个外部 Vlan id(内外 VLAN ID 转换,为了防止 VxLAN 类型网络与 VLan 类型网络混杂平面场景中出现 VLAN ID 冲突,所以无论是 VLAN 还是 VxLAN 的网络包杂经过 br-int 或 br-tun 网桥时会进行内外部 VLAN ID 转换,通过 Neutron 的算法逻辑来确保避免 VLAN ID 冲突的发生。)。需要注意的是,同一租户的相同网络里所有的虚拟机发出与接收的报文都会带有相同的外部 Vlan id,因此该 ARP 报文会被物理交换机转发到所有其他节点上。当 ARP 报文到达计算节点 2 上时,该数据报文的 Vlan id 会在虚拟交换机 br-int 上被转换成对应的内部 Vlan id,并被广播到所有与 br-int 所连的接口上,最后虚拟机 4 会应答该 ARP 广播。当 虚拟机 1 知道虚拟机 4 的 MAC 地址后,就可以直接与 虚拟机 4 进行数据通信了。


2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈



显然,Neutron 在计算节点上创建了大量的虚拟网络设备(e.g. Tap、veth、qbr、br-int、br-ethx etc.),这些虚拟网络设备的建立和运行会给云平台上的计算节点带了很大的 CPU 开销,加上这些虚拟网络设备本身存在一定的缺陷和性能瓶颈,会极大地减少计算节点上的网络带宽。因此寻找一种更加优秀的 I/O 虚拟化方案来替代目前 Neutron 实现虚拟化网络的方式,来解决 OpenStack 云平台网络 I/O 性能瓶颈问题。

SR-IOV 技术简介

SR-IOV(Single-Root I/O Virtualization,单根 I/O 虚拟化)技术是一种基于物理硬件的虚拟化解决方案,可以提高物理 I/O 设备(e.g. 网络适配器)的性能与可扩展性。SR-IOV 技术允许在虚拟机之间高效共享 PCIe(Peripheral Component Interconnect Express,快速外设组件互连)设备,由于 SR-IOV 技术是基于硬件实现的,可以使虚拟机获得与宿主机媲美的 I/O 性能。


SR-IOV 规范(由 PCI-SIG 在 http://www.pcisig.com 上进行定义和维护)定义了一 种虚拟化 PCIe 设备的标准机制,启用了 SR-IOV 并且具有适当的硬件和 OS 支持的 PCIe 设备(e.g. 以太网端口)可以显示为多个单独的物理设备(虚拟成多个 PCIe 设备,每个都具有自己的 PCIe 配置空间)。SR-IOV 定义了两种功能类型


物理功能(Physical Function,PF):PF 包含 SR-IOV 的功能结构,用于支持和管理 SR-IOV 的 PCI 功能。PF 是全功能的 PCIe,可以像其他任何 PCIe 设备一样进行发现、管理和处理。PF 拥有完全配置和控制 PCIe 设备资源的能力。


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


每个 SR-IOV 设备都可有一个 PF,每个 PF 最多可有 64,000 个与其关联的 VF。缺省情况下,SR-IOV 功能处于禁用状态,PF 充当传统 PCIe 设备。PF 可以通过寄存器创建 VF,一旦在 PF 中启用了 SR-IOV,就可以通过 PF 的总线、设备和功能编号(路由 ID)访问各个 VF 的 PCI 配置空间。每个 VF 都具有一个 PCI 内存空间,用于映射其寄存器集。VF 设备驱动程序对寄存器集进行操作以启用其功能,并且显示为实际存在的 PCI 设备。创建 VF 后,用户可以直接将每个 VF 直接分配给虚拟机,绕过虚拟机监控层(VMM),从而实现低延时和近线速。


SR-IOV 的优点:


性能:从虚拟机环境直接访问硬件。

成本降低:

  • 节能

  • 减少了适配器数量

  • 简化了布线

  • 减少了交换机端口


NIC SR-IOV: 


2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈


光纤 SR-IOV: 


2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈


注:


  • PCI(Peripheral Component Interconnect):符合 PCI 总线标准的设备就被称为 PCI 设备,PCI 总线架构中可以包含多个 PCI 设备。

  • PCIe(Peripheral Component Interconnect Express):PCI Express,简称 PCI-E,是电脑总线 PCI 的一种,它沿用了现有的 PCI 编程概念及通讯标准,但建基于更快的串行通信系统。是 Intel 提出的新一代的总线接口,PCI Express 采用了目前业内流行的点对点串行连接,比起 PCI 以及更早期的计算机总线的共享并行架构每个 PCIe 设备都有自己的专用连接,不需要向整个总线请求带宽,而且可以把数据传输率提高到一个很高的频率,达到 PCI 所不能提供的高带宽。

在 Neutron 中引入 SR-IOV 技术

SR-IOV 技术允许将单个物理网络设备同时共享给多台虚拟机,将虚拟机的虚拟端口与支持 SR-IOV 功能的物理网卡上所虚拟出来的虚拟功能 VF 相关联,这样不仅可以实现对物理网卡的共享,同时不再需要 Neutron 组件额外地去创建 Tap 设备、qbr 网桥以及 OVS 虚拟交换机,使虚拟机的流量绕过 Neutron 所创建的虚拟网络设备,直接到达物理网卡上,极大地减少了计算机点上的 CPU 开销。而且虚拟机数据处理逻辑跳过了计算节点的虚拟网络设备,由物理网卡决定数据如何处理,从而进一步释放了计算节点的 CPU 资源。这 种方式使得虚拟机达到近线速的性能,并且不需要为每台虚拟机单独去分配独立的物理网卡端口。


2月技术周 | 启用 SR-IOV 提升 Neutron 网络 I/O 性能瓶颈



虚拟机 1 处于计算节点 1 上,虚拟机 4 处于计算节点 2 上,两台虚拟机属于同一租户下同一网段内。两台计算节点上均有一张带有 SR-IOV 功能的物理网卡,其中虚拟网卡功能 VF1 是分配给虚拟机 1 的,虚拟网卡功能 VF4 是分配给虚拟机 4 的。 虚拟机所发出的报文会直接发送到与虚拟机相关联的 VF 上,然后由物理网卡来决定如何对数据报文进行处理。在图 3 中可以看到,从虚拟机 1 发出的报文直接发送到 VF1 上,然后此报文经过物理网卡的处理后流入到物理交换机上,通过物理交换机到达计算节点 2 上,最终到达虚拟机 4。


可见,SR-IOV 解决了 Neutron 在计算节点上的网络 I/O 性能瓶颈问题。但是对网络节点的网络 I/O 性能瓶颈却及就没有太大的影响。想要解决这个问题,还需要继续引入 DVR 技术或者是 DPDK 等技术才能实现,这里暂不展开。

Neutron 配置启用 SR-IOV

官方文档:https://docs.openstack.org/newton/networking-guide/config-sriov.html

Step 1. Ensure SR-IOV and VT-d are enabled in BIOS.


Step 2. Enable IOMMU in Linux by adding intel_iommu=on to the kernel parameters, for example, using GRUB.


  1. ...

  2. linux16 /boot/vmlinuz-3.10.0-862.11.6.rt56.819.el7.x86_64 root=LABEL=img-rootfs ro console=tty0 console=ttyS0,115200n8 crashkernel=auto rhgb quiet intel_iommu=on iommu=pt isolcpus=2-3,8-9 nohz=on nohz_full=2-3,8-9 rcu_nocbs=2-3,8-9 intel_pstate=disable nosoftlockup default_hugepagesz=1G hugepagesz=1G hugepages=16 LANG=en_US.UTF-8

  3. ...

Step 3. On each compute node, create the VFs via the PCI SYS interface(e.g. enp129s0f0, enp129s0f1).

  1. [root@overcloud-compute-0 ~]# cat /etc/sysconfig/network-scripts/ifcfg-enp129s0f0

  2. DEVICE="enp129s0f0"

  3. BOOTPROTO="dhcp"

  4. ONBOOT="yes"

  5. TYPE="Ethernet"

  6. [root@overcloud-compute-0 ~]# cat /etc/sysconfig/network-scripts/ifcfg-enp129s0f1

  7. DEVICE="enp129s0f1"

  8. BOOTPROTO="dhcp"

  9. ONBOOT="yes"

  10. TYPE="Ethernet"


  11. [root@overcloud-compute-0 nova]# echo 16 > /sys/class/net/enp129s0f0/device/sriov_numvfs

  12. [root@overcloud-compute-0 nova]# echo 16 > /sys/class/net/enp129s0f1/device/sriov_numvfs

Step 4. Verify that the VFs have been created and are in up state.

  1. [root@overcloud-compute-0 ~]# lspci | grep Ethernet

  2. 03:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

  3. 03:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

  4. 81:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)

  5. 81:00.1 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)

  6. 81:10.0 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  7. 81:10.1 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  8. 81:10.2 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  9. 81:10.3 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  10. 81:10.4 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  11. 81:10.5 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  12. 81:10.6 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01)

  13. 81:10.7 Ethernet controller: