Hi,欢迎来到 卡瓦邦噶!我是 laixintao,现在生活在新加坡。我的工作是 SRE,喜欢在终端完成大部分工作,对各种技术都感兴趣。我从 2013 年开始写这个博客,写的内容很广泛,运维的方法论,编程的思考,工作的感悟,除了技术内容之外,还会分享一些读书感想,旅行游记,电影和音乐等。欢迎留下你的评论。

声明:本博客内容仅代表本人观点,和我的雇主无关。本博客承诺不含有 AI 生成的内容,所有内容未加说明均为博主原创,一经发布自动进入公有领域,本人放弃所有权利。转载无需本人同意。但是依然建议在转载的时候留下本博客的链接,因为这里的很多内容在发布之后会还会不断地继续更新和追加内容。 Not By AI

重新认识 TCP 的握手和挥手:答案和解析

本文是重新认识 TCP 的握手和挥手一文的答案和解析。

第一题,3个。

Wireshark 选择 Statistics > 选择 TCP Tab 就可以看到 3 个会话,以及 IP 端口,一共使用的数据量,发送的速率等等。

Wireshark 的会话统计功能

第二个问题,找到这 3 个会话的握手包和挥手的包。

这个问题比较简单,借这个问题介绍一个 Wireshark 的很好用的功能:Profile。

Profile 可以让你在分析不同的网络问题或者网络协议的时候使用不同的配置,比如,我们在分析 HTTP 问题的时候,就让 Wireshark 切换到 HTTP 模式;在分析 TCP 性能的时候,就切换到「TCP 性能模式」。

Profile 可以在 Edit > Configuration Profiles… 打开,然后可以添加或者复制已有的模式。

进入 Profile 管理

我们新添加一个 TCP Profile。(也可以复制已有的)

添加一个新的 Profile

我们点击 Wireshark 右下角的 Profile: 可以切换,选择我们刚刚创建的 TCP 模式。

刚切换过来,和之前没有什么不同。我们可以在这个 Profile 下做一些配置。

在 View > Coloring Rules… 可以打开颜色配置,在这里可以修改 Packet 在 Wireshark 中显示的颜色。

颜色配置入口

我们删除其他所有的颜色,只留下 TCP 和 TCP flags 的配色。

Wireshark 颜色配置

这时候回到 Wireshark,就没有其他的颜色干扰了,我们想要的 TCP SYN 和 FIN 都展现在眼前。

刚刚的修改只会影响我们创建的 TCP 这个 Profile,如果你在刚刚右下角的位置切换回 Default, 那么 Default 的颜色配置又回来了。Profile 就是我们对不同问题分析的时候用的不同配置。

如果还觉得不够清晰,可以试试这个 Display Filter Button 功能。

点击这个 +,可以输入过滤条件。
输入 Label,Filter 和 Comment

点击 OK,在 + 右边会出现一个 SYN||FIN 的按钮。点击之后,就会直接应用我们给这个按钮绑定的过滤条件。

使用 Display Filter Button 快速过滤

这个按钮也是跟随 Profile 的,切换不同的 Profile 就可以展示不同的快速过滤按钮。适合将常用的 filter 给保存下来。

在第二题中,前两个会话是特殊构造的。

第一个会话的三次握手其实只有两个包。在TCP 延迟分析中,我们知道三次握手的第三个包发送出去之后就可以直接发送数据,不占用延迟时间,那么是否可以将数据和第三个包(实际上是一个 ACK)一起发送呢?答案是可以的,就和当前题目中给出的抓包文件一样。

在之前的博客 TCP可以使用两次握手建立连接吗?再多来点 TCP 吧:Delay ACK 和 Nagle 算法中也有讨论。

第二个会话中,四次挥手其实是三次,FIN 和 ACK 也合并了。读者如果没有注意到,可以再仔细观察下第二个 TCP 连接。

在第三个问题,如何区分 ACK 的是数据还是 FIN flag 呢?答案是:FIN flag 也是占用一个 sequence number 的。

我们可以用第三个会话来分析这个现象。在第三个会话中右键选择 Follow > TCP Stream.

过滤某一个单独的会话

就可以只显示和这个 TCP 会话有关的包了,在有很多 TCP 连接,我们只想分析其中一个的时候,非常有用。

除了 Wireshark 的包列表页面,我们还会看到一个明文的页面,这就是这个 TCP 会话中发送的所有数据。

TCP Stream 分析

我们这里想要分析的 seq number 和 ack number,所以可以在列表中展示这两列。直接在列的 header 右键可以编辑。

也可以打开一个包,在想要的字段上右键,选择 Apply as Column,就可以直接把这个字段放到列里面。

Apply as Column

同理,也把 ack number 放到 Column 里面。

找到一个 FIN 的包,就可以清楚的看到这个 length 为 0 的 FIN 包页增加了一个 seq number。

在 TCP 协议里面是这么说的。

A control bit (finis) occupying one sequence number, which indicates that the sender will send no more data or control occupying sequence space.

