7-2-Neutron-Open vSwitch in Neutron
网络拓扑
OvS In Neutron

OvS In Neutron 网络实现模型

双节点混合平面网络

双节点网络拓扑

NOTE : 本文中的网络设计只是为了方便描述 , 仅作参考 , 实际的网络设计会更加灵活。
Controller :
ens160:172.18.22.231(OpenStack Management Network)ens161:br-exens192:10.0.0.1(OpenStack Internal Tunnel Network)ens224:br-provider(Flat)ens256:br-provider-1(VLAN)
ovs-vsctl add-br br-ex
ovs-vsctl add-port br-ex ens161
ovs-vsctl add-br br-provider
ovs-vsctl add-port br-provider ens224
ovs-vsctl add-br br-provider-1
ovs-vsctl add-port br-provider-1 ens256Compute
ens160:172.18.22.232(OpenStack Management Network)ens192:10.0.0.2(OpenStack Internal Tunnel Network)ens224:br-provider(Flat)ens256:br-provider-1(VLAN)
ovs-vsctl add-br br-provider
ovs-vsctl add-port br-provider ens224
ovs-vsctl add-br br-provider-1
ovs-vsctl add-port br-provider-1 ens2561. OvS Bridges 与初始流表项
OvS br-int
OVS Integration Bridge 综合网桥 , 属于本地网络层网桥 , 在每个计算节点上作为本地虚拟机的直连虚拟交换机。
[root@controller ~]# ovs-ofctl dump-flows br-int
cookie=0xea50ae8e9cc7754f, duration=69221.341s, table=0, n_packets=292387, n_bytes=19859352, priority=2,in_port="int-br-provider" actions=drop
cookie=0xea50ae8e9cc7754f, duration=69222.176s, table=0, n_packets=0, n_bytes=0, priority=0 actions=resubmit(,60)
cookie=0xea50ae8e9cc7754f, duration=69222.178s, table=23, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xea50ae8e9cc7754f, duration=69222.172s, table=24, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xea50ae8e9cc7754f, duration=69222.174s, table=60, n_packets=0, n_bytes=0, priority=3 actions=NORMALOvS br-tun
OVS Tunnel Bridge 隧道网桥 , 属于隧道类型 ( VxLAN、GRE) 租户网络层网桥 , 进行数据包的内外 VID 转换以及桥接各节点上的同类型网桥。
[root@controller ~]# ovs-ofctl dump-flows br-tun
cookie=0xbababcd06622b167, duration=69203.910s, table=0, n_packets=0, n_bytes=0, priority=1,in_port="patch-int" actions=resubmit(,2)
cookie=0xbababcd06622b167, duration=69203.909s, table=0, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xbababcd06622b167, duration=69203.907s, table=2, n_packets=0, n_bytes=0, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xbababcd06622b167, duration=69203.905s, table=2, n_packets=0, n_bytes=0, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)
cookie=0xbababcd06622b167, duration=69203.903s, table=3, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xbababcd06622b167, duration=69203.901s, table=4, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xbababcd06622b167, duration=69203.900s, table=6, n_packets=0, n_bytes=0, priority=0 actions=drop
cookie=0xbababcd06622b167, duration=69203.897s, table=10, n_packets=0, n_bytes=0, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xbababcd06622b167,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:OXM_OF_IN_PORT[]),output:"patch-int"
cookie=0xbababcd06622b167, duration=69203.894s, table=20, n_packets=0, n_bytes=0, priority=0 actions=resubmit(,22)
cookie=0xbababcd06622b167, duration=69203.892s, table=22, n_packets=0, n_bytes=0, priority=0 actions=dropOvS br-provider
OVS Provider Bridge 运营商网桥 , 又称物理网桥。因为需要将运营商网络的物理网卡挂载到 Bridge 上 , 所以称为 Provider Bridge。属于非隧道类型 ( Flat、VLAN) 的租户层网桥 , 进行数据包的内外 VID 转换以及桥接各节点上的同类型网桥。为了方便后文的流表项分析 , 本文中 br-provider 专用于 Flat Network。
[root@controller ~]# ovs-ofctl dump-flows br-provider
cookie=0x9bf5dfe2802f0eee, duration=69241.924s, table=0, n_packets=0, n_bytes=0, priority=2,in_port="phy-br-provider" actions=drop
cookie=0x9bf5dfe2802f0eee, duration=69241.957s, table=0, n_packets=3936526, n_bytes=2971679460, priority=0 actions=NORMALNOTE : 当 br-provider 被 OvS Agent “接管” 之后会插入第一条流表项 , 含义是默认 ( priority=2) 将所有从 br-int 发送过来的数据包 Drop 掉 , 直到有优先级 Priority 更高的流表项写入为止。
OvS br-provider-1
OVS Provider Bridge 运营商网桥 , 同上。为了方便后文的流表项分析 , 本文中 br-provider-1 专用于 VLAN Network。
[root@controller ~]# ovs-ofctl dump-flows br-provider-1
cookie=0x5a3fea9b762885b6, duration=938.826s, table=0, n_packets=4097, n_bytes=279042, priority=2,in_port="phy-br-pr53399e" actions=drop
cookie=0x5a3fea9b762885b6, duration=938.841s, table=0, n_packets=71286, n_bytes=25799617, priority=0 actions=NORMALOvS br-ex
OVS Provider Bridge 运营商网桥 , 同上。本文中 br-ex 作为外部网络网桥 , 将 L3 Router 转发过来的数据包通过物理网络发送到外部网络 ( 第二次网关) 。
[root@controller ~]# ovs-ofctl dump-flows br-ex
cookie=0x2ab9d6fd329b3642, duration=22.621s, table=0, n_packets=0, n_bytes=0, priority=2,in_port="phy-br-ex" actions=drop
cookie=0x2ab9d6fd329b3642, duration=22.644s, table=0, n_packets=868, n_bytes=311055, priority=0 actions=NORMALNOTE : 无论是 br-provider、br-provider-1 还是 br-ex 都只是命名区别而已 , 它们均属于 OVS Provider Bridge 一类 , 根本作用是 连接 Provider Physical Network ( 运营商物理网络) 。区别命名也只是为了更好的将它们应用到网管人员设计好的网络中而已。抛开性能问题 , 实际上租户网络 ( Flat、VLAN) 和运营商网络 ( 外部网络) 均可以共用同一个 br-provider。与由 OvS Agent 创建并维护的 br-int、br-tun 不同 , br-provider 顾名思义是对接 “运营商物理网络” 的网桥 , 不属于 Neutron 的管理范畴 , 所以需要自己手动创建并挂载一张物理网卡。
2. OvS in Neutron 配置
Controller
[root@controller ~]# cat /etc/neutron/neutron.conf [DEFAULT] core_plugin = ml2 service_plugins = router allow_overlapping_ips = true transport_url = rabbit://openstack:fanguiju@controller auth_strategy = keystone notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true rpc_state_report_workers = 0 api_workers = 4 [database] connection = mysql+pymysql://neutron:fanguiju@controller/neutron [keystone_authtoken] www_authenticate_uri = http://controller:5000 auth_url = http://controller:5000 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = fanguiju [nova] auth_url = http://controller:5000 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = fanguiju [oslo_concurrency] lock_path = /var/lib/neutron/tmp [agent] root_helper_daemon = sudo /usr/bin/neutron-rootwrap-daemon /etc/neutron/rootwrap.conf root_helper = sudo /usr/bin/neutron-rootwrap /etc/neutron/rootwrap.conf [root@controller ~]# cat /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers = local,flat,vlan,vxlan tenant_network_types = vxlan extension_drivers = port_security mechanism_drivers = openvswitch,l2population [ml2_type_flat] flat_networks = provider,external [ml2_type_vlan] network_vlan_ranges = provider1:1:1000 [ml2_type_vxlan] vni_ranges = 1:1000 [root@controller ~]# cat /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] datapath_type = system bridge_mappings = provider:br-provider,provider1:br-provider-1,external:br-ex tunnel_bridge = br-tun local_ip = 10.0.0.1 [agent] tunnel_types = vxlan l2_population = True [securitygroup] firewall_driver = openvswitchCompute
[root@compute ~]# cat /etc/neutron/neutron.conf [DEFAULT] transport_url = rabbit://openstack:fanguiju@controller auth_strategy = keystone [keystone_authtoken] www_authenticate_uri = http://controller:5000 auth_url = http://controller:5000 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = fanguiju [oslo_concurrency] lock_path = /var/lib/neutron/tmp [root@compute ~]# cat /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] datapath_type = system bridge_mappings = provider:br-provider,provider1:br-provider-1 tunnel_bridge = br-tun local_ip = 10.0.0.2 [agent] tunnel_types = vxlan l2_population = True [securitygroup] firewall_driver = openvswitch
3. Flat Network ( 扁平网络)
Flat Network 的特点是无 VLAN Tagging , 所以每个 Flat Network 独占一张物理网卡 , 在生产环境中很少见。
NOTE : 一个 Provider Physical Network ( 运营商物理网络) 只能创建一个 Flat Network , 否则会触发异常。
3.1 关键配置
Controller
# /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers = flat mechanism_drivers = openvswitch [ml2_type_flat] flat_networks = provider, # /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings = provider:br-provider
Compute
# /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings = provider:br-providerprovider:br-provider中的provider只是一个Label(String) , 用于在跨节点、跨网络类型场景中标识同一个Provider Physical Network( 运营商物理网络) , 是一种friendly的用户操作方式。而br-provider则是手动创建的OVS Provider Bridge, 挂载了一张物理网卡 , 作为Flat Network的租户层网桥。
3.2 创建 Flat 类型租户网络 ( 管理员权限)
Create Flat Networkopenstack network create --enable --project admin --provider-network-type flat --provider-physical-network provider flat-net-1Create Subnetopenstack subnet create --network flat-net-1 --dhcp --ip-version 4 --subnet-range 192.168.1.0/24 --gateway 192.168.1.1 flat-subnet-1
3.2.1 网络变更

