OpenStack虚机网卡的创建过程

来源:互联网 发布:ubuntu 查看分区格式 编辑:程序博客网 时间:2024/03/28 20:45

OpenStack最基本和常用的操作就是启动虚机。虚机启动的过程中涉及很多内容,其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡。虚机的创建和管理是Nova的任务,虚机网络的创建和管理是Neutron的任务,而虚机网卡,作为连接虚机和虚机网络的桥梁,其创建和管理则同时涉及了Nova和Neutron。这次介绍一下,OpenStack中虚机的网卡的创建过程。虽然本文的介绍将基于OpenVSwitch,但是你可以发现,很少有特殊于OpenVSwitch的地方,所以其他的二层机制(例如:Linux Bridge)过程都是类似的。

注:本文的所有分析都是基于OpenStack 17年上半年的代码(Pike版本),因此本文只反应OpenStack在那个时间点的行为。

先假设我们有一个典型的基于OpenVSwitch的OpenStack环境,服务的分布如下。
这里写图片描述

Neutron L2 Agent上线

首先是所有的服务上线,看 neutron-openvswitch-agent 的启动。
● OpenVSwitch agent 启动,注册一个定时程序:

neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent.OVSNeutronAgent.__init__

● 在定时程序内部,通过RPC向Neutron Server定时上报自己的状态:

neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent.OVSNeutronAgent._report_state

● 在Neutron Server,对应的RPC处理方法中,Neutron Server将Agent上报的状态写入自己的DB:

 neutron.db.agents_db.AgentExtRpcCallback.report_state

到此为止,Neutron Server 知道了Neutron OpenVSwitch Agent的状态及相关信息,这一步的示意图如下所示。
这里写图片描述

设想有这么一个场景,如果同时有N个计算节点,由于电源问题,这些计算节点同时断电重启。那么当这些计算节点上的OpenVSwitch Agent恢复之后,由于启动时间比较集中,它们会在一个相对集中的时间点,定时向Neutron Server上报自己的状态。这涉及到Neutron Server处理RPC请求,写DB,还有一些逻辑处理。所以当N足够大时,会周期性的给Neutron Server带来高负荷。这是实际应用和优化需要注意的一个地方。

创建一个虚机,OpenStack创建逻辑端口(port)

接下来通过调用Nova的REST API创建一个虚机,并且nova scheduler将虚机分布到了计算节点。Nova计算节点上的nova-compute进程会:
● 调用Neutron REST API创建端口(port):

nova.network.neutronv2.api.API.allocate_for_instance

这里创建端口只是逻辑验证,Neutron Server会在自己的DB里面创建一个相应的,基本为空的端口
● 根据Nova掌握的信息,更新端口:

nova.network.neutronv2.api.API._update_ports_for_instance

这里nova向Neutron传递的端口信息包括(列举一部分):
device_id: 虚机的uuid
device_owner: 由compute+虚机所在的Nova Availability Zone组成的字符串,例如“compute: nova”
dns_name: 虚机的hostname, 通常为虚机name
binding:host_id: nova-compute所在的host id,可以是hostname,也可以是IP地址
binding:profile: 一些额外的信息,例如SRIOV信息

Neutron Server在收到这些信息之后,主要处理流程如下:
● nova-compute调用Neutron Server更新端口,请求在这里处理:neutron.plugins.ml2.plugin.Ml2Plugin.update_port
● 之后处理port bind:neutron.plugins.ml2.plugin.Ml2Plugin._bind_port
● 调用到Neutron ML2的Mechanism Manger做port bind:neutron.plugins.ml2.manager.MechanismManager._bind_port_level
● 再调用到Neutron ML2的OpenVSwitch Mechanism Driver做实际的port bind:neutron.plugins.ml2.drivers.mech_agent.AgentMechanismDriverBase.bind_port
1. 虽然这是个通用类,但是OVS Mechanism Driver继承自这个类。
2. 在这个方法里面,会检查在指定的host上有没有相应的L2 Agent,所以这一步依赖之前一步的Neutron OpenVSwitch Agent状态上报
3. 这里的host信息来自于nova-compute传递过来的binding:host_id
● 将OVS对应的vif_typevif_details两个属性传递给port:neutron.plugins.ml2.drivers.mech_agent.SimpleAgentMechanismDriverBase.try_to_bind_segment_for_agent
● OVS的vif_typevif_details在OVS Mechanism Driver的初始化函数里面定义:neutron.plugins.ml2.drivers.openvswitch.mech_driver.mech_openvswitch.OpenvswitchMechanismDriver.__init__
这里,定义了OVS对应的vif_type是“ovs”,而vif_details包含了一些辅助信息

