网络的组织结构
所有的故事从一张图开始
图中的网络结构由三个虚线框组成,每一个虚线框表示一个网段,同一网段内部的机器都插在二层交换机上,而网段之间靠三层交换机连起来。
可以看众多机器之间是靠交换机来组成网络的,但是二层交换机和三层交换机有什么区别呢?
二层交换机
二层交换机作用于数据链路层(从物理层往上数第二层)。它的核心功能就是,从接收到的网络包中,取出MAC头,拿到目标机器的MAC地址后,找到目标机器的MAC地址该从哪一个端口出去,然后把网络包复制到这个端口上。
Docker中有个docker0网桥、Kubernetes中有个cni0网桥,这都是作用于数据链路层的设备,干的就是这个活。
[容器网络] 近期在学习kubernetes时网络模型时,跨住容器间的网络模型,如Flannel,Calico,原理理解起来有点绕,通过复习网络相关的基础知识后清晰了不少,后面打算整理下容器网络的学习过程。
机器刚接入二层交换机某个端口的时候,是不知道这个端口对应机器的MAC地址的。更复杂的情况是,这个端口可能接入另一台交换机B,而交换B也可能接入多台机器,那这个端口可能对应着多个MAC地址。那交换机怎么知道这些信息呢?
这需要一个学习的过程。比如图中网段A的机器1对机器2发起一个HTTP请求时:
- 机器1的HTTP网络包到了交换机之后,目前交换机虽然不知道该往哪个端口复制,但是网络包一定有源MAC地址,于是它知道了机器1的MAC地址对应哪一个端口。
- 接下来它对其他所有端口都复制了网络包,其他连上这台交换机的机器以及另一台交换机都会收到数据包,另一条交换机在不知所措的时候自然也会广播所有端口。
- 机器2收到广播后会主动响应。在这个过程过后,交换机都知道了机器2的MAC地址对应哪一个端口。
- 交换机在这个过程后,会把学习成果记在地址表中。以后找机器1和机器2都不需要广播了。学习的次数越多,地址表就越趋近于完善。
这个地址表,大概是这个样子
MAC地址 | 端口 | 其他信息 |
---|---|---|
08:00:20:0A:8C:6D | 1 | ...... |
08:00:20:0A:8C:7D | 1 | ...... |
08:00:20:0A:8C:8D | 2 | ...... |
08:00:20:0A:8C:9D | 3 | ...... |
二层交换机主要实现了转发的功能,它不会改变网络包中的源MAC地址和目标MAC地址,更不可能改变源IP地址和目标IP地址,这和下面要介绍的三层交换机是不同的。
三层交换机
似乎只用二层交换机就能组织网络了,事实是不可能的,都在同一个网段,先不说机器足够多的时候,IP地址够不够用,一台交换机要它学习那么多机器的MAC地址,也是不现实的。
不同的网段得靠三层交换机来连接它们。
三层交换机作用于网络层。它会取出MAC头和IP头,找到目标IP的对应的端口,然后把网络包复制到该端口。
和二层交换机不同的是:二层交换机的端口对应的是具体的MAC地址。而IP地址具有定位功能的,所以三层交换机可以做到端口和网段对应上。
在讨论二层交换机的时候,交换机本身是没有MAC地址和IP地址,它只会转发自己接收到的网络包。而三层交换机则不一样,它充当了网段的网关,所以会和网段内的其他机器一样,有自己的MAC地址和IP地址。而且三层交换机是个"多面手",从图中可以看到,这个三层交换机同时充当了三个网段的网关,所以它有三个IP地址和三个MAC地址。
路由器就是一个三层交换机
三层交换机充当网关,当网关也有不同的情况
转发网关
当机器1向机器4发起跨网段的HTTP请求时:
-
假设机器1的IP地址是192.168.1.10,机器4的IP地址是192.168.2.10
-
机器1发现机器4和自己不在同一个网段,它是没法知道对方的MAC地址的
- 二层交换机学不了其它网段的机器信息
-
于是机器1只能先把网络包先发给网关,让它去想办法
- 先要通过
ARP
协议获取网关的MAC地址 - 机器1构建MAC头的时候,目标MAC地址实际上是网关的地址,而不是真正的目标MAC地址
- 先要通过
-
三层交换机拿到网络包后,取出IP头中的目标IP地址,根据路由规则,要去192.168.2.10/24得走192.168.1.1这张网卡对应的端口,就找到了目标机器4。
- 交换机构建网络包转发到目标机器4时,源MAC地址是交换机的,目标MAC地址是机器4的
从这个例子中发现机器1发到机器4的网络包的MAC头经过"每一跳"是会变化的,而IP头是没变过的。但是三层交换机一定不会改变IP头吗?肯定不是,以上的那种不变的情况的网关叫转发网关,下面要提到的是NAT网关。
NAT网关
如果不同的网段,网段号是一样的怎么办?在大型一点网络中,这个是很常见的情况。
比如机器1的IP地址192.168.1.10,机器4的IP地址也是192.168.1.10,它们之间通信该怎么发包。
前提条件是,每个机器都需要一个唯一的IP,可以理解为这个大的网络内的公网IP,比如机器1的公网IP是192.168.58.10,机器2的公网IP是196.168.59.10
- 机器1发包构建IP头的时候,需要把目标IP地址,换成它对应的公网IP,然后发给了网关。此时源IP是机器1的普通IP,目标IP是机器2的公网IP。
- 网关1解出IP头,由于机器A发出的网络包也要在这个网络上流通,它会将源IP改成机器1的公网IP。通过路由规则,它发现去196.168.59.10对应的端口,将网络包转发出去。
- 网关2收到包后,它的配置发现,访问196.168.59.10就是对应访问192.168.1.10,会把网络包的目标IP换成192.168.1.10,然后通过对应的端口发出去。
- 机器2收到网络包
NAT的全称是Network Address Translation,转化IP地址的意识,上诉的例子也看到了,网关转发的过程中修改了IP地址。但是何时该NAT,怎么准确NAT,它有自己非常复杂的逻辑。我作为程序员而非专业的网络工程师,暂时到此为止了。
家里的网络访问B站之类部署在公网的服务,运营商网关也将的私有IP地址NAT成公网IP地址了,服务器响应回来时,又NAT回我的私有IP地址,从而接收到对方的网络包。
到这里还留下了两个问题:
1.二层交换机每个端口都对应着MAC地址,有没有可能网络包从自己的一个端口出去,又回到自己的另一个端口了,这样交换机的世界观都崩塌了。其实也就是形成环路了,该怎么解决?
2.三层交换机的路由规则是怎么维护的?
交换机环路问题
- 要是交换机出现了这种环路问题,这个数据包是不可能发出去。
有一个数据链路层的协议叫STP
协议,它通过算法的,将带环的图,打破成最小生成树,就解决了环路的问题。
- 打破不是去拔人家网线,是通过算法避免将一个包环路发送。
- 算法的具体内容也不在这里描述,我觉得这不是网络的核心内容,然后我好像也写不太清楚。总之有了
STP
协议,妈妈再也不用担心二层交换机的环路问题了。
配置路由规则
以上例子中,三层网关怎么准发,都靠路由规则,规则是一个路由表:
目标地址(Destination) | 子网掩码(Netmask) | 网关(Gateway) | 接口(Interface) | 跃点数(Metric) |
---|---|---|---|---|
10.10.1.0 | 255.255.255.0 | - | e2 | 1 |
10.10.1.101 | 255.255.255.255 | - | e2 | 1 |
192.168.1.0 | 255.255.255.0 | - | e3 | 1 |
192.168.1.10 | 255.255.255.255 | - | e3 | 1 |
0.0.0.0 | 0.0.0.0 | 192.0.2.1 | e1 | 1 |
- 当三层交换机接收到网络包时,解出目标IP地址后,通过子网掩码可以计算出目标地址,从表中找到对应的接口(端口)或者下一跳的网关地址,然后做转发。到找不到的时候就会走目标地址为0.0.0.0这一条规则
- 跃点数表示距离目标IP地址的远近
但是它是怎么维护的呢?
最简单的就是手动配置,也就是静态路由
静态路由
这没啥说的,根据命令去配置就行了
动态路由
所有的路由规则都去手动配置也是不现实的,IP层有相关的路由协议可以生成动态路由表
首先需要提到两类算法
动态路由算法
首先是距离动态矢量路由(distance vector routing),基于Bellman-Ford算法
1.距离矢量路由算法
- 路由表有很多行,每一行的信息可以分为两部分:目标地址、自己距离它多远(跃点数)
路由器都要知道全局信息的话,它得不断更新,过程是:
- 每个路由器肯定知道自己和邻居之间的距离,比如新路由器A加入了,它的邻居路由器B知道了,于是路由器B的路由表中多了一行记录:路由器A以及距离
- 每过几秒,路由器会告诉邻居自己所掌握的全部路由表信息,于是路由器B告诉了C。注意C和B是邻居,C和A不是。
- C收到B的发来的路由表后,发现A距离B是X,自己和B距离是Y,所以C会增加一条:路由器A的IP地址以及距离X+Y
- 一会后消息传遍了整个网络,所有的路由器都知道A来了,且知道A离自己多远
它有两个问题:
1.A突然下线了,此时B不知道A下线了,B找A时,发现自己找不到,但B以为C知道,因为C的路由表原本是存了A的信息的,然后B会以为自己到A的距离是自己到C的距离加上C到A的距离, 然后更新自己的路由表。而这条信息是无用的,可怕的是这条无用的消息还会继续传播。
2.每次同步信息时,都是发送全局路由表,这个开销有点大
2.链路状态路由算法
另一类算法是链路状态路由(link state routing),基于Dijkstra算法
重在一个状态,路由器是否还存活的状态
当新路由器A加入时,会给自己的邻居们发一个echo命令
- echo是一个计算机命令,它可以基于TCP协议,服务器就在TCP端口7检测有无消息,如果使用UDP协议,基本过程和TCP一样,检测的端口也是7。 是路由也是网络中最常用的数据包,可以通过发送echo包知道当前的连接节点有那些路径,并且通过往返时间能得出路径长度。
- echo一去一回,除以2就是它们之间的距离
然后路由器会将自己和邻居之间的链路状态广播出去,然后每个路由器都会收到,所以每个路由器都能在自己本地构建一个完整的图,通过Dijkstra算法算出两点之间的最短路径。
每个路由器对外广播的都是自己对邻居echo的结果,首先不再是广播全量的路由表,开销比较小。另外能echo的邻居,一定还"活着"。
基于这两类算法,就有两种动态路由协议
动态路由协议
首先是OSPF(Open Shortest Path First),开放式最短路由优先
1.OSPF
- 它基于链路状态路由算法,目的就是寻找去对方的最短路径
- 由于主要运用于一些内部网络,这个协议也称为内部网关协议(Interior Gateway Protocal) ,IGP
另一个协议是BGP(Border Gateway Protocal),外网路由协议
2.BGP
- 这个一听就是要跨网的
- 它基于路径矢量路由算法
- 把每一个网络称为自治系统AS(Autonomous System),AS与AS之间需要通信,这自然也需要路由器
- AS之间通信用的路由器,叫边界路由器。
- 边界路由器之间的路由信息的广播是用的BGP中的eBGP协议
- 而不是每一个AS内部的机器都是直接连上的边界路由器的,这些内部路由器也需要导入边界路由器的路由信息,这是用的就是BGP中的iBGP协议
到这里,本篇的分享结束