因为
Subnet enabled DHCP所以在Flat Network创建了一个Port network:dhcp。
Port network:dhcp本质是一个br-int的Port tap{dhcp_port_uuid},Local VLAN tag:1。OvS Agent在br-int上通过Local VLAN tag实现了不同Networks之间的二层广播域隔离。[root@controller ~]# ovs-vsctl show ... Bridge br-int ... Port "tapec3789c1-0e" tag: 1 Interface "tapec3789c1-0e" type: internalPort tap{dhcp_port_uuid}的Interface设备创建在Network Namespace qdhcp-{network_uuid}, 通过Linux Network Namespace进行隔离 , 防止多网络环境的IP:DomainName解析冲突。[root@controller ~]# ip netns qdhcp-6967cf07-3846-44a3-bcb8-258eaabf69fc (id: 0) [root@controller ~]# ip netns exec qdhcp-6967cf07-3846-44a3-bcb8-258eaabf69fc ifconfig ... tapec3789c1-0e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::f816:3eff:fe83:5c9f prefixlen 64 scopeid 0x20<link> ether fa:16:3e:83:5c:9f txqueuelen 1000 (Ethernet) RX packets 409 bytes 21138 (20.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 5 bytes 446 (446.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
NOTE : 以上步骤在每个 Enabled DHCP 的 Subnet 中都会发生 , 后文中不再赘述。
NOTE : 因为 Controller 同时兼顾网络节点 , 所以 DHCP 和 Router 相关的网络设备会在 Controller 上维护 , 否则会在专属的网络节点上维护。
3.3 跨节点启动虚拟机
openstack server create --image cirros --flavor mini --network flat-net-1 --availability-zone nova:controller VM1
openstack server create --image cirros --flavor mini --network flat-net-1 --availability-zone nova:compute VM23.3.1 网络变更

在 Flat Network 创建了两个 Port compute:nova , 表示是虚拟机 vNIC 对应的 Port。

