不宜使用链路聚合的场景

最近碰到的一个诡异的案例,让我对链路聚合有了更深入的理解,可能对你也会有所启发。

问题

需要从多台相机采集图片传给电脑,总流速超过千兆,但在4千兆以下。考虑到万兆网卡和(万兆上行端口的)交换机都还太贵,决定还是使用链路聚合。

交换机上用4个千兆口连接电脑,电脑上使用一块4口千兆网卡做聚合。

不宜使用链路聚合的场景_第1张图片
图1:相机与交换机(最右边4个端口聚合连接电脑)

最初的测试

尽管我的目标是10台相机同时传数据给电脑,但第一步还是用4个相机做测试。4个相机对应于4个网口,预期的效果是应该和划分为4个子网的方式相同,即每个相机都可以跑到接近千兆。

为什么不用4个子网呢(即一个网口一个子网,带一个相机)?——因为聚合的方案配置简单,且更灵活,可以适用于N个相机,能够动态平衡负载。相当于电脑和交换机有一条4千兆的链路,随便多少个相机去分这个带宽。

聚合的缺点是有可能增加CPU负载。但这个要看聚合的模式。由于电脑主要是收数据,交换机转发来自相机来数据,所以需要交换机端也支持聚合。

使用的交换机是支持802.3ad的,所以在电脑端也要配置为802.3ad模式。下面是Ubuntu下的配置文件。

auto enp5s0f0
  iface enp5s0f0 inet manual 
  bond-master bond0
 
auto enp5s0f1
  iface enp5s0f1 inet manual 
  bond-master bond0

auto enp5s0f2
  iface enp5s0f2 inet manual 
  bond-master bond0
 
auto enp5s0f3
  iface enp5s0f3 inet manual
  bond-master bond0

auto bond0
  iface bond0 inet static
  address 10.8.5.1
  netmask 255.255.255.0
  mtu 9000 

  bond-slaves enp5s0f0 enp5s0f1 enp5s0f2 enp5s0f3
  bond-mode 802.3ad
  bond-miimon 100
  bond-lacp-rate 1

实际测试下来,网卡聚合驱动(bonding)的CPU占用不高(可以通过/proc/interrupts查看)。

在交换机管理界面上可以看到数据流量的情况。

不宜使用链路聚合的场景_第2张图片
图2:4对4流量均衡

图中可以看到连接相机的4个网口和连接电脑的4个网口流量相当,并且是均匀的。这个效果完全符合预期。

进一步的测试

于是开始增加更多的相机(当然,同时把相机的流速调低,以满足总速度不超过4千兆),但发现4个网口的流量并不均匀!

于是减少相机,退回到4个相机的情况。———诡异的事出现了,4个相机也不正常了!表现为流量只走3个网口(当然会有一个网口超流量,表现为丢帧)。

继续减少相机,结果,,3个相机也挤在两个网口上。如图(我甚至还换了一台交换机做测试)。

不宜使用链路聚合的场景_第3张图片
图3:3对2流量不均衡

不断地尝试后,发现了一点规律。

比如说A, B, C 三个相机发数据,正常(分流到3个端口);

加入相机D后,不正常,表现为A和D都走了交换机的同一端口。

这时,即使只接A和D,也是不常的,因为它俩还是走同一端口;

问题出在哪里?

碰到这种问题,首先想到的是,是不是有参数配置不对?

首先看聚合模式,有好几种流量均衡的模式,比如:balance-rr, balance-xor, balance-tlb。但它们都是说的电脑端发送数据的情况,用不同的算法,选择从哪个网口将数据发出。而我的情况是交换机发数据,交换机选择从哪个端口将数据发出,那几种模式都不是用于控制交换机的。

在交换机上,如果启用聚合,就是使用交换机支持的802.3ad模式,这时,电脑端相应的也要配置为802.3ad模式,所以对于模式,我们没什么选择的余地。

再看802.3ad模式下,还有什么参数可以配置?

有一个相关的参数是:transmit hash policy

在802.3ad模式下,有layer2和layer2+3可以选择。看文档中的说明:

xmit_hash_policy

Selects the transmit hash policy to use for slave selection in
balance-xor, 802.3ad, and tlb modes.Possible values are:

layer2

Uses XOR of hardware MAC addresses and packet type ID
field to generate the hash. The formula is

hash = source MAC XOR destination MAC XOR packet type ID
slave number = hash modulo slave count

This algorithm will place all traffic to a particular
network peer on the same slave.

