一.前言
本文主要介绍了如何分析Neutron下的VM网络数据流,并结合这些方法如何去进行网络分析和故障解决。限于篇幅、环境,这里并未提及Flow Table、br-tun等。我们将在下次见面。
总所周知,OpenStack Neutron的高度复杂性和抽象性,让众多学习和使用者望而生畏,不知所云。尽管如此,但我们不能选择逃避。
其实,暂且抛开具体细节不谈,从全局和宏观上理解Neutron的整个运行流程,也是不复杂、抽象的。
无非就是将传统的物理硬件设备(如网线、网卡、服务器、交换机、路由器等)按照TCP/IP的四个层次架构(数据链路层、网络层、传输层、应用层),通过软件编程的方式,予以全部虚拟化、软件化和抽象化。
其虚拟实现的1层到3层(TCP/IP)整个流程是:
一层的服务器及其VM(由Linux Kernel创建的qbr、tap/tun、veth、iptables这些设备分别实现相应功能)——》 二层的网络设备(由OpenvSwitch、dnsmasq创建的qvo、br-int、br-tun、br-ex、qrouter、qdhcp等设备分别实现相应功能)——》 再到,三层的传输程序(由patch-int/patch-tun等分别实现相应的功能)。
在Neutron虚拟网络中,除了Neutron本身命令外,还包括了Linux Bridge的brctl命令;OpenvSwitch的ovs-vsctl、ovs-ofctl命令和L3的NameSpace的ip netns等命令。
提到Neutron的虚拟网络功能实现,不得不先提基于Linux内核级的虚拟设备。
TAP/TUN/VETH是Linux内核实现的一对虚拟网络设备,TAP工作在二层,收发的是 MAC 层数据帧;TUN工作在三层,收发的是 IP 层数据包。Linux 内核通过TAP/TUN设备向绑定该设备的用户程序发送数据,反之,用户程序也可以像操作硬件网络设备一样,通过TAP/TUN设备接收数据。
基于TAP设备,实现的是虚拟网卡的功能,当一个TAP设备被创建时,在Linux的设备文件目录下将会生成一个对应的字符设备文件(/dev/tapX文件),而运行其上的用户程序便可以像使用普通文件一样打开这个文件进行读写。
VETH设备总是成对出现的,接收数据的一端会从另一端发送出去,理解为一根虚拟的网线即可。
至于,DVR、NFV、SDN等这些和Neutron挂钩的高大上名词,也无非是借鉴了传统的网络架构,予以虚拟化和创新罢了。最后,怎么去具体实现,那就是架构设计和软件编码的事儿了。
下面,我们将以实践且非常实用的方法来走进Neutron的虚拟网络世界中,化抽象为形象。
二.OpenStack Neutron网络连接测试
OpenStack Neutron网络连接测试,说到底是VM的网络连接测试,即与外网的网络连接情况、网络诊断和故障排错等。方法如下:
注意
由于执行的命令输出信息太多,这里予以了省略,但并不影响理解和学习。在必要处,会给出信息。
实验环境如下:
Host Server/VM:CentOS 7
OpenStack Juno Neutron:VLAN模式
Provider Network:Vlan 102、网段 172.16.102.0/24、 网关172.16.102.254
VM IP:172.16.102.5
需要注意的是,在vlan模式下,vlan tag的转换是在br-int和br-ethx两个网桥上进行。即br-int负责从int-br-ethX过来的包(带外部vlan)转换为内部vlan,而br-ethx负责从phy-br-ethx过来的包(带内部vlan)转化为外部的vlan。
同时,在VLAN模式下,没有br-tun通道网桥。
我们将根据下面这张图的流程(VM数据到外网),予以分析:
1.计算存储节点上
1)执行nova list 查看VM对应的名称和VM_UUID。
VM默认存放于路径:/var/lib/nova/instances/
# nova list
2)执行nova show $VM_UUID,查看VM所在的openstack compute node信息和instance name。
# nova show $VM_UUID
3)查看VM在哪个计算节点上。
# nova-manage vm list | grep vm-name
4)执行virsh list 查看VM状态,执行virsh dumpxml instance-XXXX查找文件中关于“Bridge”信息,查找tap的ID和网桥 qbrXXXX。
# virsh dumpxml instance-00000052
</controller>
<interface type=’bridge’>
<mac address=’fa:16:3e:3e:da:f1′/>
<source bridge=’qbrc08d85da-69′/> #OVS实现安全组的网桥
<target dev=’tapc08d85da-69′/> #虚拟网卡设备
<model type=’virtio’/> #虚拟网卡驱动
<alias name=’net0′/> #虚拟网卡别名
<address type=’pci’ domain=’0×0000′ bus=’0×00′ slot=’0×03′ function=’0×0′/>
这里,我们结合一张官网的图,来予以剖析。
从图中,我们可以知道:
eth0连接的目的设备是:tapc08d85da-69,别名为net0。
TAP设备桥接到网桥qbrXX上(都由Linux kernel创建)。qbr设备是因为不能在TAP设备上配置iptables实现安全组(Secur Group)而增加的设备。
eth0对应的tapc08d85da-69,桥接到的网桥为qbrc08d85da-69。
5)执行brctl show查看网桥qbr上的接口信息,找到tap设备和qvbXXXX接口。
备注:由于这些网桥均由Linux Kernel创建,所以使用brctl命令
# brctl show
qbrc08d85da-69 8000.3a04f8f4bda2 no qvbc08d85da-69
tapc08d85da-69
这里,可以看到网桥qbrc08d85da-69,上面有接口qvbc08d85da-69和tapc08d85da-69。
这里出现的qvbXXX和qvoXXX是一对veth pair devices,是一对虚拟的网卡设备或虚拟的网线,用来连接Linux bridge网桥和Open vSwitch设备。名字的涵义是q-quantum、v-veth、b-bridge;o-open、vswitch(quantum年代的遗留)。
6)查看qvb设备的驱动是否是veth类型。
# lshw -class network|more
7)查看qvb接口的对端peer_ifindex:number。
# ethtool -S qvbc08d85da-69
NIC statistics:
peer_ifindex: 15
8)找到peer_ifindex:number对应的接口qvoXXX。
# ip link | grep 15:
15: qvoc08d85da-69: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master ovs-system state UP mode DEFAULT qlen 1000
link/ether 4e:99:15:01:1b:07 brd ff:ff:ff:ff:ff:ff
qvbXXX和qvoXXX是一对veth设备,成对出现的。可以根据ethtool -S 查看对端的number,并且根据ip link|number:查看到对应的veth设备名称(即qvo)。
qvb是quamtum veth bridge
qvo是quamtum veth open vswitch
qvoXXX设备连接到了Open vSwitch设备的br-int集成网桥上
9)通过Open vSwitch 中的命令ovs-vsctl port-to-br qvoXXX查看qvoXXX设备所属的bridge。
# ovs-vsctl port-to-br qvoc08d85da-69
br-int
10)查看所有的网桥
# ovs-vsctl list-br
bond-br
br-int
11)查看OVS Bridge上面有哪些端口
# ovs-vsctl list-ports br-int
int-bond-br
qvo077eac35-ed
qvo1ee66407-69
qvo40816622-cf
qvo849c4847-1e
qvoace90c57-d3
qvob0b730ab-79
qvob32e78ae-7f
qvoc08d85da-69
qvodec9ff3a-92
qvoe2028755-37
qvofb9c19ea-6a
tap52a2315f-19
tap63754766-e1
# ovs-vsctl list-ports bond-br
eth3
phy-bond-br
br-int和phy-bond-br端口是用来连接Open Vswitch设备的br-int和bond-br
(即br-ethX)网桥。另外,VLAN ID的转换是发生在br-int和bond-br网桥的端口br-int和phy-bond-br上面的。
从br-int进来的数据包,其VLAN ID 会被转换为内部的VLAN ID。同理,从网络出去的数据包,经过phy-bond-br,VLAN ID会将内部VLAN ID 转化为外部VLAN ID。
12)通过ethtool -S 命令查看br-int的对端设备number;并通过ip link |grep number查看br-int的对端设备(即phy-bond-br)。
# ethtool -S br-int
# ip link | grep number
13)查看phy-bond-br连接到的虚拟外部网桥。
# ovs-vsctl port-to-br phy-bond-br
bond-br
14)查看外部网桥bond-br(即br-ethX桥)上的端口:
# ovs-vsctl list-ports bond-br
eth3
phy-bond-br
关于本例子中的bond-br外部网桥和物理网口ethX的关系是:VM数据包要到外部物理网络中,必须依靠真正的物理网卡。
这种关联,需要通过手动方式创建,这里是:
# ovs-vsctl add-port bond-br eth3。
2. 小结
基于上述数据通信流程,这样整个VM的数据链路便完全打通了。从VM的端口到qvoXXX接口的信息及网桥和端口的连接情况,再结合tcpdump 命令查看网络流量和OVS OpenFlow协议能够清楚的掌握和梳理Neutron网络的连接架构,作为网络分析的依据,为故障排除提供坚实的基础。
neutron网络目的是为OpenStack云更灵活的划分网络,在多租户的环境下提供给每个租户独立的网络环境。
neutron混合实施了第二层的VLAN和第三层的路由服务,它可为支持的网络提供防火墙,负载均衡以及IPSec VPN等扩展功能。
neutron是openstack中一个重要模块,也是比较难理解和debug的模块之一。
一、分析经典的三个节点的Havana的OpenStack中网络
图1:三节点opens网络
分为三个网络:
1、External Network/API Network,这个网络是链接外网的,无论是用户调用OpenStack的API,还是创建出来的虚拟机要访问外网,或者外网要ssh到虚拟机,都需要通过这个网络
2、Data Network,数据网络,虚拟机之间的数据传输通过这个网络来进行,比如一个虚拟机要连接到另一个虚拟机,虚拟机要连接虚拟路由都是通过这个网络来进行
3、Management Network,管理网络,OpenStack各个模块之间的交互,连接数据库,连接Message Queue都是通过这个网络来进行。
将这三个网络隔离,一方面是安全,在虚拟机里面,无论采用什么手段,干扰的都仅仅是Data Network,都不可能访问到我的数据库。一方面是流量分离,Management Network的流量不是很大的,而且一般都会比较优雅的使用,而Data Network和External Network就需要有流量控制策略。
这个网络结构有点奇怪,除了Controlller节点是两张网卡之外,其他的都多了一张网卡连接到External Network。这个网卡是用来做apt-get的,因为Compute Node按说是没有网卡连接到外网的,为了apt-get添加了eth0;NetWork Node虽然有一个网卡eth1是连接外网的,而在neutron配置好之前,这个网卡通常是没有IP的,为了apt-get也添加了eth0;有人说可以通过route规则都通过Controller连接到外网,但是对于初学者,这样比较容易操作。
二、neutron三个节点简介
neutron是用来创建虚拟网络的,所谓虚拟网络,就是虚拟机启动的时候会有一个虚拟网卡,虚拟网卡会连接到虚拟switch上,虚拟交换机连接到虚拟router上,虚拟路由器最终和物理网卡联通,从而虚拟网络和物理网络联通起来。
neutron分成多个模块分布在三个节点上。
1.Controller节点:
neutron-server,用于接受API请求创建网络,子网,路由器等,然而创建的这些东西仅仅是一些数据结构在数据库里面
2.Network节点:
neutron-l3-agent,用于创建和管理虚拟路由器,当neutron-server将路由器的数据结构创建好,neutron-l3-agent是做具体事情的,真正的调用命令行将虚拟路由器,路由表,namespace,iptables规则全部创建好。
neutron-dhcp-agent,用于创建和管理虚拟DHCP server,每个虚拟网络都会有一个DHCP server,这个DHCP server为这个虚拟网络里面的虚拟机提供IP。
neutron-openvswitch-plugin-agent,这个是用于创建L2的switch的,在Network节点上,Router和DHCP Server都会连接到二层的switch上。
3.Compute节点:
neutron-openstackvswitch-plugin-agent,这个是用于创建L2层switch的,在compute节点上,虚拟机的网卡也是连接到二层的switch上。
三、租户网络创建过程
当我们搭建好了OpenStack,然后创建好了tenant后,我们会为这个tenant创建一个网络。
图2:租户网络创建过程
结合上图说明给一个租户创建网络的流程:
1、为这个Tenant创建一个private network,不同的private network是需要通过VLAN tagging进行隔离的,互相之间广播(broadcast)不能到达,这里我们我们用的是GRE模式,也需要一个类似VLANID的东西,称为Segment ID
2、为private network创建一个subnet,subnet才是真正配置IP网段的地方,对于私网,我们常常用192.168.0.0/24这个网段
3、为这个Tenant创建一个Router,才能够访问外网
4、将private network连接到Router上
5、创建一个External Network
6、创建一个External Network的Subnet,这个外网逻辑上代表了我们数据中心的物理网络,通过这个物理网络,我们可以访问外网。因而PUBLIC_GATEWAY应该设为数据中心里面的Gateway,PUBLCI_RANGE也应该和数据中心的物理网络的CIDR一致,否则连不通。之所以设置PUBLIC_START和PUBLIC_END,是因为在数据中心中,不可能所有的IP地址都给OpenStack使用,另外的可能搭建了VMware Vcenter,可能有物理机,所以仅仅分配一个区间给OpenStack来用。
7、将Router连接到External Network
经过这个流程,从虚拟网络到物理网络即逻辑上联通了。
代码流程如下:
#!/bin/bash TENANT_NAME="openstack" TENANT_NETWORK_NAME="openstack-net" TENANT_SUBNET_NAME="${TENANT_NETWORK_NAME}-subnet" TENANT_ROUTER_NAME="openstack-router" FIXED_RANGE="192.168.0.0/24" NETWORK_GATEWAY="192.168.0.1" PUBLIC_GATEWAY="172.24.1.1" PUBLIC_RANGE="172.24.1.0/24" PUBLIC_START="172.24.1.100" PUBLIC_END="172.24.1.200" TENANT_ID=$(keystone tenant-list | grep " $TENANT_NAME " | awk '{print $2}') (1) TENANT_NET_ID=$(neutron net-create --tenant_id $TENANT_ID $TENANT_NETWORK_NAME --provider:network_type gre --provider:segmentation_id 1 | grep " id " | awk '{print $4}') (2) TENANT_SUBNET_ID=$(neutron subnet-create --tenant_id $TENANT_ID --ip_version 4 --name $TENANT_SUBNET_NAME $TENANT_NET_ID $FIXED_RANGE --gateway $NETWORK_GATEWAY --dns_nameservers list=true 8.8.8.8 | grep " id " | awk '{print $4}') (3) ROUTER_ID=$(neutron router-create --tenant_id $TENANT_ID $TENANT_ROUTER_NAME | grep " id " | awk '{print $4}') (4) neutron router-interface-add $ROUTER_ID $TENANT_SUBNET_ID (5) neutron net-create public --router:external=True (6) neutron subnet-create --ip_version 4 --gateway $PUBLIC_GATEWAY public $PUBLIC_RANGE --allocation-pool start=$PUBLIC_START,end=$PUBLIC_END --disable-dhcp --name public-subnet (7) neutron router-gateway-set ${TENANT_ROUTER_NAME} public |
四、GRE模式下网络细节
创建完网络,如果不创建虚拟机,我们发现neutron的agent还是做了很多工作的,创建了很多虚拟网卡和switch。
在compute节点上:
root@ComputeNode:~# ip addr 【显示所有接口的IP信息】
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 |
root@ComputeNode:~# ovs-vsctl show 【查看open vswitch网桥】
39f69272-17d4-42bf-9020-eecc9fe8cde6 |
在Network 节点上:
root@NetworkNode:~# ip addr 【显示所有接口的IP信息】
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 |
oot@NetworkNode:~# ip netns 【列出网络命名空间】
qrouter-b2510953-1ae4-4296-a628-1680735545ac |
oot@NetworkNode:~# ip netns exec qrouter-b2510953-1ae4-4296-a628-1680735545ac ip addr 【利用网络命名空间进行操作】
8: qg-97040ca3-2c: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN |
root@NetworkNode:~# ip netns exec qdhcp-96abd26b-0a2f-448b-b92c-4c98b8df120b ip addr 【利用网络命名空间进行操作】
9: tapde5739e1-95: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN |
root@NetworkNode:~# ovs-vsctl show 【查看open vswitch的网络状态】
d5d5847e-1c9e-4770-a68c-7a695b7b95cd |
这时如果我们在这个网络里创建一个虚拟机,在Compute Node多了下面的网卡:
13: qvof5da998c-82: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 |
如果我们按照ovs-vsctl show的网卡桥接关系,便可以画出下面的图
图3:GRE网络
GRE网络原理:
假设虚拟机VM0网卡eth0有网络数据包向外网发送,那么数据会依次经过qbr Linux Bridge设备,qvb和qvo虚拟网络设备,到达OVS网桥br-int上,br-int将数据包attach到OVS网桥br-tun上,数据包再从compute节点OVS网桥的br-tun和network节点OVS网桥br-tun构成的GRE隧道穿过,交付到Network节点的OVS网桥br-int上;网络节点的br-int通过qr设备借助Linux命名空间qrouter连通到br-ex上的qg设备,将数据包交付到OVS网桥br-ex上,最后br-ex通过网络节点的外部武力端口eth1把数据包传送到外部路由器的网关。
五、通过例子理解GRE网络
openstack为什么要创建这么多的虚拟网卡?
这些网卡看起来复杂,却是各有用处,这种虚拟网络拓扑,正是我们经常使用的物理网络的拓扑结构。
下面通过一个非常容易理解的例子来逐步分析gre网络。
我们先来回到一个最最熟悉不过的场景,我们的大学寝室,当时我们还买不起路由器,所以一般采取的方法如下图所示:
寝室长的机器上弄两张网卡,寝室买一个HUB,其他人的电脑都接到HUB上,寝室长的电脑的两张网卡一张对外连接网络,一张对内连接HUB。寝室长的电脑其实充当的便是路由器的作用。
后来条件好了,路由器也便宜了,所以很多家庭也是类似的拓扑结构,只不过将Computer1和switch合起来,变成了一个路由器,路由器也是有多个口一个连接WLAN,一个连接LAN。
图4
现在我们想象一个寝室变成了一台Hypervisor,所有的电脑都变成了虚拟机,就变成了下面的样子:
图5:单节点的openstack的网络结构
我们先忽略qbr和DHCP Server,以及namespace。
br-int就是寝室里的HUB,所有虚拟机都会连接到这个switch上,虚拟机之间的相互通信就是通过br-int来的。
Router就是寝室长的电脑,一边连接在br-int上,一边连接在对外的网口上,br-ex/eth0外面就是我们的物理网络。
图5其实就是单节点的openstack的网络结构,虽然复杂,但是就是把我们家里的,或者寝室里面的物理机搬到一个Hypervisor上了,其结构就不难理解了。
当然单节点的openstack不过是个测试环境,compute节点和network节点也是要分开的,如图3,每个机器上都有了自己的br-int。
六、GRE网络原理进一步理解
以下内容可对比图3来理解
(1)DHCP
为什么会有个DHCP Server呢,是同一个private network里的虚拟机得到IP都是通过这个DHCP Server来的,这个DHCP Server也是连接到br-int上和虚拟机进行通信的。
(2)qbr
既然qbr和br-int都是网桥,为什么不直接连到br-int,还要通过qbr,qvb,qvo岂不是多余,为什么会有qbr呢?这是和security group的概念有关。简单说就是OVS网桥br-int没有设置iptables规则的功能,但openstack又要提供安全组服务,就借助了Linux bridge(qbr)的功能,虽然OVS的br-int和linux bridge (qbr)都是二层网桥,但是为了功能互补就同时出现了。具体了解,openstack中的security group开通哪些端口,屏蔽哪些端口是用iptables来实现的,然而br-int这些虚拟bridge都是openvswitch创建的,openstack的Kernel mode和netfilter的kernel mode不兼容。一个IP包进来要么走iptables规则进行处理,要么走openvswitch的规则进行处理,br-int上有很多openvswitch的规则,比如vlan tag等,所以iptables必须要另外建立一个linux bridge(qbr)来做,因而就有了qbr,在了解拓扑结构的时候,可以将qbr忽略,看到VM直接连接到br-int上就可以了。
(3)namespace
为什么会有namespace呢,java的namespace是为了在不同namespace下有相同类名,openstack也想做到这一点。不同Tenant都创建自己的router和private network,彼此不知道别人指定了哪些网段,很有可能两个tenant都指定了192.168.0.0/24,这样不同的private network的路由表,DHCP Server就需要隔离,不然就乱了,因而就有了namespace。
(4)OVS一体化网桥br-int
每个机器上都有了自己的br-int,但是对于虚拟机和虚拟router来说,它们仍然觉得自己连接到了一个大的L2的br-int上,通过这个br-int相互通信的,它们感受不到br-int下面的虚拟网卡br-tun。所以对于多节点结构,我们可以想象br-int是一个大的,横跨所有compute和network节点的二层switch,虚拟机之间的通信以及虚拟机和Router的通信,就像在一个寝室一样的。这是一种很重要的抽象思维,好像openstack环境中所有虚拟机都连接到了一个巨型的虚拟交换机上。
然而br-int毕竟被物理的割开了,需要有一种方式将他们串联起来,openstack提供了多种方式,可以用GRE tunnel将不同机器的br-int连接起来,也可以通过VLAN将br-int连接起来,当然还可以使用vxlan。
这就是为什么openstack有了br-int这个bridge,但是不把所有的openvswitch的规则都在它上面实现。就是为了提供这种灵活性,对于虚拟机来讲,看到的是一大整个br-int,不同机器的br-int可以有多种方式连接,这在br-int下面的网卡上面实现。
(5)OVS通道网桥br-tun
br-tun也是OVS创建的虚拟网桥,它是一个中间层,接收br-int的网络数据,然后在通过特定网络协议与各个节点的br-tun相连构成一个通道层。如果所有的br-int构成的抽象层定义为虚拟二层网络,那么所有的br-tun构成的抽象层边上虚拟三层网络了。
(6)网络数据包隔离
如果有不同的Tenant,创建了不同的private network,为了在data network上对包进行隔离,创建private network的时候,需要指定vlanid或者segmentid。
从ovs-vsctl show我们可以看到,不同的tenant的private network上创建的虚拟机,连接到br-int上的时候是带tag的,所以不同tenant的虚拟机,即便连接到同一个br-int上,因为tag不同,也是不能相互通信的。然而同一个机器上的tag的计数是仅在本机有效的,并不使用我们创建private network的时候指定的全局唯一的vlanid或者segmentid,一个compute节点上的br-int上的tag 1和另一台compute节点上的br-int的tag1很可能是两码事。全局的vlanid和segmentid仅仅在br-int以下的虚拟网卡和物理网络中使用,虚拟机所有能看到的东西,到br-int为止,看不到打通br-int所要使用的vlanid和segmentid。
从局部有效的taging到全局有效的vlanid或者segmentid的转换,都是通过openvswitch的规则,在br-tun或者br-eth1上实现。