Controller :
Port compute:nova本质还是br-int的Port tap{vnic_port_uuid},Local VLAN tag:1,Port对应的Interface设备直接创建在HostOS上。[root@controller ~]# ovs-vsctl show ... Bridge br-int ... Port "tapff2655ef-40" tag: 1 Interface "tapff2655ef-40" Port "tapec3789c1-0e" tag: 1 Interface "tapec3789c1-0e" type: internalNOTE: 上述三个Port的Local VLAN tag相同 , 表示三者同属一个Network。NOTE: 因为我们启用了基于Open vSwitch的安全组 , 所以虚拟机vNIC对应的Tap设备直连到br-int上 , 再不需要Linux Bridge qbr-XXX的中继。
Compute :
在
Compute的br-int上同样会创建虚拟机vNIC对应的Port tap{vnic_port_uuid},Local VLAN tag:1,Port对应的Interface设备直接创建在HostOS上。[root@compute ~]# ovs-vsctl show ... Bridge br-int ... Port "tap9b085083-01" tag: 1 Interface "tap9b085083-01"NOTE : 同一个
Network的Port在同一个节点 (br-int) 上的Local VLAN tag肯定是相同的 , 但处于不同节点上的Local VLAN tag未必是相同的。e.g. 节点1 :
tap{vm1_vnic_port_uuid} Local VLAN tag:1;节点2 :tap{vm2_vnic_port_uuid} Local VLAN tag:2。这并不会影响同一网络的跨节点连通性 , 因为内外
VID转换的工作是由OvS流表控制的 , 只要确保同一个网络的租户网络层VID唯一即可。NOTE : 以上步骤在每个虚拟机创建的过程中都会发生 , 后文不再赘述。
3.3.2 抓包分析
VM1 ping VM2 , 在 Compute 上抓包。监听的 Interface 是 ens224 , 它是 Network “flat-net-1” 所使用的 Provider Physical Network Label “provider” 在 Compute 节点上对应的物理网卡。只有相同 Provider Physical Network Label 对应的物理网卡才会接收到跨节点通信的流量。
[root@compute ~]# tcpdump -i ens224 -nntve -p icmp and src host 192.168.1.3
tcpdump: listening on ens224, link-type EN10MB (Ethernet), capture size 262144 bytes
fa:16:3e:8c:bc:d4 > fa:16:3e:48:c3:89, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 44899, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.3 > 192.168.1.22: ICMP echo request, id 23809, seq 0, length 643.3.3 流表分析
[root@controller ~]# ovs-ofctl dump-flows br-provider
...
cookie=0x1467957b313183c6, duration=859.522s, table=0, n_packets=118, n_bytes=11918, priority=4,in_port="phy-br-provider",dl_vlan=1 actions=strip_vlan,NORMALbr-provider 追加了一条流表 , 含义是从 br-int 过来的 VLAN tag 为 1 的数据包 , 剥除 VLAN tag , 其余数据包正常转发。通过这条流表将本地网络层原本具有 Local VLAN tag 的数据包在发出到租户网络层时重新变成 Untag 数据包 , 符合 Flat Network 特征。
4. VLAN Network
VLAN Network 是具有 802.1q Tagging ( 基于 802.1.q VLAN 协议) 的网络。每个 VLAN 都是一个基于二层实现的广播域 , 同一 VLAN 中的 虚拟机之间可以通信 , 不同 VLAN 间的虚拟机之间需要通过 L3 Router 通信。VLAN 网络是应用最广泛的网络类型 , 适合中小型网络拓扑。
4.1 关键配置
Controller# /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers = vlan mechanism_drivers = openvswitch [ml2_type_vlan] network_vlan_ranges = provider1:1:1000 # /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings = provider1:br-provider-1Compute
# /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings = provider1:br-provider-1
NOTE : VLAN Network 对应的物理网络 ( e.g. br-provider-1) 应该接入的是一个 Trunk 口 , 配置的 VLAN Ranges 应该和实际的 Trunk Ranges 一致。因为 OvS Agent 只管从 network_vlan_ranges 读取数据并落库 , 并不具有 VLAN ID 可行性判断逻辑。所以 , 如果不考虑实际情况而胡乱填写 network_vlan_ranges 的话 , 就很可能出现创建网络、虚拟机成功 , 但虚拟机却无法跨网络通信的问题。
4.2 创建 VLAN 类型租户网络 ( 管理员权限)
Create VLAN Networkopenstack network create --enable --project admin --provider-network-type vlan --provider-physical-network provider1 --provider-segment 100 vlan-net-100Create Subnetopenstack subnet create --network vlan-net-100 --dhcp --ip-version 4 --subnet-range 192.168.1.0/24 --gateway 192.168.1.1 vlan-subnet-1
4.2.1 网络变更

4.3 DHCP Network Namespace 的意义
至此 , flat-net-1、vlan-net-1 都具有相同网段 192.168.1.0/24 的 Subnet , 下面看看两个 Networks 是怎么通过 Network Namespace 来实现隔离的。

当 Enabled DHCP Subnet 时 , DHCP Agent 会创建一个 Network Namespace , 并在之内启动一个 dnsmasq 服务进程。启动指令如下 :
dnsmasq --no-hosts --no-resolv \
--except-interface=lo \
--pid-file=/var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/pid \
--dhcp-hostsfile=/var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/host \
--addn-hosts=/var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/addn_hosts \
--dhcp-optsfile=/var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/opts \
--dhcp-leasefile=/var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/leases \
--dhcp-match=set:ipxe,175 \
--bind-interfaces \
--interface=tapec3789c1-0e \
--dhcp-range=set:tag0,192.168.1.0,static,255.255.255.0,86400s \
--dhcp-option-force=option:mtu,1500 \
--dhcp-lease-max=256 \
--conf-file= \
--domain=openstacklocal--dhcp-hostsfile 保存了该 Namespace 中的 IP:DomainName 映射。e.g.
[root@controller ~]# cat /var/lib/neutron/dhcp/6967cf07-3846-44a3-bcb8-258eaabf69fc/host
fa:16:3e:83:5c:9f,host-192-168-1-2.openstacklocal,192.168.1.2
[root@controller ~]# cat /var/lib/neutron/dhcp/f31b2060-ccc0-457a-948c-d805a7680faf/host
fa:16:3e:5a:9b:b2,host-192-168-1-2.openstacklocal,192.168.1.2可见 , 即便两个 Subnets 的 IP 地址 192.168.1.2 重叠了也不会造成冲突 , 因为它们分别运行在不同的 Namespace 中。除了 DHCP 之外 , dnsmasq 还会为 Subnet 提供 DNS 解析、路由表项等功能 , 它们同样需要进行网络隔离。

简而言之 , qdhcp-XXX 隔离的意义就是为了同时支撑多个 Subnets 的 IP 核心网络服务 ( DNS、DHCP、IPAM) 。
4.4 跨节点创建虚拟机
openstack server create --image cirros --flavor mini --network vlan-net-100 --availability-zone nova:controller VM3
openstack server create --image cirros --flavor mini --network vlan-net-100 --availability-zone nova:compute VM44.4.1 网络变更

4.5 虚拟机无法获取到 IP 地址的问题
问题 : Compute 上的 VM4 无法获取到 IP 地址。
TS : 预计 VM4 无法访问到同一个 Subnet 中的 DHCP 服务。手动为 VM4 配上 IP 地址并从 VM4 ping DHCP , 抓包发现 VM4 发出的 ARP 广播无法抵达 Controller , 判断为物理网络的问题。
[root@compute ~]# tcpdump -i ens256 -nntve src host 192.168.1.6
tcpdump: listening on ens256, link-type EN10MB (Ethernet), capture size 262144 bytes
fa:16:3e:e8:47:46 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 100, p 0, ethertype ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.1.2 tell 192.168.1.6, length 28解决 : VMware 原生的 VM Network 不具有 VLAN Trunking 特性 , 所以再创建一个 trunk PortGroup 来替换 VM Network 作为 OpenStack Internal VLAN Network 的物理网络接口。


