网络虚拟化--openvswitch(openstack的网络模型)

时间:2021-05-13 21:54:28

openvswitch

Open vSwitch即开放虚拟交换标准。是一款虚拟交换的软件。虚拟交换就是利用虚拟平台,通过软件的方式形成

交换机部件。跟传统的物理交换机相比,虚拟交换机同样具备众多优点,一是配置更加灵活。一台普通的服务器可以

配置出数十台甚至上百台虚拟交换机,且端口数目可以灵活选择。


实验环境:

centos7.3 3台。

node1 eth0 192.168.10.128/24 (仅主机 VMnet1)

eth1 (VMnet2)

网关 192.168.10.130

node2 eth0 192.168.10.129/24 (仅主机 VMnet1 ) (实现GRE 路由转发时用到,跨主机实现VLAN)

  eth1 (VMnet2)

网关 192.168.10.130

node3 eth0 192.168.10.129/24 (仅主机 VMnet1 )

  eth1 192.168.1.100 ( VMnet0 )

网关 192.168.1.1


安装openvswitch

他的安装包是在openstack的yum源里。所以配置openstack的yum源。

在CentOS中, ``extras``仓库提供用于启用 OpenStack 仓库的RPM包。 CentOS 默认启用``extras``仓库,

因此你可以直接安装用于启用OpenStack仓库的包。

# yum install centos-release-openstack-mitaka
在主机上升级包

# yum upgrade
安装openvswitch

# yum -y install openvswitch

启动openvswitch

# systemctl start openvswitch

创建一个桥设备

# ovs-vsctl add-br br-in
# ovs-vsctl show
1bfc1186-1781-4e91-96e9-5db9c334503e
Bridge br-in
Port br-in
Interface br-in
type: internal
ovs_version: "2.5.0"

此时一个软交换机已经诞生。他比brctl强大,多了一个VLan的功能。

ovs-vsctl 命令的使用:

show:ovsdb配置内容的查看

add-br:添加桥设备

del-br:删除桥

list-br:显示所有已定义的桥

add-port:为桥添加一个端口

del-port:移除一个端口    例:del-port br-in vif0.0

find port name=eth1:查找端口eth1的详细信息


现在激活那个eth1.然后将eth1添加至桥上

[root@localhost ~]# ip link set eth1 up
[root@localhost ~]# ovs-vsctl add-port br-in eth1
[root@localhost ~]# ovs-vsctl list-ports br-in
eth1

查看br-in的状态

[root@localhost ~]# ovs-vsctl show
1bfc1186-1781-4e91-96e9-5db9c334503e
Bridge br-in
Port br-in
Interface br-in
type: internal
Port "eth1"
Interface "eth1"
ovs_version: "2.5.0"


好了,现在启动虚拟机实例。(需要装qemu-kvm)

先创建桥接配置脚本。/etc/if-up

#!/bin/bash

bridge=br-in

if [ -n $1 ];then
ip link set $1 up
sleep 1
ovs-vsctl add-port $bridge $1
[ $? -eq 0 ] && exit 0 || exit 1
else
echo 'Error:no port specified'
exit 2
fi

启动虚拟机。

# qemu-kvm -name "test1" -m 256 -smp 1 \
> -drive file=/images/cirros/cirros-0.3.5-x86_64-disk.img,media=disk,if=virtio \
> -net nic,model=virtio,macaddr=52:54:00:00:00:01 \
> -net tap,ifname=vif0.0,script=/etc/if-up,downscript=no \
> --nographic

照此方法启动连个虚拟机。

配置两个虚拟机的IP地址。

test1 虚拟机

# ifconfig eth0 10.0.3.1 netmask 255.255.255.0 up
# ifconfig
eth0 Link encap:Ethernet HWaddr 52:54:00:00:00:01
inet addr:10.0.3.1 Bcast:10.0.3.255 Mask:255.255.255.0
。。。


test2 虚拟机

# ifconfig eth0 10.0.3.2 netmask 255.255.255.0 up
# ifconfig
eth0 Link encap:Ethernet HWaddr 52:54:00:00:00:02
inet addr:10.0.3.2 Bcast:10.0.3.255 Mask:255.255.255.0
。。。