RFC 793

总结一下,今天学习到的 Wireshark 技巧真多呢:

  • Wireshark 的会话统计功能
  • 配置 Profile 和颜色
  • 配置 Profile 的快速过滤按钮
  • 过滤单独的 TCP 会话
  • 将想看的字段展示在 Column 上面

目录

这个系列正在连载中,没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 重新认识 TCP 的握手和挥手
  12. 重新认识 TCP 的握手和挥手:答案和解析
  13. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。
如果本文对您有帮助,欢迎打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!
 

数据中心网络高可用技术之从服务器到交换机:active-backup

数据中心里面是一个个的机架(Rack),机架里面放着服务器,怎么把这些服务器连接起来呢?如果是 ToR (Top of Rack)的架构,就是在每一个机架上放一个机架交换机,这个机架的所有机器都连接顶部的交换机,然后再用聚合交换机把机架顶部的交换机连接起来。

ToR Switch, 来源

服务器和交换机是怎么连接的呢?最简单的方式是,服务器上有一个网卡,用网线把服务器和交换机连接起来。但这样做的话,我们就有了很多「单点」:网卡,网线,和交换机。高可用设计的目标就是排除掉单点,方法是添加冗余。

网卡通过 PCI 或者 PCIe 接口插在主板上,可以插多个网卡。有的网卡能够提供多个 Ethernet 接口或者多个光口

于是我们在服务器上再插上一根网卡,然后我们再用一根线连接服务器和交换机。

服务器上面有两个网卡,使用两条线连接到交换机的两个端口

现在在服务器上,我们就有了两个物理卡,对应地,我们在操作系统上可以看到两个 interface。

我们怎么配置这两个网卡呢?

首先,假设我们给两个 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 地址在不同的交换机端口之间来回切换。

它的坏处有几个:

  1. 显而易见它会给交换机带来困扰,交换机来回修改 MAC 地址表,性能会下降;
  2. 对目标地址为 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。)

命令如下:

实验用的拓扑图

在这个拓扑图中,我们对 ubuntu-5 机器配置好了 bonding。使用 prof fs 的接口可以查看当前 bonding 的配置:

可见当前的 slave 是 eth0

Failover 过程

我们已经配置好了 active-backup 模式,希望在主接口 down 之后,自动切换到 backup 接口。

但是现在的配置下,是不能自动切换的,因为 bond 不知道应该何时切换。高可用的部署中,我们加了冗余之后,还需要思考两个问题:

  1. 什么时候会触发切换;
  2. 以及如何切换。

Failover 触发

Failover 触发方案的本质是监控——要监控两条线路的状态,在一条线路 down 的时候,执行切换动作。主要的监控方式有两种:MII Monitoring 和 ARP Monitoring。

MII Monitoring

MII 的全称是 Media-independent interface,是 MAC 硬件层和传输媒介 PHY 硬件之间的接口。

MII 在 PHY 和 MAC 之间,来源

它是干什么的呢?还记得理解网络的分层模型中我们提到的「沙漏模型」吗?假设没有 MII,网卡中的 MAC 硬件直接连接物理媒介,那么每出现一种新的物理媒介,比如千兆以太网,万兆以太网,各种光纤,都要去适配所有的 MAC 硬件,是 M x N 的工作量。如果有 MII,MAC 硬件对接 MII,然后每出现一种新的媒介,只需要开发一次新的 MII 接口即可。

MII Monitoring 就是通过 MII 来检查物理物理网卡的状态,不能代表网络是通的。比如,交换机挂了,或交换机的端口挂了的情况下,服务器网卡没问题,就不会触发切换,下面会演示。

MII Monitoring 下的 Failover 测试

我们在上面的配置中,并没有配置 miimon ,所以默认是关闭的。这里再配置下检测频率为 102ms.

再看一下 bonding 状态,确认 miimon 已经生效,并且当前 active 端口是 eth0.

我们现在 ping 交换机另一端的一个主机,并且在 ping 的过程中手动 down 掉现在的 active eth0 端口。

ping 程序也显示,在切换的过程中一个包都没有丢。我们来分析一下这个切换过程。

Failover 过程分析

对于对端的主机来说,什么都没发生改变,因为这个 IP 对应的 MAC 地址没变,对端主机依然按照这个目标 IP 和 MAC 地址发送包过来。

出流量对于本机来说,切换也很简单,原先成 eth0 发送出去,现在从 eth1 发送出去即可。

入流量就麻烦了,我们的端口 down 了,交换机可不知道,入流量是交换机来决定从哪条线路发送过来的,所以流量还是会从端口 1 源源不断地进来。

发出流量已经切换,可是怎么切换接收的流量呢?

我们想要的是:让交换机现在通过端口 2 来发给我们流量,而不是端口 1.

如何做到呢?还记得交换机的工作方式吗?交换机从每一个收到的包学习 MAC 地址和端口的对应关系。利用这个,我们让交换机学习到,此时 MAC AA 对应的是端口 2,而不是端口 1。

