linux内核netdev_max_backlog设置在极端情况下导致openvswitch转发不通

近期在调试问题时,遇到了一个从配置上看openvswitch无任何问题,但却导致转发不通的问题,特此记录下说,说明在openvswitch在大规模部署时,仍需要有较多调优之处。

设想如下openstack场景,大量tenant router通过linux namespace即仿真vrouter访问外网,此namespace中通过openvswitch internal port(或veth,如果不追求转发性能)连接到另一公网namespace,此时如果公网namespace中接口数量过多,比如说2000个(比如openstack中的qg-*接口),将有可能面临转发不通的情况。

从openvswitch的角度,我们知道openvswitch在内核态存在一个datapath,负责生成内核流表,实现高性能转发,对于一个拥有上千个port的ovs bridge来说,比如对应于上面的公网namespace,收到一个arp请求报文,由于是广播,报文通守openvswitch在userspace命中默认的normal流表后将推送广播至其他所有成员端口的内核流表,此后arp报文广播复制逻辑将在内核datapath模块处执行,最终命中以下内核代码:

:::c
/*
* enqueue_to_backlog is called to queue an skb to a per CPU backlog
* queue (may be a remote CPU queue).
*/
static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail)
{
    struct softnet_data *sd;
    unsigned long flags;

    sd = &per_cpu(softnet_data, cpu);

    local_irq_save(flags);

    rps_lock(sd);
    if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {
        if (skb_queue_len(&sd->input_pkt_queue)) {
enqueue:
	        __skb_queue_tail(&sd->input_pkt_queue, skb);
		     input_queue_tail_incr_save(sd, qtail);
             rps_unlock(sd);
             local_irq_restore(flags);
             return NET_RX_SUCCESS;
		 }

         /* Schedule NAPI for backlog device
          * We can use non atomic operation since we own the queue lock
          */
         if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) {
             if (!rps_ipi_queued(sd))
                 ____napi_schedule(sd, &sd->backlog);
         }
         goto enqueue;
    }

    sd->dropped++;
    rps_unlock(sd);

    local_irq_restore(flags);

    atomic_long_inc(&skb->dev->rx_dropped);
    kfree_skb(skb);
    return NET_RX_DROP;
}

可以注意到在上面的代码中有一个对sd->input_pkt_queue的检查过程,而sd是每个cpu核心net rx soft irq用于维护待处理报文的队列,netdev_max_backlog则是内核所提供的一个配置选项,默认为1000。

在ovs datapath复制报文的过程中,将对上千个port进行遍历处理,这个处理过程中不会进行cpu释放的操作,也即将持续调用enqueue_to_backlog(openvswitch datapath最初调用的是netif_rx,但最终调到enqueue_to_backlog),那么默认的1000限制将很快达到,假如上述的qg-*所公用的外网网关碰巧位于1000个以后,那么很显然,某个vrouter namespace发过来的ARP请求则无法被发送给公网外网网关接口,导致vrouter所对应的内网无法访问外网,如果是在生产环境中,势必影响客户业务。

对于此问题的修改,有如下两种思路:

  • 直接提高netdev_max_backlog数量,根据可能存在的接口数量合理设置
  • 通过sdn控制器配置arp或动态回复arp请求

由于目前项目中所采用的方案,我们通过方案1进行了改造,较好的解决了此问题。

OpenStack网络方案近期状态分析

目前OpenStack网络方案存在有较多的选择,liberty发布已经有大半多时间,mikata版本也快要发布,有必要比较一下近期各种openstack网络方案的状态。

主流方案介绍

当前,比较主流的方案有如下几种:

  • ovs-agent
  • ofagent
  • ovn
  • dragonflow
  • midonet/opencontrail

除此之外,还有一些闭源的商业方案,如nsx、plumgrid,以及一些试验性的开源方案如networking-odl等,本次分析暂不考虑。

现有网络方案特性集及特点

  • ovs-agent
功能点支持情况备注
L2 isolation using overlayY支持vlan/vxlan/gre
arp responderY要求ovs 2.1 + l2pop使能
L3 DVRY要求每台计算节点都需要安装l3-agent,通过netns及linux协议栈实现
ovsdb nativeY独立功能,与openflow native无关,可单独开启,有一定的性能提升
openflow nativeYovs-agent将内置ryu,运行一个of app用来处理与本地ovs的交互,基本逻辑与原ovs-agent相似,通过driver机制实现不同方式的bridge操作,目前仍处于实验阶段
security groupY目前仍基于iptables,随着ovs conntrack功能的完善,可以演进至基于ovs conntrack实现
dhcpY仍采用dhcp agent
north-sourth流量Y仍采用l3agent进行集中式转发,即便在dvr模式下
  • ofagent

如同ofagent在openstack上的wiki所述:

