OVS 学习笔记

OVS 介绍

Open virtual Switch 是一个在 Apache2许可授权的 开源多层软件交换机。目标是实现一个产品质量的交换机平台,支持标准的管理接口,并开放转发功能,以供编程扩展和控制。

ovs简言之就是使用openflow表做datapath,将底层数据端口的数据流的控制面导入上层openflow交换机做路由转发,数据传输的软件层面虚拟交换机。

OVS实现

其中, ovs-vswitchd 和 datapath 共同构成了 OVS 的数据面,控制面由 controller 模块来完成,controller 一般表示的是 OpenFlow 控制器,在 OVS 中,它可以借由第三方来完成,只要支持 OpenFlow 协议即可。

  • ovs-vswitchd

    ovs-vswitched与它的内核模块datapth共同构成了OVS的数据面。它使用OpenFlow协议与OpenFlow控制器通信,使用 OVSDB 协议与 ovsdb-server 通信,使用 netlink 和 datapath 内核模块通信。

  • ovsdb-server

    ovsdb-server 是 OVS 轻量级的数据库服务,用于整个 OVS 的配置信息,包括接口、交换内容、VLAN 等,ovs-vswitchd 根据这些配置信息工作。

  • OpenFlow 控制器

    OpenFlow 控制器可以通过 OpenFlow 协议连接到任何支持 OpenFlow 的交换机,比如 ovs-ofctl 。控制器通过向交换机下发流表规则来控制数据流向。

  • Kernel Datapath

    datapath 内核模块和 ovs-vswitchd 是相互协作工作的,datapath 负责具体的收发包,而 ovs-vswitchd 通过 controller 下发的流表规则指导 datapath 如何转发包。

数据面就是以用户态的 ovs-vswitchd 和内核态的 datapath 为主的转发模块,以及与之相关联的数据库模块 ovsdb-server,控制面主要是由 ovs-ofctl 模块负责,基于 OpenFlow 协议与数据面进行交互。而管理面则是由 OVS 提供的各种工具来负责

OVS架构

OVS的主要组件是:

  • ovs-vswitchd,一个实现交换机的守护程序,以及一个用于基于流的切换的配套 Linux 内核模块。
  • ovsdb-server,一个轻量级的数据库服务器,它通过 ovs-vswitchd 查询来获取其配置。
  • ovs-dpctl,一个用于配置交换机内核模块的工具,可以控制转发规则。
  • ovs-vsctl,一个用于查询和更新 ovs-vswitchd 配置的实用程序。
  • ovs-appctl,一个向运行 Open vSwitch 守护程序发送命令的实用程序。
  • 为 Citrix XenServer 和 Red Hat Enterprise Linux 构建 RPM 的脚本和规范。XenServer RPM 允许将 Open vSwitch 安装在 Citrix XenServer 主机上,作为其交换机的直接替代品,并具有其他功能。

Open vSwitch 还提供了一些工具:

  • ovs-ofctl,一个用于查询和控制 OpenFlow 交换机和控制器的实用程序。
  • ovs-pki,用于创建和管理 OpenFlow 交换机公钥基础结构的实用程序。
  • ovs-testcontroller,一个简单的OpenFlow控制器,可能对测试有用(尽管不适用于生产)。
  • tcpdump 的扩展程序,使其能够解析 OpenFlow 消息。

所有工具的手册,内容与 linux 中的manual一致。

一些基础概念

  • Bridge: Bridge,linux 网桥是 Linux 上用来做 TCP/IP 二层协议交换的设备,一个主机中可以创建一个或者多个 Bridge 设备。
  • Port: 端口与物理交换机的端口概念类似,每个 Port 都隶属于一个 Bridge。
  • Interface: 连接到 Port 的网络接口设备。在通常情况下,Port 和 Interface 是一对一的关系, 只有在配置 Port 为 bond 模式后,Port 和 Interface 是一对多的关系。
  • Controller: OpenFlow 控制器。OVS 可以同时接受一个或者多个 OpenFlow 控制器的管理。
  • datapath: 在 OVS 中,datapath 负责执行数据交换,也就是把从接收端口收到的数据包在流表中进行匹配,并执行匹配到的动作。
  • Flow table: 每个 datapath 都和一个“flow table”关联,当 datapath 接收到数据之后, OVS 会在 flow table 中查找可以匹配的 flow,执行对应的操作, 例如转发数据到另外的端口。支持 OpenFlow 协议的交换机应该包括一个或者多个流表,流表中的条目包含:数据包头的信息、匹配成功后要执行的指令和统计信息
  • Flow : 在 OpenFlow 的白皮书中,Flow 被定义为某个特定的网络流量。例如,一个 TCP 连接就是一个 Flow,或者从某个 IP 地址发出来的数据包,都可以被认为是一个 Flow。

