怎么样使用Neutron DVR实现multi-host特性?

怎么样使用Neutron DVR实现multi-host特性?

作者:张华 发表于:2014-03-07
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明

(http://blog.csdn.net/quqi99 )

 

注意,这块的代码目前都还没有,只是提前进行一下理论分析。

先温习下l3-agent原理:l3-agent节点为所有subnet创建内部网关,外部网关,路由等。l3-agent定期同步router时会为将和该router相关联的subnet调度到相应的l3-agent节点上创建网关(根据port的device_owner属性找到对应的port, port里有subnet)。neutron支持在多个节点上启动多个l3-agent, l3-agent的调度是针对router为单位的, 试想, 如果我们创建众多的router, 每一个subnet都关联到一个router的话, 那么也意味着每一个subnet的网关都可被调度到不同的l3-agent上, 从而将不同的subnet的流量分流到不同的l3-agent节点.

但是上述方案不具有HA特性, 所以出现一个VRRP HA的Blueprint继续使用VRRP+Keepalived+Conntrackd技术解决单点l3-agent的HA问题, VRRP使用广播进行心跳检查, backup节点收不到master节点定期发出的心跳广播时便认为master死掉从而接管master的工作(删除掉原master节点上的网关的IP, 在新master节点上重设网关). 可参见我的另一博文:http://blog.csdn.net/quqi99/article/details/18799877

但是, 上述两种方案均无法解决相同子网的东西向流量不绕道l3-agent的状况, 所以又出现了一个名DVR的Blueprint, 如下图:

怎么样使用Neutron DVR实现multi-host特性?

tenant之间通过namespace隔离, tenenat下可以有多个router, 每个router具有自己的namespace, 一个subnet只能添加到一个router上 (实际上, 如果为这个subnet再生成一个port, 也是可以将这个port加入到另一个router上的, 这点待确认).

 

一, 先看L2的设计

我想问题的关键是肯定会出现多个计算节点上存在同一子网的网关,所以应该各计算节点上相同子网的所有网关使用相同的IP与MAC地址,或者也可以为每个计算节点生成唯一的DVR MAC地址。然后应该让这些IP局部ARP隔离使之成为真正的内部路由(附录一只是一种我想到的方法),下面看看ovs中的实现(参考:https://wiki.openstack.org/wiki/Neutron/DVR_L2_Agent)

怎么样使用Neutron DVR实现multi-host特性?

 

ovs agent原来的ovs流表见如下图(来自:https://wiki.openstack.org/wiki/Ovs-flow-logic, ), patch_int, gre_port, vxlan_port分别是br-tun上的三个port.

怎么样使用Neutron DVR实现multi-host特性?

进出计算节点的流量都应该替换到DVR MAC地址,流表要在上图的基础上增加,见:https://wiki.openstack.org/wiki/Neutron/DVR_L2_Agent

对于出口流量

table=1, priority=4, dl_vlan=vlan1, dl_type=arp, ar_tpa=gw1 actions:drop #一计算节点所有子网到其他计算节点其网关的arp流量

table=1, priority=4, dl_vlan=vlan1, dl_dst=gw1-mac actions:drop #一计算节点所有子网到其他计算节点其网关的流量

table=1, priority=1, dl_vlan=vlan1, dl_src=gw1-mac, actions:mod dl_src=dvr-cn1-mac,resubmit(,2) #所有出计算节点的流量使用DVR MAC

对于入口流量,还得增加一个table 9, 插在table 2&3 (改先跳到table 9) 与table 10之间:

table=9, priority=1, dl_src=dvc-cn1-mac actions=output-> patch-int

table=9, priority=0, action=-resubmit(,10)

 

二、再看L3 (IR) 的设计

肯定要增加一种port类型network:router_interface_distributed,

 

三、DNAT的设计

对于南北向的流量,如果有floating ip,流量就直接走计算节点。如果没有floating ip,则会走网络节点。

 

四、SNAT的设计

根据agent_mode的下列三种模式决定SNAT iptables规则是否设置在计算节点上( iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') )

1, legacy, 原来的全局使用一个l3-agent

2, dvr, 用计算节点上的IR来实现SNAT

3, dvr_snat, 使用了DVR特性,但是SNAT走中心化结点

当在DVR模式时,SNAT走中心化网络节点的话,不又会出现网络节点与计算节点同网关的问题吗?可以将网关的IP设置成不一样就可以了啊。这点类似于quantum时期nova中的multi-host特性,每个计算节点都有路由,但网关的IP是不一样的。

再说一下VPN, VPN肯定是需要SNAT走中心化的节点的, 在legency时使用的是qr名空间,但在dvr_snat使用的是snat_名空间,要想使用vpn应该使用snat名空间,见patch: https://review.openstack.org/#/c/143203/

 

数据结构

如上所述,因为dvr-agent处理的不是所有subnet的网关,只是和该agent相关联的subnet的网关。所以肯定需要一个数据表记录subnet和agent之间的映射关系,可参考:https://docs.google.com/document/d/1kMUO1-y4yATQNBlAkdjDu7OwFYt5cfzIzb5NKQjhb08/edit?pli=1#heading=h.ttq3kyub25tn

1, subnet_instance_binding(subnet_id, hostname, port_id, mac_address, ip_versio, ip_address, ip_version)
2, vlan_agent_binding(network_id, hostname, vlan_id)
3, dvr(hostname, dvr_mac_address)
这个表的数据如何插入,当然可以通过openstack框架自身的数据库,但那样在涉及nova的表,所以直接查该agent上的hypervisor或者ovs的数据库是个更好的方法。从这个角度讲,不用表存储这些数据,直接利用l3-agent原有的代码稍微改一下(在同步router时从数据库获取所有ports的地方改成只获取和本机虚拟机的tap的mac地址相关联的port即可)。不过全局有一个这样各机器上subnet和host映射的汇总的表的话,就可以很容易实现类似TRILL(http://blog.csdn.net/quqi99/article/details/11778413)的技术来实现使用switch id + mac来同时转发的模式(好处在于不同switch上的vm可以具有相同的ip)。


CLI:
更新CLI:
1, router-create, 应能列出router对象新增的distribute属性
原有CLI:
1, router-list-on-l3-agent
2, router-port-list
3, router-show
新增CLI:
1, l3-agent-list-hosting-snat
2, l3-agent-snat-add

3, l3-agent-snat-remove

 

Devstack:

http://blog.csdn.net/quqi99/article/details/25923921

neutron.conf
router_distributed = True
l3-agent.ini
agent_mode = dvr_snat
ml2_conf.ini
ml2 section
append ",l2population" to mechanism_drivers
agent section
l2_population = True
tunnel_types = vxlan
enable_distributed_routing = True



附录一, 一种使用linux bridge时针对网络上出现多个相当IP的网关的ebtables隔离设想(文中已经描述了使用ovs隔离的流规则),注意:未测试
ebtables无法做到同时结合二层和三层的条件(三层的ip-dst和二层的arp-ip-dst)为网关IP返回不同的mac地址(arpreply-mac), 我们可以换一种思路, 令这些同ip的网关的mac地址也相同, 然后这些网关再自己决定它们该接收何冲流量,
FORWARD -m physdev --physdev-in <ex_gw> -d 10.0.0.0/8 -j DROP
FORWARD -m physdev --physdev-in qr-FFF -d ! 10.0.0.0/8 -j DROP
因为内部网关和外部网关的IP相同, 也应该隔离内部网关和外部网关上的ARP流量:
ebtables -t broute -A BROUTING -p arp -i gw --arp-ip-dst 10.0.0.0/8 -j DROP
ebtables -t broute -A BROUTING -p arp -i gw --arp-ip-src 10.0.0.0/8 -j DROP
ebtables -t filter -A FORWARD -p arp --arp-ip-dst 10.10.10.96/27 -j ACCEPT
ebtables -t nat -A POSTROUTING -p arp -o gw --arp-ip-src 10.10.10.100 -j ACCEPT
由于令内部网关与外部网关的IP相同,使用etables统一应答它们的MAC地址:
ebtables -t nat -A PREROUTING -i tapPORT_ID -p arp --arp-opcode Request --arp-ip-dst REMOTE_VM_IP -j arpreply --arpreply-mac REMOTE_VM_MAC --arpreply-target ACCEPT
也需要隔离内部网关上的DHCP广播:
FORWARD -m physdev --physdev-in qr-FFF -d 255.255.255.255 -p udp --dport 67 -j DROP

FORWARD -m physdev --physdev-out qr-FFF -d 255.255.255.255 -p udp --dport 67 -j DROP

 

测试环境快速搭建

juju bootstrap
bzr branch lp:openstack-charm-testing && cd openstack-charm-testing/
juju-deployer -c ./next.yaml -d xenial-mitaka
juju status |grep message
juju set neutron-api l2-population=True enable-dvr=True
./configure
source ./novarc
nova boot --flavor 2 --image trusty --nic net-id=$(neutron net-list |grep ' private ' |awk '{print $2}') --poll i1
nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0
nova secgroup-add-rule default tcp 22 22 0.0.0.0/0


nova floating-ip-create
nova floating-ip-associate i1 $(nova floating-ip-list |grep 'ext_net' |awk '{print $2}')


neutron net-create private_2 --provider:network_type gre --provider:segmentation_id 1012
neutron subnet-create --gateway 192.168.22.1 private_2 192.168.22.0/24 --enable_dhcp=True --name private_subnet_2
ROUTER_ID=$(neutron router-list |grep ' provider-router ' |awk '{print $2}')
SUBNET_ID=$(neutron subnet-list |grep '192.168.22.0/24' |awk '{print $2}')
neutron router-interface-add $ROUTER_ID $SUBNET_ID
nova hypervisor-list
nova boot --flavor 2 --image trusty --nic net-id=$(neutron net-list |grep ' private_2 ' |awk '{print $2}') --hint force_hosts=juju-zhhuabj-machine-16 i2

 

相关配置

1, neutron.conf on the node hosted neutron-api
router_distributed = True
 

2, ml2_conf.ini on the node hosts neutron-api
mechanism_drivers =openvswitch,l2population
 

3, l3_agent on the node hosted nova-compute
agent_mode = dvr
 

4, l3_agent on the node hosted l3-agent
agent_mode = dvr_snat
 

5, openvswitch_agent.ini on all nodes
[agent]
l2_population = True
enable_distributed_routing = True

 

基于策略的路由

如路由器上有多个接口,一个为IF1(IP为IP1), 与之相连的子网为P1_NET
a, 创建路由表T1, echo 200 T1 >> /etc/iproute2/rt_tables
b, 设置main表中的缺省路由,ip route add default via $P1
c, 设置main表中的路由,ip route add $P1_NET dev $IF1 src IP1, 注意一定要加src参数, 与步骤d)配合
d, 设置路由规则, ip rule add from $IP1 table T1, 注意与上步c)配合
e, 设置T1表中的路由, ip route add $P1_NET dev $IF1 src $IP1 table T1 && ip route flush cache
f, 负载均衡, ip route add default scope global nexthop via $P1 dev $IF1 weight 1 \
nexthop via $P2 dev $IF2 weight 1


1, 中心化SNAT流量 (计算节点没有FIP时会这么走)

怎么样使用Neutron DVR实现multi-host特性?
虚机不需要浮动IP,只想出外网,可以配置在L3 agent上配置一个中心化的SNAT即可。
a, 假设虚机的网段为13.0.0.0/24, 既然计算节点启动了DVR,那么计算节点然后在qrouter名空间上有它的网关13.0.0.1
b, 网络节点会多出一个snat名空间,里面的网卡同时有13.0.0.5和外网IP(192.168.122.218),并且有snat规则(-A neutron-l3-agent-snat -s 13.0.0.0/24 -j SNAT --to-source 192.168.122.218)
c, 计算节点需要把snat流量往13.0.0.5 (中心化的网络节点会有一个同子网但不同网关的IP)上导,所以它也会在计算节点的qrouter上创建一个路由策略:
ip netns exec qrouter-xxx ip route add 13.0.0.0/24 dev qr-078b1a5a-d0 src 13.0.0.1
ip netns exec qrouter-xxx ip rule add from 13.0.0.1 table 218103809
ip netns exec qrouter-xxx ip route add 13.0.0.0/24 dev qr-078b1a5a-d0 src 13.0.0.1 table 218103809

d, 今后VPNaaS流量应该区分出来只走中心化的网络节点

f, 完整的路径如下:

 

计算节点只有qrouter-xxx名空间(计算节点有FIP才有fip-xxx名空间)
$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip addr show
19: qr-48e90ad1-5f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UNKNOWN group default qlen 1
  link/ether fa:16:3e:f8:fe:97 brd ff:ff:ff:ff:ff:ff
  inet 192.168.21.1/24 brd 192.168.21.255 scope global qr-48e90ad1-5f
    valid_lft forever preferred_lft forever

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip rule list
0: from all lookup local 
32766: from all lookup main 
32767: from all lookup default
3232240897: from 192.168.21.1/24 lookup 3232240897

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip route list table main
192.168.21.0/24 dev qr-48e90ad1-5f proto kernel scope link src 192.168.21.1

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip route list table 3232240897
default via 192.168.21.3 dev qr-48e90ad1-5f

网络节点, sg-xxx上的接口即为同子网关的一个专用于SNAT的IP
$ sudo ip netns list
qrouter-9a3594b0-52d1-4f29-a799-2576682e275b (id: 2)
snat-9a3594b0-52d1-4f29-a799-2576682e275b (id: 1)
qdhcp-7a8aad5b-393e-4428-b098-066d002b2253 (id: 0)

$ sudo ip netns exec snat-9a3594b0-52d1-4f29-a799-2576682e275b ip addr show
2: qg-b825f548-32@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UP group default qlen 1000
  link/ether fa:16:3e:c4:fd:ad brd ff:ff:ff:ff:ff:ff link-netnsid 0
  inet 10.5.150.0/16 brd 10.5.255.255 scope global qg-b825f548-32
    valid_lft forever preferred_lft forever
3: sg-95540cb3-85@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UP group default qlen 1000
  link/ether fa:16:3e:62:d3:26 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  inet 192.168.21.3/24 brd 192.168.21.255 scope global sg-95540cb3-85
    valid_lft forever preferred_lft forever

$ sudo ip netns exec snat-9a3594b0-52d1-4f29-a799-2576682e275b iptables-save |grep SNAT
-A neutron-l3-agent-snat -o qg-b825f548-32 -j SNAT --to-source 10.5.150.0
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 10.5.150.0

 

2, 计算节点有FIP时SNAT走计算节点

怎么样使用Neutron DVR实现multi-host特性?

这个和下面的DNAT类似,计算节点上因为已经有FIP那SNAT也从计算节点出,故需要将SNAT流量从qrouter-xxx名空间通过rfp-xxx与fpr-xxx这对peer devices采用16这个路由表导到fip-xxx名空间。

$ sudo ip netns list
qrouter-9a3594b0-52d1-4f29-a799-2576682e275b
fip-57282976-a9b2-4c3f-9772-6710507c5c4e

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip addr show
4: rfp-9a3594b0-5@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UP group default qlen 1000
  link/ether 3a:63:a4:11:4d:c4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  inet 169.254.109.46/31 scope global rfp-9a3594b0-5
    valid_lft forever preferred_lft forever
  inet 10.5.150.1/32 brd 10.5.150.1 scope global rfp-9a3594b0-5
    valid_lft forever preferred_lft forever
19: qr-48e90ad1-5f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UNKNOWN group default qlen 1
  link/ether fa:16:3e:f8:fe:97 brd ff:ff:ff:ff:ff:ff
  inet 192.168.21.1/24 brd 192.168.21.255 scope global qr-48e90ad1-5f
    valid_lft forever preferred_lft forever

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip rule list
0: from all lookup local 
32766: from all lookup main 
32767: from all lookup default
57481:from 192.168.21.5 lookup 16
3232240897: from 192.168.21.1/24 lookup 3232240897

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip route list table main
169.254.109.46/31 dev rfp-9a3594b0-5 proto kernel scope link src 169.254.109.46
192.168.21.0/24 dev qr-48e90ad1-5f proto kernel scope link src 192.168.21.1

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b ip route list table 16
default via 169.254.106.115 dev rfp-9a3594b0-5

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b iptables-save |grep SNAT
-A neutron-l3-agent-float-snat -s 192.168.21.5/32 -j SNAT --to-source 10.5.150.1

$ sudo ip netns exec fip-57282976-a9b2-4c3f-9772-6710507c5c4e ip addr show
5: fpr-9a3594b0-5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UP group default qlen 1000
  link/ether fa:e7:f9:fd:5a:40 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  inet 169.254.106.115/31 scope global fpr-9a3594b0-5
    valid_lft forever preferred_lft forever
17: fg-d35ad06f-ae: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc noqueue state UNKNOWN group default qlen 1
  link/ether fa:16:3e:b5:2d:2c brd ff:ff:ff:ff:ff:ff
  inet 10.5.150.2/16 brd 10.5.255.255 scope global fg-d35ad06f-ae
    valid_lft forever preferred_lft forever

$ sudo ip netns exec qrouter-9a3594b0-52d1-4f29-a799-2576682e275b route -n
Kernel IP routing table
Destination   Gateway     Genmask     Flags Metric Ref  Use Iface
169.254.106.114 0.0.0.0     255.255.255.254 U   0   0    0 rfp-9a3594b0-5
192.168.21.0  0.0.0.0     255.255.255.0  U   0   0    0 qr-48e90ad1-5f

$ sudo ip netns exec fip-57282976-a9b2-4c3f-9772-6710507c5c4e route -n
Kernel IP routing table
Destination   Gateway     Genmask     Flags Metric Ref  Use Iface
0.0.0.0     10.5.0.1    0.0.0.0     UG  0   0    0 fg-d35ad06f-ae
10.5.0.0    0.0.0.0     255.255.0.0   U   0   0    0 fg-d35ad06f-ae
10.5.150.1   169.254.106.114 255.255.255.255 UGH  0   0    0 fpr-9a3594b0-5
169.254.106.114 0.0.0.0     255.255.255.254 U   0   0    0 fpr-9a3594b0-5


3, 计算节点上的DNAT流量(一个计算节点需要两个外网IP)

网络节点上是qrouter(管子网网关)和snat(用于snat流量,有qg-接口与用于SNAT的与子网网关不同的sg-接口)。

计算节点上是qrouter(管子网网关、fpr:169.254.31.29/24, 浮动IP)和fip(fg:外网网关,rpf:169.254.31.28), qrouter与fip两个名空间通过fpr与rpf两个peer设备关联,流量从fip:fg经peer设备导到qouter里的浮动IP里去。若计算节点需要处理snat流量才有snat名空间。
继续给上面的虚机13.0.0.6配置一个浮动IP(192.168.122.219)
a, 计算节点的qrouter名空间除了有它的网关13.0.0.1外,还会有它的浮动IP(192.168.122.219),和rfp-db5090df-8=169.254.31.28/31
有了DNAT规则(-A neutron-l3-agent-PREROUTING -d 192.168.122.219/32 -j DNAT --to-destination 13.0.0.6)
-A neutron-l3-agent-POSTROUTING ! -i rfp-db5090df-8 ! -o rfp-db5090df-8 -m conntrack ! --ctstate DNAT -j ACCEPT
c, 计算节点除了有qrouter名空间外,还多出一个fip名空间。它多用了一个外网IP(192.168.122.220), 和fpr-fpr-db5090df-8=169.254.31.29/31
d, 处理snat流量和之前一样由路由表21813809处理(以下所有命令均是在名空间执行的,为了清晰我去掉了)
ip route add 13.0.0.0/24 dev rfp-db5090df-8 src 13.0.0.1
ip rule add from 13.0.0.1 table 218103809
ip route add 13.0.0.0/24 dev rfp-db5090df-8 src 13.0.0.1 table 218103809
e, qrouter名空间内处理dnat流量新增路由表16处理,把从虚机13.0.0.6出来的流量通过rfp-db5090df-8导到fip名空间
ip rule add from 13.0.0.6 table 16
ip route add default gw dev rfp-db5090df-8 table 6
f, fip名空间处理dnat流量,
ip route add 169.254.31.28/31 dev fpr-db5090df-8
ip route add 192.168.122.219 dev fpr-db5090df-8
ip route add 192.168.122.0/24 dev fg-c56eb4c0-b0


4, 东西流量(同一tenant下不同子网)
东西流量由计算节点自行处理。
除了上面的虚机(13.0.0.6, 192.168.122.219),再新增一个运行在不同计算节点的不同子网的虚机(11.0.0.3). 两个计算节点的qrouter名空间同时有了两个子网的网关, 13.0.0.1和11.0.0.1,这些网关局部有效。如节点2上的vm2(11.0.0.3)与节点1上的vm1(13.0.0.6)通信时。
a, vm2发给vm1因为跨网了,所以是发给它的网关 (DEST: 11.0.0.1's MAC, SRC: 11.0.0.3's MAC)
b, vm2上的dvr router (DEST: 13.0.0.6's MAC, SRC: 13.0.0.1's MAC)
c, 隧道,两层 (DEST: 13.0.0.6's MAC, SRC: 节点2's DVR MAC)
d, vm1上的dvr router (DEST: 13.0.0.6's MAC, SRC: 13.0.0.1's MAC)
怎么样使用Neutron DVR实现multi-host特性?

缺点

  • 目前不支持创建分布式的HA router,即不支持DVR下计算节点上的router也支持HA。bug见:https://bugs.launchpad.net/neutron/+bug/1365473
  • VPNaaS流量应该区分出来只走中心化的网络节点(即dvr_snat节点)
  • 对FWaaS也可能有影响,

参考

https://wiki.openstack.org/wiki/Neutron/DVR/HowTo

https://blueprints.launchpad.net/neutron/+spec/neutron-ovs-dvr

https://wiki.openstack.org/wiki/Neutron/DVR_L2_Agent

https://docs.google.com/document/d/1kMUO1-y4yATQNBlAkdjDu7OwFYt5cfzIzb5NKQjhb08/edit?pli=1#heading=h.ttq3kyub25tn

http://www.cnblogs.com/sammyliu/p/4713562.html

https://kimizhang.wordpress.com/2014/11/25/building-redundant-and-distributed-l3-network-in-juno/