即,我们使用 MAC AA 作为 Src MAC 从端口 2 发送点数据出去,交换机就知道了。

在 Failover 的时候用 tcpdump 来抓包,我们可以观察到在 backup 的端口发送出去的内容。

哈,eth1 发送出去了一个 ARP 请求,它问:「谁有 192.168.1.10 的地址?请告诉我。」可是这个 IP 明明是它自己呀。它这是明知故问呀,没有人会回复给他,它也不需要答案,发送这个 ARP 请求的目标是让交换机知道现在 MAC AA 对应端口 2。

从交换机的视角,就如同 MAC AA 原先插在端口 1 上,现在移动到了端口 2 上一样,交换机会立即把发往这个 MAC 地址的流量切换到端口 2.

这种 ARP 的用法叫做 Gratuitous ARP(不必要的ARP),有的时候是不要的 ARP 请求,有的时候是不必要的 ARP 响应(我们会在后面见到)。在 RFC 5227 中定义。

实验:交换机端口 down

我们继续用 MII Monitoring 的方式,来测试一下模拟交换机 down,看能否触发切换。

确认当前的 active 端口:

然后我们 ping 其中的一个主机,同时去交换机 down 掉 eth0 连接的端口。

可以看到现在无法完成切换,此时的 active interface 还是 eth0。因为 MII Monitoring 依然认为服务器的物理链路是好的。

ARP Monitoring

ARP Monitoring 的原理就是用 ARP 协议一直对某一个 IP 询问 MAC 地址,如果能够得到回复,说明网络没问题,如果得不到回复,就执行切换。

我们首先设置 miimon 为 0 来关闭 MII Monitoring,然后开启 ARP Monitoring,主要设置两个选项:ARP 检测的地址和检测的频率,这里使用 1s。

然后开始测试切换,查看现在 active 的端口是 eth1

然后开始 ping,在 ping 的时候在交换机把 eth1 端口 down 掉。

可以看到第 11-13 个包丢了,一共丢了三个包。因为我们设置的频率是 1s,所以切换有些慢的。ARP Monitoring 比 MII Monitoring 能检测的网络范围更广泛。

ARP 也有问题,就是如果我们使用的 192.168.1.12 有问题,有时候回复 ARP 有时候不回复,就会导致端口来回 flapping。一个解决方法是,ARP Monitoring 支持多个 IP 检查目标,就可以避免 false alarm。

另一个坏处是会给网络带来额外的 ARP 流量。但现在网络都是万兆起步,这点流量可以忽略。

分析和总结

本文讨论了 Linux bonding 模式中的一种:active-backup。这个模式的特点是:

  • 不需要交换机特殊配置,在交换机看来就是两个独立的端口;
  • 由于交换机视角是独立的,所以我们可以把两根线接到两个不同的交换机上,可以实现多交换机的 Failover,避免交换机的单点故障;
  • 一次只能利用一条线路,假设两条1G线路,总速率还是 1G,有一条线总是浪费的;

本质上这是个冷备方案,我们不喜欢冷备方案,因为它浪费资源。下一篇文章我们讨论如何将两条线路都利用起来。

数据中心网络高可用技术系列

  1. 数据中心网络高可用技术:序
  2. 数据中心网络高可用技术之从服务器到交换机:active-backup
 

数据中心网络高可用技术:序

数据中心的网络和家用网络有很大不同,家用网络一个小路由器就够了,挂了的话,就忍受一下没有网络的时间,然后去网上下单再买一个换上。数据中心可不行,所有的东西都要设计成高可用的。

这篇文章来讲讲在数据中心网络用到的技术,主要是和高可用相关的。

本文谈到的网络,重点是3层及以下。因为 4 层是端对端的,如今主要是在服务端软件进行高可用设计,我们在四层负载均衡漫谈里讨论了这些技术并且分析了很多案例。

意义和必要性

高可用指的是,任何一个组件坏了,都不影响其他的服务。比如说,服务器的电源坏了,还要能继续提供服务。怎么做到呢?再加一个电源。高可用的本质就是加机器(冗余硬件),笑。

现在的软件都是高可用设计了,无状态的可以随意扩展,有状态的比如 Etcd 可以部署多个实例,实例之间自己选举,自动 Failover,是不是对机器的可用性要求就低了?

假设一台服务器运行1年不出问题的概率是 99%,那么如果部署一万台服务器的话,1年之内就会有 1% * 10000 = 100 台会出现问题。如果部署的足够多,那么小概率发生的事情在绝对数量上就不会小。如果机器的可用性不够高,那么即使像 Etcd 3 副本部署,同时有 2 台出现故障的概率也会变大。而且,同一个批次的硬件很可能在大约同一时间出现故障,我就见过多次 RAID 里同时坏两块盘的情况。

