Neutron架构之Neutron-plugin Core-plugin(ML2)篇
Neutron-server接收两种请求:
REST API请求:接收REST API请求,并将REST API分发到对应的Plugin(L3RouterPlugin)。
RPC请求:接收Plugin agent请求,分发到对应的Plugin(NeutronL3agent)。
Neutron-plugin分为Core-plugin和Service-plugin。
Core-plugin:ML2负责管理二层网络,ML2主要包括Network、Subnet、Port三类核心资源,对三类资源进行操作的REST API是原生支持的。
Service-plugin:实现L3-L7网络,包括Router、Firewall、VPN。
core-plugin
core-plugin(ML-2)
|- - - - - - - - - - - - - - - - - - - - - - - |
Type Manager(Vxlan) - - - - - - - Mechanism Manager(openvswitch )
每部分又分为Manager和Driver
执行流
_eventlet_wsgi_server():
service.serve_wsgi(service.NeutronApiService)
WsgiService.start
config.load_paste_app(app_name)
neutron.api.v2.router:APIRouter.factory
Neutron插件是Neutron的一个重要组成部分,它允许Neutron与不同的网络技术进行集成。
首先,我们需要安装Neutron插件。Neutron插件可以是Open vSwitch、Linux Bridge、Cisco Nexus、VMware NSX等。在安装Neutron插件之前,我们需要确保已经安装了Neutron服务。
以Open vSwitch为例,我们可以使用以下命令安装Open vSwitch插件:
sudo apt-get install neutron-plugin-openvswitch-agent
安装Neutron插件后,我们需要配置它。配置文件通常位于/etc/neutron/plugins/
目录下。以Open vSwitch为例,我们需要编辑/etc/neutron/plugins/ml2/ml2_conf.ini
文件。
在该文件中,我们需要配置以下参数:
- type_drivers:指定支持的网络类型,例如vlan、vxlan等。
- tenant_network_types:指定租户网络类型,例如vlan、vxlan等。
- mechanism_drivers:指定网络机制驱动程序,例如Open vSwitch、Linux Bridge等。
- bridge_mappings:指定物理网络和虚拟网络之间的映射关系。
以下是一个示例配置文件:
[ml2]
type_drivers = flat,vlan,vxlan
tenant_network_types = vxlan
mechanism_drivers = openvswitch
[ml2_type_flat]
flat_networks = external
[ml2_type_vxlan]
vni_ranges = 1:1000
[securitygroup]
enable_security_group = True
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
[ovs]
bridge_mappings = external:br-ex
在上面的示例中,我们指定了Open vSwitch插件支持的网络类型为flat、vlan和vxlan。我们还指定了租户网络类型为vxlan,并将机制驱动程序设置为Open vSwitch。我们还指定了物理网络和虚拟网络之间的映射关系,例如将外部网络映射到br-ex网桥。
完成Neutron插件的配置后,我们需要重启Neutron服务以使配置生效。我们可以使用以下命令重启Neutron服务:
sudo service neutron-server restart
sudo service neutron-plugin-openvswitch-agent restart
neutron-plugin 分为 core-plugin 和 service-plugin 两类
L2-L3称为core plugin,包含network、subnet、port
L4-L7称为service plugin,包含router、firewall、loadbalancer、VPN、metering等等
Openstack neutron加载plugin
RESOURCES = {'network': 'networks',
'subnet': 'subnets',
'subnetpool': 'subnetpools',
'port': 'ports'}
SUB_RESOURCES = {}
COLLECTION_ACTIONS = ['index', 'create']
MEMBER_ACTIONS = ['show', 'update', 'delete']
REQUIREMENTS = {'id': attributes.UUID_PATTERN, 'format': 'json'}
class APIRouter(wsgi.Router):
@classmethod
def factory(cls, global_config, **local_config):
return cls(**local_config)
def __init__(self, **local_config):
#创建mapper
mapper = routes_mapper.Mapper()
#获取NeutornManage的core_plugin,这个定义在/etc/neutron/neutron.conf,比如我的是core_plugin = ml2
plugin = manager.NeutronManager.get_plugin()
#扫描neutron/extensions下所有的extension
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)
col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
member_actions=MEMBER_ACTIONS)
#构建映射规则函数
def _map_resource(collection, resource, params, parent=None):
allow_bulk = cfg.CONF.allow_bulk
allow_pagination = cfg.CONF.allow_pagination
allow_sorting = cfg.CONF.allow_sorting
#创建controller
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
path_prefix = None
if parent:
path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
parent['member_name'],
collection)
mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS,
path_prefix=path_prefix,
**col_kwargs)
# mapper.collection这个就是构建映射规则
#collection:一个字符串,就是资源的复数形式,比如networks
#resource:一个字符串,就是资源的单数形式,比如network
return mapper.collection(collection, resource,
**mapper_kwargs)
#构建/的映射规则
mapper.connect('index', '/', controller=Index(RESOURCES))
for resource in RESOURCES:
#针对每一个核心资源(network、subnet等),调用_map_resource,构建映射规则
_map_resource(RESOURCES[resource], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
RESOURCES[resource], dict()))
resource_registry.register_resource_by_name(resource)
#SUB_RESOURCES是空的,没啥用
for resource in SUB_RESOURCES:
_map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
SUB_RESOURCES[resource]['collection_name'],
dict()),
SUB_RESOURCES[resource]['parent'])
#清除当前配置在策略引擎中的所有规则。其在单元测试中和核心API router初始化的最后调用,确保在所有扩展加载之后加载规则
policy.reset()
#初始化wsgi.Router
super(APIRouter, self).__init__(mapper)
四种组网模型
Type Manager
#neutron/plugins/ml2/managers.py
class TypeManager(stevedore.named.NamedExtensionManager):
"""Manage network segment types using drivers."""
def __init__(self):
# Mapping from type name to DriverManager
self.drivers = {}
LOG.info(_("Configured type driver names: %s"),
cfg.CONF.ml2.type_drivers)
super(TypeManager, self).__init__('neutron.ml2.type_drivers',
cfg.CONF.ml2.type_drivers,
invoke_on_load=True)
LOG.info(_("Loaded type driver names: %s"), self.names())
self._register_types()
self._check_tenant_network_types(cfg.CONF.ml2.tenant_network_types)
def create_network_segments(self, context, network, tenant_id):
"""Call type drivers to create network segments."""
segments = self._process_provider_create(network)
session = context.session
with session.begin(subtransactions=True):
network_id = network['id']
if segments:
for segment in segments:
segment = self.reserve_provider_segment(
session, segment)
db.add_network_segment(session, network_id, segment)
else:
segment = self.allocate_tenant_segment(session)
db.add_network_segment(session, network_id, segment)
Type Drivers
- Flat模型最为简单,所有的虚拟机共用一个私有IP网段,IP地址在虚拟机启动时完成注入,虚拟机间的通信直接通过HyperVisor中的网桥转发,公网流量在该网段的网关上进行NAT(Nova-network实现为开启nova-network主机内核的iptables,Neutron实现为网络节点上的l3-agent)。Flat DHCP模型与Flat区别在于网桥中开启了DHCP进程,虚拟机通过DHCP消息获得IP地址(Nova-network实现为nova-network主机中的dnsmaq,Neutron实现为网络节点上的dhcp-agent)。
Dnsmasq 的工作原理
Dnsmasq 在接受到用户的一个 DNS 请求时,首先会查找 /etc/hosts 这个文件,如果 /etc/hosts 文件没有请求的记录,然后查找 /etc/resolv.conf 中定义的外部 DNS(也叫上游 DNS 服务器,nameserver 配置),外部 DNS 通过递归查询查找到请求后响应给客户端,然后 dnsmasq 将请求结果缓存下来(缓存到内存)供后续的解析请求。
配置 Dnsmasq 为 DNS 缓存服务器,同时在 /etc/hosts 文件中加入本地内网解析,这样一来每当内网机器查询时就会优先查询 hosts 文件,这就等于将 /etc/hosts 共享给全内网机器使用,从而解决内网机器互相识别的问题。相比逐台机器编辑 hosts 文件或者添加 Bind DNS 记录,仅编辑一个 hosts 文件,这简直太容易了。
- VLAN模型引入了多租户机制,虚拟机可以使用不同的私有IP网段,一个租户可以拥有多个IP网段。虚拟机IP通过DHCP消息获取IP地址(Nova-network实现为nova-network主机中的dnsmaq,Neutron实现为网络节点上的dhcp-agent)。网段内部虚拟机间的通信直接通过HyperVisor中的网桥转发,同一租户跨网段通信通过网关路由,不同租户通过网关上的ACL进行隔离,公网流量在该网段的网关上进行NAT(Nova-network实现为开启nova-network主机内核的iptables,Neutron实现为网络节点上的l3-agent)。如果不同租户逻辑上共用一个网关,则无法实现租户间IP地址的复用。
- Overlay模型(主要包括GRE和VxlAN隧道技术),相比于VLAN模型有以下改进。1)租户数量从4K增加到16million;2)租户内部通信可以跨越任意IP网络,支持虚拟机任意迁移;3)一般来说每个租户逻辑上都有一个网关实例,IP地址可以在租户间进行复用;4)能够结合SDN技术对流量进行优化。
TYPE=0x8100,PRI是优先级字段,CFI不怎么常用,以太网中CFI=0。后面12位的VID就是VLAN的标签位,也就是说802.1q中一个局域网中最多有4096个VLAN,不过不同的交换机对VLAN的规划是不同的,用户实际在一台交换机上能用的VLAN数目要比4096少很多。很简单的一个字段吧,没有什么分域,更不能用来寻址,就只是一个标签,不过这对于传统局域网里的网络虚拟化来说就足够了。
vxlan与vlan区别:
vxlan支持更多的二层网络
vxlan使用12位bit表示vlan ID,因此最多支持2^12=4094个vlan
vxlan使用的ID使用24位bit,最多可以支持2^24个
已有的网络路径利用效率更高
vlan使用spanning tree protocol避免环路,会将一半的网络路径阻塞
vxlan的数据包封装成UDP通过网络层传输,可以使用所有的网络路径
防止物理交换机Mac表耗尽
vlan需要在交换机的Mac表中记录Mac物理地址
vxlan采用隧道机制,Mac物理地址不需记录在交换机
依赖物理的二层来建立虚拟的大二层(vlan模式)
物理的二层指的是:物理网络是二层网络,基于以太网协议的广播方式进行通信
虚拟的二层指的是:neutron实现的虚拟的网络也是二层网络(openstack的vm机所用的网络必须是大二层),也是基于以太网协议的广播方式进行通信,但毫无疑问的是该虚拟网络依赖于物理的二层网络
依赖物理的三层来建立虚拟的大二层(gre模块与vxlan模式)
物理的三层指的是:物理网络是三层网络,基于ip路由的方式进行通信
虚拟的二层指的是:neutron实现的虚拟的网络仍然是二层网络(openstack的vm机所用的网络必须是大二层),仍然是基于以太网协议的广播方式进行通信,但毫无疑问的是该虚拟网络依赖于物理的三层网络,这有点类似于VPN的概念,根本原理就是将私网的包封装起来,最终打上隧道的ip地址传输。
用户(例如demo用户)可以在自己的project下创建网络(L2 network),该网络就是在大二层的基础上划分出来的,是一个隔离的二层网段。类似于物理网络世界中的虚拟 LAN (VLAN),每建立一个l2 network,都会被分配一个段ID,该段ID标识一个广播域,这个ID是被随机分配的,除非使用管理员身份在管理员菜单下,才可以手动指定该ID
子网是一组 IPv4 或 IPv6 地址以及与其有关联的配置。它是一个地址池,OpenStack 可从中向虚拟机 (VM) 分配 IP 地址。每个子网指定为一个无类别域间路由 (Classless Inter-Domain Routing) 范围,必须与一个网络相关联。除了子网之外,租户还可以指定一个网关、一个域名系统 (DNS) 名称服务器列表,以及一组主机路由。这个子网上的 VM 实例随后会自动继承该配置。
每个网络使用自己的 DHCP Agent,每个 DHCP Agent 在一个 Network namespace 内
不同网络内的IP地址可以重复(overlapping)
跨网络的子网之间的流量必须走 L3 Virtual Router
L2 network之Provider与Tenant的区别
Provider network 是由 Admin 用户创建的,而 Tenant network 是由 tenant 普通用户创建的。
Provider network 和物理网络的某段直接映射,比如对应某个 VLAN,因此需要预先在物理网络中做相应的配置。而 tenant network 是虚拟化的网络,Neutron 需要负责其路由等三层功能。
对 Flat 和 VLAN 类型的网络来说,只有 Provider network 才有意义。即使是这种类型的 tenant network,其本质上也是对应于一个实际的物理段。
对 GRE 和 VXLAN 类型的网络来说,只有 tenant network 才有意义,因为它本身不依赖于具体的物理网络,只是需要物理网络提供 IP 和 组播即可。
Provider network 根据 admin 用户输入的物理网络参数创建;而 tenant work 由 tenant 普通用户创建,Neutron 根据其网络配置来选择具体的配置,包括网络类型,物理网络和 segmentation_id。
创建 Provider network 时允许使用不在配置项范围内的 segmentation_id。
Mechanism Manager
ML2 plugin 通过 neutron.plugins.ml2.managers.MechanismManager 与配置了的 mechanism driver 交互。
MechanismManager管理MechanismDriver。
MechanismDriver在创建、更新、删除网络或端口的时候被调用,负责二层网络技术底层的具体实现,和不同的网
络设备进行交互。在每次事件中会调用到MechanismDriver的两个方法
Mechanism Driver
这部分是对各种 L2 技术的支持,例如 Open vSwitch,linux bridge 等等。
会按照配置顺序依次调用每一个Driver的对应函数来完成,比如对需要配置交换机的操作,可能Open vSwitch虚拟交换机和外部真实的物理交换机比如Cisco交换机都需要进行配置,这个时候需要Open vSwitch Mechanism Driver和Cisco Mechanisam Driver都被调用处理。
extension drivers,type drivers 和 mechanism drivers,如果其中任何一个不能接受新创建的 Network,都会导致创建 Network 失败,并且清除在 Network 在 DB 中的记录。
整个流程与创建 Network 类似。差别是少了调用 type drivers,因为 type drivers 针对的是 Network type 的支持,所以没有必要在 Port create 中调用。另一方面,创建 Port 会调用 rpc 通知 L2 agents,这是因为 Port 在 ML2 中还只是一个内存或者 DB 中的对象,真正在 SDN 中起作用的,是在各个 L2 agents 上的虚拟端口。ML2 通过 rpc 通知到 L2 agents,并创建相应的虚拟端口,这样,虚机,虚拟路由器,DHCP 服务等都能基于虚机端口提供网络服务。
Neutron 默认采用的 开源Open vSwitch 创建的虚机交换机
Mechanism Manager分发操作并具体传递操作到Mechanism Driver的方式与Type Manager相同,但是一个需要Mechanism Driver处理的操作是:会按照配置顺序依次调用每一个Driver的对应函数来完成,比如对需要配置交换机的操作,可能Open vSwitch虚拟交换机和外部真实的物理交换机比如Cisco交换机都需要进行配置,这个时候需要Open vSwitch Mechanism Driver和Cisco Mechanisam Driver都被调用处理。
yum install openstack-neutron-openvswitch -y
vi /etc/neutron/plugins/ml2/openvswitch_agent.ini
[ovs]
tunnel_bridge = br-tun
local_ip = 192.168.100.20 #隧道IP地址 管理网卡IP地址
integration_bridge = br-int
tenant_network_type = vxlan
tunnel_type = vxlan
tunnel_id_ranges = 1:1000
enable_tunneling = true
[agent]
tunnel_types = vxlan
l2_population = true
[securitygroup]
firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
enable_security_group = true
systemctl start neutron-openvswitch-agent.service
systemctl enable neutron-openvswitch-agent.service
ifconfig
brctl show
ovs-vsctl show
Neutron-VLAN-OVS
与Linux bridge不同,openvswitch 不是通过eth1.1 eth1.2这样的vlan接口来隔离不同的vlan,而是通过openvswitch的流表规则来指定如何对进出br-int的数据进行转发,实现不同vlan的隔离。
图中计算节点的所有虚拟机都连接在int网桥上,虚拟机分为两个网络。Int网桥会对到来的数据包根据network的不同打上vlan id号,然后转发到eth网桥,eth网桥直连物理网络。这时候流量就从计算节点到了网络节点。
网络节点的ehx int网桥的功能相似,多了一个ex网桥,这个网桥是管理提前创建好的,和物理网卡相连,ex网桥和int网桥之间通过一对patch-port相连,虚拟机的流量到达int网桥后经过路由到ex网桥。
OpenvSwitch的组成
1.ovs的主要组成模块如下:
ovs-vswitchd:OVS守护进程是,OVS的核心部件,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换(flow-based switching)。它和上层 controller 通信遵从 OPENFLOW 协议,它与 ovsdb-server 通信使用 OVSDB 协议,它和内核模块通过netlink通信,它支持多个独立的 datapath(网桥),它通过更改flow table 实现了绑定和VLAN等功能。
ovsdb-server:轻量级的数据库服务,主要保存了整个OVS 的配置信息,包括接口啊,交换内容,VLAN啊等等。ovs-vswitchd 会根据数据库中的配置信息工作。它于 manager 和 ovs-vswitchd 交换信息使用了OVSDB(JSON-RPC)的方式。
ovs-dpctl:一个工具,用来配置交换机内核模块,可以控制转发规则。
ovs-vsctl:主要是获取或者更改ovs-vswitchd 的配置信息,此工具操作的时候会更新ovsdb-server 中的数据库。
ovs-appctl:主要是向OVS 守护进程发送命令的,一般用不上。
ovsdbmonitor:GUI 工具来显示ovsdb-server 中数据信息。
ovs-controller:一个简单的OpenFlow 控制器
ovs-ofctl:用来控制OVS 作为OpenFlow 交换机工作时候的流表内容。
2.OpenvSwitch的工作流程
通过ovs实现虚拟机和外部通信的过程,通信流程如下:
1.VM实例 instance 产生一个数据包并发送至实例内的虚拟网络接口 VNIC,图中就是 instance 中的 eth0.
2.这个数据包会传送到物理机上的VNIC接口,如图就是vnet接口.
3.数据包从 vnet NIC 出来,到达桥(虚拟交换机) br100 上.
4.数据包经过交换机的处理,从物理节点上的物理接口发出,如图中物理机上的 eth0 .
5.数据包从 eth0 出去的时候,是按照物理节点上的路由以及默认网关操作的,这个时候该数 据包其实已经不受你的控制了.
注:一般 L2 switch 连接 eth0 的这个口是一个 trunk 口, 因为虚拟机对应的 VNET 往往会设置 VLAN TAG, 可以通过对虚拟机对应的 vnet 打 VALN TAG 来控制虚拟机的网络广播域. 如果跑多个虚拟机的话, 多个虚拟机对应的 vnet 可以设置不同的 vlan tag, 那么这些虚拟机的数据包从 eth0(4)出去的时候, 会带上TAG标记. 这样也就必须是 trunk 口才行。
# 添加网桥:
ovs-vsctl add-br br0
# 列出所有网桥:
ovs-vsctl list-br
# 判断网桥是否存在:
ovs-vsctl br-exists br0
# 将物理网卡挂载到网桥上:
ovs-vsctl add-port br0 eth0
# 列出网桥中的所有端口:
ovs-vsctl list-ports br0
# 列出所有挂载到网卡的网桥:
ovs-vsctl port-to-br eth0
# 查看ovs的网络状态:
ovs-vsctl show
# 删除网桥上已经挂载的网口:
ovs-vsctl del-port br0 eth0
# 删除网桥:
ovs-vsctl del-br br0
# 设置控制器:
ovs-vsctl set-controller br0 tcp:ip:6633
# 删除控制器:
ovs-vsctl del-controller br0
# 设置支持OpenFlow Version 1.3:
ovs-vsctl set bridge br0 protocols=OpenFlow13
# 删除OpenFlow支持设置:
ovs-vsctl clear bridge br0 protocols
# 设置vlan标签:
ovs-vsctl add-port br0 vlan3 tag=3 -- set interface vlan3 type=internal
# 删除vlan标签:
ovs-vsctl del-port br0 vlan3
# 查询 VLAN:
ovs-vsctl show
ifconfig vlan3
# 查看网桥上所有交换机端口的状态:
ovs-ofctl dump-ports br0
# 查看网桥上所有的流规则:
ovs-ofctl dump-flows br0
# 查看ovs的版本:
ovs-ofctl -V# 给端口配置tagovs-vsctl set port br-ex tag=101
OpenFlow协议定义了交换机在报文匹配失败时向控制器申请流表的方法,当交换机收到一个不能被当前流表各条流匹配的数据包时,通过将失配报文的相关信息封装在Packet-In消息中发送给控制器,让控制器知晓报文失配情况,由控制器通过Flow-Mod等消息向交换机安装新流表。
extension
在neutron/plugins/ml2/manager.py文件中,除了Type Manager与Mechanism之外还定义有Extension Manger
extension应该放在neutron/extensions文件夹下,或者在配置文件中设置api_extensions_path
extension的class名应该和文件同名,当然首字母应该大写
应该实现neutron.api.extensions.py中ExtensionDescriptor定义的接口
在对应的plugin的supported_extension_aliases 中增加我们extension的别名。
create_network
create_network_in_db
extension_manager.process_create_network
type_manager.create_network_segments
mechanism_manager.create_network_precommit
db commit
mechanism_manager.create_network_postcommit
create_subnet
create_subnet_in_db
extension_manager.process_create_subnet
mechanism_manager.create_subnet_precommit
db commit
mechanism_manager.create_subnet_postcommit
create_port
create_port_in_db
extension_manager.process_create_port
port biding
mechanism_manager.create_port_precommit
db commit
mechanism_manager.create_port_postcommit