4.5.1 抓包分析
VM3 ping VM4 , 在 Compute 上抓包。再次强调 , 只有相同 Provider Physical Network Label 对应的物理网卡才会接收到跨节点通信的流量。因为 VLAN Network 的 Label 是 provider1 , 所以从配置文件可知在 Compute 上监听的 Interface 是 ens256。并且可以看到数据包均具有 VLAN tag:100。
[root@compute ~]# tcpdump -i ens256 -nntve -p icmp and src host 192.168.1.12
tcpdump: listening on ens256, link-type EN10MB (Ethernet), capture size 262144 bytes
fa:16:3e:5c:7f:fd > fa:16:3e:ba:60:dd, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 25722, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.12 > 192.168.1.11: ICMP echo request, id 24321, seq 30, length 644.5.2 流表项分析
[root@controller ~]# ovs-ofctl dump-flows br-provider-1
...
cookie=0xe68b683827a8e499, duration=1287.762s, table=0, n_packets=378, n_bytes=36670, priority=4,in_port="phy-br-pr53399e",dl_vlan=2 actions=mod_vlan_vid:100,NORMALbr-provider-1 添加了一条流表项 , 含义是将从 br-int 发送过来的具有 Local VLAN tag:2 的数据包的 VLAN tag 修改为 100。
在出报文的时候 , 完成了一次 VLAN Network 的内->外 VID 转换 :
[root@controller ~]# ovs-ofctl dump-flows br-int
...
cookie=0x4442a4feb6c04e73, duration=47473.976s, table=0, n_packets=49144, n_bytes=4911584, priority=3,in_port="int-br-pr53399e",dl_vlan=100 actions=mod_vlan_vid:2,resubmit(,60)br-int 添加了一条流表项 , 含义是将从 br-provider-1 发送过来的 VLAN tag:100 的数据包的 VLAN tag 修改为 Local VLAN tag:2。
在入报文的时候 , 完成了一次 VLAN Network 的外->内 VID 转换 :
5. VxLAN Network
VxLAN Network 是基于隧道 ( Tunnel) 技术的 Overlay 覆盖网络。所谓 Overlay 就是覆盖在三层网络之上的自定义网络 , 从用户的角度来看 , 它屏蔽了底层网络的复杂性。VxLAN 网络通过唯一的 Segmentation ID ( VNI) 来进行划分 , 将二层的数据帧封装在三层数据包进行传输 ( L2 in L3) , 从而克服 VLAN 和物理网络基础设施的限制。
VxLAN 的传输协议是 IP+UDP , 它定义了一个 MAC-in-UDP 的封装格式。在原始的 Layer 2 数据帧前加上 VxLAN Header , 然后再放到 UDP 数据报和 IP 数据包中。通过 MAC-in-UDP 封装 , VxLAN 能够在 Layer 3 网络上建立起了一条 Layer 2 的隧道。VxLAN 引入了 8-byte VXLAN Header , 其中 VNI ( VXLAN Network Identifier) 占 24-bit。VxLAN Header 和原始的 L2 Frame 被封装到 UDP 数据报中。这 24-bit 的 VNI 用于标示不同的二层网段 , 能够支持 16777216 个 LAN。

VxLAN 使用 VTEP ( VxLAN tunnel endpoint , 隧道端点) 设备来处理 VxLAN 的封装和解封装。每个 VTEP 有一个 IP interface 并配置上一个 IP 地址。VTEP 使用该 IP 作为目的 IP 或源 IP 来封装 Layer 2 frame , 并通过该 IP interface 传输和接收封装后的 VxLAN 数据包。

5.1 关键配置
Controller# /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers = vxlan mechanism_drivers = openvswitch [ml2_type_vxlan] vni_ranges = 1:1000 # /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] datapath_type = system # VTEP tunnel_bridge = br-tun local_ip = 10.0.0.1 [agent] tunnel_types = vxlanCompute# /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] datapath_type = system # VTEP tunnel_bridge = br-tun local_ip = 10.0.0.2 [agent] tunnel_types = vxlan
5.2 创建 VxLAN 类型租户网络 ( 管理员权限)
Create VxLAN Networkopenstack network create --enable --project admin --provider-network-type vxlan --provider-segment 1000 vxlan-net-1000Create Subnetopenstack subnet create --network vxlan-net-1000 --dhcp --ip-version 4 --subnet-range 172.16.1.0/24 --gateway 172.16.1.1 vxlan-subnet-1
网络变更

5.3 跨节点创建虚拟机
openstack server create --image cirros --flavor mini --network vxlan-net-1000 --availability-zone nova:controller VM5
openstack server create --image cirros --flavor mini --network vxlan-net-1000 --availability-zone nova:compute VM65.3.1 网络变更

在 VxLAN Network 上启动虚拟机时 , 除了会在 OvS br-int 上创建 vNIC 相应的 Port。同时还会分别在 Controller、Compute 上的 OvS br-tun 上创建 VTEP Port vxlan-0a000002 和 vxlan-0a000001 , 建立 VxLAN Tunnel。就相当于执行了 OvS 指令 :
# Host A
ovs-vsctl add-br br-vxlan
# Host B
ovs-vsctl add-br br-vxlan
# Host A 上添加连接到 Host B 的 Tunnel Port
ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=<host_b_ip>
# Host B 上添加连接到 Host A 的 Tunnel Port
ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=<host_a_ip>5.3.2 抓包分析
VM5 ping VM6 , 在 Compute 上抓包。如果直接抓 ICMP 协议是抓不到数据包的 , 因为从 VM5 发出的 ICMP request 包被加上 VxLAN header 最终封装成 IP+UDP 网络包进行传输。
[root@compute ~]# tcpdump -i ens192 -nnvte -p icmp
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes所以 , 我们无论是监听 IP 数据包还是 UDP 数据报都是可以抓到包的。
[root@compute ~]# tcpdump -i ens192 -nnvte -p ip
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
00:50:56:bf:18:9f > 00:50:56:bf:68:83, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 40765, offset 0, flags [DF], proto UDP (17), length 134)
10.0.0.1.52193 > 10.0.0.2.4789: VXLAN, flags [I] (0x08), vni 1000
fa:16:3e:4a:36:b5 > fa:16:3e:20:a2:69, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 41744, offset 0, flags [DF], proto ICMP (1), length 84)
172.16.1.20 > 172.16.1.9: ICMP echo request, id 23553, seq 16, length 64
00:50:56:bf:68:83 > 00:50:56:bf:18:9f, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 33286, offset 0, flags [DF], proto UDP (17), length 134)
10.0.0.2.37669 > 10.0.0.1.4789: VXLAN, flags [I] (0x08), vni 1000
fa:16:3e:20:a2:69 > fa:16:3e:4a:36:b5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 60034, offset 0, flags [none], proto ICMP (1), length 84)
172.16.1.9 > 172.16.1.20: ICMP echo reply, id 23553, seq 16, length 64
[root@compute ~]# tcpdump -i ens192 -nnvte -p udp
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
00:50:56:bf:18:9f > 00:50:56:bf:68:83, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 48222, offset 0, flags [DF], proto UDP (17), length 134)
10.0.0.1.52193 > 10.0.0.2.4789: VXLAN, flags [I] (0x08), vni 1000
fa:16:3e:4a:36:b5 > fa:16:3e:20:a2:69, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 44342, offset 0, flags [DF], proto ICMP (1), length 84)
172.16.1.20 > 172.16.1.9: ICMP echo request, id 23553, seq 35, length 64
00:50:56:bf:68:83 > 00:50:56:bf:18:9f, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 40124, offset 0, flags [DF], proto UDP (17), length 134)
10.0.0.2.37669 > 10.0.0.1.4789: VXLAN, flags [I] (0x08), vni 1000
fa:16:3e:20:a2:69 > fa:16:3e:4a:36:b5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 62271, offset 0, flags [none], proto ICMP (1), length 84)
172.16.1.9 > 172.16.1.20: ICMP echo reply, id 23553, seq 35, length 645.3.3 流表分析
NOTE : 下述以 Controller 的 OvS br-tun 流表项为了 , Compute 类同。