OFAgent is a neutron core-plugin, implemented as ML2 mechanism driver. It aims to support pure OpenFlow1.3 switches.

ofagent关注的更多是指向向设备(如vswitch/pswitch)可移植性,因此基于纯openflow协议进行实现,而纵观业界,基本上没有基于纯openflow的openstack网络方案(目前仍成功商用的则是bigswitch的big fabric,但其采用了深度修改的openflow),因此这种方式可能是过于理想化的方案,可能并不一定能够容易落地。

从ovs的状态来看,ovs 2.5中为支持conntrack,引入了ct_state/ct这样的match/atction,这些nicira扩展落入openflow spec的时间点可能还比较长,而为了实现dv足够多的功能,ovs将会继续引入扩展,如果基于纯openflow实现的话,这些功能都将难以利用。

从wiki上的比较来看,ofagent也不支持dvr,实现上同样基于ryu,而部署模式上与ovs-agent相似,在compute/network节点都需部署。

  • ovn

ovn是由vmware所主导的新项目,其介绍如下所示:

OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. Services such as DHCP are also desirable features. Just like OVS, OVN’s design goal is to have a production-quality implementation that can operate at significant scale.

目前已经实现的功能集如下:

功能点支持情况备注
L2 isolation using overlayYhypervisor间仅支持geneve/stt,与l2gw之间可采用vxlan连接
arp responderY直接基于流表实现
L3 DVRY不依赖netns,基于ovs patch port及纯流表实现,对vrouter网关的icmp请求将由流表进行回复
ovsdb nativeYovsdb目前既做northbound db,又做southbound db,同时又做hypervisor上的本地db,但操作上均是通过c语言访问native接口实现的
openflow nativeY在hypervisor上需运行一个基于c语言实现的local controller,通过native openflow消息操作vswitchd
security groupN基于OVN ACL实现,目前已经提供部分代码,尚未实现
dhcpY暂时仍采用dhcp agent,后续将内置实现
north-sourth流量Y暂仍采用l3 agent,后续将内置实现,并预期支持L3HA

nn从功能点上来说,ovn还有大量的功能有待开发,并且很多功能一方面依赖于ovs,一方面依赖于kernel,尽管邮件列表上计划在mikita版本有试验性的支持,从当前现状来看,达到生产级质量水平的可能性较小。

从架构上来看,尽管ovn也认识到northdb、southdb目前都存在ovsdb-server上存在单点故障并且ovsdb尚不支持clustering,但目前仍关注于feature的完备性,故而还未投入过多精力处理。

  • dragonflow

Dragonflow implements Neutron using a lightweight embedded SDN Controller. Dragonflow is available in two configurations: Distributed and Centralized. Our project mission is to Implement advanced networking services in a manner that is efficient, elegant and resource-nimble

dragonflow由华为以色利团队开发,相比ofagent那种仅为提升性能而采用ryu实现openflow native语义的方式,dagonflow更为激进,采用了ryu app来实现l2转发及l3 dvr,并且从代码上来看,dhcp功能也已经做为ryu app实现了,并且dragonflow强调自己实现了pluggable db的支持,即通过引入外置第三方db来实现数据同步,与openstack rpc解耦,相比ovn采用ovsdb来说,db层面的可靠性及扩展性更好。

目前dragonflow的功能如下:

功能点支持情况备注
L2 isolation using overlayY支持vxlan/gre
arp responderY基于流表实现
L3 DVRY基于流表实现,无需netns
ovsdb nativeY通过ovsdb python idl访问ovsdb
openflow nativeY内置ryu,通过openflow协议处理packet-in及流表
security groupN计划采用ovs conntrack实现,但尚未开发
dhcpY采用运行在ryu里的dhcp app,实现分布式dhcp
xnorth-sourth流量Y仍采用l3agent进行集中式转发
  • midonet

midonet是midokura公司开源的openstack网络方案,核心实现是基于分布式数据库(zookeeper)实现neutron数据分发,并通过ovs kmod实现转发,但在用户态完全舍弃ovs,采用自己通过java+scala实现的本地控制器,其核心转发过程称为simulation,即通过本地控制器所获知到的虚拟网络拓朴,来判断一个报文最终的转发结果,并将结果添加到ovs kmod所维护的内核态流表中。

国外有部署云客户采用该公司的方案,国内据说乐视私有云采用了该方案。

目前midonet的功能如下:

功能点支持情况备注
L2 isolation using overlayY支持vxlan
arp responderY由本地控制器仿真实现
L3 DVRY由本地控制器仿真实现,将下发至内核态流表
security groupY由本地控制器仿真实现
dhcpY由本地控制器实现
north-sourth流量Y支持DNAT/SNAT,并实现多l3时的snat同步,并可以和外部通过bgp进行l3对接
load-balancerY由HA Proxy实现
  • opencontrail

