|
Openstack允许不同租户创建自己的网络,并通过租户自己的Router连接到外网。网络是允许重叠的,即不同租户都可以创建了192.168.1.0/24这个网段,都有一个192.168.1.1的IP,这样就需要在底层实现对不同租户网络的隔离。
Neutron(openstack中的网络管理模块)实现隔离的方式不止一种(根据不同的plugin而不同),我只讨论我所配置的Openstack(应该也是最觉的配置方式之一)的情况,即OVS_Neutron_Plugin,即选用Open vSwith作为虚拟网桥的方式,对处于不同物理节点上的ovs bridge,采用GRE隧道的方式连接(另外一种是Vlan)。
根据查看Neutron相关部分代码和实验验证,得知Neutron是通过Vlan的方式来作网络隔离的,这一点也不意外。下表列出了某计算节点上br-int的端口配置情况,可以看到不同的网络分配了不同的tag值,这里的tag就是Vlan ID,所有从这些port进入的数据都会被加上相应的Vlan ID在交换机里传输和处理,不同的Vlan的端口是不会接收到其它Vlan的数据的,这样就在一个物理节点为解决了隔离问题。
root@compute1:~# ovs-vsctl show
a9505710-5f04-4e25-88a5-03b04a367393
Bridge br-int
Port "qvo29608ccb-bb"
tag: 2
Interface "qvo29608ccb-bb"
Port br-int
Interface br-int
type: internal
Port "qvocaad8131-1c"
tag: 1
Interface "qvocaad8131-1c"
Port "qvoae9adf7e-04"
tag: 3
Interface "qvoae9adf7e-04"
Port patch-tun
Interface patch-tun
type: patch
options: {peer=patch-int}
Port "qvo222d72f1-80"
tag: 1
Interface "qvo222d72f1-80"
Port "qvo68d15feb-77"
tag: 3
Interface "qvo68d15feb-77"
ovs_version: "2.0.0"
为什么说只是在一个物理节点上解决了隔离问题,因为Neutron为同一个网络在不同的物理节点上(比如控制节点和计算节点)上分配的Vlan ID是不一样的,这样做的原因我想一是为了部署的灵活,更重要的是为了充分地利用Vlan资源。因为Vlan ID最大只能到4096,在复杂的云网络中是比较稀缺的资源,不同的物理节点上自由分配Vlan ID可以使资源的利用最大化,特别是在系统的物理节点非常多的情况下。
那么既然不同物理节点的Vlan分配不一致,但是不同物理节点上的虚拟网络必须要保持一致,这就需要一个翻译的过程。前面已经提到,不同节点间的虚拟网络是通过GRE隧道连接的,这个翻译正是在数据进出隧道的时候完成的。我们可以看Neutron在某计算节点的br-tun桥中为此而下属OpenFlow流规则。
root@compute1:~# ovs-ofctl dump-flows br-tun |grep "vlan"
cookie=0x0, duration=1302.92s, table=2, n_packets=4, n_bytes=300, idle_age=1285, priority=1,tun_id=0x1 actions=mod_vlan_vid:3,resubmit(,10)
cookie=0x0, duration=1304.725s, table=2, n_packets=3, n_bytes=210, idle_age=1290, priority=1,tun_id=0x3 actions=mod_vlan_vid:1,resubmit(,10)
cookie=0x0, duration=1303.878s, table=2, n_packets=6, n_bytes=468, idle_age=1288, priority=1,tun_id=0x4 actions=mod_vlan_vid:2,resubmit(,10)
cookie=0x0, duration=1303.035s, table=21, n_packets=0, n_bytes=0, idle_age=1303, hard_age=1302, priority=1,dl_vlan=3 actions=strip_vlan,set_tunnel:0x1,output:2
cookie=0x0, duration=1303.994s, table=21, n_packets=0, n_bytes=0, idle_age=1303, hard_age=1302, priority=1,dl_vlan=2 actions=strip_vlan,set_tunnel:0x4,output:2
cookie=0x0, duration=1304.784s, table=21, n_packets=0, n_bytes=0, idle_age=1304, hard_age=1302, priority=1,dl_vlan=1 actions=strip_vlan,set_tunnel:0x3,output:2
可以看到此Openstack下有三个租户网络,以粗体部分为例,解释一下这些流规则的语义:第4条规则,当有vlan id = 3的数据准备进入隧道离开本节点到达对岸节点的时候,将会去掉原有的Vlan ID,而加上tun id =1;第1条规则,则是相反的操作,当有tun id=1的数据从隧道出来的时候,将会为其加上vlan id=3,再由下一级流表进行后续处理,这样就在一个节点上完成了vlan id=3 tun id=1的翻译(转换)。
我们再来看此隧道对面的节点(为控制节点)的隧道又是如何配置的呢?
root@controller:~# ovs-ofctl dump-flows br-tun |grep vlan
cookie=0x0, duration=1398.155s, table=2, n_packets=0, n_bytes=0, idle_age=1398, priority=1,tun_id=0x1 actions=mod_vlan_vid:1,resubmit(,10)
cookie=0x0, duration=1397.476s, table=2, n_packets=0, n_bytes=0, idle_age=1397, priority=1,tun_id=0x5 actions=mod_vlan_vid:2,resubmit(,10)
cookie=0x0, duration=1396.634s, table=2, n_packets=0, n_bytes=0, idle_age=1396, priority=1,tun_id=0x3 actions=mod_vlan_vid:4,resubmit(,10)
cookie=0x0, duration=1397.033s, table=2, n_packets=0, n_bytes=0, idle_age=1397, priority=1,tun_id=0x4 actions=mod_vlan_vid:3,resubmit(,10)
cookie=0x0, duration=1396.687s, table=21, n_packets=4, n_bytes=300, idle_age=1385, priority=1,dl_vlan=4 actions=strip_vlan,set_tunnel:0x3,output:2
cookie=0x0, duration=1397.139s, table=21, n_packets=6, n_bytes=468, idle_age=1383, priority=1,dl_vlan=3 actions=strip_vlan,set_tunnel:0x4,output:2
cookie=0x0, duration=1397.537s, table=21, n_packets=0, n_bytes=0, idle_age=1397, priority=1,dl_vlan=2 actions=strip_vlan,set_tunnel:0x5,output:2
cookie=0x0, duration=1398.218s, table=21, n_packets=4, n_bytes=300, idle_age=1380, priority=1,dl_vlan=1 actions=strip_vlan,set_tunnel:0x1,output:2
可以看到在控制节点上,将tun id 1 翻译成了vlan id =1,证明在不同的物理节点上为不同的网络分配Vlan确实是不同的。
下图是Neutron实现整个隔离过程的总结,两台虚拟机属于同一虚拟网络,但它们在各自的节点上分配的Vlan Id却不同,对过各自的Tunnel交换机对Vlan GRE的转换,实现一致性。
接下来将分析一旦OVS网桥接上SDN控制器将是什么情况。从某种意义上,我们也可以说Neutron其实就是一个SDN的控制器,因为通过它可以进行端口的创建和删除,流规则的配置等操作,但是可惜的是它不是通过开放的OpenFlow和OF-Config来实现这些操作的,而是通过OVS特有的接口OVSDB的方式来实现的。对研究OpenFlow的人来说,常常需要将Neutron所创建的这些OVS Bridge连接上OpenFlow的控制器。这里选用的是Floodlight,它是Big Switch公司控制器的开源版,当OVS Bridge连上Openflow控制器之后,它就由一个OpenFlow hybrid的混合交换机变成一个Openflow交换机,它的行为就遵从OpenFlow协议的规范,在每个交换机里的流规则不再是一条简单的Normal动作通配一切,而是由控制器对每一条流的源/目的等信息进行解析再根据策略实时下发转发规则,这对网络流量的行为监控和管理都十分有利。但在下发任何规则之前,Floodlight将会将接入的交换机原有的规则全部清空(也许也可以保留或者部分保留,待研究,但这不是根本问题)。
清空规则对tunnel的影响是最大的,如前所示Neutron为了保证网络的隔离和不同物理节点上虚拟网络的连续性,在br-tun的bridge上配置了一系列的转换规则,清除这些规则意味着转换关系的丢失。如果只是转换关系的丢失,我们完全可以通过控制器再次添加相同的OpenFlow流来弥补,但关键的问题还不在于此。前面提到Neutron为每一个租户网络分配了不同的Vlan,属于某个租户网络的Port都统一打上了对应的Vlan tag,以使每一个出现在网络中的数据帧都有对应的vlan标记。但是这种为交换机access port加上vlan tag就可以为数据包自动加上相应vlan标记的方式是传统网络设备的特性,并不是OpenFlow规范所支持的内容,也就是说,前面提到过的OVS Bridge上的tag(如下),在OVS作为OpenFlow交换机的时候是无意义的(除非使用非OpenFlow规范内的Normal动作)。
Bridge br-int
Port "qvo68d15feb-77"
tag: 3
Interface "qvo68d15feb-77"
也就是说,一旦将OVS Bridge连上控制器,由虚拟主机进入交换机中的数据包将不再被加上Vlan tag,网络隔离被破坏。事实上我们也可以从wireshark抓取的GRE封装包来印证,这是未连接控制器的数据时,由Vlan3的虚拟机发出的数据包。可以看到只有Gre隧道的key=0x1,而封装的以太帧中也没有Vlan信息(进入br-int时应是有的,但是进入tunnel的时候被strip_vlan动作去掉了,参见前文),符合前面的分析。
而一旦连接上控制器,数据包没有Vlan信息(进入br-int就没有),Gre key =0(没有相应的规则添加tun id了),亦即不再有任何标识来隔离不同网络。
更进一步地,为了证明我们的分析,现在只将br-tun连接控制器,这时我们可以看到数据包变成了如下情况:
因为控制器清除了br-tun上的转换信息,所以隧道数据gre key = 0,没有被设置,但是br-int并没有连接控制器,它依然是一个openflow hybrid的交换机,里面只有一条规则,即对所有数据包执行Normal动作,OpenFlow对Normal没有规范,完全由厂商自行设计实现,代表传统转发行为,所以它端口的vlan tag依然有效,GRE封装的以太帧类型是802.1Q(即Vlan),其ID=3。
至此,问题的根源已经明了,那就是ovs端口上的tag标签只是一种传统交换机的特性,不是OpenFlow规范里的内容。虽然OVS是我们最常用的虚拟OpenFlow交换机,但是它却远不只是一个OpenFlow交换机,它还支持许多传统交换机的功能,比如设置Mirror,它也有自己特性,比如OVSDB接口。而端口的Vlan tag刚好是一个OpenFlow之外的特性。
事实上我们查看OpenFLow的协议文档(1.3.2版本)关于端口配置的部分(7.2.1节:Port Structures):
enum ofp_port_config {
OFPPC_PORT_DOWN = 1 |
|