高可用设计对维护操作也很友好。为了不剧透网络部分好玩的内容,我们还是拿电源举例。现在的服务器大部分是 Dual PSU (Dual Power supply unit,双电源)设计,支持热插拔。如果一个电源坏了,另一个直接继续提供服务,技术人员可以拔掉坏的,换上新的,不需要停机时间。否则的话就麻烦了,需要联系机器负责人,机器负责人联系上面运行的进程负责人,大家开始迁移服务,然后才能操作。

像双电源的设计还可以缩小故障影响范围,比如两个电源单位,分别去连接两条电线。假设一个强电线路挂了,就和电源单位挂了一样,另一个电源直接上岗。如果没有的话,可能大批的服务器就下线了。

说到底,最重要的是交付的服务质量。对于数据中心来说,交付的服务就是机器,提高服务质量就是最大程度提高机器的可用性。向客户提供服务的时候,尽最大努力提高服务的质量;使用别人的服务的时候,要假设别人的服务可能挂掉。所以 PaaS 平台使用这些机器,要假设机器会挂,使用其他技术手段提高 PaaS 的可用性,提供给别人用;SaaS 使用 PaaS 的时候,也假设 PaaS 也可能会挂,通过技术提高自己软件服务的可用性。

所以在数据中心中,机器都尽可能设计成了高可用的:硬盘用 RAID 做容器,电源做冗余,网络做冗余。这个博客使用的虚拟机,已经 1117 天没有关机了,可用性非常高。

kawabangga.com 所在的服务器运行时间

这个系列文章就来谈谈网络是怎么实现高可用的。比如,从服务器到交换机的链路如何做到高可用,从交换机到交换机,从服务器到网关路由器,等等。

明确一下网络高可用的目标。服务器最终的目标是提供服务,服务通过应用层网络协议提供出去:HTTP,webRTC 等等。这些协议都是基于 TCP 或者 UDP,由于四层的高可用技术主要是四层负载均衡解决了,四层负载均衡一般使用的是商用服务器,在这些服务器前面的网络设备主要工作在二层和三层,对于这些设备来说,四层网络就是这些设备要传输的数据。这些设备要做的就是:确保四层数据的传输是高可用的,或者说,尽量在三层 IP 协议上保证可用性,这也是 IP 协议的设计:Best effort.

最后我们这样总结一下这些技术的最终目标:在服务器上 ping 一个互联网 IP,中间的硬件无论哪一个挂了,高可用的网络设计都要保证能够继续 ping 的通(中间可能会有短暂的丢包)。

基础知识回顾

在讨论这些好玩的技术之前,我们先重新认识一下机房中的设备,和这些设备的工作方式。和网络相关的,主要就是服务器、交换机、和路由器了。

服务器

服务器是最常见的终端设备了,也是开发者最熟悉的设备。它没有网络的转发功能,是其他网络设备的主要用户。

在讨论服务器的时候,我们要从两个方面分开讨论:

  1. 发出流量,又叫 Transmit, outbound;
  2. 接收流量,又叫 Receive, inbound;

流量都是通过网络单元的形式发送出去的,在三层叫做包,二层叫做帧。服务器拿 Linux 来距离,在发送数据的时候,无论是 TCP 还是 UDP,都是先封装成三层数据包,再封装成二层数据帧,最后通过物理层让网卡发送数据。不是所有的数据都是基于三层协议发送的,但是数据一定是通过二层协议发送出去的,因为二层是连接物理层的电信号和数字信号的地方。数据中心的二层协议一般就是 Ethernet 了。

数据封装成三层包很简单,因为应用发送数据的时候已经指定了 IP 地址,kernel 根据这些信息封装添加 I P header 就完成了。

封装成二层也很简单,主要是添加二层 Header 和计算 CRC。Header 中最重要的是放上自己的 MAC 地址和目标的 MAC 地址。怎么知道目标 MAC 地址应该写啥呢?首先根据要发送的目标 IP 和自己的子网判断目标 IP 是否和自己在同一个子网,如果在同一个子网,那么目标 MAC 地址就写目标 IP 的 MAC 地址;如果不在同一个子网,就写默认网关 (default gateway) 的 MAC 地址。那又怎么知道目标 IP 或者默认网关的 MAC 地址是什么呢?机器本地是没有办法知道的,所以要通过网络协议询问,即 ARP 协议。

想知道一个 IP 对应的 MAC 地址,就设置一个问题,问「谁有 IP xxx 的 MAC 地址?如果知道,请回复给 IP yyy,yyy 的 MAC 地址是 B。」前面提到 ARP 也是基于二层的,我们就得把这个问题封装到二层中,来源 MAC 就是自己的 MAC,B,目标 MAC 地址是 FF:FF:FF:FF:FF:FF,即广播给所有的人。这时候所有人会收到这个 ARP 问题,但是只有 IP 是 xxx 的会回复,告知其 MAC 地址。其他人虽然不回复,但是也会收到这个 ARP 问题,通过这个 ARP 问题,所有的人都知道了:「哦,虽然不需要我回答,但是我也获得了 yyy 的 MAC 地址,我先记下来,说不定以后用得到呢。」

