假设想要两张卡都使用,对于出方向的流量,前文提到过 MAC flapping 问题:如果同一个 MAC 一会从端口 1 发出去流量,一会从端口 2 发出流量,交换机通过收到的包学习 MAC 地址会感到困惑。所以,如果两个 interface 都往外发送数据的话,不能使用相同的 MAC 地址。
这样,问题就解决了:对于发出去的流量,我们可以用两个 interface 来发,两个 interface 使用不同的 MAC 地址,对于交换机来说,就像是两个不同的主机。
对于接收流量,我们还是只能用一个 interface 来接收。这一点怎么做到呢?还记得别人是怎么发送流量给我们的吗?需要先发送 ARP 通过 IP 获得 IP 对应的 MAC 地址,然后才能将这个 MAC 封装为二层帧的目标来发送。所以,在回应 ARP 请求的时候,我们总是使用其中一个 interface 的 MAC 地址来回复。
接收端的流量如何切换呢?我们现在有两个 MAC 地址,切换接收端流量,就要让发送流量的地方来切换。一个很直观的解决思路是切换 ARP。即,让其他的发送者知道,现在应该发送给另一个 MAC 地址了。但是 ARP 太慢了,在 ARP 更新到发送者那里之前,流量都会因为发送到了挂掉的 MAC 地址而被丢弃掉。
那么有没有更快的切换方式呢?发送者的流量是发送给交换机,交换机再发送给我们的。所以一个更好的解决办法是让交换机来切换,交换机原来通过端口1发给我们,现在通过端口2发送我们。如果要实现快速切换,就要求不涉及去通知发送者,发送者还是使用原来的 MAC 地址进行发送。客户端的目标 MAC 地址不能变,但是交换机转发的端口改变,这不就和我们在 数据中心网络高可用技术之从服务器到交换机:active-backup 中讨论的 Gratuitous ARP 一样吗?不过这里需要额外做的事情是,我们的主 MAC 地址所在的 interface 已经挂了,这个 MAC 地址原来在 interface A 上,现在要转移到 interface B 上,需要交换两个 interface 的 MAC 地址。(所以,balance-tlb 模式需要硬件支持改写 MAC 地址才行。)
总结一下这个切换过程,我们要保持主 MAC 地址总是可用来做到不影响 ARP 缓存,快速切换,那么就要:
交换主和备的 MAC 地址,这样主 MAC 地址迁移到了备上,继续存活;
通过备 interface (现在的主)发送一个 GARP,让交换机来更新 Mac address table,对于目标 MAC 原先发送给端口1,现在发送给端口2.
2:bond0:<BROADCAST,MULTICAST,MASTER,UP,LOWER_UP>mtu1500qdisc noqueue state UP mode DEFAULTgroup defaultqlen1000
link/ether1a:50:9c:11:58:a3 brd ff:ff:ff:ff:ff:ff
35:eth0:<BROADCAST,MULTICAST,SLAVE>mtu1500qdisc fq_codel master bond0 state DOWN mode DEFAULTgroup defaultqlen1000
link/ether9e:fa:f1:b5:0c:28brd ff:ff:ff:ff:ff:ff
36:eth1:<BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP>mtu1500qdisc fq_codel master bond0 state UNKNOWN mode DEFAULTgroup defaultqlen1000
link/ether1a:50:9c:11:58:a3 brd ff:ff:ff:ff:ff:ff
与此同时,ping 也没有失败。
这个方案可以在发送方向使用两张卡,也能做快速的自动切换。有没有办法能够在接收方向也能用上两张卡呢?
Balance-alb 模式 (adaptive load balancing, mode 6)
要想让收到的流量也负载均衡,就涉及到发送者了。因为我们作为流量的接收方,有两个 MAC 地址,对于让流量发送到哪一个 interface 来说无能为力。
发送者是怎么决定发给哪一个 MAC 的呢?又回到了 ARP。
那么我们现在有两个 interface,分别有两个 MAC 地址,我们希望这两张 interface 各收到 50% 的流量,可以该怎么做呢?对于两个 MAC 地址,可以让 50% 的发送者发给 MAC AA,另外的 50% 的发送者发送给 MAC BB。具体来说,就是在响应 ARP 的时候,我们有时对别人说,这个 IP 对应的 MAC 地址是 AA,有时说此 IP 对应的是 BB。这样就可以对流量做到一定程度的负载均衡。这种方法叫做 ARP negotiation。
首先,我们在回答 ARP 问题的时候,对于相同的 Client,要给出相同的答案。不能让人家一会发送到 MAC AA,一会又发送到 MAC BB,这样可能造成 TCP 乱序影响性能,对于负载均衡也不好,无法保证两个 interface 收到的流量是均衡的。这个问题的解决方法就是追踪我们回复给不同的客户端什么 MAC 地址,后续对于此客户端都回复一样的 MAC 地址。
第二个问题更难。主机得到 IP 和 MAC 之间的对应关系,不光是靠发出去 ARP 问题来询问,还会从收到的 ARP 问题中学习。当一个主机 Z 收到 ARP 问题:「谁有 xx 的地址,如果有的话,请告诉 192.168.1.10,192.168.1.10 的 MAC 地址是 YY。」不管这个主机 Z 有没有 xx 的地址,它都会从这个问题中学习到:192.168.1.10 对应的 MAC 地址是 YY,并且缓存在自己的 ARP 缓存中。
157:eth0:<BROADCAST,MULTICAST,UP,LOWER_UP>mtu1500qdisc fq_codel state UNKNOWN mode DEFAULTgroup defaultqlen1000
link/ether a6:99:c3:0d:14:93brd ff:ff:ff:ff:ff:ff
158:eth1:<BROADCAST,MULTICAST,UP,LOWER_UP>mtu1500qdisc fq_codel state UNKNOWN mode DEFAULTgroup defaultqlen1000
link/ether82:e2:5d:48:67:fc brd ff:ff:ff:ff:ff:ff
我们怎么配置这两个网卡呢?
首先,假设我们给两个 interface 分别配置一个 IP 地址,这样的话,在交换机和其他设备看来,这个服务器其实是两个机器,因为它是两个不同的 IP,已经失去了高可用的意义了。因为软件是需要基于 IP 来部署和交互的,拿 Etcd 来讲,假设我们部署 Etcd 在这个机器上,它就只能 listen 其中的一个 IP。如果部署两个实例分别 listen 一个 IP,那在其他实例看起来,好像是有两个不同的实例,但其实是部署在一个物理机上,这样,反而降低了可用性。因为物理机一挂就会导致两个 Etcd 实例一起失败。综上,我们要让服务器对外看起来还是一个 IP 才行。
我们在考虑:假设给这两个网卡配置成一样的 MAC 地址行不行?这样,对外看起来只有一个 IP 和一个 MAC 地址,发送数据的时候可以用两条线,接收数据的时候,无论哪一条线收到都可以。看起来完美,但是忽略了一个重要的角色——交换机。
还记得交换机是怎么工作的吗?它从每一个收到的包学习 MAC 地址。两条线接到交换机上,对于交换机来说,这就是两个客户端。假设这两个网卡(客户端)的 MAC 地址一样,都是 MAC AA,那么当网卡 A 从交换机端口 1 发送数据出去的时候,交换机学习到:「MAC AA 对应端口1,发往 MAC AA 的数据都转发给端口 1」;但是当网卡 B 从端口 2 发送出去的时候,来源 MAC 地址也是 AA,交换机就学习到:「OK,现在 MAC AA 是在端口 2 了,让我修改我的 MAC 地址表,之后如果发给 MAC AA 我就往端口 B转发就好了」。过了一会,交换机的端口 1 又收到了 MAC AA 发来的数据,交换机迷惑了——「怎么这个 MAC AA 一会插在端口 1 上,一会插在端口 2 上,切换如此频繁,到底是谁的手速这么快?」。
这就是 MAC flapping 问题,指的就是同一个 MAC 地址在不同的交换机端口之间来回切换。
它的坏处有几个:
显而易见它会给交换机带来困扰,交换机来回修改 MAC 地址表,性能会下降;
对目标地址为 MAC AA 的,交换机会时而发给端口 1,时而发给端口 2,可能会造成同一个 TCP 连接的乱序问题,从而造成性能下降。
综上,我们不能对两个网卡使用相同的 MAC 地址。
但是我们可以把两张卡配置成一样的 MAC 地址,却不同时使用呀。
最简单的方案是:我们总是使用一张物理卡,当这个网卡不可用的时候,我们转而无缝切换到另一张网卡。这就是 Linux bonding 的 active-backup 模式。
配置 bonding active-backup 模式(mode 1)
从命令行配置 bonding 模式非常直观,大体的步骤是,把要配置的网卡先 down 掉,然后创建一个 bond 模式的 interface,将物理网卡设置为 bond 的 slave,最后把 bond 设置为 up,完事。(不需要手动设置物理网卡为 up,会自动 up。)
它是干什么的呢?还记得理解网络的分层模型中我们提到的「沙漏模型」吗?假设没有 MII,网卡中的 MAC 硬件直接连接物理媒介,那么每出现一种新的物理媒介,比如千兆以太网,万兆以太网,各种光纤,都要去适配所有的 MAC 硬件,是 M x N 的工作量。如果有 MII,MAC 硬件对接 MII,然后每出现一种新的媒介,只需要开发一次新的 MII 接口即可。
MII Monitoring 就是通过 MII 来检查物理物理网卡的状态。如果物理网卡挂了,通过 MII 就可以检测到。但是 MII 检查通过不能代表网络是通的。
(之前我有一个误解,我觉得 MII 只能检测网卡自身问题,无法检测像是网线不良,对端是否插线,对端设备挂了,对端设备端口挂了等问题,但是后来发现 PHY 芯片是可以检测到这些问题的,这里稍微展开一下。)