SDN融合方案中,neutron底层网络运行机制


1、服务部署情况

一般而言,neutron-server和各neutron-plugin部署在控制节点或者网络节点上,而neutron agent则部署在网络节点上和计算节点上。我们先来简单地分析控制端neutron-server和neutron-plugin的工作,然后再分析设备端neutron-agent的工作。

1.1 compute节点的部署情况

  • keystone认证服务
  • nova-comput服务
  • OVS service和OVS-agent

1.2 network节点的部署情况

  • OVS service
  • OVS agent
  • L3 agent
  • DHCP agent
  • metadata agent
  • keystone服务

1.3 controller节点的部署情况

  • SQL server
  • message queue
  • keystone service
  • neutron server
  • ml2 plugin

2、neutron底层实现机制

2.1 总体介绍

在neutron和SDN方案中,neutron底层网络的实现包括:

  • agent是处理数据流的网络设备(OVS,Router,负载均衡等)
  • agent是SDN controller(ODL,ONOS等)

具体关系包括两种类型,如图所示:

第一种:neutron作为SDN controller

第二种:neutron作为application

第一种类型:neutron相当于SDN控制器 第二种类型:neutron作为SDN的应用,将业务告知给SDN controller,neutron的角色更多的是super controller或者网络编排器,来完成openstack中有关于网络业务的集中分发,将应用的RESTful API分发给相应的SDN进行处理。具体实现方法包括:

  • 特定的SDN controller plugin
  • ML2 plugin和定制的mechanism driver

2.2 应用举例

openstack和SDN集成中,以openDayLight为例,可以通过实现mechanism driver来与neutron对接,利用ml2 plugin的北向 RESTful API实现功能。

通过一个openstack用户应用的调用为例进行说明(ml2配置的ODL的mechanism driver,以及一个VXLAN的driver):

(1)用户在horizon界面发从对网络操作的一个请求,包装成RESTful API,并送给neutron server,这可以作为一个北向RESTful API; (2)neutron server将RESTful API给到ml2 plugin,并会将配置信息同步给 neutron database(注:由于ODL driver并不实现pre commit,只实现post commit,会出现openstack的数据库和ODL数据库的不一致性); (3)ml2 plugin调用RESTFul API给到ODL controller; (4)ODL接收到请求后,就会利用南向的plugins/网络协议(openFlow,OVSDB等)来对网络进行相应的操作。

创建虚机时的网络配置


1、neutron服务回顾

1.1、neutron server

neutron server: 对收到的 rest api 请求进行解析,并最终转换成对该 plugin(core or service) 中相应方法的调用,启动后,根据配置文件动态加载对应的core plugin 和 service plugin。

Neutron-server可以理解为一个专门用来接收Neutron REST API调用的服务器,然后负责将不同的rest api分发到不同的neutron-plugin上。

1.2、plugins

plugin主要用来帮助实现neutron server发来的API请求,主要分为core plugin和service plugin。

core plugin

core plugin实现了对标准API的支持(network,subnet,port),这里主要指ml2 plugin。

  • network:代表一个隔离的二层网段,是为创建它的租户而保留的一个广播域。subnet和port始终被分配给某个特定的network。Network的类型包括Flat,VLAN,VxLAN,GRE等等。
  • subnet:代表一个IPv4/v6的CIDR地址池,以及与其相关的配置,如网关、DNS等等,该subnet中的 VM 实例随后会自动继承该配置。Sunbet必须关联一个network。
  • port: 代表虚拟交换机上的一个虚机交换端口。VM的网卡VIF连接 port 后,就会拥有 MAC 地址和 IP 地址。Port 的 IP 地址是从 subnet 地址池中分配的。

ML2 plugin作为L2的总控,其实现包括Type和Mechanism两部分,每部分又分为Manager和Driver。Type指的是L2网络的类型(如Flat、VLAN、VxLAN等),与厂家实现无关。Mechanism则是各个厂家自己设备机制的实现,如下图所示。当然有ML2,对应的就可以有ML3,不过在Neutron中L3的实现只负责路由的功能,传统路由器中的其他功能(如Firewalls、LB、VPN)都被独立出来实现了,因此暂时还没有看到对ML3的实际需求。

service plugin

  • service plugin:用来实现extension API,包括路由服务,防火墙,VPN等,Service-plugin,即为除core-plugin以外其它的plugin,包括l3 router、firewall、loadbalancer、VPN、metering等等,主要实现L3-L7的网络服务。这些plugin要操作的资源比较丰富,对这些资源进行操作的REST API被neutron-server看作Extension API,需要厂家自行进行扩展。

neutron server启动的时候会启动一个core plugin和多个service plugin Neutron-plugin可以理解为不同网络功能实现的入口,各个厂商可以开发自己的plugin。Neutron-plugin接收neutron-server分发过来的REST API,向neutron database完成一些信息的注册,然后将具体要执行的业务操作和参数通知给自身对应的neutron agent。

1.3、agent

neutron agent主要是做为plugin的后端,通过RPC远端接受plugin的命令来对本地网络资源的操作,并将结果通过call back反馈给plugin。功能分为两大类:其一就是和plugin之间的rpc消息处理,用于和plugin进行交互,接受plugin命令等,调用plugin方法并汇报状态信息等;其二就是对本地资源进行操作,包括iptables的表项,配置dhcp服务等。

  • ml2 core agent:提供l2功能,该agent可以装在计算节点和网络节点上
  • dhcp agent:提供对虚机的dhcp管理和分配,可以安装在任意的节点上
  • l3 agent: 提供路由服务(iptables规则的配置),可以安装在任意的节点上
  • other agent:提供其他的网络服务,比如metadata、metering等。

1.4、drivers

通过被agent的调用,来具体实施网络操作。

2、vm启动时网络配置过程

2.1、openstack组件之间的协作

大致描述

  • Nova-compute向neutron-server请求虚拟机对应的Port资源
  • Neutron-server根据neutron-database生成Port资源。
  • Neutron-server通知Dhcp agent虚拟机信息。
  • Dhcp agent将虚拟机信息通知给dhcp server。
  • 虚拟机接入并启动。
  • 虚拟机从dhcp server处获得IP地址。

主要进程

  • Nova-compute向neutron-server发送create_port的REST API请求,生成新的Port资源。
  • Neutron-server收到该REST请求,通过APIRouter路由到ML2的create_port方法。该方法中,获得了neutron-database新生成的Port,并通知ML2 Mechanism Driver该Port的生成。
  • Nova-compute向neutron发送update_port的REST API请求
  • Neutron-server收到该REST请求,通过APIRouter路由到ML2的update_port方法。该方法中,在neutron-database更新该Port的状态,并根据ML2 Mechanism Driver的不同,决定后续的处理:若Mechanism Driver为hyperv/linuxbridge/ofagent/openvswitch,则需要通过ML2的update_port方法中执行rpc远程调用update_port;对于其余的Mechanism Driver,ML2的update_port方法调用其的update_port_postcommit方法进行处理,这些Mechanism Driver可能使用非rpc方式与自身的agent通信(如REST API、Netconf等)。
  • ML2执行完update_port方法后,Port资源在wsgi中对应的Controller实例通过DhcpAgentNotifyAPI实例rpc通知给网络节点上的dhcp agent(也可能通过一些调度机制通知给分布在计算节点上的dhcp agent)。
  • Dhcp agent收到该rpc通知,通过call_driver方法将虚拟机MAC与IP的绑定关系传递给本地的DHCP守护进程Dnsmaq。
  • Nova-compute通过libvirt driver的spawn方法将虚拟机接入网络,然后启动虚拟机。 到这里,虚拟机启动过程中的网络处理就都结束了,虚拟机间就可以开始通信了。

南北、东西流量网络实现原理


本部分主要讨论vm的南北流量和东西流量之间的数据包转发机制,原理上使用neutron的ml2 plugin的实现方法,属于纯neutron方案。

(1)南北流向:主要是指vm(Project network)和外面网络(external network)之间的数据包流向,这里存在两种不同的情况:

  • vm利用fixed IP地址向外网发送数据包;
  • 外界通过floating IP向内部虚拟机发送网络数据包

(2)东西流向:主要是vm之间的网络数据包的流向和转发,也存在两种不同的情况:

  • 在同一个子网中的不同虚机之间进行网络包的传输和转发
  • 在不同子网中的不同虚机之间进行网络包的传输和转发

1、南北流量实现机制

1.1 Project network向External network发送网络包

在多机环境下,在compute node上创建虚机后会分配一个fixed IP来进行通信,以172.21.11.42上的host01为例,对Project network向External network的网络流量进行分析。

环境准备

  • External network

    • Network:172.21.11.0/24
    • Project network 路由外部网关:172.21.11.234
  • Project network

    • Network: 10.0.1.0/24
    • Gateway :10.0.1.1
  • Compute node (host01)

    • IP address:10.0.1.3

过程分析

计算节点的网络包转发过程

  • (1)首先,创建的虚拟机host01将数据包通过tap设备发送给Linux Bridge,该数据包包含着Project network的目的MAC地址10.0.1.1;
  • (2)接着,Linux Bridge根据安全组规则对数据包进行处理,如果符合规则就通过端口qvb发送给OVS的网桥br-int的端口qvo;
  • (3)然后,br-int在qvo端口处对数据包添加相应的vlan tag,并将带有vlan tag的数据包发送给br-tun网桥;
  • (4)最后,br-tun网桥收到带有vlan tag的数据包后,按照流表规则,将vlan tag匹配的数据包去掉tag,添加对应的vxlan tunnel号,通过接口发送到VXLAN隧道中去.

网络节点的网络包转发过程

  • (1)首先,网络节点的OVS网桥br-tun收到计算节点发过来的带有vxlan tunnel号的网络包,按照流表规则进行处理,遇到vxlan tunnel号匹配的网络包就会剥掉vxlan tunnel号,并添加对应的vlan tag,发送给br-int;
  • (2)接着,br-int收到带有vlan tag的数据包后,通过端口qr发送给路由器的qr端口,路由器的qr端口存放着该project network的网关IP地址(10.0.1.1);
  • (3)然后,在路由服务的namespace中,由于路由器的qg端口存放着project network路由器接口地址(172.21.11.234),就可以利用iptables将该地址作为源地址,进行SNAT转换;
  • (4)最后,路由器namespace将SNAT转换后的数据包通过qg端口发送给br-int网桥,br-int网桥再转发给br-ex网桥,br-ex通过外部网卡将数据包发送给外部的网络,这样数据包就从虚拟机发送到外部的网络中去了。

通过namespace查看路由服务内的信息如下所示:

1
2
3
4
5
6
[root@host41 ~]# ip netns exec qrouter-ec5e63fc-c5a4-4925-9767-154583432d21 ip route
default via 172.21.11.1 dev qg-4306bf11-af 
10.0.1.0/24 dev qr-4d5301c0-7c  proto kernel  scope link  src 10.0.1.1 
10.1.0.0/24 dev qr-c4a064a6-c5  proto kernel  scope link  src 10.1.0.1 
172.21.11.0/24 dev qg-4306bf11-af  proto kernel  scope link  src 172.21.11.234 
192.168.0.0/24 dev qr-622bfadb-9b  proto kernel  scope link  src 192.168.0.1 

1.2 External network向Project network发送网络包

在多机环境下,如果External network能够连接到内部的虚拟机,那么需要给虚拟机分配一个floating ip,还是以创建的虚机host01为例,给它分配一个floating ip(172.21.11.209),现在分析具体的流量转发实现。

环境准备

  • External network

    • Network:172.21.11.0/24
    • Project network 路由外部网关:172.21.11.234
  • Project network

    • Network: 10.0.1.0/24
    • Gateway :10.0.1.1
  • Compute node (host01)

    • IP address:10.0.1.3
    • floating IP:172.21.11.209

过程分析