table 0 : 第一张流表的编号 , 所有网络包都要通过 table 0。table 0 在这里主要用作数据包来源的划分 (内/外) 。
cookie=0xfaee3d9234e8f8fa, duration=431758.646s, table=0, n_packets=2779206, n_bytes=224498704, priority=1,in_port="patch-int" actions=resubmit(,2)
cookie=0xfaee3d9234e8f8fa, duration=2108.689s, table=0, n_packets=1364, n_bytes=129334, priority=1,in_port="vxlan-0a000002" actions=resubmit(,4)
cookie=0xfaee3d9234e8f8fa, duration=431758.644s, table=0, n_packets=0, n_bytes=0, priority=0 actions=drop从
Port patch-int( 内部) 发送过来的数据包resubmit到table 2处理。从
Port vxlan-0a000002( 外部) 发送过来的数据包resubmit到table 4处理。默认丢弃数据包。
5.3.4 处理外部送入的数据
table 4 : 作为对外部数据包的处理 , 主要有两点。一个是进行 “外=>内 VID 转换” , 另一个是进行 MAC 地址表学习 ( 转交到 table 10 完成) 。
cookie=0xfaee3d9234e8f8fa, duration=369.313s, table=4, n_packets=193, n_bytes=18272, priority=1,tun_id=0x3e8 actions=mod_vlan_vid:4,resubmit(,10)
cookie=0xfaee3d9234e8f8fa, duration=496998.621s, table=4, n_packets=0, n_bytes=0, priority=0 actions=drop进行
VxLAN Network的 “外=>内 VID 转换” , 然后 resubmit 到 table 10 处理。默认丢弃数据包。
table 10 : 进行 MAC 地址表学习 , 学习外部虚拟机对本地虚拟机访问数据包的 MAC 地址 , 本质是生成数据包返程 ( 本地=>远程) 的转发规则。
cookie=0xfaee3d9234e8f8fa, duration=496998.615s, table=10, n_packets=44971, n_bytes=4269468, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xfaee3d9234e8f8fa,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:OXM_OF_IN_PORT[]),output:"patch-int"learn(table=20,hard_timeout=300,priority=1,cookie=0xfaee3d9234e8f8fa,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:OXM_OF_IN_PORT[])往 table 20 中添加对返程包的正常转发规则
learn():MAC学习函数。table=20: 指定学习表编号 , 即往table 20添加返程包流表项。NXM_OF_VLAN_TCI[0..11], 相当于NXM_OF_VLAN_TCI[0..11]=NXM_OF_VLAN_TCI[0..11]: ( 匹配条件) 流表项匹配的网络包 VLAN ID 和当前处理的网络包VLAN ID一样 ( e.g. VLAN tag:4) 。NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]: ( 匹配条件) 流表项匹配的网络包目的 MAC 地址和当前处理的网络包源MAC地址一样 ( e.g. 从Compute:VM 6发送过来的访问数据包的源MAC地址为fa:16:3e:20:a2:69) 。load (load:value−>dst[start…end]):: ( 动作) 写入区域。因为是返程包 , 所以下述含义是进行 “内=>外VID转换” , 网络包发送出去的时候 ,
VLAN ID设为0并设置Tunnel ID。load:0->NXM_OF_VLAN_TCI[]: 将0写入流表项匹配网络包的VLAN ID。load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]: 将当前的TUN ID写入到流表项匹配网络包的TUN ID。
output (output:src[start…end]): ( 动作) 输入端口output:OXM_OF_IN_PORT[]: 将流表项匹配网络包从当前处理的网络包的 in_port ( 输入端口) 输出。 ( 从同一个端口返程)
output:"patch-int": “外=>内 VID 转换” 之后的数据包转发到 br-int。
5.3.5 处理内部发出的数据
table 2 : 从内部 OvS br-int 发送过来的网络包主要是单播包和广播 ( 组播) 包两种类型。table 2 对这两种类型的网络包进行分发。
单播 ( Unicast) : 在发送者和每个接收者之间实现点对点网络连接。如果一台发送者同时给多个的接收者传输相同的数据 , 也必须相应的复制多份相同的数据包。如果有大量主机希望获得数据包的同一份拷贝时 , 将导致发送者负担沉重、延迟长、网络拥塞;
广播 ( Broadcasting) : 是指在 IP 子网内广播数据包 , 所有在子网内部的主机都将收到这些数据包。广播意味着网络向子网每一个主机都投递一份数据包 , 不论这些主机是否乐于接收该数据包。所以广播的使用范围非常小 , 只在本地子网内有效 , 通过路由器、VLAN 和网络设备控制广播传输区域。
组播 ( Multicast) : 在发送者和每个接收者之间实现点对多点网络连接。如果一台发送者同时给多个接收者传输相同的数据 , 也只需复制一份相同的数据包。它提高了数据传送效率 , 减少了骨干网络出现拥塞的可能性。组播解决了单播和广播方式效率低的问题。当网络中的某些用户需求特定信息时 , 组播源 ( 即组播信息发送者) 仅发送一次信息 , 组播路由器借助组播路由协议为组播数据包建立树型路由 , 被传递的信息在尽可能远的分叉路口才开始复制和分发。
cookie=0xfaee3d9234e8f8fa, duration=431758.641s, table=2, n_packets=929179, n_bytes=104019186, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xfaee3d9234e8f8fa, duration=431758.637s, table=2, n_packets=1850027, n_bytes=120479518, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)br-int发送过来的单播包00:00:00:00:00:00/01:00:00:00:00:00,resubmit到table 20处理。br-int发送过来的多播或广播包01:00:00:00:00:00/01:00:00:00:00:00,resubmit到table 22处理。
table 20 : 处理 br-int 发送过来的单播包 , 这类数据包通常有明确的 “点对点连接” 信息 , 例如 : MAC 地址。
cookie=0xfaee3d9234e8f8fa, duration=226.964s, table=20, n_packets=149, n_bytes=15172, priority=2,dl_vlan=4,dl_dst=fa:16:3e:20:a2:69 actions=strip_vlan,load:0x3e8->NXM_NX_TUN_ID[],output:"vxlan-0a000002"
cookie=0xfaee3d9234e8f8fa, duration=216.630s, table=20, n_packets=0, n_bytes=0, hard_timeout=300, priority=1,vlan_tci=0x0004/0x0fff,dl_dst=fa:16:3e:20:a2:69 actions=load:0->NXM_OF_VLAN_TCI[],load:0x3e8->NXM_NX_TUN_ID[],output:"vxlan-0a000002"
cookie=0xfaee3d9234e8f8fa, duration=496998.612s, table=20, n_packets=1067130, n_bytes=117276295, priority=0 actions=resubmit(,22)将
Local VLAN tag:3, 目的MAC地址为fa:16:3e:b2:38:82的网络包去掉VLAN tag, 设定VxLAN VNI为0x3e8并输出到Port vxlan-0a000002。第二条流表项 , 与第一条类似。区别在于第二条流表项是自
table 10学习而来的。主要是针对MAC地址的学习 , 所以一把被称为MAC学习表 (MAC Learning table) 。对于没有学习到规则的数据包 ,
resubmit到table 22处理。
table 22 : 处理 br-int 发送过来的广播或多播包。
cookie=0xfaee3d9234e8f8fa, duration=226.968s, table=22, n_packets=68, n_bytes=6436, priority=1,dl_vlan=4 actions=strip_vlan,load:0x3e8->NXM_NX_TUN_ID[],output:"vxlan-0a000002"
cookie=0xfaee3d9234e8f8fa, duration=496998.611s, table=22, n_packets=3195237, n_bytes=255860585, priority=0 actions=drop将
Local VLAN tag:4的数据包去除VLAN tag, 添加上Tunnel ID并由Port vxlan-0a000002输出。默认丢弃数据包。
6. 基于 OvS 的 L3 Router
Neutron L3 Router 是一个 Service Plugins ( 扩展插件) , 由 L3 Agent 提供服务。默认情况下 ( 非 DVR) L3 Agent 运行在网络节点 , 所以跨网络的三层转发流量都会途经网络节点。
NOTE : 本文环境中 Controller 重叠了控制节点、网络节点、计算节点。
[root@controller ~]# openstack network agent list
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+
| ID | Agent Type | Host | Availability Zone | Alive | State | Binary |
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+
| 41925586-9119-4709-bc23-4668433bd413 | Metadata agent | controller | None | :-) | UP | neutron-metadata-agent |
| 43281ac1-7699-4a81-a5b6-d4818f8cf8f9 | Open vSwitch agent | controller | None | :-) | UP | neutron-openvswitch-agent |
| b815e569-c85d-4a37-84ea-7bdc5fe5653c | DHCP agent | controller | nova | :-) | UP | neutron-dhcp-agent |
| d1ef7214-d26c-42c8-ba0b-2a1580a44446 | L3 agent | controller | nova | :-) | UP | neutron-l3-agent |
| f55311fc-635c-4985-ae6b-162f3fa8f886 | Open vSwitch agent | compute | None | :-) | UP | neutron-openvswitch-agent |
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+6.1 关键配置
# /etc/neutron/neutron.conf
[DEFAULT]
...
service_plugins = router
# /etc/neutron/l3_agent.ini
[DEFAULT]
interface_driver = openvswitch
external_network_bridge = br-exinterface_driver: 指定Mechanism Driver类型。external_network_bridge: 指定连接外部网络 ( 公网) 的网桥 , 默认是OvS br-ex。
6.2 创建集中式 Router
openstack router create --enable --centralized --project admin router16.3 添加相同网段的 Subnets 到 Router
每创建一个 Router 实例都会在网络节点创建一个 qrouter-{router_uuid} Network Namespace , 用于隔离路由表项。e.g.
[root@controller ~]# ip netns exec qrouter-703b2e70-af1b-4945-b0ff-f5efb1e5164d route -nne
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface但需要注意的是 , 不同 Router 可以加入相同的网段的 Subnet , 因为 Router 之间是隔离的。但相同网段的 Subnet 不应该加入到同一个 Router , 因为这样没有跨网段转发的意义 , 而且还会造成 IP 地址重叠的问题。下面我们首先尝试将都具有 192.168.1.0/24 网段的 Subnets vlan-subnet-1 和 flat-subnet-1 加入到 router1。
[root@controller ~]# openstack subnet list
+--------------------------------------+----------------+--------------------------------------+----------------+
| ID | Name | Network | Subnet |
+--------------------------------------+----------------+--------------------------------------+----------------+
| 85c68fdd-85f7-4f19-9538-ff82b5c8c5f0 | vxlan-subnet-1 | be8ca1f5-f243-4640-b7e1-4107fe16dd70 | 172.16.1.0/24 |
| a6a0e4a3-60e9-4bcf-9c7b-210807637571 | flat-subnet-1 | 6967cf07-3846-44a3-bcb8-258eaabf69fc | 192.168.1.0/24 |
| d3cf6b12-f78b-42c0-9e0b-29ec8915007e | vlan-subnet-1 | f31b2060-ccc0-457a-948c-d805a7680faf | 192.168.1.0/24 |
+--------------------------------------+----------------+--------------------------------------+----------------+
[root@controller ~]# openstack router add subnet router1 vlan-subnet-1
[root@controller ~]# openstack router add subnet router1 flat-subnet-1虽然整个过程不会触发异常中断 , 但实际上只有 vlan-subnet-1 的网关接口 ( Port) 192.168.1.1 成功加入了 Router。而 flat-subnet-1 则属于 “无效添加”。