通过 ARP 协议拿到了 MAC 地址,就可以完成二层的封装了,最后通过物理层发送出去。ARP 协议的目的就是找到 IP 和 MAC 地址的对应,这个记录会在机器上缓存一段时间,减少 ARP 查询量,也能减少 ARP 带来的延迟。

接收流量的行为很简单,网卡从物理层收到网络数据,解析成二层数据帧,判断目标 MAC 地址是否是发给我的,如果不是就丢弃,如果是,就把数据交给 kernel。如果是广播报,就根据协议判断是否需要回复,如上面提到的 ARP。

交换机

交换机和路由器都是流量转发设备,对它们来说,流量就没有发出和接收的区别了,它们的工作都是将收到的网络包转发出去。交换机工作在二层,路由器工作在三层。

交换机的转发逻辑很简单,它只看二层的 header 来完成转发。交换机有一个 MAC address table,是 MAC 地址和交换机端口的对应,交换机启动的时候 MAC address table 是空的,也不需要配置和导入数据,交换机可以自己「学习」。收到一个二层数据帧,交换机做的事情如下:

  1. 把来源 MAC 地址和来源端口号记录到 MAC address table 中,这就是交换机的「学习」过程,它从每一个收到的包进行学习;
  2. 根据目标 MAC 查找自己的 MAC address table;
  3. 如果找到目标 MAC 对应的端口号,就从这个端口把数据转发出去;
  4. 如果找不到,就转发到除了来源端口之外的其他端口(参考上文服务器收到帧的操作,如果不是发给自己的,会丢弃);
交换机通过收到的包学习 MAC 地址和端口的对应关系

MAC 地址表中,一个 MAC 地址只能对应唯一一个端口,不能对应多个端口。即,表示这个 MAC 转发到这个端口;但是一个端口可以对应多个 MAC 地址,或者说,多个 MAC 地址可以对应到同一个端口,因为一个端口后面不仅仅可以是服务器,也可以是交换机。

路由器

路由器工作在三层,但是三层必须要依靠二层承载才能工作,所以路由器里面也有一个二层实现。在每次收发数据包的时候,它的工作方式是和服务器一样的,都是通过 ARP 询问 MAC 地址,然后把要发送的三层包添加二层 header(主要是 MAC 地址),发送出去。对于交换机的视角来说,路由器和服务器一样,都是它的「客户」。

在转发数据包的时候,路由器每次拿到一个 IP 包,就检查自己的路由表,找到一个出口端口,然后从这个端口发送出去。

它和交换机一样,主要的工作是「转发」,只不过交换机参考的是 MAC 与端口的对应表,路由器参考的是 IP 与端口的对应表。

路由表怎么来呢?路由器有两个「面」:控制面和数据面。控制面负责生成路由表,数据面负责将进来的包经过查找路由表转发出去。

路由表有两种方式生成,一种是静态的配置。其实 Linux 服务器也有一个路由表,通过 ip route 命令可以看,只不过里面的项目很少,最重要的是默认网关,默认网关一般使用静态配置。但是对于路由器来说,端口太多,只有一个默认网关是不够的,在数据中心中,基本上不使用静态配置的方式。

另一种就是动态协商,比如 EIGRP,BGP,OSPF 等等。简单来说,就是路由器之间互相交换自己能够连接到的子网的信息,路由器根据这些信息生成路由表。

讲了这么多无聊的东西,我们终于可以开始讨论有趣的高可用方案了。不过无聊归无聊,后面用到的技术其实就是对这些基础行为的巧妙利用(或者说,滥用?)。所以复习一下还是有必要的。

下一篇文章,我们从分析如何让服务器到交换机的连接做到高可用开始讨论。

数据中心网络高可用技术系列

  1. 数据中心网络高可用技术:序
  2. 数据中心网络高可用技术之从服务器到交换机:active-backup
 

理解网络的分层模型

虽然刚接触网络的时候,就首先学习了分层的模型,大部分的教材也是会在最开始就讲解网络的分层模型,但是与网络分层相关的实践却很少。

笔者直到工作多年以后,对此的理解才慢慢清晰起来。这一篇就讲一下我对「分层」的理解。这些概念看起来很简单,但是结合实践之后,会有不断有新的理解冒出来,越来越深入。

网络是如何连接的?

我们访问一个网站,本质是发送多个 HTTP 请求,然后收到 HTTP 响应。DNS 先抛开不说,因为它的作用是名字和 IP 的对应,拿到 IP 之后,我们和服务器之间的交互就不需要 DNS 了。假设我们现在已经知道了网站的 IP,那请求是怎么从我们的笔记本电脑发送到网站的呢?

从物理层面,这个数据包要从电脑发送到路由器,路由器发送给运营商,运营商内部经过很多的路由器和交换机,最后到达服务器。整个转发链路可以看作是经过了很多次设备两两之间进行转发,每一次包被转发都发生在两个设备之间(广播也可以看作是很多次两个设备之间的转发)。所以这个问题可以聚焦于每两个设备之间转发的时候,对数据包做了哪些处理。