network node的网络包转发过程

  • (1)首先,外部external network发送网络包到OVS的网桥br-ex,接着br-ex会将该网络包发送给br-int进行处理;
  • (2)接着,br-int将网络包通过qg接口送给路由服务qrouter,在路由namespace中的qg端口上 包含有host01的floating IP(172.21.11.209),在qr端口上包含有路由的外部网关地址(172.21.11.234)。在qrouter中,iptables services会将qr端口中的网关地址(172.21.11.234)作为源地址进行DNAT转换;
  • (3)然后,qrouter将网络包发送给br-int,br-int将网络包添加vlan tag发送给br-tun;
  • (4)最后,br-tun根据流表规则剥掉匹配的网络包的vlan tag,添加相应的vxlan tunnel号,扔到vxlan隧道中去。

compute node的网络包转发过程

  • (1)首先,计算节点上的br-tun接受vxlan隧道中的带有vxlan tunnel号的网络包,根据流表剥掉匹配的vxlan tunnel号,并添加对应的vlan tag,发送给br-int;
  • (2)接着,br-int将数据包发送给Linux bridge进行处理;
  • (3)然后,Linux Bridge根据安全组规则对数据包进行处理;
  • (4)最后,linux bridge将处理后的数据包通过tap设备发送给虚机host01。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@host41 ~]# ip netns exec qrouter-ec5e63fc-c5a4-4925-9767-154583432d21 iptables -t nat -S 
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N neutron-l3-agent-OUTPUT
-N neutron-l3-agent-POSTROUTING
-N neutron-l3-agent-PREROUTING
-N neutron-l3-agent-float-snat
-N neutron-l3-agent-snat
-N neutron-postrouting-bottom
-N neutron-vpn-agen-OUTPUT
-N neutron-vpn-agen-POSTROUTING
-N neutron-vpn-agen-PREROUTING
-N neutron-vpn-agen-float-snat
-N neutron-vpn-agen-snat
-A PREROUTING -j neutron-vpn-agen-PREROUTING
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-vpn-agen-OUTPUT
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-vpn-agen-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A neutron-l3-agent-OUTPUT -d 172.21.11.209/32 -j DNAT --to-destination 10.0.1.3
-A neutron-l3-agent-POSTROUTING ! -i qg-4306bf11-af ! -o qg-4306bf11-af -m conntrack ! --ctstate DNAT -j ACCEPT
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697
-A neutron-l3-agent-PREROUTING -d 172.21.11.209/32 -j DNAT --to-destination 10.0.1.3
-A neutron-l3-agent-float-snat -s 10.0.1.3/32 -j SNAT --to-source 172.21.11.209
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -o qg-4306bf11-af -j SNAT --to-source 172.21.11.234
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 172.21.11.234

2、东西流量实现机制

2.1 同一子网中的不同虚机转发网络包

环境描述

  • Project network

    • Network: 10.0.1.0/24
  • Compute node 1:

    • host01:10.0.1.3
  • Compute node 2:

    • host02:10.0.1.4
  • host01和host02属于同一project network(10.0.1.0/24),创建的时候分别部署在不同的compute node上

  • host01向host02发送网络包

过程分析

compute node1的网络包转发过程

  • (1)首先,host01先将带有目的地址10.0.1.4的network-10.0.1.3的数据包发送给linux bridge;
  • (2)接着,Linux Bridge根据安全组规则对network-10.0.1.3的数据包进行处理,规则匹配后将数据包发送到br-int;
  • (3)然后,br-int给network-10.0.1.3的网络添加vlan tag,并扔给br-tun,br-tun收到网络包后,根据流表规则匹配vlan tag,符合的对vlan tag进行剥离,添加相应的vxlan tunnel号扔给VXLAN隧道;

compute node2的网络包转发过程

  • (1)首先,br-tun接收到VXLAN隧道内的网络数据包,将匹配的vxlan tunnel号的网络包剥离vxlan号,添加network-10.0.1.4的vlan tag,并将处理后的数据包扔给br-int;
  • (2)接着,br-int将数据包交给linux bridge;
  • (3)然后,Linux Bridge根据安全组规则对network-10.0.1.4的数据包进行处理,将处理后的数据包通过tap设备交给host02。

2.2 不同子网中的不同虚机转发网络包

环境描述

  • Project network1

    • Network: 10.0.1.0/24
  • Project network2

    • Network: 10.1.0.0/24
  • Compute node 1:

    • host03:10.1.0.4
  • Compute node 2:

    • host02:10.0.1.4
  • 两个Project network同属于一个路由namespace

  • host03向host02发送网络包

过程分析

compute node1的网络包转发过程

  • (1)首先,host01先将带有目的网关地址10.1.0.1的network-10.1.0.4的数据包发送给linux bridge;
  • (2)接着,Linux Bridge根据安全组规则对network-10.1.0.4的数据包进行处理,规则匹配后将数据包发送到br-int;
  • (3)然后,br-int给network-10.1.0.4的网络添加vlan tag,并扔给br-tun,br-tun收到网络包后,根据流表规则匹配vlan tag,符合的对vlan tag进行剥离,添加相应的vxlan tunnel号扔给VXLAN隧道;

network node的网络包转发过程

  • (1)首先,br-tun收到VXLAN隧道内的网络包,匹配规则剥掉vxlan tunnel号,添加network-10.1.0.4的vlan tag,将数据包扔给br-int;
  • (2)接着,br-int将带有network-10.1.0.4的vlan tag的数据包扔给路由服务的qr-1端口,qr-1端口包含network-10.1.0.4的网关地址10.1.0.1,然后路由器将数据包广播路由给qr-2端口,qr-2端口包含network-10.0.1.4的网关地址10.0.1.1;
  • (3)然后,路由服务qrouter将数据包交给br-int,br-int将数据包添加上network-10.0.1.4的vlan tag;
  • (4)最后,br-int将数据包扔给br-tun,br-tun匹配vlan tag后,剥离vlan tag,并添加可以识别network-10.0.1.4的vxlan tunnel号,然后扔到VXLAN隧道中去。

compute node2的网络包转发过程

  • (1)首先,br-tun接收到VXLAN隧道内的网络数据包,将匹配的vxlan tunnel号的网络包剥离vxlan号,添加network-10.0.1.4的vlan tag,并将处理后的数据包扔给br-int;
  • (2)接着,br-int将数据包交给linux bridge;
  • (3)然后,Linux Bridge根据安全组规则对network-10.0.1.4的数据包进行处理,将处理后的数据包通过tap设备交给host02。

VLAN和VXLAN流表实现机制


1、neutron中的网络概念

1.1 网络的命名空间

在 neutron中,网络名字空间可以被认为是隔离的拥有单独网络栈(网卡、路由转发表、iptables)的环境,只有拥有同样网络名字空间的设备,才能看到彼此。namespace的好处就是可以隔离网络设备和服务,在neutron中,使用ip netns来列举出所有的命名空间。

1
2
3
4
[root@host41 ~]# ip netns
qrouter-ec5e63fc-c5a4-4925-9767-154583432d21
qdhcp-ce0869f1-b055-4914-85f9-9398bad6de7c
qdhcp-3a14c59d-37f1-42a7-a135-29466583d3e2

以qdhcp开头的是DHCP服务的命名空间,以qrouter开头的是路由器服务的命名空间。 为了方便的使用namespace,可以使用ip netns exec namespaceID commond来对命名空间进行操作,如下所示:

1
2
3
4
5
6
7
[root@host41 ~]# ip netns exec qrouter-ec5e63fc-c5a4-4925-9767-154583432d21 route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.21.11.1     0.0.0.0         UG    0      0        0 qg-4306bf11-af
10.0.1.0        0.0.0.0         255.255.255.0   U     0      0        0 qr-4d5301c0-7c
10.1.0.0        0.0.0.0         255.255.255.0   U     0      0        0 qr-c4a064a6-c5
172.21.11.0     0.0.0.0         255.255.255.0   U     0      0        0 qg-4306bf11-af

1.2 Security Group的概念

Security group是一些规则的集合,用来对虚拟机的访问流量进行控制,起到虚拟防火墙的作用,在启动虚拟机的实例时,可以将一个或者多个安全组与该实例关联,在nova中默认的存在一个default的安全组,可以在里面添加相应的规则。

在具体的实现上,底层使用的iptables,由于OVS并不支持iptables规则的tap设备,所以在compute节点使用Linux bridge进行实现。

要查看相应的安全组的规则,需要在compute节点上进行查看有三种方式,INPUT(进入),OUTPUT(出去)和FORWARD(转发)。 以INPUT为例,安全组是对应的bridge来说的,为了区分不同的安全组,可以通过查找vm上的tap设备来找到对应的安全组规则,这样就可以实现不同虚机和不同规则的安全组连接。 下面来查找INPUT的iptables规则,可以看到和安全组相关的规则都被重定向到neutron-openvswi-INPUT。