OpenFlow介绍

OpenFlow是一种控制面和数据面通信的网络通信协议,应用于SDN架构中控制器和转发器之间的通信。

软件定义网络SDN的一个核心思想就是“转发、控制分离”,要实现转、控分离,就需要在控制器与转发器之间建立一个通信接口标准, 允许控制器直接访问和控制转发器的转发平面。OpenFlow引入了“流表”的概念,转发器通过流表来指导数据包的转发。 控制器正是通过OpenFlow提供的接口在转发器上部署相应的流表,从而实现对转发平面的控制。

链路及交换机

cisco网络中,交换机在局域网中最终稳定状态的接口类型主要有四种:access/trunk/ multi/ dot1q-tunnel。

  1. access: 主要用来接入终端设备,如PC机、服务器、打印服务器等。
  2. trunk: 主要用在连接其它交换机,以便在线路上承载多个vlan。
  3. multi: 在一个线路中承载多个vlan,但不像trunk,它不对承载的数据打标签。主要用于接入支持多vlan的服务器或者一些网络分析设备。现在基本不使用此类接口,在cisco的网络设备中,也基本不支持此类接口了。
  4. dot1q-tunnel: 用在Q-in-Q隧道配置中。

vlan的链路类型可以分为接入链路和干道链路。

  1. 接入链路(access link)指的交换机到用户设备的链路,即是接入到户,可以理解为由交换机向用户的链路。由于大多数电脑不能发送带vlan tag的帧,所以这段链路可以理解为不带vlan tag的链路。
  2. 干道链路(trunk link)指的交换机到上层设备如路由器的链路,可以理解为向广域网走的链路。这段链路由于要靠vlan来区分用户或者服务,所以一般都带有vlan tag。

隧道介绍

隧道技术介绍:

是在现有的物理网络之上构建一层虚拟网络,上层应用只与虚拟网络相关,以此实现的虚拟网络比物理网络配置更加灵活, 并能够实现跨主机的L2通信以及必要的租户隔离。不同隧道技术其大体思路均是将以太网报文使用隧道协议封装, 然后使用底层IP网络转发封装后的数据包,其差异性在于选择和构造隧道的协议不同。 常见隧道技术有两种gre或vxlan

  • General Router Encapsulation

在GRE中,需要被传输和封装的报文称之为payload packet,而用于封装和传输的协议则成为delivery protocol。 GRE在封装的时候,除了payload和delivery协议的header外,会生成一个GRE header。GRE header + payload一起被delivery协议封装用于传输,GRE header会包含payload的一些信息,包括checksum、version、payload的协议类型等。可以看到,通过这个GRE header的协议类型字段,当脱取这一层delivery层后,就可以解析为原数据包格式,通过GRE header中的协议类型我们就能知道协议类型了,既然知道了协议类型,那么就有能力解析了。

由于GRE是一种通用的格式,我们可以使用GRE进行很多不同种类的封装。比如我们可以使用PPTP协议来进行VPN,可以使用IPv4来包裹IPv6。比较常见的delivery协议一般是IP协议。 不过GRE在设计的时候有一个问题,那就是没有考虑加密。因此现在常见的需要加密的封装一般是用的IPsec协议。

  • Virtual eXtensible Local Area Network

简单的说就是扩充了的VLAN,相比于GRE的通用性,VXLAN主要用于封装、转发2层报文。 其使得多个通过三层连接的网络可以表现的和直接通过一台一台物理交换机连接配置而成的网络一样处在一个LAN中。 其将二层报文加上个vxlan header,封装在一个UDP包中进行传输。vxlan header会包括一个24位的ID(称为VNI), 含义类似于VLAN id或者上面提到的GRE的tunnel id。

