2.3、什么是veth-pair技术?#
====================
我们刚才做类比理解docker0的时候说:把机房里的电脑都连接在交换机上,类比成docker 容器作为一台设备都连接着宿主机上的docker0。那具体的实现落地实现用的是啥技术呢?
答案是:veth pair
veth pair的全称是:virtual ethernet,就是虚拟的以太网卡。
说到以太网卡大家都不陌生呀,不就是我们常见的那种叫eth0或者是ens的网络设备吗?
那这个veth pair是怎么玩的呢?有啥用呢?大家可以看下面这张图
veth-pair设备总是会成对的出现,用于连接两个不同network-namespace.
就上图来说,从network-namespace1的veth0中发送的数据会出现在 network-namespace2的veth1设备中。
虽然这种特性很好,但是如果出现有多个容器,你就会发现组织架构会越来越复杂,越来越乱
不过好在我们已经循序渐进的了解Linux网桥(docker0),以及这里的veth-pair设备,于是我们可以把整体的架构图重新绘制成下面这样
因为不同容器有自己隔离后的network-namespace所以他们都有自己的网络协议栈
那我们能不能找到容器里面的网卡和物理机上的哪张卡是一对网络vethpair设备呢?
如下:
Copy# 进入容器
~]# docker exec -ti 545ed62d3abf /bin/bash
/# apt-get install ethtool
/# ethtool -S eth0
NIC statistics:
peer_ifindex: 55
回到宿主机
Copy~]# ip addr
…
55: vethf0a8bcb@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ae:eb:5c:2f:7d:c3 brd ff:ff:ff:ff:ff:ff link-netnsid 10
inet6 fe80::aceb:5cff:fe2f:7dc3/64 scope link
valid_lft forever preferred_lft forever
意思是就是说,容器545ed62d3abf的eth0网卡和宿主机通过ip addr命令查看的网络设备标号55的设备组成一对vethpair设备,彼此流量互通!
三、同一个局域网中不同主机的互联原理#
===================
先看个简单的,同一个局域网中的不同主机A、B之间是如何互联交换数据的。如下图
那,既然是同一个局域网中,说明A、B的ip地址在同一个网段,如上图就假设它们都在192.168.1.0网段。
还得再看下面这张OSI 7层网络模型图。
主机A向主机B发送数据,对主机A来说数据会从最上层的应用层一路往下层传递。比如应用层使用的http协议、传输层使用的TCP协议,那数据在往下层传递的过程中,会根据该层的协议添加上不同的协议头等信息。
根据OSI7层网络模型的设定,对于接受数据的主机B来说,它会接收到很多数据包!这些数据包会从最下层的物理层依次往上层传递,依次根据每一层的网络协议进行拆包。一直到应用层取出主机A发送给他的数据。
那么问题来了,主机B怎么判断它收到的数据包是否是发送给自己的呢?万一有人发错了呢?
答案是:根据MAC地址,逻辑如下。
Copyif 收到的数据包.MAC地址 == 自己的MAC地址{
// 接收数据
// 处理数据包
}else{
// 丢弃
}
那对于主机A来说,它想发送给主机B数据包,还不能让主机B把这个数据包扔掉,它只能中规中矩的按以太网网络协议要求封装将要发送出去的数据包,往下传递到数据链路层(这一层传输的数据要求,必须要有目标mac地址,因为数据链路层是基于mac地址做数据传输的)。
那数据包中都需要哪些字段呢?如下:
Copysrc ip = 192.168.1.2 //源ip地址,交换机
dst ip = 192.168.1.3 //目标ip地址
//本机的mac地址(保证从主机B回来的包正常送达主机A,且主机A能正常处理它)
src mac = 主机A的mac地址
dst mac = 主机B的mac地址//目标mac地址
其中的dst ip好说,我们可以直接固定写,或者通过DNS解析域名得到目标ip。
那dst mac怎么获取呢?
这就不得不说ARP协议了! ARP其实是一种地址解析协议,它的作用就是:以目标ip为线索,找到目的ip所在机器的mac地址。也就是帮我们找到dst mac地址!大概的过程如下几个step
推荐阅读:白日梦的DNS笔记
简述这个过程:主机A想给主机B发包,那需要知道主机B的mac地址。
-
主机A查询本地的arp 高速缓存中是否已经存在dst ip和dst mac地址的映射关系了,如果已存在,那就直接用。
-
本地arp高速缓存中不存在dst ip和dst mac地址的映射关系的话那就只能广播arp请求包,同一网段的所有机器都能收到arp请求包。
-
收到arp请求包的机器会对比arp包中的src ip是否是自己的ip,如果不是则直接丢弃该arp包。如果是的话就将自己的mac地址写到arp响应包中。并且它会把请求包中src ip和src mac的映射关系存储在自己的本地。
补充:
交换机本身也有学习能力,他会记录mac地址和交换机端口的映射关系。比如:mac=a,端口为1。
那当它接收到数据包,并发现mac=a时,它会直接将数据扔向端口1。
嗯,在arp协议的帮助下,主机A顺利拿到了主机B的mac地址。于是数据包从网络层流转到数据链路层时已经被封装成了下面的样子:
Copysrc ip = 192.168.1.2
src mac = 主机A的mac地址
dst ip = 192.168.1.3
dst mac = 主机B的mac地址
网络层基于ip地址做数据做转发
数据链路基于mac地址做数据转发
根据OIS7层网络模型,我们都知道数据包经过物理层发送到机器B,机器B接收到数据包后,再将数据包向上流转,拆包。流转到主机B的数据链路层。
那主机B是如何判断这个在数据链路层的包是否是发给自己的呢?
答案前面说了,根据目的mac地址判断。
Copy// 主机B
if 收到的数据包.MAC地址 == 自己的MAC地址{
if dst ip == 本机ip{
// 本地处理数据包
}else{
// 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
}
}else{
// 直接丢弃
}
这个例子比较简单,dst ip就是主机B的本机ip 所以它自己会处理这个数据包。
那数据包处理完之后是需要给主机A一个响应包,那问题又来了,响应包该封装成什么样子呢?对主机B来说响应包也需要src ip、src mac、dst ip、dst mac
Copysrc ip = 192.168.1.3
src mac = 主机B的mac地址
dst ip = 192.168.1.2
src mac = 主机A的mac地址 (之前通过arp记录在自己的arp高速缓存中了,所以,这次直接用)
同样的道理,响应包也会按照如下的逻辑被主机A接受,处理。
Copy// 主机A
if 收到的数据包.MAC地址 == 自己的MAC地址{
if dst ip == 本机ip{
// 本地处理数据包
}else{
// 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
}
}else{
// 直接丢弃
}
这一次,让我在百度告诉你,当你请求www.baidu.com时都发生了什么?
四、容器网络互通原理#
===========
有了上面那些知识储备呢?再看我们今天要探究的问题,就不难了。
如下红字部分:同一个宿主机上的不同容器是如何互通的?
那我们先分别登陆容器记录下他们的ip
Copy9001的ip是:172.17.0.2
9002的ip是:172.17.0.3
先看实验效果:在9001上curl9002
Copy/# curl 172.7.88.3