[root@controller ~]# openstack router show router1
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
| admin_state_up | UP |
| availability_zone_hints | |
| availability_zones | nova |
| created_at | 2019-04-08T06:24:09Z |
| description | |
| distributed | False |
| external_gateway_info | None |
| flavor_id | None |
| ha | False |
| id | 703b2e70-af1b-4945-b0ff-f5efb1e5164d |
| interfaces_info | [{"subnet_id": "d3cf6b12-f78b-42c0-9e0b-29ec8915007e", "ip_address": "192.168.1.1", "port_id": "2e95d893-af62-40dd-9560-3fbe39e9b217"}] |
| name | router1 |
| project_id | a2b55e37121042a1862275a9bc9b0223 |
| revision_number | 3 |
| routes | |
| status | ACTIVE |
| tags | |
| updated_at | 2019-04-08T06:45:07Z |
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+6.4 添加不同网段的 Subnets 到 Router
[root@controller ~]# openstack subnet list
+--------------------------------------+----------------+--------------------------------------+----------------+
| ID | Name | Network | Subnet |
+--------------------------------------+----------------+--------------------------------------+----------------+
| 85c68fdd-85f7-4f19-9538-ff82b5c8c5f0 | vxlan-subnet-1 | be8ca1f5-f243-4640-b7e1-4107fe16dd70 | 172.16.1.0/24 |
| a6a0e4a3-60e9-4bcf-9c7b-210807637571 | flat-subnet-1 | 6967cf07-3846-44a3-bcb8-258eaabf69fc | 192.168.1.0/24 |
| d3cf6b12-f78b-42c0-9e0b-29ec8915007e | vlan-subnet-1 | f31b2060-ccc0-457a-948c-d805a7680faf | 192.168.1.0/24 |
+--------------------------------------+----------------+--------------------------------------+----------------+
[root@controller ~]# openstack router add subnet router1 vlan-subnet-1
[root@controller ~]# openstack router add subnet router1 vxlan-subnet-16.4.1 网络变更