opencontrail为juniper所开发的独立网络虚拟化方案,采用专用的内核模块实现virtual network,不依赖于ovs,并采用大量的私有或公有如bgp协议实现控制平面,与juniper的设备有较好的对接,国外部分公有云如tcpcloud等采用其方案。

从部署上来说该方案与现有方案差异较大,不做过多描述。

选型建议

从上述分析来看,除社区ovsagent外,midonet/opencontrail是较为完善的方案,但前者社区较小(尽管目前部分代码已经托管至openstack官方),且采用小众语言开发,深入定制有一定的难度;如果半年内要升级的话,ovsagent似乎是较为可行的选择,半年后可以关注ovn/dragonflow等相关项目的状态再做选择。

至于转发性能方案,各方案主要还是解决管理上的复杂度,性能上dvr算是一个较大的亮点,转发性能上可以通过后续引入对组网影响较小的vxlan offload nic来提升。

Midonet聪明的Tunnel Key分配策略

拥抱开源如今成了一种潮流,既Juniper家的OpenContrail成为开源一揽子(之所以称为一揽子是用于区别现有OpenStack中由社区所维护的基于分散组件的松耦合方式)OpenStack网络虚拟化方案的首发明星后,Midukura这家公司也将自家的OpenStack网络虚拟化方案以开源方式来运作了,相比于OpenContrail的工程师的设备商研发背景,Midukura更具有IT运维背景(这点可以从其技术堆栈如scala编程语言、zookeeper/cassandra数据库、分布式架构推测出来,关于更细节的内容,后面我会陆续补充相关的分析),因此我个人还是更看好Midokura的方案,而从自己实际部署及验证的情况来看,Midokura要靠谱的多一点,当然这只是一家之言,也并非本文的重点,就不再展开。

在本文中我想谈的是,Midonet Tunnel Key的使用策略,与我们通常在基于OVS的方案中看到Tunnel Key(不论是NVGRE还是VXLAN)一般用做租户标识(即Virtual Network Identifier)不同,Midonet Tunnel Key则可以理解为按VIF分配的(实际上是基于Midonet的外部虚拟端口分配的,这里为了简化理解,可以简单认为就是按VIF分配的),而前者其实也是IETF相关标准文档所描述的用法。

Midonet之所以这样做的原因,与其架构实现有较大的关系,Midonet的架构总结起来,有如下的这几种特色:

  • 全分布式架构,每个节点上运行一个Midolman进程(跑在JVM上),用于负责从分布式数据库(zookeeper)同步并发布虚拟网络配置信息,因此实现了去中心化,每个节点可以独立计算出转发结果
  • 虚拟拓朴仿真机制,Midolman从VIF上收到报文后,通过拓朴转发仿真(与我们常规的Bridge->Router->Router->Bridge转发类似),就可以计算出目的VIF,从而得知目的虚拟机所在的主机,并在通过Underlay网络的IP以NVGRE/VXLAN的方式将报文Overlay传送给目的主机前就将报文编辑好
  • 内核转发采用OVS datapath,即采用exact match的flow转发(megaflow暂不支持),以实现次包(即首包之后的包)的内核转发,提高转发性能

结合上面的介绍,Midonet采用基于VIF分配的原因就比较清楚的:

  • 即然源端可以直接仿真出目的VIF且完成报文编辑,因此到对端唯一需要做的就是知道对端的VIF对应的OVS datapath接口是什么,NVGRE/VXLAN封装中的Tunnel Key则提供了一个简单方便的编码点,于是midonet就这么用了,另外一个Host上通过Zookeeper分配的Tunnel Key空间有10位数(非标准限制,而是Zookeeper的限制),对于一个cpu有限、memory有限的主机来说,10位数的VM已经远超能力极限的(咱们又不是天河一号,天河一号估计也不行),已经足足够用了
  • Midonet采用与OVS相似的机制,转件层面采用wildcard match流表,内核采用exact match流表,因此在主机上的Midolman扫描到VIF对应的TAP接口时,Midolman就可以为这个VIF生成一条匹配域为该VIF在该主机上所申请到的Tunnel Key为匹配项的wildcard match流表,其出接口即为该VIF所对应的OVS datapath接口,当收到含有该Tunnel Key的报文时,可以直接命中该wildcard match流表,提取报文字段生成exact match流表出接口继承该wildcard流表向OVS datapath下发即可,Wildcard match流表查询及exact match流表下发会非常迅速,也弥补了Java/Scala代码在未被JIT编译器优化时的性能损失,是一个非常优雅的方案

以上便是我个人对于Midonet Tunnel Key分配的理解,在软件定义网络这股风潮日近的今天,来自于互联网IT企业所带来的新思维,新用法,也许会继续改变设备商的开发模式,设备商要想办法适应并拥抱这种变化了。