数据包结构

这个图做了简化,只保留了每一层中最重要的地址信息。

二层做转发的时候,比如笔记本发送给路由器,会把 SRC MAC 地址设置为自己的 MAC 地址,DST MAC 地址设置为路由器的 MAC 地址。

它完全不看三层和三层以上的内容。所以数据包对它来说就像是这个样子,IP 地址等三层内容变成了它的数据:

二层转发视角

电脑转给路由器,设置 MAC 地址看起来很没必要?是的,这种相当于点对点的网络,没有地址也不会发错的。也确实存在一些其他的二层技术,比如 Frame Relay,二层的包头就没有 MAC 地址(但是有类似 MAC 地址作用的东西)。

但是下面这个结构,MAC 地址就很有用了。比如电脑 1 要发送给电脑 2 内容,DST MAC 地址写 MAC 2,然后交给交换机,交换机就会转发给电脑2。如果没有 MAC 的话,交换机收到这个包,就不知道应该发给 2 还是 3 了。

交换机在子网转发

如果有了 MAC 地址,交换机就可以把这个包从 MAC 地址对应的端口转发出去。

如今的大部分交换机都是非常「智能」的设备,可以通过查询 MAC 地址查找对应的端口进行转发。四十年前的设备不这么智能——它从一个端口收到包,就直接转发给其他所有的端口,就是 Hub(集线器)。所有的主机都会收到这个包,但是会检查 MAC 地址是否属于自己,如果不是,就丢弃。Ethernet 本身的设计就是将包转发到所有的端口,按广播的设计来的,所以它有冲突检测等设计,如今已经很少用到了。

三层转发同理,它只关心三层的内容,最重要的是 IP 地址。检查目标 IP,然后根据自己的路由表,在多个接口(路由器一般有多个接口,连接不同的网络,家用路由器可以理解为有两个接口,一个连接家里的内网,一个连接 ISP 那边的网络)选择其中的一个,然后通过这个接口将数据发送出去。路由表可以理解成是三层设备的一个转发数据库,是它最重要的依据,它的功能就是给定一个 目标 IP,可以从中查询出一个物理出口,然后就可以使用这个物理出口转发出去。

这个时候,二层网络就成了它的载体:二层用什么技术不重要,只要能给三层把封装好的三层包发到对面就可以,Ethernet 可以,Wi-Fi 可以,Frame Relay 也可以。如果是 Ethernet 的话,就把三层包封装到一个二层里面,即在外面贴上 DST MAC 地址是对面的设备,SRC MAC 是自己的 MAC,最后用物理层发送。

所有上层协议都可以依赖 IP 协议发送自己的数据,TCP, UDP, ICMP 等等,都是将 IP 作为载体。

互联网的沙漏,图来自Computer Networking: A Top-Down Approach, 7th Edition, Figure 4.13

通过互联网我们可以访问世界各地的网站(从网络的角度说,就是服务器,设备),就需要我们的电脑和世界上所有的服务器之间,都存在两两连接的线路,这些线路通常都是物理线路,海底光缆,ATM 线路,双绞线,铜线等等。无线技术一般只会用在接入层,因为它信噪比较低,转发设备之间很少用无线技术,一般都是物理线路连接的。所以网络上有人吵架的时候说「我要顺着网线爬过去掐死你」,假设他真的能够顺着网线爬,那么他确实能够从家里的网线爬到世界的任何地方。

所有的设备之间能够互相通讯,这就要求这些设备之间都运行了某种一样的协议,才能懂彼此的语言。这个协议就是 IP。IP 的上层有很多协议,下层也可以依赖各种协议,但是中间必须是 IP。每一个网络的自治域内可能使用非 IP 协议(但是我好像没听说过),但是自治域之间,都是用 IP 交换信息的,交换 IP 路由信息的协议都是用 BGP。就像一个沙漏一样

不同层的设备

网络中所有两个设备之间的转发都要经过物理层。物理层能做的事情相对较少。「集线器」就是物理层的设备,说它是物理层是因为二层及以上的数据都是它的内容,它连二层的内容也不看。从一个口收到数据,无论是什么,都直接复制到其他的口发出去。像是一个「端口复读机」。

二层设备主要是按照 MAC 地址转发,它不修改包的内容。不过上面的例子不知读者有没有发现一个问题——交换机怎么知道哪一个接口对应 MAC2 呢?如何将收到的包发送出去?其实很简单,如果它不知道,就把这个包发送给所有的接口。和「集线器」不同的是,交换机可能一开始不知道 MAC 和端口的对应,但是它可以学习。学习主要通过收到的包的来源 MAC 地址,比如这个包是从接口 1 收到的,而且包里面有一个 SRC MAC 是 MAC1,那二层设备就记住了,下次发给 MAC1 的时候,我就直接发送给接口 1,不用发给所有人了。这就叫「MAC 地址学习」,学习到的内容叫做 MAC 地址表,存储在内存中。