1
2
3
4
5
6
7
8
[root@host42 ~]# iptables --line-numbers -vnL INPUT
Chain INPUT (policy ACCEPT 18860 packets, 26M bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1    8542K 4539M neutron-openvswi-INPUT  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
2        0     0 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:53
3        0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53
4        0     0 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:67
5        0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:67

继续查看neutron-openvswi-INPUT的iptables规则表,可以看到几个不同的tap设备对应着不同的neutron-openvswi规则,这主要是因为在该计算节点已经创建了几台不同的虚拟,而这tap设备就是与虚机的bridge所对应。

1
2
3
4
5
6
7
[root@host42 ~]# iptables --line-numbers -vnL neutron-openvswi-INPUT
Chain neutron-openvswi-INPUT (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 neutron-openvswi-o0920e155-8  all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in tap0920e155-86 --physdev-is-bridged /* Direct incoming traffic from VM to the security group chain. */
2        0     0 neutron-openvswi-o101e9de7-b  all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in tap101e9de7-be --physdev-is-bridged /* Direct incoming traffic from VM to the security group chain. */
3        0     0 neutron-openvswi-o81b5c22c-8  all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in tap81b5c22c-86 --physdev-is-bridged /* Direct incoming traffic from VM to the security group chain. */
4        0     0 neutron-openvswi-oa1c05cb3-9  all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in tapa1c05cb3-90 --physdev-is-bridged /* Direct incoming traffic from VM to the security group chain. */

在network01网络创建的host01的hostnameID为11f1d000-615b-4cee-b760-baef886b6637,那么找到所对应的tap设备后,找出其iptables的规则,如下所示

1
2
 [root@host42 11f1d000-615b-4cee-b760-baef886b6637]# iptables --line-numbers -vnL neutron-openvswi-INPUT|grep tapa1c05cb3-90
4        0     0 neutron-openvswi-oa1c05cb3-9  all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in tapa1c05cb3-90 --physdev-is-bridged /* Direct incoming traffic from VM to the security group chain. */

可以看出规则被重定向都neutron-openvswi-oa1c05cb3-9,接着查看neutron-openvswi-oa1c05cb3-9的iptables规则,可以看到从虚机发送到DHCP的请求直接通过,而其他的请求则会转到neutron-openvswi-sa1c05cb3-9。

1
2
3
4
5
6
7
8
9
10
[root@host42 11f1d000-615b-4cee-b760-baef886b6637]# iptables --line-numbers -vnL neutron-openvswi-oa1c05cb3-9
Chain neutron-openvswi-oa1c05cb3-9 (2 references)
num   pkts bytes target     prot opt in     out     source               destination         
1        2   648 RETURN     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp spt:68 udp dpt:67 /* Allow DHCP client traffic. */
2       27  1710 neutron-openvswi-sa1c05cb3-9  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
3        0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp spt:67 udp dpt:68 /* Prevent DHCP Spoofing by VM. */
4        0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED /* Direct packets associated with a known session to the RETURN chain. */
5       27  1710 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           
6        0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            state INVALID /* Drop packets that appear related to an existing connection (e.g. TCP ACK/FIN) but do not have an entry in conntrack. */
7        0     0 neutron-openvswi-sg-fallback  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* Send unmatched traffic to the fallback chain. */

接着查看neutron-openvswi-sa1c05cb3-9的规则,这条chain主要检查从vm发出来的网包,是否是openstack所分配的IP和MAC,如果不匹配,则禁止通过。这将防止利用vm上进行一些伪装地址的攻击。

OUTPUT和FORWARD的规则查看方法和INPUT的类似,可以依据上述方法进行安全组的学习

2、计算节点的流量转发机制

2.1 网桥介绍

在多机环境下,分别在两个计算节点上部署虚机,网桥整体组成如下所示:

在compute节点上主要有两种网桥,一种是linux bridge,另外一种就是OVS网桥。

(1)linux网桥

主要用来绑定security group的iptables规则,与实际的转发并没有直接的关系,具体查看语法如下:

1
2
3
4
5
6
7
8
9
10
11
[root@host42 ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
qbr0920e155-86          8000.0a6f338bd56b       no              qvb0920e155-86
                                                        tap0920e155-86
qbr101e9de7-be          8000.ba2a0fd52af0       no              qvb101e9de7-be
                                                        tap101e9de7-be
qbr81b5c22c-86          8000.166b1b7bbfb7       no              qvb81b5c22c-86
                                                        tap81b5c22c-86
qbra1c05cb3-90          8000.7aeb0ca3b0bc       no              qvba1c05cb3-90
                                                        tapa1c05cb3-90
virbr0          8000.525400e1a3da       yes             virbr0-nic

(2)OVS网桥

在compute节点上主要包括两种,br-int和br-tun,br-int主要进行vlan标签的设置和转发,而br-tun主要用于vxlan标签的设置和转发流量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@host42 ~]# ovs-vsctl show
eff53d4c-55b1-4a65-9889-c4480580d456
    Bridge br-tun
        fail_mode: secure
        Port "vxlan-ac150b2b"
            Interface "vxlan-ac150b2b"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.43"}
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}
        Port "vxlan-ac150b2c"
            Interface "vxlan-ac150b2c"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.44"}
        Port "vxlan-ac150b29"
            Interface "vxlan-ac150b29"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.41"}
        Port br-tun
            Interface br-tun
                type: internal
    Bridge br-int
        fail_mode: secure
        Port "qvoa1c05cb3-90"
            tag: 6
            Interface "qvoa1c05cb3-90"
        Port "qvo81b5c22c-86"
            tag: 8
            Interface "qvo81b5c22c-86"
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port "qvo0920e155-86"
            tag: 2
            Interface "qvo0920e155-86"
        Port "qvo101e9de7-be"
            tag: 2
            Interface "qvo101e9de7-be"
        Port br-int
            Interface br-int
                type: internal
    ovs_version: "2.4.0"


2.2 Vlan标签设置以及流表转发机制

在ovs中,通过网桥br-int来对vlan和mac进行转发,主要是作为一个二层交换机使用,所包含的接口主要包括两类: - linux bridge过来的qvo-xxx - 往外的 patch-tun 接口,连接到 br-tun 网桥

这样就可以通过qvo-xxx 接口上为每个经过的网络分配一个内部 vlan的tag,如果在同一个neutron网络里启动了多台虚机,那么它们的tag都是一样的,如果是在不同的网络,那么vlan tag就会不一样。在多机环境中,分别在网络10.0.1.0/24和10.1.0.0/24里创建了两台虚机host01和host03,那么对应的tag分别为6和8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Bridge br-int
    fail_mode: secure
    Port "qvoa1c05cb3-90"      //host01所在网络对应的端口,vlan号为6
        tag: 6
        Interface "qvoa1c05cb3-90"
    Port "qvo81b5c22c-86"      //host03所在网络对应的端口,vlan号为8
        tag: 8
        Interface "qvo81b5c22c-86"
    Port patch-tun
        Interface patch-tun
            type: patch
            options: {peer=patch-int}
    Port "qvo0920e155-86"
        tag: 2
        Interface "qvo0920e155-86"
    Port "qvo101e9de7-be"
        tag: 2
        Interface "qvo101e9de7-be"
    Port br-int
        Interface br-int
            type: internal

接着查看两台虚机所在网络的端口号,可以看到对应的port number分别为15和17,查看端口号主要为了方便读取流表的转换规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@host42 ~]# ovs-ofctl show br-int
OFPT_FEATURES_REPLY (xid=0x2): dpid:0000fa2a90a1c942
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
 3(patch-tun): addr:c6:0c:c4:5b:c9:bc
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
 5(qvo101e9de7-be): addr:be:27:b5:da:bd:7a
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 0 Mbps max
 11(qvo0920e155-86): addr:0e:ea:1b:6d:d9:91
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 0 Mbps max
 15(qvoa1c05cb3-90): addr:4e:b1:8d:af:52:8f
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 0 Mbps max
 17(qvo81b5c22c-86): addr:ca:ef:7d:cb:50:ee
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 0 Mbps max
 LOCAL(br-int): addr:fa:2a:90:a1:c9:42
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0

知道各自的port number后,对br-int的流表进行查看,找到port=15和port=17的流表规则如下所示:

1
2
3
4
5
6
7
8
9
[root@host42 ~]# ovs-ofctl dump-flows br-int
 cookie=0xb05867d95f1c0bc0, duration=7330.618s, table=0, n_packets=0, n_bytes=0, idle_age=7330, priority=10,icmp6,in_port=15,icmp_type=136 actions=resubmit(,24)
 cookie=0xb05867d95f1c0bc0, duration=7330.581s, table=0, n_packets=0, n_bytes=0, idle_age=7330, priority=10,icmp6,in_port=17,icmp_type=136 actions=resubmit(,24)
  cookie=0xb05867d95f1c0bc0, duration=7330.603s, table=0, n_packets=6, n_bytes=252, idle_age=5203, priority=10,arp,in_port=15 actions=resubmit(,24)
 cookie=0xb05867d95f1c0bc0, duration=7330.568s, table=0, n_packets=4, n_bytes=168, idle_age=5202, priority=10,arp,in_port=17 actions=resubmit(,24)
  cookie=0xb05867d95f1c0bc0, duration=7330.627s, table=24, n_packets=0, n_bytes=0, idle_age=7330, priority=2,icmp6,in_port=15,icmp_type=136,nd_target=fe80::f816:3eff:fe54:74c7 actions=NORMAL
 cookie=0xb05867d95f1c0bc0, duration=7330.588s, table=24, n_packets=0, n_bytes=0, idle_age=7330, priority=2,icmp6,in_port=17,icmp_type=136,nd_target=fe80::f816:3eff:fe1e:125e actions=NORMAL
  cookie=0xb05867d95f1c0bc0, duration=7330.610s, table=24, n_packets=6, n_bytes=252, idle_age=5203, priority=2,arp,in_port=15,arp_spa=10.0.1.3 actions=NORMAL
 cookie=0xb05867d95f1c0bc0, duration=7330.574s, table=24, n_packets=4, n_bytes=168, idle_age=5202, priority=2,arp,in_port=17,arp_spa=10.1.0.4 actions=NORMAL

先根据priority的优先级找到table=0的flow规则,不管是ARP还是icmp都交给table=24来进行处理。

  • vm发出ARP报文 : 以host01的虚拟网络为例,flow entry遇到从15口进入的arp,并且arp来源(arp_spa)是10.0.1.3的,让数据包通过(normal)。
1
2
  cookie=0xb05867d95f1c0bc0, duration=7330.610s, table=24, n_packets=6, n_bytes=252, idle_age=5203, priority=2,arp,in_port=15,arp_spa=10.0.1.3 actions=NORMAL
 cookie=0xb05867d95f1c0bc0, duration=7330.574s, table=24, n_packets=4, n_bytes=168, idle_age=5202, priority=2,arp,in_port=17,arp_spa=10.1.0.4 actions=NORMAL
  • vm发出ICMP报文 以host01的虚拟网络为例,flow entry遇到从15口进入的,目标地址为fe80::f816:3eff:fe1e:125e的icmp包就可以通过。
1
2
  cookie=0xb05867d95f1c0bc0, duration=7330.627s, table=24, n_packets=0, n_bytes=0, idle_age=7330, priority=2,icmp6,in_port=15,icmp_type=136,nd_target=fe80::f816:3eff:fe54:74c7 actions=NORMAL
 cookie=0xb05867d95f1c0bc0, duration=7330.588s, table=24, n_packets=0, n_bytes=0, idle_age=7330, priority=2,icmp6,in_port=17,icmp_type=136,nd_target=fe80::f816:3eff:fe1e:125e actions=NORMAL

2.2 Vxlan标签设置以及流表转发机制

neutron中的vxlan的设置规则相对来说就更加复杂一些,主要利用br-tun网桥来实现,该网桥主要根据自身的规则将合适的网包经过 VXLAN 隧道送出去,可以从两个维度来进行思考:

  • 从vm内部过来的数据包进行规则的设置,数据包带着正确的vlan tag过来,从正确的tunnel扔出去;
  • 数据包从外面public network带着正确的vxlan tunnel号过来,要修改到对应的内部的vlan tag扔到里面去。

以该多机环境为例,考虑创建的虚拟机在不同的计算节点,由于host01和host02分别创建在节点172.21.11.42和节点172.21.11.43上,所以会有不同的vxlan tunnelID,以host01所在的172.21.11.42的compute节点为例,查看br-tun网桥的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Bridge br-tun
    fail_mode: secure
    Port "vxlan-ac150b2b"
        Interface "vxlan-ac150b2b"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.43"}
    Port patch-int
        Interface patch-int
            type: patch
            options: {peer=patch-tun}
    Port "vxlan-ac150b2c"
        Interface "vxlan-ac150b2c"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.44"}
    Port "vxlan-ac150b29"
        Interface "vxlan-ac150b29"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.42", out_key=flow, remote_ip="172.21.11.41"}
    Port br-tun
        Interface br-tun
            type: internal

