OpenVSwitch实现浅谈(一)

2020-07-01 00:00:00 模块 内核 虚拟 网络 数据包

在过去10几年里面,虚拟化已经改变了应用,数据,服务的实现部署方式。据Gartner 2016的报道[1],80%的x86 workload已经是虚拟化,其中大部分是虚拟机,但是容器所占比例正以极快的增长速度。

服务器的虚拟化给数据中心网络带来了根本性的变化。在传统的数据中心网络架构基础上,出现了一个新的,位于物理服务器内的接入层。这个新的接入层包含的设备是运行在x86服务器中的vSwitch,而这些vSwitch连接着一个服务器内的多个workload(包括容器和虚机)。

vSwitch的早期代表是Linuxbridge,它在设计之初就是为了提供基本的网络连接,因此它只是模拟了ToR交换机的行为,并将自己接入到现有的物理网络中。这样实现的优点是,现有物理网络的理论和协议可以直接套用,不需要重复设计。缺点就是,作为物理网络的延伸,使得虚拟workload的网络与物理网络紧耦合,影响了虚拟化本身带来的诸如灵活,快速上线的优势。

随着网络虚拟化(network virtualization)的出现,为连接虚拟workload的网络提供了另一种可能。物理网络仍然由物理网络设备管理,虚拟workload的网络,单独由vSwitch管理,并且在现有物理网络(underlay)基础之上,再定义一个独立的overlay网络(例如VxLAN)。这个overlay网络不受物理网络设备控制,完全由vSwitch控制。

OpenVSwitch就是基于这个设计思想实现的。OpenVSwitch是一个多层的,开源虚拟交换机。不过到目前为止,LinuxBridge也支持了VxLAN,OpenVSwitch也能够支持对应于物理网络的VLAN,FLAT网络。

OpenFlow

除了基于overlay网络的思想设计以外,OpenVSwitch的另一大特点就是基于OpenFlow。

传统的交换机,不论是硬件的,还是软件的,所具备的功能都是预先内置的,需要使用某个功能的时候,进行相应的配置即可。而OpenVSwitch通过OpenFlow实现了交换机的可编程。OpenFlow可以定义网络包在交换机中的处理流程(pipeline),因此支持OpenFlow的交换机,其功能不再是固定的,通过OpenFlow可以软件定义OpenVSwitch所具备的功能。

OpenFlow以多个Table串行工作的方式来处理网络数据包,如下图所示。

OpenFlow的灵活性是实现SDN必不可少的一部分,但是在一些实际场景中,因为涉及的功能多且复杂,相应的OpenFlow pipeline会变得很长。直观上来看,pipeline越长,处理一个网络包需要的时间也越长。这是OpenFlow从理论到实际的一个问题,OpenVSwitch为此尝试过很多优化。

对于一个Linux系统来说,可以分为用户空间(user space)和内核空间(kernel space),网络设备接入到内核空间。如果需要将数据传输到用户程序则需要通过内核空间将数据上送到用户空间,如果需要在网络设备之间转发数据,直接在内核空间就可以完成。

作为运行在x86服务器中的软件交换机,直观上来看,应该在内核空间来实现转发。因此,OpenVSwitch在早期的时候,在Linux内核模块实现了所有的OpenFlow的处理。当时的OpenVSwitch内核模块,接收网络数据包,根据OpenFlow规则,一步步的Match,并根据Action修改网络数据包,后从某个网络设备送出。

但是这种方式很快就被认为是不能实际应用的。首先,虽然在内核实现可以缩短网络数据包在操作系统的路径,但是在内核进行程序开发和更新也更加困难,以OpenVSwitch的更新速度,完全在内核实现将变得不切实际。其次,完全按照OpenFlow pipeline去处理网络包,势必要消耗大量CPU,进而降低网络性能。

因此,新版本(2.x版本)的OpenVSwitch采用了一种很不一样的方式来避免这些问题。

OpenVSwitch架构

现在OpenVSwitch主要由三个部分组成:

  • ovsdb-server:OpenFlow本身被设计成网络数据包的一种处理流程,它没有考虑软件交换机的配置,例如配置QoS,关联SDN控制器等。ovsdb-server是OpenVSwitch对于OpenFlow实现的补充,它作为OpenVSwitch的configuration database,保存OpenVSwitch的持久化数据。
  • ovs-vswitchd:运行在用户空间的转发程序,接收SDN控制器下发的OpenFlow规则。并且通知OVS内核模块该如何处理网络数据包。
  • OVS内核模块:运行在内核空间的转发程序,根据ovs-vswitchd的指示,处理网络数据包

OpenVSwitch中有快速路径(fast path)和慢速路径(slow path)。其中ovs-vswitchd代表了slow path,OVS内核模块代表了fast path。现在OpenFlow存储在slow path中,但是为了快速转发,网络包应该尽可能的在fast path中转发。因此,OpenVSwitch按照下面的逻辑完成转发。

当一个网络连接的个网络数据包(首包)被发出时,OVS内核模块会先收到这个packet。但是内核模块现在还不知道如何处理这个包,因为所有的OpenFlow都存在ovs-vswitchd,因此它的默认行为是将这个包上送到ovs-vswitchd。ovs-vswitchd通过OpenFlow pipeline,处理完网络数据包送回给OVS内核模块,同时,ovs-vswitchd还会生成一串类似于OpenFlow Action,但是更简单的datapath action。这串datapath action会一起送到OVS内核模块。因为同一个网络连接的所有网络数据包特征(IP,MAC,端口号)都一样,当OVS内核模块收到其他网络包的时候,可以直接应用datapath action。因此,这里将OVS内核模块与OpenFlow协议解耦了,OpenFlow的小改动影响不到内核模块。

这样,成功的解决了之前的问题。首先,内核模块不用关心OpenFlow的变化,不用考虑OpenVSwitch的代码更新,这些都在ovs-vswitchd完成。其次,整个网络连接,只有首包需要经过OpenFlow pipeline的处理,其余的包在内核模块匹配网络包特征,直接应用datapath action,就可以完成转发。

OVS内核模块通过缓存来保持datapath action的记录。稍微早期的内核模块实现了名为microflow的缓存,这个缓存就是一个hash map,key是所有OpenFlow可能匹配的值对应的hash值,包括了网络2-4层header数据和一些其他的metadata例如in_port,value就是datapath action。hash map可以实现O(1)的查找时间,这样可以在OVS内核模块中实现高效的查找转发。

看起来似乎是个完美的方案,但是在实际应用的时候存在一些问题。下一篇讲一下具体的问题和解决方法。

相关文章