两个虚拟机是可以互相ping通的。

# ping 10.0.3.2
PING 10.0.3.2 (10.0.3.2): 56 data bytes
64 bytes from 10.0.3.2: seq=0 ttl=64 time=5.327 ms
64 bytes from 10.0.3.2: seq=1 ttl=64 time=0.037 ms
。。。



接下来就可以开始试验了,将两个虚拟机放到不同的VLAN里面。

[root@localhost ~]# ovs-vsctl set port vif0.0 tag=10
[root@localhost ~]# ovs-vsctl set port vif1.0 tag=11

上面的命令将vif0.0设置为10号VLan,vif1.0设置为11号VLan。


十分简单吧。

现在再来实现一个场景,就是test1和test2虚拟机在一个交换机上,并且属于不同的VLan。而test3虚拟在另

一个交换机上,并且,同在10号VLan中。如下图所示

网络虚拟化--openvswitch(openstack的网络模型)

先添加一个交换机。

# ovs-vsctl add-br br-test

修改/etc/if-up和if-down,将里面的桥由br-in改成br-test。

启动一个虚拟机test3,接在这个br-test这个交换机上。

# qemu-kvm -name "test3" -m 256 -smp 1 \
-drive file=/images/cirros/test2.qcow2,media=disk,if=virtio \
-net nic,model=virtio,macaddr=52:54:00:00:00:03 \
-net tap,ifname=vif2.0,script=/etc/if-up2,downscript=/etc/if-down2 \
--nographic \


配置网卡信息。

# ifconfig eth0 10.0.3.3 netmask 255.255.255.0 up
# ifconfig
eth0 Link encap:Ethernet HWaddr 52:54:00:00:00:03
inet addr:10.0.3.3 Bcast:10.0.3.255 Mask:255.255.255.0

接着创建一对网卡,将两个交换机连在一起。

# ip link add switch0 type veth peer name switch1
# ip link set switch0 up
# ip link set switch1 up
# ovs-vsctl add-port br-in switch0
# ovs-vsctl add-port br-test switch1

将test3设置为10号VLan

# ovs-vsctl set port vif2.0 tag=10

测试一下看看。在test1上来看看。

# ping 10.0.3.3
PING 10.0.3.3 (10.0.3.3): 56 data bytes
64 bytes from 10.0.3.3: seq=0 ttl=64 time=1.734 ms

## test1在 10 号VLAN,可以ping通test3

# ping 10.0.3.2
PING 10.0.3.2 (10.0.3.2): 56 data bytes

--- 10.0.3.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

# 虽然test1和test2在同一台交换机上,但是没有在相同的VLan中。所以不能ping通、


添加网关,使得真机与虚拟机可以通信,或者说可以虚拟机上外网。

# ip netns add r0   #添加namespace
# ip link add sif0 type veth peer name rif0 #添加一对网卡
# ip link set sif0 up
# ip link set rif0 up
# ip link set rif0 netns r0 #把网卡rif0给r0
# ovs-vsctl add-port br-in sif0 #把网卡sif0给br-in



激活添加在r0上的rif0网卡

# ip netns exec r0 ip link set rif0 up

并且给r0上网关添加一个IP地址。

# ip netns exec r0 ip addr add 10.0.3.254/24 dev rif0

配置dnsmasq
# yum -y install dnsmasq
# ip netns exec r0 dnsmasq -F 10.0.3.200,10.0.3.230,86400 -i rif0 #配置dns服务器
# ip netns exec r0 ss -unl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 *:53 *:*
UNCONN 0 0 *:67 *:*
UNCONN 0 0 :::53 :::*



启动一个虚拟机实例。

# qemu-kvm -name "test6" -m 256 -smp 1 \
-drive file=/images/cirros/test1.qcow2,media=disk,if=virtio \
-net nic,model=virtio,macaddr=52:54:00:00:00:11 \
-net tap,ifname=vif0.0,script=/etc/if-up,downscript=/etc/if-down \
--nographic