vxlan-ac150b29就是计算节点(42)和控制节点(41)在发送数据包时候的vxlan tunnel端口,patch-int端口主要连通br-int上的patch-tun端口。 下面来具体查看br-tun的流表规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@host42 ~]# ovs-ofctl dump-flows br-tun
NXST_FLOW reply (xid=0x4):
 cookie=0xb05867d95f1c0bc0, duration=1195937.201s, table=0, n_packets=14103, n_bytes=1150634, idle_age=194, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
 cookie=0xb05867d95f1c0bc0, duration=1195936.854s, table=0, n_packets=15517, n_bytes=1237168, idle_age=194, hard_age=65534, priority=1,in_port=2 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1195918.788s, table=0, n_packets=16807, n_bytes=1494619, idle_age=173, hard_age=65534, priority=1,in_port=3 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1195913.601s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,in_port=4 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1195937.201s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xb05867d95f1c0bc0, duration=1195937.200s, table=2, n_packets=12884, n_bytes=1078184, idle_age=194, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
 cookie=0xb05867d95f1c0bc0, duration=1195937.200s, table=2, n_packets=1219, n_bytes=72450, idle_age=199, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)
 cookie=0xb05867d95f1c0bc0, duration=1195937.199s, table=3, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xb05867d95f1c0bc0, duration=1151786.751s, table=4, n_packets=567, n_bytes=70654, idle_age=18902, hard_age=65534, priority=1,tun_id=0x1003a actions=mod_vlan_vid:2,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=86611.109s, table=4, n_packets=71, n_bytes=6213, idle_age=173, hard_age=65534, priority=1,tun_id=0x10049 actions=mod_vlan_vid:6,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=77340.369s, table=4, n_packets=58, n_bytes=4496, idle_age=10412, hard_age=65534, priority=1,tun_id=0x1003d actions=mod_vlan_vid:8,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=1195937.198s, table=4, n_packets=18446, n_bytes=1576993, idle_age=471, hard_age=65534, priority=0 actions=drop
 cookie=0xb05867d95f1c0bc0, duration=1195937.198s, table=6, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xb05867d95f1c0bc0, duration=1195937.197s, table=10, n_packets=13878, n_bytes=1154794, idle_age=173, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xb05867d95f1c0bc0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1
 cookie=0xb05867d95f1c0bc0, duration=199.589s, table=20, n_packets=2, n_bytes=374, hard_timeout=300, idle_age=194, hard_age=194, priority=1,vlan_tci=0x0006/0x0fff,dl_dst=fa:16:3e:fd:2e:d2 actions=load:0->NXM_OF_VLAN_TCI[],load:0x10049->NXM_NX_TUN_ID[],output:2
 cookie=0xb05867d95f1c0bc0, duration=173.176s, table=20, n_packets=0, n_bytes=0, hard_timeout=300, idle_age=173, priority=1,vlan_tci=0x0006/0x0fff,dl_dst=fa:16:3e:11:8d:9d actions=load:0->NXM_OF_VLAN_TCI[],load:0x10049->NXM_NX_TUN_ID[],output:3
 cookie=0xb05867d95f1c0bc0, duration=1195937.197s, table=20, n_packets=1, n_bytes=98, idle_age=10418, hard_age=65534, priority=0 actions=resubmit(,22)
 cookie=0xb05867d95f1c0bc0, duration=1151786.757s, table=22, n_packets=136, n_bytes=10890, idle_age=19085, hard_age=65534, dl_vlan=2 actions=strip_vlan,set_tunnel:0x1003a,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=86611.115s, table=22, n_packets=19, n_bytes=1822, idle_age=199, hard_age=65534, dl_vlan=6 actions=strip_vlan,set_tunnel:0x10049,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=77340.375s, table=22, n_packets=16, n_bytes=1592, idle_age=10418, hard_age=65534, dl_vlan=8 actions=strip_vlan,set_tunnel:0x1003d,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=1195937.185s, table=22, n_packets=91, n_bytes=7490, idle_age=65534, hard_age=65534, priority=0 actions=drop

虽然表面上比较复杂,先要明确数据包出口的方向,由于在br-tun上存在两个方向的数据包转发,从内部vm过来的还有外部过来的,在该compute节点(172.21.11.42)上,br-tun的port值为1的端口表示从内部过来的数据包转发,port值为2表示数据包从外部进来的,port值为3表示数据包是从另外一个compute节点上的vm过来的。

上述的转发逻辑可以归结如下所示:

table=0

主要关注in_port=1,2和3的流表规则,从 1 端口(patch-int)进来的内部vm的网络包,扔给表 2 处理,从 2 端口(vxlan-ac150b29)外面进来的网包,扔给表 4 处理,从3端口(vxlan-ac150b2b)另外一个compute节点进来的数据包,扔给表4处理,其他的做drop处理。

1
2
3
4
5
 cookie=0xb05867d95f1c0bc0, duration=1198890.122s, table=0, n_packets=14103, n_bytes=1150634, idle_age=3147, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
 cookie=0xb05867d95f1c0bc0, duration=1198889.775s, table=0, n_packets=15519, n_bytes=1237300, idle_age=347, hard_age=65534, priority=1,in_port=2 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1198871.709s, table=0, n_packets=16807, n_bytes=1494619, idle_age=3126, hard_age=65534, priority=1,in_port=3 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1198866.522s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,in_port=4 actions=resubmit(,4)
 cookie=0xb05867d95f1c0bc0, duration=1198890.122s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

table=2

从里面vm出来的单播数据包扔给表20处理,多播和广播的数据包交给表22处理。

1
2
 cookie=0xb05867d95f1c0bc0, duration=1198890.121s, table=2, n_packets=12884, n_bytes=1078184, idle_age=3147, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
 cookie=0xb05867d95f1c0bc0, duration=1198890.121s, table=2, n_packets=1219, n_bytes=72450, idle_age=3152, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)

table=3

经过表3处理的数据包全部扔掉

1
cookie=0xb05867d95f1c0bc0, duration=1198890.120s, table=3, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

table=4

主要处理外部过来的数据包,这里包括网络节点来的数据包以及其他compute节点过来的数据包。

  • 匹配vxlan tunnel号为0x1003a的数据包,添加vlan=2的tag,扔给表10进行学习,再扔给br-int
  • 匹配vxlan tunnel号为0x10049的数据包,添加vlan=6的tag,扔给表10进行学习,再扔给br-int
  • 匹配vxlan tunnel号为0x1003d的数据包,添加vlan=8的tag,扔给表10进行学习,再扔给br-int
  • 其它不匹配的直接扔掉
1
2
3
4
 cookie=0xb05867d95f1c0bc0, duration=1154739.672s, table=4, n_packets=567, n_bytes=70654, idle_age=21855, hard_age=65534, priority=1,tun_id=0x1003a actions=mod_vlan_vid:2,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=89564.030s, table=4, n_packets=71, n_bytes=6213, idle_age=3126, hard_age=65534, priority=1,tun_id=0x10049 actions=mod_vlan_vid:6,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=80293.290s, table=4, n_packets=58, n_bytes=4496, idle_age=13365, hard_age=65534, priority=1,tun_id=0x1003d actions=mod_vlan_vid:8,resubmit(,10)
 cookie=0xb05867d95f1c0bc0, duration=1198890.119s, table=4, n_packets=18448, n_bytes=1577125, idle_age=347, hard_age=65534, priority=0 actions=drop

table=6

经过表6的数据包直接扔掉处理。

1
 cookie=0xb05867d95f1c0bc0, duration=1198890.119s, table=6, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

table=10

表10的作用是用来学习network节点或者其它compute节点过来的外部数据包,主要是vxlan tunnel进来的包,学习结束后往表20中添加对返程包的正常转发规则,然后扔给br-int。

table=20 说明是修改表 20 中的规则,后面是添加的规则内容;

  • NXM_OF_VLAN_TCI[0..11],匹配跟当前流同样的 VLAN 头,其中 NXM 是 Nicira Extensible Match 的缩写;
  • NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],包的目的 mac 跟当前流的源 mac 匹配;
  • load:0->NXM_OF_VLAN_TCI[],将 vlan 号改为 0;
  • load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],将 tunnel 号修改为当前的 tunnel 号;
  • output:NXM_OF_IN_PORT[],从当前入口发出。
1
 cookie=0xb05867d95f1c0bc0, duration=1198890.118s, table=10, n_packets=13878, n_bytes=1154794, idle_age=3126, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xb05867d95f1c0bc0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1

table=20

将数据包直接交给表22处理

1
 cookie=0xb05867d95f1c0bc0, duration=1198890.118s, table=20, n_packets=1, n_bytes=98, idle_age=13371, hard_age=65534, priority=0 actions=resubmit(,22)

table=22

  • 对于vlan tag=2的数据包,去掉valn tag,添加vxlan tunnel号为0x1003a,扔给端口2,3,和4
  • 对于vlan tag=6的数据包,去掉valn tag,添加vxlan tunnel号为0x10049,扔给端口2,3,和4
  • 对于vlan tag=8的数据包,去掉valn tag,添加vxlan tunnel号为0x1003d,扔给端口2,3,和4
  • 其它不匹配的就直接丢掉
1
2
3
4
 cookie=0xb05867d95f1c0bc0, duration=1154739.678s, table=22, n_packets=136, n_bytes=10890, idle_age=22038, hard_age=65534, dl_vlan=2 actions=strip_vlan,set_tunnel:0x1003a,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=89564.036s, table=22, n_packets=19, n_bytes=1822, idle_age=3152, hard_age=65534, dl_vlan=6 actions=strip_vlan,set_tunnel:0x10049,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=80293.296s, table=22, n_packets=16, n_bytes=1592, idle_age=13371, hard_age=65534, dl_vlan=8 actions=strip_vlan,set_tunnel:0x1003d,output:2,output:3,output:4
 cookie=0xb05867d95f1c0bc0, duration=1198890.106s, table=22, n_packets=91, n_bytes=7490, idle_age=65534, hard_age=65534, priority=0 actions=drop

3、网络节点的Vxlan流量转发机制

多机环境下,网络节点的网桥整体结构如下所示:

在该多机环境下,网络节点和控制节点部署在一起,网络节点所部署的neutron服务,包括DHCP服务和路由服务等。 该controller节点主要包括三种类型的网桥,br-int,br-tun,br-ex。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
[root@host41 ~]# ovs-vsctl show
304203dc-5cc9-4998-9908-2077e4d0782e
    Bridge br-tun
        fail_mode: secure
        Port "vxlan-ac150b2c"
            Interface "vxlan-ac150b2c"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.44"}
        Port "vxlan-ac150b2a"
            Interface "vxlan-ac150b2a"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.42"}
        Port br-tun
            Interface br-tun
                type: internal
        Port "vxlan-ac150b2b"
            Interface "vxlan-ac150b2b"
                type: vxlan
                options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.43"}
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}
    Bridge br-int
        fail_mode: secure
        Port "qr-99977504-99"
            tag: 5
            Interface "qr-99977504-99"
                type: internal
        Port "qr-ed283f80-4c"
            tag: 8
            Interface "qr-ed283f80-4c"
                type: internal
        Port int-br-vlan
            Interface int-br-vlan
                type: patch
                options: {peer=phy-br-vlan}
        Port "tapdba45207-16"
            tag: 2
            Interface "tapdba45207-16"
                type: internal
        Port "tap003dc11c-ff"
            tag: 8
            Interface "tap003dc11c-ff"
                type: internal
        Port "qr-3eac92eb-2c"
            tag: 3
            Interface "qr-3eac92eb-2c"
                type: internal
        Port "tap84987a29-8c"
            tag: 6
            Interface "tap84987a29-8c"
                type: internal
        Port "qr-86e75355-42"
            tag: 2
            Interface "qr-86e75355-42"
                type: internal
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port "tapb82869ee-b7"
            tag: 14
            Interface "tapb82869ee-b7"
                type: internal
        Port "tap5b3887ae-96"
            tag: 5
            Interface "tap5b3887ae-96"
                type: internal
        Port "tapaeef9fcb-b4"
            tag: 3
            Interface "tapaeef9fcb-b4"
                type: internal
        Port "qr-c4a064a6-c5"
            tag: 15
            Interface "qr-c4a064a6-c5"
                type: internal
        Port "qr-4d5301c0-7c"
            tag: 14
            Interface "qr-4d5301c0-7c"
                type: internal
        Port "tap14a62a38-17"
            tag: 4
            Interface "tap14a62a38-17"
                type: internal
        Port "qr-a0ad0357-0e"
            tag: 2
            Interface "qr-a0ad0357-0e"
                type: internal
        Port int-br-ex
            Interface int-br-ex
                type: patch
                options: {peer=phy-br-ex}
        Port "qr-5726f55a-28"
            tag: 1
            Interface "qr-5726f55a-28"
                type: internal
        Port br-int
            Interface br-int
                type: internal
        Port "tap01676ab5-12"
            tag: 1
            Interface "tap01676ab5-12"
                type: internal
        Port "tap3dbb06d6-c8"
            tag: 15
            Interface "tap3dbb06d6-c8"
                type: internal
    Bridge br-ex
        Port "qg-69307f08-2f"
            Interface "qg-69307f08-2f"
                type: internal
        Port br-ex
            Interface br-ex
                type: internal
        Port phy-br-ex
            Interface phy-br-ex
                type: patch
                options: {peer=int-br-ex}
        Port "qg-4306bf11-af"
            Interface "qg-4306bf11-af"
                type: internal
        Port "ens160"
            Interface "ens160"
        Port "qg-7a817caf-50"
            Interface "qg-7a817caf-50"
                type: internal
    ovs_version: "2.4.0"

3.1 vxlan标签设置以及流表转发机制

