四、容器网络互通原理#
有了上面那些知识储备呢?再看我们今天要探究的问题,就不难了。
如下红字部分:同一个宿主机上的不同容器是如何互通的?
那我们先分别登陆容器记录下他们的ip
9001的ip是:172.17.0.2 9002的ip是:172.17.0.3
先看实验效果:在9001上curl9002
/# curl 172.7.88.3 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } ...
实验结果是网络互通!
我们再完善一下上面的图,把docker0、以及两个容器的ip补充上去,如下图:
那两台机器之前要通信是要遵循OSI网络模型、和以太网协议的。
我们管172.17.0.2
叫做容器2
我们管172.17.0.3
叫做容器3
比如我们现在是从:容器2上curl 容器3,那么容器2也必须按照以太网协议将数据包封装好,如下
src ip = 172.17.0.2 src mac = 容器2的mac地址 dst ip = 172.17.0.3 dst mac = 容器3的mac地址 ???
那现在的问题是容器3的mac地址是多少?
删掉所有容器,重新启动,方便实验抓包
容器2会先查自己的本地缓存,如果之前没有访问过,那么缓存中也没有任何记录!
:/# arp -n
不过没关系,还有arp机制兜底,于是容器2会发送arp请求包,大概如下
1、这是一个arp请求包 2、我的ip地址是:172.17.0.2 3、我的mac地址是:容器2的mac地址 4、请问:ip地址为:172.17.0.3的机器,你的mac地址是多少?
容器2会查询自己的路由表,将这个arp请求从自己的gateway发送出去
/# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.7.88.1 0.0.0.0 UG 0 0 0 eth0 172.7.88.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
我们发现容器2的网关对应的网络设备的ip就是docker0的ip地址,并且经由eth0发送出去!
哎?eth0不就是我们之前说的veth-pair设备吗?
并且我们通过下面的命令可以知道它的另一端对应着宿主机上的哪个网络设备:
/# ethtool -S eth0 NIC statistics: peer_ifindex: 53
而且我们可以下面的小实验,验证上面的观点是否正确
# 在容器中ping百度 ~]# ping 220.181.38.148 # 在宿主机上抓包 ~]# yum install tcpdump -y ~]# tcpdump -i ${vethpair宿主机侧的接口名} host 220.181.38.148 ...
所以说从容器2的eth0出去的arp请求报文会同等的出现在宿主机的第53个网络设备上。
通过下面的这张图,你也知道第53个网络设备其实就是下图中的veth0-1
所以这个arp请求包会被发送到docker0上,由docker0拿到这个arp包发现,目标ip是172.17.0.3
并不是自己,所以docker0会进一步将这个arp请求报文广播出去,所有在172.17.0.0
网段的容器都能收到这个报文!其中就包含了容器3!
那容器3收到这个arp报文后,会判断,哦!目标ip就是自己的ip,于是它将自己的mac地址填充到arp报文中返回给docker0!
同样的我们可以通过抓包验证,在宿主机上
# 在172.17.0.2容器上ping172.17.0.3 /# ping 172.17.0.3 ~]# tcpdump -i vethdb0d222 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on vethdb0d222, link-type EN10MB (Ethernet), capture size 262144 bytes 17:25:30.218640 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28 17:25:30.218683 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28 17:25:30.218686 IP 172.17.0.2.54014 > 172.17.0.3.http: Flags [S], seq 3496600258, win 29200, options [mss 1460,sackOK,TS val 4503202 ecr 0,nop,wscale 7], length 0
于是容器2就拿到了容器3的mac地址,以太网数据包需要的信息也就齐全了!如下:
src ip = 172.17.0.2 src mac = 容器2的mac地址 dst ip = 172.17.0.3 dst mac = 容器3的mac地址
再之后容器2就可以和容器3正常互联了!
容器3会收到很多数据包,那它怎么知道哪些包是发给自己的,那些不是呢?可以参考如下的判断逻辑
if 响应包.mac == 自己的mac{ // 说明这是发给自己包,所以不能丢弃 if 响应包.ip == 自己的ip{ // 向上转发到osi7层网络模型的上层 }else{ // 查自己的route表,找下一跳 } }else{ // 直接丢弃 }
五、实验环境#
# 下载 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/changwu/nginx:1.7.9-nettools # 先启动1个容器 ~]# docker run --name mynginx1 -i -t -d -p 9001:80 nginx-1.7.9-nettools:latest eb569b938c07e95ccccbfc654c1fee6364eea55b20f5394382ff42b4ccf96312 ~]# docker run --name mynginx2 -i -t -d -p 9002:80 nginx-1.7.9-nettools:latest 545ed62d3abfd63aa9c3ae196e9d7fe6f59bbd2e9ae4e6f2bd378f23587496b7 # 验证 ~]# curl 127.0.0.1:9001
六、推荐阅读#
2、这一次,让我在百度告诉你,当你请求www.baidu.com时都发生了什么?