分别在两个
Networks创建了Gateway Port network:router_interface。

两个
Networks的Gateway Port都接入了Router router1, 类型为 “内部接口”。
Controller的br-int上添加了qr-{vlan_subnet_gw_port_uuid}和qr-{vxlan_subnet_gw_port_uuid}两个设备 , 它们分别具有各自Network的Local VLAN tag。[root@controller ~]# ovs-vsctl show ... Bridge br-int ... Port "qr-2e95d893-af" tag: 2 Interface "qr-2e95d893-af" type: internal ... Port "qr-5a408fb2-e3" tag: 4 Interface "qr-5a408fb2-e3" type: internalqr-{vlan_subnet_gw_port_uuid}和qr-{vxlan_subnet_gw_port_uuid}被隔离在Network Namespace qrouter-{router_uuid}中 , 并配置上了各自的Network Gateway IP地址。即通过qr-xxx设备 , 将两个Networks接入同一个 “路由空间” 中。[root@controller ~]# ip netns exec qrouter-703b2e70-af1b-4945-b0ff-f5efb1e5164d ifconfig ... qr-2e95d893-af: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::f816:3eff:fe4c:c8a8 prefixlen 64 scopeid 0x20<link> ether fa:16:3e:4c:c8:a8 txqueuelen 1000 (Ethernet) RX packets 2250 bytes 181720 (177.4 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 19 bytes 1474 (1.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 qr-5a408fb2-e3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 172.16.1.1 netmask 255.255.255.0 broadcast 172.16.1.255 inet6 fe80::f816:3eff:fef3:aec2 prefixlen 64 scopeid 0x20<link> ether fa:16:3e:f3:ae:c2 txqueuelen 1000 (Ethernet) RX packets 1640 bytes 133168 (130.0 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 19 bytes 1474 (1.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0因为两个
Networks都是直接接入Router的 , 所以在Namespace自动添加了两台直连路由 (U) 表项 :[root@controller ~]# ip netns exec qrouter-703b2e70-af1b-4945-b0ff-f5efb1e5164d route -nne Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 172.16.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-5a408fb2-e3 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-2e95d893-af
6.4.2 跨网段访问原理
Network 的跨网段访问流量会经由 qr-XXX Interface 涉设备进入 Network Namespace qrouter-XXX。然后内核 TCP/IP 协议栈根据 qrouter-XXX 中的路由规则对访问流量进行路由、改写数据帧的源 MAC 地址和目的 MAC 地址之后 , 流量再经过路由目的 qr-YYY Interface 进入另一个 Network。从而实现不同网段之间的路由转发。

7. 基于 OvS 的 External Network
7.1 关键配置
# /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
type_drivers = flat
mechanism_drivers = openvswitch
[ml2_type_flat]
flat_networks = external
[ovs]
datapath_type = system
bridge_mappings = external:br-ex7.2 创建外部 ( 运营商) 网络
在前文提到过 External Network 是一种特殊的 ( 具有 Floating IP 功能) 运营商网络 , Neutron 通过记录运营商的 “网络核心三要素” 来使用它。受限于本文的实践环境 , 运营商网络的物理网卡 ens161 连接在 vCenter VM Network ( Untag Network) , 所以我们将采用 Flat 网络类型来创建 External Network。
Create External Networkopenstack network create --enable --project admin --external --provider-network-type flat --provider-physical-network external ext_netCreate Subnet:External Network的网段记录了运营商网络的网段 ,External Network的Gateway IP地址记录了运营商网络的Gateway IP地址 ( 第二层网关IP地址) 。openstack subnet create --network ext_net --ip-version 4 --no-dhcp --subnet-range 172.18.22.0/24 --allocation-pool start=172.18.22.241,end=172.18.22.250 --gateway 172.18.22.1 ext_subnet-1
NOTE : 因为运营商网络 IP 地址资源有限 , 所以建议限定 External Subnet 的 IP 地址池。而且外部网络一般不直接为虚拟机分配 IP 地址 , 所以也不需要 Enable Subnet DHCP。
7.3 创建第一层网关 ( Router)
因为要连接第二层网关 ( 运营商物理路由器) , 所以我们需要创建外部网络。而第一层网关 ( Router) 是通过 Neutron 创建的 , 是接入外部网络的中转站。

