我非要捅穿这 Neutron(四)Open vSwitch in Neutron
目录
文章目录
前文列表
《我非要捅穿这 Neutron(一)网络实现模型篇》
《我非要捅穿这 Neutron(二)上层资源模型篇》
《我非要捅穿这 Neutron(三)架构分析与代码实现篇(基于 OpenStack Rocky)》
《手动部署 OpenStack Rocky 双节点》
OvS In Neutron 网络拓扑
OvS In Neutron 网络实现模型
双节点混合平面网络
双节点网络拓扑
NOTE:本文中的网络设计只是为了方便描述,仅作参考,实际的网络设计会更加灵活。
Controller:
- ens160:172.18.22.231(OpenStack Management Network)
- ens161:br-ex
- ens192: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 ens256
Compute
- 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 ens256
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=NORMAL
OvS 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=drop
OvS 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=NORMAL
NOTE:当 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=NORMAL
OvS 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=NORMAL
NOTE:无论是 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 的管理范畴,所以需要自己手动创建并挂载一张物理网卡。
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 = openvswitch
- Compute
[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
Flat Network(扁平网络)
Flat Network 的特点是无 VLAN Tagging,所以每个 Flat Network 独占一张物理网卡,在生产环境中很少见。
NOTE:一个 Provider Physical Network(运营商物理网络)只能创建一个 Flat Network,否则会触发异常。
关键配置
- 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-provider
provider:br-provider
中的 provider 只是一个 Label (String),用于在跨节点、跨网络类型场景中标识同一个 Provider Physical Network(运营商物理网络),是一种 friendly 的用户操作方式。而 br-provider 则是手动创建的 OVS Provider Bridge,挂载了一张物理网卡,作为 Flat Network 的租户层网桥。
创建 Flat 类型租户网络(管理员权限)
- Create Flat Network
openstack network create --enable --project admin --provider-network-type flat --provider-physical-network provider flat-net-1
- Create Subnet
openstack 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
网络变更
- 因为 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: internal
- Port 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 上维护,否则会在专属的网络节点上维护。
跨界点启动虚拟机
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 VM2
网络变更
- 在 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: internal
NOTE:上述三个 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:以上步骤在每个虚拟机创建的过程中都会发生,后文不再赘述。
抓包分析
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 64
流表分析
[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,NORMAL
br-provider 追加了一条流表,含义是从 br-int 过来的 VLAN tag 为 1 的数据包,剥除 VLAN tag,其余数据包正常转发。通过这条流表将本地网络层原本具有 Local VLAN tag 的数据包在发出到租户网络层时重新变成 Untag 数据包,符合 Flat Network 特征。
VLAN Network
VLAN Network 是具有 802.1q Tagging(基于 802.1.q VLAN 协议) 的网络。每个 VLAN 都是一个基于二层实现的广播域,同一 VLAN 中的 虚拟机之间可以通信,不同 VLAN 间的虚拟机之间需要通过 L3 Router 通信。VLAN 网络是应用最广泛的网络类型,适合中小型网络拓扑。
关键配置
- 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-1
- Compute
# /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 的话,就很可能出现创建网络、虚拟机成功,但虚拟机却无法跨网络通信的问题。
创建 VLAN 类型租户网络(管理员权限)
- Create VLAN Network
openstack network create --enable --project admin --provider-network-type vlan --provider-physical-network provider1 --provider-segment 100 vlan-net-100
- Create Subnet
openstack 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
网络变更
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)。
跨界点创建虚拟机
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 VM4
网络变更
虚拟机无法获取到 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 的物理网络接口。
抓包分析
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 64
流表项分析
[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,NORMAL
br-provider-1 添加了一条流表项,含义是将从 br-int 发送过来的具有 Local VLAN tag:2 的数据包的 VLAN tag 修改为 100。
在出报文的时候,完成了一次 VLAN Network 的内->外 VID 转换:
本地网络层 | 租户网络层 |
---|---|
VLAN 2 | VLAN 100 |
[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 转换:
租户网络层 | 本地网络层 |
---|---|
VLAN 100 | VLAN 2 |
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 数据包。
关键配置
- 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 = vxlan
- Compute
# /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
NOTE:上文介绍过 VxLAN 是 L2 in L3 的网络,这意味着 VxLAN Network 正常运作的关键在于隧道端点(VTEP)及其 IP 地址的连通性,而不需要进行额外的 “br-provider” 配置。
创建 VxLAN 类型租户网络(管理员权限)
- Create VxLAN Network
openstack network create --enable --project admin --provider-network-type vxlan --provider-segment 1000 vxlan-net-1000
- Create Subnet
openstack 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
网络变更
跨界点创建虚拟机
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 VM6
网络变更
在 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>
抓包分析
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 64
流表分析
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 处理。
- 默认丢弃数据包。
处理外部送入的数据
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 处理。
租户网络层 | 本地网络层 |
---|---|
VNI 1000 | VLAN 4 |
- 默认丢弃数据包。
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。
处理内部发出的数据
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 输出。
- 默认丢弃数据包。
基于 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 |
+--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+
关键配置
# /etc/neutron/neutron.conf
[DEFAULT]
...
service_plugins = router
# /etc/neutron/l3_agent.ini
[DEFAULT]
interface_driver = openvswitch
external_network_bridge = br-ex
interface_driver
:指定 Mechanism Driver 类型。external_network_bridge
:指定连接外部网络(公网)的网桥,默认是 OvS br-ex。
创建集中式 Router
openstack router create --enable --centralized --project admin router1
添加相同网段的 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 |
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+
添加不同网段的 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-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: internal
- qr-{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
跨网段访问原理
Network 的跨网段访问流量会经由 qr-XXX Interface 涉设备进入 Network Namespace qrouter-XXX。然后内核 TCP/IP 协议栈根据 qrouter-XXX 中的路由规则对访问流量进行路由、改写数据帧的源 MAC 地址和目的 MAC 地址之后,流量再经过路由目的 qr-YYY Interface 进入另一个 Network。从而实现不同网段之间的路由转发。
基于 OvS 的 External Network
关键配置
# /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-ex
创建外部(运营商)网络
在前文提到过 External Network 是一种特殊的(具有 Floating IP 功能)运营商网络,Neutron 通过记录运营商的 “网络核心三要素” 来使用它。受限于本文的实践环境,运营商网络的物理网卡 ens161 连接在 vCenter VM Network(Untag Network),所以我们将采用 Flat 网络类型来创建 External Network。
- Create External Network
openstack network create --enable --project admin --external --provider-network-type flat --provider-physical-network external ext_net
- Create 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。
创建第一层网关(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。虽然位置改变了,但功能不变。
访问外部网络
任意租户网络接入到第一层网关(router-ext)之后,该租户网络的虚拟机就可以访问外网了。如果同时还希望外部网络可以访问租户虚拟机,那么就为虚拟机分配 Floating IP 地址即可。
- 将租户网络 flat-subnet-1 接入第一层网关
openstack router add subnet router-ext flat-subnet-1
- 创建 Floating IP
openstack floating ip create --project admin ext_net
- 将 Floating IP 绑定到虚拟机 VM1
openstack 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: internal
- qr-{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 访问。
外部网络访问原理
租户网络通过 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的更多相关文章
- 我非要捅穿这 Neutron(一)网络实现模型篇
目录 文章目录 目录 前言 传统网络到虚拟化网络的演进 单一平面网络到混合平面网络的演进 Neutron 简述 Neutron 的网络实现模型 计算节点网络实现模型 内外 VID 转换 网络节点网络实 ...
- 我非要捅穿这 Neutron(三)架构分析与代码实现篇(基于 OpenStack Rocky)
目录 文章目录 目录 Neutron 的软件架构分析与实现 Neutron Server 启动流程 获取 WSGI Application Core API & Extension API C ...
- 我非要捅穿这 Neutron(二)上层资源模型篇
目录 文章目录 目录 Neutron 的资源模型 Network 运营商网络和租户网络 创建运营商网络 创建租户网络 创建外部网络 Network 小结 Subnet IP 核心网络服务 Subnet ...
- 理解 OpenStack 高可用(HA)(3):Neutron 分布式虚拟路由(Neutron Distributed Virtual Routing)
本系列会分析OpenStack 的高可用性(HA)概念和解决方案: (1)OpenStack 高可用方案概述 (2)Neutron L3 Agent HA - VRRP (虚拟路由冗余协议) (3)N ...
- 理解 neutron(15):Neutron linux-bridge-agent 创建 linux bridge 的简要过程
学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...
- 理解 neutron(15):Neutron Linux Bridge + VLAN/VXLAN 虚拟网络
学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...
- 重学c#系列——c# 托管和非托管资源与代码相关(四)
前言 这是续第三节. 概况垃圾回收与我们写代码的关系: 强引用和弱引用 针对共享 Web 承载优化 垃圾回收和性能 应用程序域资源监视 正文 强引用和弱引用 垃圾回收器不能回收仍在引用的对象的内存-- ...
- SQL Server-聚焦聚集索引对非聚集索引的影响(四)
前言 在学习SQL 2012基础教程过程中会时不时穿插其他内容来进行讲解,相信看过SQL Server 2012 T-SQL基础教程的童鞋知道前面写的所有内容并非都是摘抄书上内容,如若是这样那将没有任 ...
- 手动部署 OpenStack Rocky 双节点
目录 文章目录 目录 前言 OpenStack 架构 Conceptual architecture Logical architecture 网络选型 Networking Option 1: Pr ...
随机推荐
- 《Linux就该这么学》day1-day2
ps:原谅我的书法出自鲁迅的<野草> <Linux就该这么学>书本介绍: 本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极 ...
- 取消任务(Task)
private static void TaskCancelDemo() { //向应该被取消的 System.Threading.CancellationToken 发送信号 Cancellatio ...
- GIT和SVN的区别(面试)
Cit是分布式,而SVN不是分布式 存储内容的时候,Git按元数据方式存储,而SVN是按文件 Git没有一个全局版本号,SVN有,目前为止这是SVN相比Git缺少的最大的一个特征 Git的内容完整性要 ...
- WPF界面开发技巧大放送!DevExpress WPF格式化日期时间值
DevExpress广泛应用于ECM企业内容管理. 成本管控.进程监督.生产调度,在企业/政务信息化管理中占据一席重要之地.通过DevExpress WPF Controls,您能创建有着强大互动功能 ...
- k8sStatefulSet控制器
一.StatefulSet概述 应用程序存在有状态和无状态两种类别,因为无状态类应用的pod资源可按需增加.减少或重构,而不会对由其提供的服务产生除了并发相应能力之外的其他严重影响.pod资源的常用控 ...
- php类知识---最疯狂的魔术方法serialize,_sleep,__wakeup,unserialize,__autoload,__clone
serialize-----把实例化的对象写入文件 __sleep 调用serialize时触发 <?php class mycoach { public function __construc ...
- [Functional Programming] propSatisfies with implies
// implies :: ((a -> Boolean), (a -> Boolean)) -> a -> Boolean const implies = (p, q) =& ...
- Mysql历史版本下载地址
Mysql历史版本下载地址:http://downloads.mysql.com/archives/community/
- Python 特点
优点 简单 -- Python 是一种代表简单主义思想的语言.阅读一个良好的 Python 程序就感觉像是在读英语一样,尽管这个英语的要求非常严格!Python 的这种伪代码本质是它最大的优点之一.它 ...
- CSS的水平居中和垂直居中
水平居中如果不太熟悉盒子模型的话属实不太好理解,其实就是控制其他属性来让border之内的内容被控制在父容器中间就行了,最经典的就是使用{margin: 0 auto}了,控制其上下外边框为0,左右 ...