查看虚拟机的配置。

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 52:54:00:00:00:11 brd ff:ff:ff:ff:ff:ff
inet 10.0.3.230/24 brd 10.0.3.255 scope global eth0
inet6 fe80::5054:ff:fe00:11/64 scope link
valid_lft forever preferred_lft forever
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.3.254 0.0.0.0 UG 0 0 0 eth0
10.0.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

上述只是想让虚拟机和真机通信


最后来实现一下,如果第四台主机不仅不再一个交换机上,而且还不在一台真机上。这就有点复杂了,怎么实现呢?

可能我的描述还是有些不准确,请看看下面这张图。

网络虚拟化--openvswitch(openstack的网络模型)


test1 如何和test6去通信。(假设test1和test6处在同一个Vlan中),而不让test2和test6去通信。

这就需要GRE技术了。Generic Routing Encapsulation:通用路由封装。是一种隧道技术。

类似LVS的隧道技术,ip内有ip。


给两边的eth1网卡都加上IP,因为两台主机的eth1需要通信。

主机一:

# ip addr add 192.168.20.1/24 dev eth1

主机二:

# ip addr add 192.168.20.2/24 dev eth1

要确保。两台主机可以ping通。


在第一个主机上的br-in交换机上添加一个接口(gre0不存在没关系)

# ovs-vsctl add-port br-in gre0

将eth1配置为隧道。

# ovs-vsctl set interface gre0 type=gre options:remote_ip=192.168.20.2
# ovs-vsctl list interface gre0
_uuid : ef8244cd-7d0e-4273-ade8-32ba231c6f4e
admin_state : up
bfd : {}
bfd_status : {}
cfm_fault : []
cfm_fault_status : []
cfm_flap_count : []
cfm_health : []
cfm_mpid : []
cfm_remote_mpids : []
cfm_remote_opstate : []
duplex : []
error : []
external_ids : {}
ifindex : 0
ingress_policing_burst: 0
ingress_policing_rate: 0
lacp_current : []
link_resets : 0
link_speed : []
link_state : up
lldp : {}
mac : []
mac_in_use : "8a:74:71:11:1b:64"
mtu : []
name : "gre0"
ofport : 13
ofport_request : []
options : {remote_ip="192.168.20.2"}
other_config : {}
statistics : {collisions=0, rx_bytes=0, rx_crc_err=0, rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=0, tx_bytes=338, tx_dropped=0, tx_errors=0, tx_packets=1}
status : {tunnel_egress_iface="eth1", tunnel_egress_iface_carrier=up}
type : gre


同样,在二号主机的eth1上通杨进行相同的配置,IP指向对方。

# ovs-vsctl add-port br-in gre0

# ovs-vsctl set interface gre0 type=gre option:remote_ip=192.168.20.1

观察一下两个主机的设置的交换机。

主机一:

[root@localhost ~]# ovs-vsctl show
1bfc1186-1781-4e91-96e9-5db9c334503e
Bridge br-in
Port "vif2.0"
Interface "vif2.0"
Port "vif1.0"
Interface "vif1.0"
Port "gre0"
Interface "gre0"
type: gre
options: {remote_ip="192.168.20.2"}
Port br-in
Interface br-in
type: internal
ovs_version: "2.5.0"


主机二:

[root@localhost ~]# ovs-vsctl show
88260195-15df-41d5-ab55-2401905c4c38
Bridge br-in
Port br-in
Interface br-in
type: internal
Port "vif2.0"
Interface "vif2.0"
Port "vif1.0"
Interface "vif1.0"
Port "gre0"
Interface "gre0"
type: gre
options: {remote_ip="192.168.20.1"}
ovs_version: "2.5.0"



OK,现在在主机一上启动一个虚拟机,发现,在主机二上的DHCP服务器也可以给主机一上的服务器提供IP了。

# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 52:54:00:00:00:02 brd ff:ff:ff:ff:ff:ff
inet 10.0.3.215/24 brd 10.0.3.255 scope global eth0
inet6 fe80::5054:ff:fe00:2/64 scope link
valid_lft forever preferred_lft forever