类似于计算节点的VXLAN tunnel,network节点的VXALN规则也是通过br-tun网桥来实现的,该网桥主要根据自身的规则将合适的网包经过 VXLAN 隧道送出去,可以从两个维度来进行思考:

  • 从vm内部过来的数据包进行规则的设置,数据包带着正确的vlan tag过来,从正确的tunnel扔出去;
  • 数据包从外面public network带着正确的vxlan tunnel号过来,要修改到对应的内部的vlan tag扔到里面去。

以多机环境中172.21.11.41上的network节点为例,查看br-tun网桥的信息。可以看到不同的VXLAN tunnel号对应不同的网络连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Bridge br-tun
    fail_mode: secure
    Port "vxlan-ac150b2c"
        Interface "vxlan-ac150b2c"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.44"}
    Port "vxlan-ac150b2a"
        Interface "vxlan-ac150b2a"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.42"}
    Port br-tun
        Interface br-tun
            type: internal
    Port "vxlan-ac150b2b"
        Interface "vxlan-ac150b2b"
            type: vxlan
            options: {df_default="true", in_key=flow, local_ip="172.21.11.41", out_key=flow, remote_ip="172.21.11.43"}
    Port patch-int
        Interface patch-int
            type: patch
            options: {peer=patch-tun}

下面来具体分析br-tun网桥上的流表信息,分析方法和之前计算节点上的br-tun网桥基本一致,具体流表信息如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@host41 ~]# ovs-ofctl dump-flows br-tun
NXST_FLOW reply (xid=0x4):
 cookie=0xa08ff20bf20b5fae, duration=1640812.296s, table=0, n_packets=557112, n_bytes=280383648, idle_age=20, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
 cookie=0xa08ff20bf20b5fae, duration=1204517.666s, table=0, n_packets=14004, n_bytes=1142528, idle_age=8775, hard_age=65534, priority=1,in_port=2 actions=resubmit(,4)
 cookie=0xa08ff20bf20b5fae, duration=1204499.592s, table=0, n_packets=502898, n_bytes=59684255, idle_age=20, hard_age=65534, priority=1,in_port=3 actions=resubmit(,4)
 cookie=0xa08ff20bf20b5fae, duration=1204494.411s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,in_port=4 actions=resubmit(,4)
 cookie=0xa08ff20bf20b5fae, duration=1640812.286s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1640812.282s, table=2, n_packets=552989, n_bytes=280198986, idle_age=20, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
 cookie=0xa08ff20bf20b5fae, duration=1640812.282s, table=2, n_packets=4123, n_bytes=184662, idle_age=19451, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)
 cookie=0xa08ff20bf20b5fae, duration=1640812.282s, table=3, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1640748.151s, table=4, n_packets=747602, n_bytes=98745467, idle_age=20, hard_age=65534, priority=1,tun_id=0x10059 actions=mod_vlan_vid:1,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640748.106s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,tun_id=0x10052 actions=mod_vlan_vid:2,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640748.081s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,tun_id=0x1005d actions=mod_vlan_vid:3,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640748.055s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,tun_id=0x1000c actions=mod_vlan_vid:4,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640748.007s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,tun_id=0x1001d actions=mod_vlan_vid:5,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640747.977s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=1,tun_id=0x10068 actions=mod_vlan_vid:6,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1213356.221s, table=4, n_packets=937, n_bytes=99354, idle_age=27478, hard_age=65534, priority=1,tun_id=0x1003a actions=mod_vlan_vid:8,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=95292.240s, table=4, n_packets=119, n_bytes=10570, idle_age=8748, hard_age=65534, priority=1,tun_id=0x10049 actions=mod_vlan_vid:14,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=95266.306s, table=4, n_packets=106, n_bytes=8850, idle_age=18993, hard_age=65534, priority=1,tun_id=0x1003d actions=mod_vlan_vid:15,resubmit(,10)
 cookie=0xa08ff20bf20b5fae, duration=1640812.281s, table=4, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1640812.281s, table=6, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1640812.281s, table=10, n_packets=764906, n_bytes=100191850, idle_age=20, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xa08ff20bf20b5fae,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1
 cookie=0xa08ff20bf20b5fae, duration=1883.824s, table=20, n_packets=551, n_bytes=252408, hard_timeout=300, idle_age=20, hard_age=19, priority=1,vlan_tci=0x0001/0x0fff,dl_dst=fa:16:3e:b4:00:8c actions=load:0->NXM_OF_VLAN_TCI[],load:0x10059->NXM_NX_TUN_ID[],output:3
 cookie=0xa08ff20bf20b5fae, duration=1640812.281s, table=20, n_packets=1968, n_bytes=161462, idle_age=2375, hard_age=65534, priority=0 actions=resubmit(,22)
 cookie=0xa08ff20bf20b5fae, duration=1213356.228s, table=22, n_packets=9, n_bytes=378, idle_age=65534, hard_age=65534, dl_vlan=8 actions=strip_vlan,set_tunnel:0x1003a,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.358s, table=22, n_packets=30, n_bytes=1316, idle_age=65534, hard_age=65534, dl_vlan=2 actions=strip_vlan,set_tunnel:0x10052,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.351s, table=22, n_packets=15, n_bytes=686, idle_age=65534, hard_age=65534, dl_vlan=3 actions=strip_vlan,set_tunnel:0x1005d,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.344s, table=22, n_packets=2, n_bytes=140, idle_age=65534, hard_age=65534, dl_vlan=4 actions=strip_vlan,set_tunnel:0x1000c,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.337s, table=22, n_packets=3186, n_bytes=161198, idle_age=2375, hard_age=65534, dl_vlan=1 actions=strip_vlan,set_tunnel:0x10059,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.331s, table=22, n_packets=15, n_bytes=686, idle_age=65534, hard_age=65534, dl_vlan=5 actions=strip_vlan,set_tunnel:0x1001d,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640746.324s, table=22, n_packets=2, n_bytes=140, idle_age=65534, hard_age=65534, dl_vlan=6 actions=strip_vlan,set_tunnel:0x10068,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=95292.249s, table=22, n_packets=16, n_bytes=1208, idle_age=65534, hard_age=65534, dl_vlan=14 actions=strip_vlan,set_tunnel:0x10049,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=95266.313s, table=22, n_packets=30, n_bytes=2200, idle_age=18999, hard_age=65534, dl_vlan=15 actions=strip_vlan,set_tunnel:0x1003d,output:3,output:2,output:4
 cookie=0xa08ff20bf20b5fae, duration=1640812.267s, table=22, n_packets=173, n_bytes=14398, idle_age=65534, hard_age=65534, priority=0 actions=drop

将上述的流表规则进行整理可以得到:

3.2 VLAN标签设置以及流表转发机制

在network节点上,vlan tag的设置主要在br-int网桥上进行,作为一个正常的二层交换设备进行使用,只是根据vlan和mac进行数据包的转发。接口类型包括:

  • tap-xxx,连接到网络 DHCP 服务的命名空间;
  • qr-xxx,连接到路由服务的命名空间;
  • patch-tun 接口,连接到 br-tun 网桥。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Bridge br-int
    fail_mode: secure
    Port "qr-99977504-99"
        tag: 5
        Interface "qr-99977504-99"
            type: internal
    Port "qr-ed283f80-4c"
        tag: 8
        Interface "qr-ed283f80-4c"
            type: internal
    Port int-br-vlan
        Interface int-br-vlan
            type: patch
            options: {peer=phy-br-vlan}
    Port "tapdba45207-16"
        tag: 2
        Interface "tapdba45207-16"
            type: internal
    Port "tap003dc11c-ff"
        tag: 8
        Interface "tap003dc11c-ff"
            type: internal
    Port "qr-3eac92eb-2c"
        tag: 3
        Interface "qr-3eac92eb-2c"
            type: internal
    Port "tap84987a29-8c"
        tag: 6
        Interface "tap84987a29-8c"
            type: internal
    Port "qr-86e75355-42"
        tag: 2
        Interface "qr-86e75355-42"
            type: internal
    Port patch-tun
        Interface patch-tun
            type: patch
            options: {peer=patch-int}
    Port "tapb82869ee-b7"
        tag: 14
        Interface "tapb82869ee-b7"
            type: internal
    Port "tap5b3887ae-96"
        tag: 5
        Interface "tap5b3887ae-96"
            type: internal
    Port "tapaeef9fcb-b4"
        tag: 3
        Interface "tapaeef9fcb-b4"
            type: internal
    Port "qr-c4a064a6-c5"
        tag: 15
        Interface "qr-c4a064a6-c5"
            type: internal
    Port "qr-4d5301c0-7c"
        tag: 14
        Interface "qr-4d5301c0-7c"
            type: internal
    Port "tap14a62a38-17"
        tag: 4
        Interface "tap14a62a38-17"
            type: internal
    Port "qr-a0ad0357-0e"
        tag: 2
        Interface "qr-a0ad0357-0e"
            type: internal
    Port int-br-ex
        Interface int-br-ex
            type: patch
            options: {peer=phy-br-ex}
    Port "qr-5726f55a-28"
        tag: 1
        Interface "qr-5726f55a-28"
            type: internal
    Port br-int
        Interface br-int
            type: internal
    Port "tap01676ab5-12"
        tag: 1
        Interface "tap01676ab5-12"
            type: internal
    Port "tap3dbb06d6-c8"
        tag: 15
        Interface "tap3dbb06d6-c8"
            type: internal

转发流表也比较简单,表0中进行正常的转发,其他的就直接丢弃。

1
2
3
4
5
6
[root@host41 ~]# ovs-ofctl dump-flows br-int
NXST_FLOW reply (xid=0x4):
 cookie=0xa08ff20bf20b5fae, duration=1641332.633s, table=0, n_packets=628544, n_bytes=39425496, idle_age=0, hard_age=65534, priority=2,in_port=19 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1641332.769s, table=0, n_packets=1322424, n_bytes=380681779, idle_age=10, hard_age=65534, priority=0 actions=NORMAL
 cookie=0xa08ff20bf20b5fae, duration=1641332.762s, table=23, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop
 cookie=0xa08ff20bf20b5fae, duration=1641332.755s, table=24, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

3.3 和外部网络通信实现机制

主要通过br-ex网桥和public network进行通信, 一个是挂载的物理接口上,如 ens160,网包将从这个接口发送到外部网络上。 另外一个是 qg-xxx 这样的接口,是连接到 router 服务的网络名字空间中,里面绑定一个路由器的外部 IP,作为 nAT 时候的地址,另外,网络中的 floating IP 也放在这个网络名字空间中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Bridge br-ex
    Port "qg-69307f08-2f"
        Interface "qg-69307f08-2f"
            type: internal
    Port br-ex
        Interface br-ex
            type: internal
    Port phy-br-ex
        Interface phy-br-ex
            type: patch
            options: {peer=int-br-ex}
    Port "qg-4306bf11-af"
        Interface "qg-4306bf11-af"
            type: internal
    Port "ens160"
        Interface "ens160"
    Port "qg-7a817caf-50"
        Interface "qg-7a817caf-50"
            type: internal

查看流表规则,基本就是正常的转发动作。

1
2
3
4
[root@host41 ~]# ovs-ofctl dump-flows br-ex
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=1641423.664s, table=0, n_packets=8606, n_bytes=541782, idle_age=120, hard_age=65534, priority=2,in_port=2 actions=drop
 cookie=0x0, duration=1641423.740s, table=0, n_packets=47181071, n_bytes=65307678044, idle_age=0, hard_age=65534, priority=0 actions=NORMAL

3.4 DHCP服务实现机制

dhcp服务是通过dnsmasq进程(轻量级服务器,可以提供dns、dhcp、tftp等服务)来实现的,该进程绑定到dhcp名字空间中的br-int的接口上。可以查看相关的进程。

3.5 路由服务实现机制