在上面GRE的例子中,是通过路由器来进行GRE协议的封装和解封的, 在VXLAN中这类封装和解封的组件有个专有的名字叫做VTEP。相比起VLAN来说,好处在于其突破了VLAN只有4094子网的限制, 同时架设在UDP协议上后其扩展性提高了不少(因为UDP是高层协议,屏蔽了底层的差异,换句话说屏蔽了二层的差异)。

OVS 安装与使用

linux可以直接安装sudo apt-get install openvswitch-switch,但是执行ovs-tcpdump报错:

    lk233@vm-5gc:~$ sudo ovs-tcpdump 
    Traceback (most recent call last):
    File "/usr/bin/ovs-tcpdump", line 27, in <module>
        import netifaces
    ImportError: No module named netifaces

可是我python3使用pip3 -l查看的包里有netifaces

检查发现系统默认pip是python3,可是执行python默认是python2 😅

# 如下检查版本和 使用alternatives选择默认版本
pip -V
python -V

sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.6 2
sudo update-alternatives --config python

ovs-tcpdump -h
ERROR: Please install the correct Open vSwitch python support
       libraries (version 2.9.0).
       Alternatively, check that your PYTHONPATH is pointing to
       the correct location.
# 如果之后报如上的错误安装ovs python 支持包
pip3 install ovs
# 或者这里下源码包执行安装 https://pypi.org/project/ovs/#modal-close
sudo python3 ./setup.py install

一切安装完毕后,可以检测到后台服务与控制版本信息

OVS 基础教程:

docker容器使用OVS教程

当然OVS的功能不局限于这些,没有必要花费大量事件学习如何使用这个工具,知道大致的思路和运行逻辑,就需要思考怎么将ovs运用到实际中。 下面是一些基础应用:

  1. Open vSwitch with KVM
  2. Encrypt Open vSwitch Tunnels with IPsec
  3. Open vSwitch with SELinux
  4. Open vSwitch with Libvirt
  5. Open vSwitch with SSL
  6. Using LISP tunneling
  7. Connecting VMs Using Tunnels
  8. Connecting VMs Using Tunnels (Userspace)
  9. Isolating VM Traffic Using VLANs
  10. Quality of Service (QoS) Rate Limiting
  11. How to Use the VTEP Emulator
  12. Monitoring VM Traffic Using sFlow
  13. Using Open vSwitch with DPDK

实际应用-多主机间容器通信

方案选型

我最近需要做分布式容器间的通信相关工作,如果按照传统的方案建立隧道维护相当麻烦,于是想到采用ovs的方案来做。

下面是物理场景的大概的示意图:

  • 方案1:gre隧道 ovs的gre与命令行的直接搭建gre隧道做点对点链路无异,该方案即为 Connecting VMs Using Tunnels,在实际测试中我添加多个相同主机间隧道会报错,所以没法用多个网桥配多个gre隧道间隔主机对间的流量。

  • 方案2: vlan 使用vlan(Isolating VM Traffic Using VLANs)可以做到全部主机attach到一个网桥上从而多主机通信,但是主机间的接入网卡由于工作在二层设备下ip地址及绑定其ip程序都会失效,不是很推荐使用这个。

  • 方案3: vxlan隧道 所以应该用 ovs 管理一个vxlan隧道局域网, OVS在计算和网络节点上建立隧道Port来连接各节点上的网桥br-int(br-int是举例的网桥名),这样所有网络和计算节点上的br-int互联形成了一个大的虚拟的跨所有节点的逻辑网桥(内部靠tunnel id或VNI隔离不同子网),这个逻辑网桥对虚拟机和qrouter是透明的,内部数据不变转发出机器的数据就像交换机根据tag进行转发传递数据。

于是参考了这个 Connecting VMs Using Tunnels (Userspace) 方案

多个主机上桥接到br-vxlan的虚拟机就像连接到同一个交换机一样,可以实现跨主机的L2连接,同时又完全与物理网络隔离。 所有数据都到达中间主机转发寻路 与gre隧道类似,相同主机间添加多个vxlan隧道会报错。

疑惑:

vxlan问题在于主控主机作为 OVS交换机,若是host2内容器2向host3主机容器3发送数据,host1依旧会接收并转发数据包,但实际上host1向host3的数据完全走物理交换机一跳即可,不知道数据量大了后所有数据都汇总在host1进行处理转发是否会达到性能瓶颈

具体实施

# 查看OVS网桥及其端口
sudo ovs-vsctl show

# 设定主网桥 br-vxlan
# 主机 192.168.163.134 上
sudo ovs-vsctl add-br br-vxlan
# 主机 192.168.163.140 上
sudo ovs-vsctl add-br br-vxlan
# 主机 192.168.163.141 上
sudo ovs-vsctl add-br br-vxlan

# 网卡添加至对应网桥
sudo ovs-vsctl add-port br-vxlan veth1.0.df
sudo ovs-vsctl add-port br-vxlan veth2.0.2e
sudo ovs-vsctl add-port br-vxlan veth3.0.ec

添加通信隧道(双向添加)
# 主机 192.168.163.140上 添加连接到 192.168.163.134 141 的Tunnel Port
sudo ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.163.134
sudo ovs-vsctl add-port br-vxlan tun1 -- set Interface tun1 type=vxlan options:remote_ip=192.168.163.141

# 主机 192.168.163.134上 添加连接到 192.168.163.140 的Tunnel Port
sudo ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.163.140

# 主机 192.168.163.141上 添加连接到 192.168.163.140 的Tunnel Port
sudo ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.163.140

当在用户空间运行Open vSwitch而不是基于内核的Open vSwitch时,需要这个额外的网桥br-phy。这个网桥的目的是允许使用内核网络堆栈进行路由和ARP解析。数据路径需要查找路由表和ARP表,准备隧道头并将数据发送到输出端口。

添加phy网桥后原有容器内数据无法ping通,可能是由于路由没配好? 但是按照手册加不上路由 所以最后我是跳过phy和ens33attach步骤,结果也正常。

# 设定phy网桥()
sudo ovs-vsctl --may-exist add-br br-phy \
    -- set Bridge br-phy datapath_type=netdev \
    -- br-set-external-id br-phy bridge-id br-phy \
    -- set bridge br-phy fail-mode=standalone \
         other_config:hwaddr=<mac address of eth33 interface>

sudo ovs-vsctl add-br br-phy  \
    -- set bridge br-phy fail-mode=standalone \
         other_config:hwaddr=00:0c:29:ef:1b:c5

# 将物理网卡ens33 attach 上 br-phy网桥
sudo ovs-vsctl --timeout 10 add-port br-phy ens33
sudo ip addr add 192.168.163.140/24 dev br-phy
sudo ip link set br-phy up
sudo ip addr flush dev eth1 2>/dev/null
sudo ip link set ens33 up
sudo iptables -F

# 添加 VXLAN 路由
# 按照手册 ovs-appctl ovs/route/add 添加路由失败,暂时未解决。
# 显示 VXLAN 路由
ovs-appctl ovs/route/show

检验现象:可以在容器内互相ping通172网段ip即可

gre隧道的做法与vxlan中不加br-phy的做法基本一样,就不复述了,vlan的方案上面的教程写的很详细。

抓包测试

若不是使用容器或者其他网络空间(netns )的设备是抓不到下面的port的包,而且传输的数据可能是封装过后的,不太好分析。所以试试手册里提到的ovs-tcpdump。它是用python实现的捕获ovs的数据包的tcpdump补丁。(ovs−tcpundump程序从stdin读取tcpdump−xx输出,查找十六进制数据包数据,并在stdout上将每个以太网转储为一个十六进制字符串)

感觉这个程序很久不维护了,使用python3执行有很多问题。

# 首先使用管理员模式, 否则下述报错
Exception: Unable to connect to /var/run/openvswitch/db.sock

# 之后的报错是因为rw不支持,推荐将 rw 改为 r+b
File "/usr/bin/ovs-tcpdump", line 65, in _install_tap_linux tapdev_fd = open('/dev/net/tun', 'rw')
ValueError: must have exactly one of create/read/write/append mode

# 后面又报错,查询资料说是python3不兼容的问题,把缓冲改为0 即open函数 传参 buffering = 0
io.UnsupportedOperation: File or stream is not seekable