三层设备就是按照 IP 转发了。三层设备的负担更重一些,因为无法像二层那样简单地从要转发的包里面「学习」路由信息。因为发送者也不知道怎么转发到终点 IP 呀,人家指望着你呢,已经把包交给到你的手上了。三层设备根据自己的路由表转发,路由表可以通过静态配置。比如配置成往 xx 这个网段发送的话,就走接口1,如果往 yy 那个网段发送的话,就走接口2,如果找不到的话,就走接口3,类似这种。但是机房内设备众多,不可能一一手动配置,一般都是动态生成的。即,所有的路由器都知道自己的直连网络,然后在路由器之间交换彼此的信息,这样,大家彼此依靠,共同描绘一个转发地图。具体的路由协议内容非常复杂,就不展开了。总之,三层设备之间在交换两种信息:一种是路由信息,这部分我们也叫「控制面」;另一种就是实际要转发的数据了,我们叫「数据面」。

注意这一段的描述中,我用「二层设备」和「三层设备」来描述,而不是「交换机」和「路由器」,因为现代化的网络中,二层设备和三层设备的界线已经很模糊了,很多交换机都可以做三层转发。比如我们常说的「家用路由器」,它其实是一个 交换机+路由器+NAT + 防火墙。路由器听起来也比交换机高级,其实也不是,机房中有很多交换机是高规格的,比路由器要贵的多,能力也比路由器要猛,几百个口都能达到线速。

网络协议与网络分层

上面的问题,还剩下最后一块拼图:电脑知道要三层怎么封装,加上 IP 就行了,然后得封装好二层才能发出去。路由器的 IP 是手动设置好(或者通过 DHCP 协议得到)的,但是路由器的 MAC (DST MAC) 怎么写呢?

你要在一个陌生的班级的教室里要找李小明,怎么找?肯定是大喊一声「谁叫李小明呀?」

同理,当发送者不知道一个 IP 对应的 MAC 地址的时候,就发给网络内的所有人:「如果有人是这个IP,请回复给我,我的 MAC 是 MAC1」。于是路由器就回复给 MAC1「是我是我」,回复的包 SRC MAC 也设置为自己的 MAC。

所以说 ARP 是几层协议呢?大部分资料说是二层,也有的说二层和三层之间。

我觉得这种讨论没有意义。因为我们实际上在讨论两个东西而且试图将两个概念融合在一起。即:

  • 协议工作在几层(基于几层实现)?
  • 协议为几层提供服务?

如果分开讨论,就清楚很多了。ARP 基于二层(意味着只用到了二层的功能,不需要三层的东西就可以工作)实现,为三层提供服务,帮助找到 IP 对应的 MAC 地址。

我们可以给很多有类似争议的协议定义:

  • TLS 基于四层实现,为应用层提供服务;
  • TCP 基于三层实现,为应用层提供服务;
  • ICMP 工作在三层,为三层提供服务;
  • EIGRP 和 OSPF 基于 IP 协议,为三层提供服务;
  • BGP 基于四层实现,为三层提供服务(是不是很神奇?因为 BGP 通过 TCP 交换路由信息,为三层提供转发路由);

为什么要分层?

我的理解是:为了让每一个协议可以处理部分的问题,不同的协议之间可以互相工作。比如 IP 协议只要一个二层帮他转发,任何二层协议都可以,这样,在 Wi-Fi 大普及,2G 升级 3G 升级 4G 等等的时候,我们都可以继续用 IP 协议,不需要跟着升级换代。就像公司划分了不同的部门,每一个部门负责一部分事情,只要将事情做好,部门内部做事的方式与其他的部门无关。

每一个协议都给出了自己的承诺:比如三层协议,它承诺尽可能保证包到达目的地址;四层向应用层保证,用我传输的数据一定不会丢包,一定不会不完整,一定不会乱序,放心好了。所以我们在做应用层编程的时候,从来不需要关心(数据层面的)乱序,丢包等问题。

基于下层协议的承诺来工作,节省了很多力气。有点类似我们编程的时候,不是从头开始写字符串处理,而是用各种库一样。

理解协议

作为用户,我们在使用这些协议的时候,要知道这些协议提供的功能是什么,要求是什么。有点像用三方库之前要阅读文档一样。

这样很多问题其实也就不是问题了。比如「粘包」问题,是一个被人诟病的面试题。它是问「如果使用 TCP 发送多个包,这些包粘在一起无法分开怎么办?」这么问出来就显得提问者不懂 TCP,因为 TCP 的设计就是帮助用户发送一个字节流,它本身就没有「包」这个概念,所有的数据就是要「粘」在一起发送的。这并不能说是一个问题,而是 TCP 本身的特性。如果你使用 TCP 协议,你就要在这之上设计自己的协议,把自己的协议设计成可以让 TCP 使用「流」的方式传送。比如,HTTP 协议是使用 \r\n\r\n 来分割来 Header 和 Body,然后通过读 Header 中 Content-length 的长度来判断 Body 要读到那里;Redis 协议大致是先用一个数字表示内容的长度,读完了的话,再读就是下一个请求了。