neutron中的路由服务主要是提供跨子网间的网络通信,包括虚拟想访问外部网络等。路由服务主要利用namespace实现不同网络之间的隔离性。另外,router还可以实现tenant network和external network之间的网络连接,通过SNAT实现tenant network往external network的网络连通性(fixed IP),通过DNAT实现external network往tenant network的网络连通性(floating IP),

1
2
3
4
[root@host41 tmp]# ip netns
qrouter-ec5e63fc-c5a4-4925-9767-154583432d21
qdhcp-ce0869f1-b055-4914-85f9-9398bad6de7c
qdhcp-3a14c59d-37f1-42a7-a135-29466583d3e2

在该network节点上创建的路由服务是qrouter-ec5e63fc-c5a4-4925-9767-154583432d21,于是可以进一步查看namespace中的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[root@host41 tmp]# ip netns exec qrouter-ec5e63fc-c5a4-4925-9767-154583432d21 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
41: qr-4d5301c0-7c: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether fa:16:3e:12:54:fb brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.1/24 brd 10.0.1.255 scope global qr-4d5301c0-7c
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe12:54fb/64 scope link 
       valid_lft forever preferred_lft forever
42: qg-4306bf11-af: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether fa:16:3e:d3:a5:32 brd ff:ff:ff:ff:ff:ff
    inet 172.21.11.234/24 brd 172.21.11.255 scope global qg-4306bf11-af
       valid_lft forever preferred_lft forever
    inet 172.21.11.209/32 brd 172.21.11.209 scope global qg-4306bf11-af
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fed3:a532/64 scope link 
       valid_lft forever preferred_lft forever
43: qr-c4a064a6-c5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether fa:16:3e:fc:33:6f brd ff:ff:ff:ff:ff:ff
    inet 10.1.0.1/24 brd 10.1.0.255 scope global qr-c4a064a6-c5
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fefc:336f/64 scope link 
       valid_lft forever preferred_lft forever
47: qr-622bfadb-9b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether fa:16:3e:e7:8d:5d brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global qr-622bfadb-9b
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fee7:8d5d/64 scope link 
       valid_lft forever preferred_lft forever

其中,qg-4306bf11-af是连接br-ex网桥的端口,负责外面的数据包的转发和处理,qr-XXX用于连接br-int网桥,处理带有VLAN tag的网络包。

1
2
3
4
5
6
[root@host41 ~]# ip netns exec qrouter-ec5e63fc-c5a4-4925-9767-154583432d21 ip route
default via 172.21.11.1 dev qg-4306bf11-af 
10.0.1.0/24 dev qr-4d5301c0-7c  proto kernel  scope link  src 10.0.1.1 
10.1.0.0/24 dev qr-c4a064a6-c5  proto kernel  scope link  src 10.1.0.1 
172.21.11.0/24 dev qg-4306bf11-af  proto kernel  scope link  src 172.21.11.234 
192.168.0.0/24 dev qr-622bfadb-9b  proto kernel  scope link  src 192.168.0.1 

从上面规则中可以看出,从三个不同的qr-XXX端口进来的数据包都会通过qg-4306bf11-af发送到br-ex中去,从而到达外网。

综上所述,在多机环境下,计算节点和网络节点的整体网桥连接以及VLAN和VXLAN实现原理如下所示:

HA(High Availability)的定义

可用性

提供在本地系统的单个组件故障情况下,能够继续访问应用的能力,最好的可用性就是,如果一台机器宕机了,使用服务的用户完全感觉不到

故障恢复

如果出现宕机,在该机器上运行的服务肯定得做故障切换(failover),切换主要分为两个维度:

  • RTO(Recovery Time Objective):主要是指服务恢复的时间
  • RPO(Recovery Point Objective):主要是指切换时向前恢复的数据的时间长度,RPO=1天意味着恢复时使用一天前的数据,那么一天之内的数据就丢失了。
补充概念
  • Active/Passive:主要包括一台活动提供服务的服务器和一台待命不对外提供服务的服务器,一般在讲failover就是属于这个,也是cluster HA基本款。
  • Active/Active:两台(或N台)同时运作,每台机器可以跑一个运行的实例,当一台机器出现宕机时,就会将运行的实例切换到另外一台机器上去,那么另外一台机器就会运行两个实例,AA模式保证了两台机器资源都被利用。

HA(High Availability)的实现

(一)冗余

硬件

  • 电源:采用双路供电
  • 磁盘:可以分为RAID0、RAID1、RAID5等

    • RAID0:又称为stripe或者striping,代表了RAID级别中最高的存储性能,原理就是把连续的数据分散到多个磁盘上进行存取,这样,系统有数据请求就可以被多个磁盘并行的执行,每个磁盘执行属于它自己的那部分数据请求,这样可以充分利用总线的带宽。
    • RAID1:通过磁盘数据镜像实现数据冗余,在成对的独立磁盘上产生互为备份的数据,当原始数据繁忙时,可直接从镜像拷贝中读取数据,这样就提高了读取性能,并保证了数据的可靠性和安全性。当一个磁盘失效时,系统就可以自动切换到镜像磁盘上读写。
    • RAID5:可以理解为RAID0和RAID1的折中方案,可以为系统提供数据安全保障,但保障程度要比mirror低,而磁盘空间利用率要比mirror高。
    • RAID10:主要是一个RAID0和RAID1的结合,利用奇偶校验实现条带集镜像,继承了RAID0的快速和RAID1的安全
  • 网卡:(bonding,双路)在linux服务器上可以利用bonding技术,将多个网卡当做一个网卡使用,不仅提高了带宽而且提高了可用性。

  • 交换机:LACP、VRRP

  • LACP(Link Aggregration Control Protocol,链路汇聚控制协议):是一种实现链路动态汇聚的协议,LACP协议通过LACPDU(LACP Data Unit,链路汇聚控制协议数据单元)与对端交互信息。当启用某一端口的LACP协议后,该端口将发送LACPDU向对端通告自己的系统优先级,系统MAC地址,端口优先级,端口号和操作Key。对端接受到这些信息后,将这些信息与其他端口所保存的信息比较以选择能够汇聚的端口,从而双发可以对端口加入或退出某个动态汇聚组达成一致。

  • 虚拟路由冗余协议(Virtual Router Redundancy Protocol,简称VRRP)是由IETF提出的解决局域网中配置静态网关出现单点失效现象的路由协议,VRRP广泛应用在边缘网络中,它的设计目标是支持特定情况下IP数据流量失败转移不会引起混乱,允许主机使用单路由器,以及及时在实际第一跳路由器使用失败的情形下仍能够维护路由器间的连通性。

软件

  • 协议:HTTP、广播
  • 负载均衡:LVS、haproxy、nginx

LVS 采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。为此,在设计时需要考虑系统的透明性、可伸缩性、高可用性和易管理性。

Nginx 是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好。

HAProxy 提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。

  • 集群:主主、主备;同步,异步

(二)校验

  • checksum(TCP校验和):是一个端到端的校验和,由发送端计算,然后由接收端验证。
  • CRC(Cyclic redundancy check循环冗余校验):主要用来检验数据传输和保存后可能出现的错误,生成的数字在传输的过程或者存储之前计算出来并且附加到数据的后面。
  • 重传

(三)综合

  • 校验+冗余

(四)Failover

又称故障切换,指系统中其中一项设备或服务失效而无法运作时,另一项设备或服务即可自动接手原失效系统所执行的工作。

为了实现自动的故障切换,通常需要在冗余的组件之间启用心跳机制,以相互检测彼此的状态,在检测到故障时自动进行切换

  • 监控
  • 切换

(五)相关软件

  • keepalived
  • pacemaker
  • heartbeat + DRBD(AP实现方式)

DRBD(Distributed Replicated Block Device)是一个用软件实现的、无共享的、服务器之间镜像块设备内容的存储复制解决方案。号称“网络RAID”。通过网络通信来同步镜像整个设备,当数据写入本地的DRBD设备上的文件系统时,数据会同时被发送到网络中的另外一台主机之上,并以完全相同的形式记录在一个文件系统中。本地节点(主机)与远程节点(主机)的数据可以保证实时的同步,并保证IO的一致性。

说明:RabbitMQ是一种消息中间件,并以其高效的集群部署和HA得到广泛的应用

有关queue_declare的总结

在publish/subscribe这种方式中,如果将queue中定义的prefetch_count设置为1,那么创建的queue就会认为只会提取一个消息。这种场景适合于希望一段时间消息被固定在一个消费者上。如果不设置这个参数,可能在product产生多个消息发出后,一个消费者就可能从RabbitMQ中取所有的消息,而不让其他消费者对消息进行消费。

这里需要注意的一个问题就是,一个消费者只能绑定一个queue,一个exchange可以定义多个queue。而多个消费者可能就是在轮询的接受信息。

RabbitMQ的HA

这个HA的特性是RabbitMQ的特点之一,所以对开发者来说就显得比较重要。 主要包括两部分:

  • 服务可靠:这里的服务可靠性主要是指RabbitMQ的集群,保证元数据的HA

rabbitmq cluster 实现vhost&exchange&queue&binding等元数据的同步

  • 数据可靠:这里的数据可靠性主要是针对mirror queue,镜像队列可以将消息复制到集群的多个节点上去,这样如果出现某一个节点down的情况,也可以保证整个集群式高可用的。

mirror queue 实现队列中消息的同步

如何实现RabbitMQ的高可用性

可靠的消息传递
  • Pubilsher confirm

  • Delivery Acknowledge

  • 故障时消息不会消失:多个副本;写磁盘

消息队列服务HA
  • 避免单点故障

  • 可扩展的消息集群

实现高可用的集群
  • rabbitmq cluster

  • mirror queue

  • 共享存储

  • pacemaker:实现服务监控和故障转移

比较好的参考资料:

https://www.rabbitmq.com/ha.html

https://www.rabbitmq.com/clustaering.html

http://www.rabbitmq.com/pacemaker.html

http://pdf.th7.cn/down/files/1312/RabbitMQ%20in%20Action.pdf

http://my.oschina.net/moooofly/blog/284542

http://my.oschina.net/hncscwc/blog/186350

RabbitMQ troubleshooting介绍

排错工具

通过监控查看rabbitmq的状态是否正常
  • rabbitmqctl & rabbitmqadmin
1
2
3
启动或终止rabbitmq
用户、权限、集群、policy管理
查看队列、exchange、queue、channel、consumer、状态...
  • rabbitmq manangement plugin
    查看日志,确定具体的错误
  • 启动日志:/var/log/rabbitmq/startup_{log,err}
  • 关闭日志:/var/log/rabbitmq/shutdown_{log,err}
  • 运行日志:/var/log/rabbitmq/rabbit@.log

排错思路

  • rabbitmq server 本身的问题
  • 应用程序的问题

常见故障检测

  • rabbitmq server启动失败
  • rabbitmq 网络分区
  • rabbitmq 不接受消息
  • rabbitmq 消息堆积

rabbitmq server启动失败

  • cookie不一致
  • hostname发生了变化
  • 集群:连接不上其他节点

网络分区

  • 出现网络分区的情况下,各自独立提供服务,但是不再同步消息
  • net_ticktime:默认60-120秒;这个时间段内节点之间会发送4个tick包
  • mnesia不一致:网络中断,不能通讯

rabbitmq不接受消息

rabbitmq内部发生了阻塞
  • flow control:内存、磁盘
  • 消息堆积
    连接异常导致空等

rabbitmq消息堆积

在出现网络连接异常时,client端没有重新建立连接,一直等待,不消费消息

不能检测网络故障并自动重连

  • RabbitMQ heartbeat
  • tcp keep alive
  • net.ipv4.tcp_retries2

参考资料

https://www.rabbitmq.com/management.html

https://www.rabbitmq.com/nettick.html

http://stackoverflow.com/questions/5907527/application-control-of-tcp-retransmission-on-linux

performance tuning for RabbitMQ