#最后依旧是有问题无法抓包 抓包失败并且提示关闭监听端口
Please use ovs-vsctl to remove the ports and mirrors created.
ex: ovs-vsctl --db=unix:/var/run/openvswitch/db.sock del-port mib.4.db

修改后的代码:

之后的抓包依旧有问题,且没有报错提醒 暂时无法解决

实在是太折腾了,一个工具用的这么麻烦 😒我用的是lxc容器各链路使用ovs链接,完全可以执行容器内的tcpdump。

看到还有其他方法:使用端口镜像来抓包, ovs-tcpdump应该也是这么监听的所以直接按照这篇博客试了试,实践后确定可以抓没有tag流的原始包

注意: tcpdump 抓包时加-l可以不缓存实时刷新

最后 Vxlan方案 的host1的容器1内的抓包结果验证了我之前的问题

数据包包含vlan tag且中间的主控主机参与了数据的处理转发,host2与host3间的容器互相ping,都会是host1转发了该icmp包。request和reply都传送了两次即host1-host2、host1-host3两条隧道都走了。在host2、host3间加直达隧道也无效,依旧host1参与处理转发。

也许直达链路最好的还是添加隧道做直达处理,如果多个主机接入一个虚拟交换机处理,该虚拟交换机是需要处理所有流经的数据包的。

关于下属子节点间的数据区分,可以像vlan一样直接加个tag vxlan添加tag,总之 做ovs交换机的主机必然是需要接收所有数据 由它做数据区分 转发。

流表监听

下面是一些常用的监听命令:

查看流表:
ovs-ofctl dump-flows br-tun
查看port收发包情况:
ovs-ofctl dump-ports br-tun

当然参考了这篇文章后Monitoring VM Traffic Using sFlow监听数据更方便了。

为了防止Java环境设置、安装软件等一系列配置环境的问题,后面的 sFlow 监听我使用 sflowtrend docker,确保具备docker环境。之后我会写一篇关于docker、lxc容器的文章。

配置完docker必要环境后,直接pull镜像并运行即可。

docker pull sflow/sflowtrend
docker run -v <你的映射路径>:/var/local/sflowtrend-pro \
-p 6343:6343/udp -p 8087:8087 -p 8443:8443 \
-h sflowtrend-pro -e TZ=Asia/Chungking<使用北京时间时区改为Asia/Shanghai> -d \
--restart unless-stopped sflow/sflowtrend

以如下网络做示例监听:

后面以第一种情况做示例

查询docker网卡ip:

docker ps #查看运行容器
docker inspect --format '{{ .NetworkSettings.IPAddress }}' 容器id #查看容器ip

开始设定监听sFlow

# 收集监听数据的容器或应用进程主机的ip 及端口(默认6343)
COLLECTOR_IP=192.168.254.2
# COLLECTOR_IP注意要跟sFlow系统配置的COLLECTOR_IP相同
COLLECTOR_PORT=6343
# 发送监听数据的网桥的主机与收集数据的监听主机通信使用的接口
# 由于是同主机使用的docker,所以所有容器都attach在docker0网桥上,一般此处为ens33
AGENT_IP=docker0
HEADER_BYTES=128 # 帧头长度
SAMPLING_N=64    # 采样率
POLLING_SECS=10  # 轮询时间
# 监听ovs网桥
m_bridge=b.5.35

sudo ovs-vsctl -- --id=@sflow create sflow agent=${AGENT_IP} \
    target="\"${COLLECTOR_IP}:${COLLECTOR_PORT}\"" header=${HEADER_BYTES} \
    sampling=${SAMPLING_N} polling=${POLLING_SECS} \
      -- set bridge ${m_bridge} sflow=@sflow

# 执行成功后返回 sFlow UUID
# 删除sFlow监听项目
sudo ovs-vsctl remove bridge ${m_bridge} sflow <sFlow UUID>
# 查看全部监听
sudo ovs-vsctl list sflow

浏览器打开 http://localhost:8087 或 https://localhost:8443, 如果是远程主机访问将localhost改为运行这个容器/app的主机IP

这里有一些基本的使用说明

最后结果采集的很详细,但是主机负载之类的数据没有采集,应该是需要snmp协议监听才有。

sFlow、NetFlow、SNMP三者之间有什么不同?

网络拓扑: 网络拓扑

筛选流量: 筛选流量