或者执行 CLI 创建 :
openstack router create --enable --centralized --project admin router-ext
openstack router set --enable-snat --external-gateway ext_net router-ext网络变更
在外部网络
ext_net上添加了Pot network:router_gateway( 区别于network:router_interface) 。
将
Pot network:router_gateway接入第一层网关router-ext, 是第一层网关接入外部网络的端口。
在
Controller的br-int上添加了Port qg-{router_gateway_port_uuid}设备。[root@controller ~]# ovs-vsctl show ... Bridge br-int ... Port "qg-424af937-c4" tag: 8 Interface "qg-424af937-c4" type: internal ...Port qg-{router_gateway_port_uuid}的Interface设备创建在Router对应的Network Namespace中 , 设置上了第一层网关IP地址172.18.22.245。[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d ifconfig ... qg-424af937-c4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.22.245 netmask 255.255.255.0 broadcast 172.18.22.255 inet6 fd00:310:22:0:f816:3eff:fea0:276a prefixlen 64 scopeid 0x0<global> inet6 fe80::f816:3eff:fea0:276a prefixlen 64 scopeid 0x20<link> ether fa:16:3e:a0:27:6a txqueuelen 1000 (Ethernet) RX packets 2438 bytes 124540 (121.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 29 bytes 1786 (1.7 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0第一层网关的
Network Namespace中 , 因为qg-XXX直接连接着外部网络 , 所以存在一条直连路由 (U) 表项。又因为第一层网关的最终目的是将流表转发到第二层网关 , 所以默认的下一跳 ( 默认网关路由表项UG) 是第二层网关IP地址。[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d route -nne Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 172.18.22.1 0.0.0.0 UG 0 0 0 qg-424af937-c4 172.18.22.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-424af937-c4从
router_ext的Namespace可以ping通第二次网关的IP地址。[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d ping 172.18.22.1 PING 172.18.22.1 (172.18.22.1) 56(84) bytes of data. 64 bytes from 172.18.22.1: icmp_seq=1 ttl=255 time=21.4 ms从外部网络也可以
ping通过第一层网关的外部接口的IP地址。╭─mickeyfan@localhost ~ ╰─$ ping 172.18.22.245 PING 172.18.22.245 (172.18.22.245): 56 data bytes 64 bytes from 172.18.22.245: icmp_seq=0 ttl=62 time=10.032 ms
NOTE : 旧版本的 qg-XXX 是挂载在 br-ex 上的 , 新版本则挂载在 br-int。虽然位置改变了 , 但功能不变。
7.4 访问外部网络
任意租户网络接入到第一层网关 ( router-ext) 之后 , 该租户网络的虚拟机就可以访问外网了。如果同时还希望外部网络可以访问租户虚拟机 , 那么就为虚拟机分配 Floating IP 地址即可。
将租户网络
flat-subnet-1接入第一层网关openstack router add subnet router-ext flat-subnet-1创建
Floating IPopenstack floating ip create --project admin ext_net将
Floating IP绑定到虚拟机VM1openstack server add floating ip VM1 172.18.22.242
网络变更

租户网络添加了
Port network:router_interface。
在
Controller的br-int上添加了相应的Port qr-{router_interface_port_uuid}, 与租户网络具有相同的Local VLAN tag:1。[root@controller ~]# ovs-vsctl show ... Bridge br-int ... Port "qr-6b394d55-47" tag: 1 Interface "qr-6b394d55-47" type: internalqr-{router_interface_port_uuid}的Interface设备创建在第一层网关的Network Namespace中 , 租户网络通过qr-XXX设备接入特定的 “路由空间”。[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d ifconfig ... qg-424af937-c4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.22.245 netmask 255.255.255.0 broadcast 172.18.22.255 inet6 fd00:310:22:0:f816:3eff:fea0:276a prefixlen 64 scopeid 0x0<global> inet6 fe80::f816:3eff:fea0:276a prefixlen 64 scopeid 0x20<link> ether fa:16:3e:a0:27:6a txqueuelen 1000 (Ethernet) RX packets 13279 bytes 680632 (664.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 75 bytes 5842 (5.7 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 qr-6b394d55-47: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::f816:3eff:feeb:f148 prefixlen 64 scopeid 0x20<link> ether fa:16:3e:eb:f1:48 txqueuelen 1000 (Ethernet) RX packets 4751 bytes 246730 (240.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 198 bytes 17998 (17.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0第一层网关的路由表项 :
[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d route -nne Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 172.18.22.1 0.0.0.0 UG 0 0 0 qg-424af937-c4 172.18.22.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-424af937-c4 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-6b394d55-47默认网关路由 (
UG) , 跳到第二层网关。外部网络直连路由 (
U) , 转发至外部网络。租户网络直连路由 (
U) , 转发至内部租户网络。
命名空间中的
Floating IP NAT iptables规则 :[root@controller ~]# ip netns exec qrouter-ccce705c-5b0f-4bf7-a82d-94f143a88d3d iptables -nvL -t nat Chain neutron-l3-agent-OUTPUT (1 references) pkts bytes target prot opt in out source destination 2 168 DNAT all -- * * 0.0.0.0/0 172.18.22.242 to:192.168.1.3 Chain neutron-l3-agent-PREROUTING (1 references) pkts bytes target prot opt in out source destination 16 960 REDIRECT tcp -- qr-+ * 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 2 168 DNAT all -- * * 0.0.0.0/0 172.18.22.242 to:192.168.1.3 Chain neutron-l3-agent-float-snat (1 references) pkts bytes target prot opt in out source destination 0 0 SNAT all -- * * 192.168.1.3 0.0.0.0/0 to:172.18.22.242虚拟机可以访问外部网络 :

外部网络可以通过
Floating IP访问虚拟机 :╭─mickeyfan@localhost ~ ╰─$ ping 172.18.22.242 PING 172.18.22.242 (172.18.22.242): 56 data bytes 64 bytes from 172.18.22.242: icmp_seq=0 ttl=61 time=218.465 ms
问题 : 外部网络无法 ping 通过 Floating IP , 虚拟机也无法 ping 通外网。
TS : 在第一次网关的 Network Namespace 中可以 ping 通第二层外部网关 IP 地址 172.18.22.1 , 也可以 ping 通过租户网络的网关 IP 地址 192.168.1.1。唯独 ping 不通虚拟机的 Fixed IP 地址 192.168.1.3 , 所以判断为虚拟机安全组规则导致的。
原因 : 需要为虚拟机配置安全组规则 , 允许 ICMP 访问。
7.5 外部网络访问原理
租户网络通过 qr-XXX 将外部网络访问流量输入第一层网关的路由空间 , 然后内核 TCP/IP 协议栈根据 qrouter-XXX 中的路由表项进行跨网络 ( 租户网络 => 外部网络) 路由。如果是外部网络访问 , 就将数据包交给 qg-XXX , 根据为 qg-XXX 设置的 iptables NAT 规则对数据包进行网络地址转换 ( Floating IP 的实现原理) 。再经由 br-int、br-ex 之间的 Patch Peer 将数据包发送给 OvS br-ex , 最终由挂载到 br-ex 的物理网卡送出外部网络。
参考链接
我非要捅穿这 Neutron ( 四) Open vSwitch in Neutron - 云物互联 - 博客园 (cnblogs.com)
《我非要捅穿这 Neutron ( 一) 网络实现模型篇》 《我非要捅穿这 Neutron ( 二) 上层资源模型篇》 《我非要捅穿这 Neutron ( 三) 架构分析与代码实现篇 ( 基于 OpenStack Rocky) 》 《手动部署 OpenStack Rocky 双节点》