This algorithm is 802.3ad compliant.

这个文档来自于Linux Ethernet Bonding Driver HOWTO。由于是标准,我们可以认为交换机上也是使用同样的算法。

这个算法的意思是:用源地址、目标地址以及包类型得到一个hash值,然后除以4(聚合的端口/网口数),根据余数,决定数据发往哪个端口。

这里面首先说明了,来自同一个相机的数据(这里的应用协议是固定的,所以packet type ID固定)一定是发往同一个端口的,而不是像Round-robin这样的策略,使用不同的端口轮流发送。

第二点说明了,它是根据hash降以总端口数的余数来选择端口。另一个策略 layer2+3 我没有贴出来,它只不过是hash算法不同,最后也是看余数。

那么,问题就在这里了。

我的运气有多好?

这是一个数学问题:随机生成4个0~3之间的整数,它们完全不同的概率是多少?

概率组合有点忘了,好像应该是 3/4 * 2/4 * 1/4 = 3/32 = 9.375%.

这就是在交换机上随便接4个相机,在4个端口聚合的模式下,它们的数据正好从4个不同的端口转发出去的概率。

我最初接的4个相机正好中了这9.375%的彩。而后来回退到4个相机的时候,其实是慌乱中换了相机,接着再怎么换相机,都有冲突。因为90.625%的概率就是会相撞,所以有冲突才是常态。

模拟实验

为了理解什么场景下适合使用聚合,我们可以做一个模拟实验。

交换机使用4个端口聚合。模拟在接N个设备的情况下,每个端口会转发多少个设备的流量。

模拟代码(Python)如下:

import matplotlib.pyplot as plt
import random                                                                                        
                                                                                                     
def simulate(device, round=100):
    plt.figure(figsize=(10, 7))
    fof = [0 for x in range(device + 1)]                                                             
    for i in range(round):                                                                           
        freq = [0, 0, 0, 0]                                                                          
        for j in range(device):                                                                      
            port = random.randint(0, 3)                                                              
            freq[port] += 1                                                                          
        for f in freq:                                                                               
            fof[f] += 1                                                                              
    plt.plot(fof)
    plt.title("N of device traffic to ports: {} devices {} rounds".format(device, round))
    plt.show() 
  • 内循环用一个简单的随机数模拟每个设备来的流量会经哪个端口转发出去;
  • freq 统计每个端口转发了几个设备的流量;
  • 最后的结果fof是统计频率的频率。

举个例子,比如freq = [1, 2, 1, 0]表示:

  • 1个设备的流量走了端口0,
  • 2个走了端口1,
  • 1个走了端口2,
  • 没有流量走端口3;

相应的fof的统计是:

  • 0流量的情况加1次(没有流量走端口3),
  • 1流量的情况加2次(端口0和端口2各1次),
  • 2流量的情况加1次(端口1)。

模拟的结果:

不宜使用链路聚合的场景_第4张图片
图4:4设备模拟结果

在这个图中,可以看到,只有4个设备时,经常有端口没有流量。1万次试验就有1.25万个端口没流量,这几乎平均每次都有一个端口没流量,有端口没流量就意味着有的端口会转发来自至少两个设备来的流量,这就是前面说的流量不均衡的情况。

不宜使用链路聚合的场景_第5张图片
图5:40设备模拟结果
不宜使用链路聚合的场景_第6张图片
图6:400设备模拟结果

随着设备的增多,流量会越来越平均。比如400个设备时,一个端口大多数情况下都会转发100个左右(80~120)设备的流量。

结语

链路聚合适用于终端数量多,每个终端流速不太高的情况。这样很容易从统计上达到平衡,不至于出现某一聚合端口流速超出带宽的情况。其实这也是服务器上的常见场景。比如一台服务器,要接受成千上万的访问,每个访问的流量都不高,但总体上会超过单网口的带宽,这样就适合用多网口做链路聚合。

而对于我这个案例,终端数量少,每个终端的流速高(接近单网口的带宽),加上是终端发数据为主,交换机上的聚合模式有限,这样就很容易由于流量不平衡导致单个聚合端口超出带宽。

通过这个例子,还理解了为什么要有那么多种聚合模式、hash策略。比如服务器上的应用,可能并不能完全用Mac地址来做hash,于是有了layer3, layer4的hash,这样才可以标识区分internet上的终端,以达到流量均衡。

另一个感受是:在技术上,再诡异的事情总会有一个合理的解释。

你可能感兴趣的:(不宜使用链路聚合的场景)