vif_details里面包含了一个字段OVS_HYBRID_PLUG,如果这个字段为True,则最后虚机的网卡和 br-int 之间会有一个 Linux Bridge 来应用 iptables 规则。如果你了解过OpenStack底层OpenVSwitch网卡连接方式,那么你一定见过下面这张图,其中浅紫色的qbrXXX,就是因为这个字段为True才会在后面的步骤被创建。
这里写图片描述

这部分Neutron的行为都是在ML2中完成。这部分除了更新Neutron自身的数据之外,比较重要的就是将vif_typevif_details作为port的一部分数据,返回给nova-compute。到此为止,虚机的网卡还没有创建,所有的操作都还只是在逻辑层面,只有数据库的数据发生了变化。并且,在Neutron的数据库中,port的状态现在是Down。但是,Nova和Neutron都知道了接下来要创建的网卡的具体信息,这一步的实际意义在于两个相对独立的项目之间的数据同步。现在,OpenStack整体示意图如下所示:
这里写图片描述

Nova-compute创建虚拟网卡

虽然说Neutron是OpenStack里面的网络服务项目,但是OpenStack里面的虚机网卡,却是由Nova创建的。nova-compute在从Neutron Server拿到了端口的信息之后(通过update port的返回数据):
● 调用相应的虚拟化Driver,继续创建虚机:nova.compute.manager.ComputeManager._build_and_run_instance
● 在Driver内部,创建网络相关内容:nova.virt.libvirt.driver.LibvirtDriver.spawn
● 在Driver内部,通过调用os-vif库,创建虚机网卡。由于nova-compute现在已经知道了虚机网卡的所有信息,适用于虚机的网卡被创建出来:nova.virt.libvirt.driver.LibvirtDriver.plug_vifs
至此,虚机的虚拟网卡真正的创建出来了。但是,在Neutron的数据库中,port的状态现在是Down。到此为止,OpenStack的整体示意图如下所示:
这里写图片描述

Neutron检测虚拟网卡状态并更新port状态

虚机的虚拟网卡被插入到OVS网桥上,对于Neutron来说,接下来就是接管这个网卡。
● Neutron OpenVSwitch Agent进程中会监听OVS网桥的状态:neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent.OVSNeutronAgent.rpc_loop
● 当发现有新增的虚拟网卡时,先从Neutron Server获取详细的网卡信息:neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent.OVSNeutronAgent.treat_devices_added_or_updated

nova-compute在创建虚拟网卡的时候,已经将Neutron port id和一些其他信息写入到OVS port/interface中,因此Neutron从新增的虚拟网卡就能知道对应的port是那个,下图是截取的OVS数据,里面的iface-id就是Neutron port对应的ID
这里写图片描述
● Neutron OpenVSwitch Agent本地更新完虚拟网卡之后,再通过RPC通知Neutron Server端口上线:neutron.plugins.ml2.drivers.openvswitch.agent.ovs_neutron_agent.OVSNeutronAgent._bind_devices
至此,Neutron已经接管了虚拟网卡,并且在Neutron的数据库中,port的状态现在是Active。Neutron从这个时候开始正式接管虚机网络。OpenStack整体示意图如下所示:
这里写图片描述

总结

所以,简单来说,在OpenStack中,首先需要各个服务上线;之后Nova会创建逻辑网卡,但是Nova只知道虚机所在的host;Neutron会根据所在的host,判断出相应的网络虚拟化机制,例如ovs,linuxbridge,Neutron会把这些信息回传给Nova;Nova拿到这些信息,调用相应的方法创建虚拟网卡,并接入到虚机;Neutron会监听网桥上端口的变化,发现有上线的端口,与自己本身的数据进行匹配,匹配到了之后接管这个虚拟网卡。对于Neutron来说,它不关心虚拟网卡接的是虚机还是容器还是别的什么,它只能看到虚拟网卡。

作者简介:肖宏辉,毕业于中科院研究生院,8年的工作经验,其中6年云计算开发经验,OpenStack社区积极活跃,有超过300个commit和超过30000行代码的贡献。目前关注SDN/NFV等虚拟网络技术。本文所有观点仅代表作者个人观点,与作者现在或者之前所在的公司无关。

转载自 SDNLAB 微信公众号。