下来观察一下gre的基于隧道的通信技术。

# tcpdump -i eth1 -nn 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
22:23:04.841452 IP 192.168.20.1 > 192.168.20.2: GREv0, length 102: IP 10.0.3.215 > 10.0.3.230: ICMP echo request, id 26625, seq 40, length 64
22:23:04.842654 IP 192.168.20.2 > 192.168.20.1: GREv0, length 102: IP 10.0.3.230 > 10.0.3.215: ICMP echo reply, id 26625, seq 40, length 64
22:23:05.844358 IP 192.168.20.1 > 192.168.20.2: GREv0, length 102: IP 10.0.3.215 > 10.0.3.230: ICMP echo request, id 26625, seq 41, length 64
22:23:05.846782 IP 192.168.20.2 > 192.168.20.1: GREv0, length 102: IP 10.0.3.230 > 10.0.3.215: ICMP echo reply, id 26625, seq 41, length 64
22:23:06.248433 IP 192.168.20.1 > 192.168.20.2: GREv0, length 346: IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 52:54:00:00:00:01, length 296
22:23:06.846852 IP 192.168.20.1 > 192.168.20.2: GREv0, length 102: IP 10.0.3.215 > 10.0.3.230: ICMP echo request, id 26625, seq 42, length 64
22:23:06.848156 IP 192.168.20.2 > 192.168.20.1: GREv0, length 102: IP 10.0.3.230 > 10.0.3.215: ICMP echo reply, id 26625, seq 42, length 64


表面是192.168.20.1 在ping192.168.20.2,但是实质上有在里面封装了一个10.0.3.215 对10.0.3.230 的通信



还可以做个实验。一号主机的两台虚拟机一个在10号VLAN中,一个在11号VLAN中

二号主机的两台虚拟机一个在10号VLAN中,一个在11号VLAN中。

那么这下一号主机的两台主机是不能互相通信的。


最后再演示一下VXLAN的技术。

现在不要gre功能了,VXLAN本身支持VLAN的功能,有极大的提升了数量,还有基于隧道的技术。

所以要先取消gre。

# ovs-vsctl del-port br-in gre0

两个节点都需要去执行。

添加VXLAN

主机一:

# ovs-vsctl add-port br-in vx0 
ovs-vsctl: Error detected while setting up 'vx0'.  See ovs-vswitchd log for details.

# ovs-vsctl set interface vx0 type=vxlan options:remote_ip=192.168.20.2


主机二:

# ovs-vsctl add-port br-in vx0

ovs-vsctl: Error detected while setting up 'vx0'.  See ovs-vswitchd log for details.

# ovs-vsctl set interface vx0 type=vxlan options:remote_ip=192.168.20.1



OK了,看,是不是和gre很像很像。


最后配置整个openstack 的网络模型。整个网络模型是这样的。再由一个节点让,所有的内部虚拟机可以去访问

外部网络,同时呢,又可以让外部网络可以访问内部虚拟机网络。

网络虚拟化--openvswitch(openstack的网络模型)

这下就需要第三台主机了,这台主机专门去做网络节点,负责网络数据的转发(内网与外网之间)

这台主机的模型是这样的。3块网卡,一个桥接(用来与外部网络去通信)