trade-off

  • 对性能的需求:1k/s,10k/s,100k/s

  • 对消息持久性的需求

  • 通信模式:pub-sub,fanout,topic

  • 集群的规模

性能调优

  • 放开文件描述符的限制

  • 提高内存的占用量

  • 提高磁盘读写性能


RabbitMQ概述

RabbitMQ可以做什么?

RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,可用于在分布式系统中存储转发消息,主要有以下的技术亮点:

  • 可靠性
  • 灵活的路由
  • 集群部署
  • 高可用的队列消息
  • 可视化的管理工具

RabbitMQ主要用于系统间的双向解耦,当生产者(productor)产生大量的数据时,消费者(consumer)无法快速的消费信息,那么就需要一个类似于中间件的代理服务器,用来处理和保存这些数据,RabbitMQ就扮演了这个角色。

如何使用RabbitMQ

  • Erlang语言包
  • RabbitMQ安装包

基本概念

1.Broker

用来处理数据的消息队列服务器实体

2.虚拟主机(vhost)

由RabbitMQ服务器创建的虚拟消息主机,拥有自己的权限机制,一个broker里可以开设多个vhost,用于不同用户的权限隔离,vhost之间是也完全隔离的。

3.生产者(productor)

产生用于消息通信的数据

4.信道(channel)

消息通道,在AMQP中可以建立多个channel,每个channel代表一个会话任务。

5.交换机(exchange)

(1)接受消息,转发消息到绑定的队列,总共有四种类型的交换器:direct,fanout,topic,headers。

  • direct:转发消息到routing-key指定的队列

  • fanout:转发消息到所有绑定的队列,类似于一种广播发送的方式。

  • topic:按照规则转发消息,这种规则多为模式匹配,也显得更加灵活

(2).交换器在RabbitMQ中是一个存在的实体,不能改变,如有需要只能删除重建。

(3).topic类型的交换器利用匹配规则分析消息的routing-key属性。

(4).属性

  • 持久性:声明时durable属性为true
  • 自动删除:绑定的queue删除也跟着删除
  • 惰性:不会自动创建

6.队列(queue)

(1).队列是RabbitMQ的内部对象,存储消息

(2).可以动态的增加消费者,队列将接受到的消息以轮询(round-robin)的方式均匀的分配给多个消费者

(3).队列的属性

  • 持久性:如果启用,队列将会在server重启之前有效
  • 自动删除:消费者停止使用之后就会自动删除
  • 惰性:不会自动创建
  • 排他性:如果启用,队列只能被声明它的消费者使用。

7.两个key

  • routing-key:消息不能直接发到queues,需要先发送到exchanges,routing-key指定queues名称,exchanges通过routing-key来识别与之绑定的queues
1
2
3
channel.queue_publish(exchange=exchange_name,
                     routing-key="rabbitmq",
                     body="openstack")
  • binding-key:主要是用来表示exchanges和queues之间的关系,为了区别queue_publish的routing-key,就称作binding-key。
1
2
3
channel.queue_bind(exchange=exchange_name,
                  queue=queue_name,
                  routing-key="rabbitmq")

8.绑定(binding)

表示交换机和队列之间的关系,在进行绑定时,带有一个额外的参数binding-key,来和routing-key相匹配。

9.消费者(consumer)

监听消息队列来进行消息数据的读取

10.高可用性(HA)

(1).在consumer处理完消息后,会发送消息ACK,通知通知RabbitMQ消息已被处理,可以从内存删除。如果消费者因宕机或链接失败等原因没有发送ACK,则RabbitMQ会将消息重新发送给其他监听在队列的下一个消费者。

1
channel.basicConsume(queuename, noAck=false, consumer);

(2).消息和队列的持久化

(3).镜像队列,实现不同节点之间的元数据和消息同步

RabbitMQ在OpenStack中的应用

RPC之neutron专题

基于RabbitMQ的RPC消息通信是neutron中跨模块进行方法调用的很重要的一种方式,根据上面的描述,要组成一个完整的RPC通信结构,需要信息的生产者和消费者。

  • client端:用于产生rpc消息。
  • server端:用于监听消息数据并进行相应的处理。

1.neutron-agent中的RPC

在dhcp_agent、l3_agent、metadata_agent,metering_agent的main函数中都存在一段创建一个rpc服务端的代码,下面以dhcp_agent为例。

1
2
3
4
5
6
7
8
9
10
def main():
    register_options(cfg.CONF)
    common_config.init(sys.argv[1:])
    config.setup_logging()
    server = neutron_service.Service.create(
        binary='neutron-dhcp-agent',
        topic=topics.DHCP_AGENT,
        report_interval=cfg.CONF.AGENT.report_interval,
        manager='neutron.agent.dhcp.agent.DhcpAgentWithStateReport')
    service.launch(cfg.CONF, server).wait()

最核心的,也是跟rpc相关的部分包括两部分,首先是创建rpc服务端。

1
2
3
4
5
server = neutron_service.Service.create(
    binary='neutron-dhcp-agent',
    topic=topics.DHCP_AGENT,
    report_interval=cfg.CONF.AGENT.report_interval,
    manager='neutron.agent.dhcp.agent.DhcpAgentWithStateReport')

该代码实际上创建了一个rpc服务端,监听指定的topic并运行manager上的tasks。

create()方法返回一个neutron.service.Service对象,neutron.service.Service继承自neutron.common.rpc.Service类。

首先看neutron.common.rpc.Service类,该类定义了start方法,该方法主要完成两件事情:一件事情是将manager添加到endpoints中;一件是创建rpc的consumer,分别监听topic的队列消息。

而在neutron.service.Service类中,初始化中生成了一个manager实例(即neutron.agent.dhcp_agent.DhcpAgentWithStateReport);并为start方法添加了周期性执行report_state方法和periodic_tasks方法。report_state方法没有具体实现,periodic_tasks方法则调用manager的periodic_tasks方法。

manager实例(即neutron.agent.dhcp_agent.DhcpAgentWithStateReport)在初始化的时候首先创建一个rpc的client端,通过代码

1
 self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)

该client端实际上定义了report_state方法,可以状态以rpc消息的方式发送给plugin。

manager在初始化后,还会指定周期性运行_report_state方法,实际上就是调用client端的report_state方法。

至此,对rpc服务端的创建算是完成了,之后执行代码。

1
service.launch(server).wait()

service.launch(server)方法首先会将server放到协程组中,并调用server的start方法来启动server。

2.neutron-plugin中的RPC

主要对ML2Plugin进行分析,包括两个类:RpcCallbacks和AgentNotifierApi。

  • RpcCallbacks:负责当agent往plugin发出rpc请求时候,plugin实现请求的相关动作,除了继承自父类(dhcp rpc、dvr rpc、sg_db rpc和tunnel rpc)中的方法,还包括get_port_from_device、get_device_details、get_devices_details_list、update_device_down、update_device_up、get_dvr_mac_address_by_host、get_compute_ports_on_host_by_subnet、get_subnet_for_dvr等方法。

  • AgentNotifierApi:负责当plugin往agent发出rpc请求(plugin通知agent)的时候,plugin端的方法。

1
2
3
4
5
6
7
def start_rpc_listeners(self):
    """RpcCallbacks中实现的方法:Start the RPC loop to let the plugin communicate with agents."""
    self._setup_rpc()
    self.topic = topics.PLUGIN
    self.conn = n_rpc.create_connection(new=True)
    self.conn.create_consumer(self.topic, self.endpoints, fanout=False)
    return self.conn.consume_in_threads()

创建一个通知rpc的客户端,用于向OVS的agent发出通知。所有plugin都需要有这样一个发出通知消息的客户端,创建了一个OVS agent的通知rpc客户端。之后,创建两个跟service agent相关的consumer,分别监听topics.PLUGIN

ovs_neutron_agent也会创建RPC的consumer,用来监听topics.UPDATE、topics.DELETE等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def setup_rpc(self):
        self.agent_id = 'ovs-agent-%s' % self.conf.host
        self.topic = topics.AGENT
        self.plugin_rpc = OVSPluginApi(topics.PLUGIN)
        self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN)
        self.dvr_plugin_rpc = dvr_rpc.DVRServerRpcApi(topics.PLUGIN)
        self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)

        # RPC network init
        self.context = context.get_admin_context_without_session()
        # Handle updates from service
        self.endpoints = [self]
        # Define the listening consumers for the agent
        consumers = [[topics.PORT, topics.UPDATE],
                     [topics.PORT, topics.DELETE],
                     [constants.TUNNEL, topics.UPDATE],
                     [constants.TUNNEL, topics.DELETE],
                     [topics.SECURITY_GROUP, topics.UPDATE],
                     [topics.DVR, topics.UPDATE],
                     [topics.NETWORK, topics.UPDATE]]

3.neutron-server中的RPC

这个rpc服务端主要通过neutron.server中主函数中代码执行

1
neutron_rpc = service.serve_rpc()

方法的实现代码(目录:neutron/neutron/service.py)如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def serve_rpc():
    plugin = manager.NeutronManager.get_plugin()
    service_plugins = (
        manager.NeutronManager.get_service_plugins().values())

    if cfg.CONF.rpc_workers < 1:
        cfg.CONF.set_override('rpc_workers', 1)
    if not plugin.rpc_workers_supported():
        LOG.debug("Active plugin doesn't implement start_rpc_listeners")
        if 0 < cfg.CONF.rpc_workers:
            LOG.error(_LE("'rpc_workers = %d' ignored because "
                          "start_rpc_listeners is not implemented."),
                      cfg.CONF.rpc_workers)
        raise NotImplementedError()

    try:
        rpc = RpcWorker(service_plugins)
        LOG.debug('using launcher for rpc, workers=%s', cfg.CONF.rpc_workers)
        session.dispose()
        launcher = common_service.ProcessLauncher(cfg.CONF, wait_interval=1.0)
        launcher.launch_service(rpc, workers=cfg.CONF.rpc_workers)
        if (cfg.CONF.rpc_state_report_workers > 0 and
            plugin.rpc_state_report_workers_supported()):
            rpc_state_rep = RpcReportsWorker([plugin])
            LOG.debug('using launcher for state reports rpc, workers=%s',
                      cfg.CONF.rpc_state_report_workers)
            launcher.launch_service(
                rpc_state_rep, workers=cfg.CONF.rpc_state_report_workers)

        return launcher
    except Exception:
        with excutils.save_and_reraise_exception():
            LOG.exception(_LE('Unrecoverable error: please check log for '
                              'details.'))

其中,RpcWorker(plugin)主要通过调用plugin的方法来创建rpc服务端,最重要的工作是调用plugin的start_rpc_listeners来监听消息队列:

1
self._servers = self._plugin.start_rpc_listeners()

该方法在大多数plugin中并未被实现,目前ml2支持该方法。

在neutron.plugin.ml2.plugin.ML2Plugin类中,该方法创建了一个topic为topics.PLUGIN的消费rpc。

1
2
3
4
5
6
7
8
def start_rpc_listeners(self):
        self.endpoints = [rpc.RpcCallbacks(self.notifier, self.type_manager),
                          agents_db.AgentExtRpcCallback()]
        self.topic = topics.PLUGIN
        self.conn = n_rpc.create_connection(new=True)
        self.conn.create_consumer(self.topic, self.endpoints,
                                  fanout=False)
        return self.conn.consume_in_threads()

RPC之nova专题

在Openstack中,每一个Nova服务初始化时会创建两个队列,一个名为“NODE-TYPE.NODE-ID”,另一个名为“NODE-TYPE”,NODE-TYPE是指服务的类型,NODE-ID指节点名称。

1.nova中实现exchange的种类

  • direct:初始化中,各个模块对每一条系统消息自动生成多个队列放入RabbitMQ服务器中,队列中绑定的binding-key要与routing-key匹配
  • topic:各个模块也会自动生成两个队列放入RabbitMQ服务器中。

2.nova中调用RPC的方式

  • RPC.CALL:用于请求和响应方式
  • RPC.CAST:只是提供单向请求