再举个例子,就是对 HTTP 协议的误解。作为用户(甚至是程序开发者),我们对 HTTP 的感受可能是:GET 只需要输入一个 URL,Post 需要输入 URL 和 body,URL 在浏览器的地址栏可以看到,但是 Body 内容看不到,所以有人甚至会认为 Post 方法比 GET 更安全。但其实,如果抓包就会发现,GET 和 POST 两个 HTTP method 发送了相同的数据结构内容,GET 和 Post 就是一个字段的区别。所以面试官问:GET 和 POST 有什么区别吗?我觉得从协议结构上除了一个 method 字段之外没有区别:

  • GET 也可以发送 Body;
  • POST 也可以传输 url params;
  • POST 不会比 GET 更安全,POST 的 body 经过抓包也是一览无余;

网络的分层对问题排查的意义

工作中,在排查问题的时候,网络分层也至关重要。

从上面的描述,我们可以得出结论:如果某一层出现问题,这一层上面的所有协议都会有问题。因为上层协议是基于下层来实现的。在排查问题的时候,比如我们连不上数据库,首先使用 nc -v 10.0.0.1 3306 来确认问题,如果连不上说明 TCP 层有问题,然后去 ping 10.0.0.1,如果能通,说明三层没有问题,这就意味着三层及以下不需要排查了,像什么 ARP,路由信息,都不用查了——三层能通四层不通,问题必定出在四层(可能是防火墙 block 了端口)。相反,如果 ping 不通,就需要继续排查下层协议的问题,有没有可能是 ARP 的问题,等等。以此类推,如果在 nc -v 10.0.0.1 3306 这一步发现可以连通,那么说明四层及以下没有问题,就往上看就好了,是不是数据库程序卡住了?

在描述问题的时候,分层模型也能让我们更好地与同事交流。我经常看到有人这么问:「请检查下数据库有没有问题」,问题是,DBA 可能无法登陆你的机器,无法使用你的环境做测试,经过一些检查,他只能告诉你「没问题」。但是问题是有的,可能不是数据库的问题,于是就找更多的人来帮忙,最后大家在群里面面相觑,问题还是在那里。假设用户理解了网络的分层呢?他可能这么提问:「我们连接这个 IP 不通 10.0.0.1 ,端口是 3306,但是可以 ping 通这个 IP」,DBA 马上就可以开始定位问题了。

但是实际上在抓包和分析的时候,没有人会事先告诉你这是第几层的问题。解决实际的问题不是考试,所以我们必须理解每一层的职责是什么,才能根据具体的现象去判断应该在哪一层来寻求线索。

参考资料

目录

这个系列正在连载中,没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 重新认识 TCP 的握手和挥手
  12. 重新认识 TCP 的握手和挥手:答案和解析
  13. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。
如果本文对您有帮助,欢迎打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!
 

重新认识 TCP 的握手和挥手

在教科书上,我们学过 TCP 的 3 次握手和 4 次挥手,请读者思考一下:TCP 握手一定会使用 3 个包吗?TCP 的挥手一定会使用 4 个包吗?

下载这个抓包文件,并且分析以下问题:

问题1: 这个抓包文件中,一共有几个 TCP 连接?

问题2: 对于每一个 TCP 连接,请找出来和 TCP 握手有关的 3 个包,以及和 TCP 挥手有关的 4 个包

TCP 是可靠的传输层协议,这意味着在 TCP 连接中,丢失的每一个数据都会被识别出来。考虑这种情况:在一个 TCP 连接中,发送端发送完最后一个 segment,然后发送 FIN 包结束连接。即,发送端发送了两个包。这时候:

  • 假设 segment 数据丢失了,那么发送端不会得到这个 segment 的 ACK,需要重传 segment 数据;
  • 如果 FIN 丢了,也需要重传 FIN,4 次挥手中,ACK 的作用就是确保 FIN 也得到了 ACK 确认。

那么最后的问题是:如果发送端发送了 segment 数据和 FIN,但是只收到一个 ACK,发送端如何知道 ACK 的是 segment 数据,还是 FIN 包,还是二者都是?注意,ACK 会有 ACK 的序列号,表示这个号码之前的数据都已收到。但是 FIN 的数据长度是 0。那么 ACK 如何区分 FIN这个包 和 FIN 之前的数据呢?

发送端的问题

提示:TCP 肯定已经处理好了这种情况,所以答案就在 RFC 793 中。如果不想阅读 RFC,那么可以仔细分析 ACK number 来找到答案。

目录

这个系列正在连载中,没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 重新认识 TCP 的握手和挥手
  12. 重新认识 TCP 的握手和挥手:答案和解析
  13. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。
如果本文对您有帮助,欢迎打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!