一个仅主机(所有的内部网络使用的线路,有可能包括openstack的主控制节点

一个仅主机(所有虚拟机计算几点,加上网络节点)

网络虚拟化--openvswitch(openstack的网络模型)

好了。这个节点需要2个虚拟交换机,一个使用openvswitch去做,因为需要VXLan创建的Vlan技术。

第一个交换机与外部网络相接,可以直接使用brctl命令去创造。

我这里直接用配置文件了,eth1网卡是桥接网卡。

ifcfg-eth1

DEVICE="eth1"
BOOTPROTO="static"
ONBOOT="yes"
NM_CONTOLLED="no"
BRIDGE="br-ex"

ifcfg-br-ex

DEVICE="br-ex"
BOOTPROTO="static"
ONBOOT="yes"
IPADDR="192.168.1.30"
NETMASK="255.255.255.0"
GATEWAY="192.168.1.1"
NM_CONTOLLED="no"
TYPE="Bridge"

好了创建netns。

# ip netns add r0

# ip link add sin0 type veth peer name rin0

# ip link add sex0 type veth peer name rex0

# ip link set sin0 up

# ip link set sex0 up

# ip link set rin0 netns r0

# ip link set rex0 netns r0



创建新的内部桥

(在新主机上(网络控制节点))

# ovs-vsctl add-br br-in

# ovs-vsctl add-port br-in gre0

# ovs-vsctl set interface gre0 type=gre options:remote_ip=192.168.20.2

(在虚拟机主机上(192.168.20.2)再创建一个gre隧道,与之通信)


添加一对网卡。一个在r0上,一个在br-in上

# ip netns exec r0 ifconfig rin0 10.0.1.254/24 up

# ovs-vsctl add-port br-in sin0


OK,现在内部的虚拟机就可以去和10.0.1.254这个网关去联系了。

# ping 10.0.1.254(这里是10.0.1.1主机)
PING 10.0.1.254 (10.0.1.254): 56 data bytes
64 bytes from 10.0.1.254: seq=0 ttl=64 time=12.323 ms
64 bytes from 10.0.1.254: seq=1 ttl=64 time=2.020 ms


看看抓包结果。

01:27:12.187837 IP 192.168.20.3 > 192.168.20.2: GREv0, length 102: IP 10.0.1.254 > 10.0.1.1: ICMP echo reply, id 29953, seq 19, length 64


现在配置br-ex(注意:一对网卡,在r0上的需要配置Ip,在交换机上的不需要配置IP)

# brctl addif br-ex sex0

# ip netns exec r0 ifconfig rex0 192.168.1.30/24 up

# ip netns exec r0 sysctl -w net.ipv4.ip_forward=1

net.ipv4.ip_forward = 1


此时。内部网络的虚拟机已经可以去PING通网络调度节点上的外部地址了。

# ping 192.168.1.30
PING 192.168.1.30 (192.168.1.30): 56 data bytes
64 bytes from 192.168.1.30: seq=0 ttl=64 time=2.452 ms
64 bytes from 192.168.1.30: seq=1 ttl=64 time=2.754 ms
64 bytes from 192.168.1.30: seq=2 ttl=64 time=1.045 ms
64 bytes from 192.168.1.30: seq=3 ttl=64 time=5.689 ms


配置网络IP地址转换。

# ip netns exec r0 iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -j SNAT --to-source 192.168.1.30

此时,内部虚拟机已经可以去ping通网络调度主机的局域网内的主机了。


现在开始内外映射去做。内网对外网的IP访问。

所以先删掉上面配置的ip。

# ip netns exec r0 iptables -t nat -F


配置地址映射。(这个192.168.1.31是专门映射给内部虚拟机使用的)

# ip netns exec r0 ifconfig rex0:0 192.168.1.31/24 up

添加两条规则

# ip netns exec r0 iptables -t nat -A POSTROUTING -s 10.0.1.1/24 -j SNAT --to-source 192.168.1.31

# ip netns exec r0 iptables -t nat -A PREROUTING -d 192.168.1.31 -j DNAT --to-destination 10.0.1.1

好了。此时,连我的windows都可以ping通虚拟机了。

网络虚拟化--openvswitch(openstack的网络模型)


抓包得到:(192.168.1.103是windows的IP)

01:53:25.266024 IP 192.168.20.2.41374 > 192.168.20.1.4789: VXLAN, flags [I] (0x08), vni 0
IP 192.168.1.103 > 10.0.1.1: ICMP echo request, id 1, seq 10264, length 40
01:53:25.266459 IP 192.168.20.1.34056 > 192.168.20.2.4789: VXLAN, flags [I] (0x08), vni 0
IP 10.0.1.1 > 192.168.1.103: ICMP echo reply, id 1, seq 10264, length 40
01:53:25.270680 IP 192.168.20.2 > 192.168.20.3: GREv0, key=0x0, length 82: IP 10.0.1.1 > 192.168.1.103: ICMP echo reply, id 1, seq 10264, length 40