3.nova中模块的逻辑功能

  • Invoker:向消息队列中发送系统请求信息,如Nova-API和Nova-Scheduler,通过RPC.CALL和RPC.CAST两个进程发送系统请求消息。
  • Worker:从消息队列中获取Invoker模块发送的系统请求消息以及向Invoker模块回复系统响应消息,如Nova-Compute、Nova-Volume和Nova-Network,对RPC.CALL做出响应。

4.nova中的exchange domain

  • direct exchange domain: Topic消息生产者(Nova-API或者Nova-Scheduler)与Topic交换器生成逻辑连接,通过PRC.CALL或者RPC.CAST进程将系统请求消息发往Topic交换器。交换器根据不同的routing-key将系统请求消息转发到不同的类型的消息队列。Topic消息消费者探测到新消息已进入响应队列,立即从队列中接收消息并调用执行系统消息所请求的应用程序。

    • 点到点消息队列:Topic消息消费者应用程序接收RPC.CALL的远程调用请求,并在执行相关计算任务之后将结果以系统响应消息的方式通过Direct交换器反馈给Direct消息消费者。

    • 共享消息队列:Topic消息消费者应用程序只是接收RPC.CAST的远程调用请求来执行相关的计算任务,并没有响应消息反馈。

  • topic exchange domain: Direct交换域并不是独立运作,而是受限于Topic交换域中RPC.CALL的远程调用流程与结果,每一个RPC.CALL激活一次Direct消息交换的运作。

以nova启动虚拟机的过程为例,详细介绍RPC通信过程。

RPC.CAST缺少了系统消息响应流程。一个Topic消息生产者发送系统请求消息到Topic交换器,Topic交换器根据消息的Routing Key将消息转发至共享消息队列,与共享消息队列相连的所有Topic消费者接收该系统请求消息,并把它传递给响应的Worker进行处理,其调用流程如图所示:


(一) 功能和原理

设计集群的目的

  • 允许消费者和生产者在RabbitMQ节点崩溃的情况下继续运行
  • 通过增加更多的节点来扩展消息通信的吞吐量

1 集群配置方式

RabbitMQ可以通过三种方法来部署分布式集群系统,分别是:cluster,federation,shovel

  • cluster:

    • 不支持跨网段,用于同一个网段内的局域网
    • 可以随意的动态增加或者减少
    • 节点之间需要运行相同版本的RabbitMQ和Erlang
  • federation:应用于广域网,允许单台服务器上的交换机或队列接收发布到另一台服务器上交换机或队列的消息,可以是单独机器或集群。federation队列类似于单向点对点连接,消息会在联盟队列之间转发任意次,直到被消费者接受。通常使用federation来连接internet上的中间服务器,用作订阅分发消息或工作队列。

  • shovel:连接方式与federation的连接方式类似,但它工作在更低层次。可以应用于广域网。

2 节点类型

  • RAM node:内存节点将所有的队列、交换机、绑定、用户、权限和vhost的元数据定义存储在内存中,好处是可以使得像交换机和队列声明等操作更加的快速。

  • Disk node:将元数据存储在磁盘中,单节点系统只允许磁盘类型的节点,防止重启RabbitMQ的时候,丢失系统的配置信息。

问题说明: RabbitMQ要求在集群中至少有一个磁盘节点,所有其他节点可以是内存节点,当节点加入或者离开集群时,必须要将该变更通知到至少一个磁盘节点。如果集群中唯一的一个磁盘节点崩溃的话,集群仍然可以保持运行,但是无法进行其他操作(增删改查),直到节点恢复。

解决方案:设置两个磁盘节点,至少有一个是可用的,可以保存元数据的更改。

3 Erlang Cookie

Erlang Cookie是保证不同节点可以相互通信的密钥,要保证集群中的不同节点相互通信必须共享相同的Erlang Cookie。具体的目录存放在/var/lib/rabbitmq/.erlang.cookie。

说明: 这就要从rabbitmqctl命令的工作原理说起,RabbitMQ底层是通过Erlang架构来实现的,所以rabbitmqctl会启动Erlang节点,并基于Erlang节点来使用Erlang系统连接RabbitMQ节点,在连接过程中需要正确的Erlang Cookie和节点名称,Erlang节点通过交换Erlang Cookie以获得认证。

4 镜像队列

功能和原理 RabbitMQ的Cluster集群模式一般分为两种,普通模式和镜像模式。

  • 普通模式:默认的集群模式,以两个节点(rabbit01、rabbit02)为例来进行说明。对于Queue来说,消息实体只存在于其中一个节点rabbit01(或者rabbit02),rabbit01和rabbit02两个节点仅有相同的元数据,即队列的结构。当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ会临时在rabbit01、rabbit02间进行消息传输,把A中的消息实体取出并经过B发送给consumer。所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到rabbit01节点中还未消费的消息实体。如果做了消息持久化,那么得等rabbit01节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。

下面表示在集群配置下的不同节点创建队列的情况

下图表示在集群配置下的不同节点创建交换器和队列的绑定的情况

  • 镜像模式:将需要消费的队列变为镜像队列,存在于多个节点,这样就可以实现RabbitMQ的HA高可用性。作用就是消息实体会主动在镜像节点之间实现同步,而不是像普通模式那样,在consumer消费数据时临时读取。缺点就是,集群内部的同步通讯会占用大量的网络带宽。

实现机制 镜像队列实现了RabbitMQ的高可用性(HA),具体的实现策略如下所示:

ha-mode ha-params 功能
all 镜像队列将会在整个集群中复制。当一个新的节点加入后,也会在这 个节点上复制一份。
exactly count 镜像队列将会在集群上复制count份。如果集群数量少于count时候,队列会复制到所有节点上。如果大于Count集群,有一个节点crash后,新进入节点也不会做新的镜像。
nodes node name 镜像队列会在node name中复制。如果这个名称不是集群中的一个,这不会触发错误。如果在这个node list中没有一个节点在线,那么这个queue会被声明在client连接的节点。

实例列举:

1
2
queue_args("x-ha-policy":"all") //定义字典来设置额外的队列声明参数
channel.queue_declare(queue="hello-queue",argument=queue_args)

如果需要设定特定的节点(以rabbit@localhost为例),再添加一个参数

1
2
3
queue_args("x-ha-policy":"nodes",
           "x-ha-policy-params":["rabbit@localhost"])
channel.queue_declare(queue="hello-queue",argument=queue_args)

可以通过命令行查看那个主节点进行了同步

1
rabbitmqctl list_queue name slave_pids synchronised_slave_pids

(二) RabbitMQ Cluster 配置

1 单机多节点部署

在启动RabbitMQ节点之后,服务器默认的节点名称是Rabbit和监听端口5672,如果想在同一台机器上启动多个节点,那么其他的节点就会因为节点名称和端口与默认的冲突而导致启动失败,可以通过设置环境变量来实现,具体方法如下:

  • 首先在机器上设置两个节点rabbit和rabbit_01
1
2
3
4
5
rabbitmqctl stop //先停止运行节点,再进行集群部署
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit //设置环境变量指定端口和节点名称
rabbitmq-server -detached //后台启动节点
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit_01 //设置环境变量指定端口和节点名称
rabbitmq-server -detached //后台启动节点

或者通过添加/etc/rabbitmq/rabbitmq-env.conf文件来进行设置:

1
2
3
4
NODE_PORT=5672
NODENAME=rabbit
NODE_PORT=5673
NODENAME=rabbit_01
  • 将rabbit_01节点添加到第一个集群节点rabbit中
1
2
3
4
rabbitmqctl -n rabbit_01@localhost stop_app //停止rabbit_01节点的应用
rabbitmqctl -n rabbit_01@localhost join_cluster rabbit@localhost //将rabbit_01添加到集群节点rabbit中去
rabbitmqctl cluster_status //查看集群节点的状态
rabbitmqctl -n rabbit_01@localhost start_app //启动rabbit_01节点的应用
1
2
3
4
5
6
7
//可以看到如下信息,说明节点添加成功,表明都是磁盘类型的节点
Cluster status of node rabbit@localhost ...
[{nodes,[{disc,[rabbit@localhost,rabbit_01@localhost]}]},
 {running_nodes,[rabbit@localhost]},
 {cluster_name,<<"rabbit@localhost">>},
 {partitions,[]},
 {alarms,[{rabbit@localhost,[]}]}]

2 多机多节点部署

不同于单机多节点的情况,在多机环境,如果要在cluster集群内部署多个节点,需要注意两个方面:

  • 保证需要部署的这几个节点在同一个局域网内
  • 需要有相同的Erlang Cookie,否则不能进行通信,为保证cookie的完全一致,采用从一个节点copy的方式

环境介绍

RabbitMQ节点 IP地址 工作模式 操作系统
rabbitmqCluster 192.168.118.133 DISK CentOS 7.0 - 64位
rabbitmqCluster01 192.168.118.134 DISK CentOS 7.0 - 64位
rabbitmqCluster02 192.168.118.135 DISK CentOS 7.0 - 64位

cluster部署过程

  • 局域网配置 分别在三个节点的/etc/hosts下设置相同的配置信息
1
2
3
  192.168.118.133 rabbitmqCluster
  192.168.118.134 rabbitmqCluster01
  192.168.118.135 rabbitmqCluster02
  • 设置不同节点间同一认证的Erlang Cookie 采用从主节点copy的方式保持Cookie的一致性
1
2
[root@rabbitmqCluster01]# scp /var/lib/rabbitmq/.erlang.cookie 192.168.118.134:/var/lib/rabbitmq
[root@rabbitmqCluster02]# scp /var/lib/rabbitmq/.erlang.cookie 192.168.118.135:/var/lib/rabbitmq
  • 使用 -detached运行各节点
1
2
rabbitmqctl stop
rabbitmq-server -detached 
  • 查看各节点的状态
1
2
3
[root@rabbitmqCluster]#rabbitmqctl cluster_status
[root@rabbitmqCluster01]#rabbitmqctl cluster_status
[root@rabbitmqCluster02]#rabbitmqctl cluster_status
  • 创建并部署集群,以rabbitmqCluster01节点为例:
1
2
3
[root@rabbitmqCluster01]#rabbitmqctl stop_app
[root@rabbitmqCluster01]#rabbitmqctl join_cluster rabbit@rabbitmqCluster
[root@rabbitmqCluster01]#rabbitmqctl start_app
  • 查看集群状态
1
[root@rabbitmqCluster]#rabbitmqctl cluster_status

RabbitMQ负载均衡配置

前言:从目前来看,基于RabbitMQ的分布式通信框架主要包括两部分内容,一是要确保可用性和性能,另一个就是编写当节点发生故障时知道如何重连到集群的应用程序。负载均衡就是解决处理节点的选择问题。

安装HAProxy

选择开源的HAProxy为RabbitMQ集群做负载均衡器,在CentOS 7.0中安装HAProxy。

  • 安装epel
1
rpm -ivh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm//
  • 安装HAProxy
1
yum -y install haproxy
  • 配置HAProxy
1
2
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
vim /etc/haproxy/haproxy.cfg
  • 添加配置信息
1
2
3
4
5
6
7
8
9
10
11
listen rabbitmq_local_cluster 127.0.0.1:5670 //前段IP,供product和consumer来进行选择,由于5672端口已经默认使用,这里选择5670端口
     mode tcp   //负载均衡选项
     balance roundrobin //轮询算法将负载发给后台服务器
     server rabbit 127.0.0.1:5672 check inter 5000 rise 2 fall 3//负载均衡中的集群节点配置,这里选择的rabbit节点
  
  listen private_monitoring :8100
     mode http
     option httplog
     stats enable
     stats uri       /stats
     stats refresh 60s
  • 设置开机启动
1
2
chkconfig haproxy on
service haproxy start
  • 访问localhost:8100/stats就可以看到具体的控制界面。