docker容器网络通信原理-单宿主机场景

一、要探究的问题

同一台宿主机上的多个docker容器各自拥有独立的network namespace,这些容器都有一套独立的网络栈(网卡、回环设备、路由表、iptables规则),那么这些容器是如何通信的?

二、关于网桥

要实现多个使用不同网络栈的容器能够互相通信,就需要有一个交换机把他们关联起来,网桥就扮演了交换机的角色。网桥是一个工作在数据链路层的设备,根据MAC地址学习将数据包转发到网桥的不同端口上。

docker项目会在宿主机上创建一个名称为docker0的网桥,例如:

docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.31.0.1  netmask 255.255.255.0  broadcast 172.31.0.255
        inet6 fe81::22:32ff:fec1:c794  prefixlen 64  scopeid 0x20<link>
        ether 03:43:89:c1:c7:94  txqueuelen 0  (Ethernet)
        RX packets 719013  bytes 163165160 (155.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 552671  bytes 1085555452 (1.0 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

那么容器是如何连接到docker0网桥上的?

这里就需要介绍Veth Pair这样一个虚拟设备:

Veth Pair的特点:这个设备被创建出来之后,是以两张虚拟网卡的形式成对出现,其中一张网卡发出的数据包可以直接出现在对应的另一张网卡上,即使这两张卡在不同的network namespace里面也可以实现。

在容器通信的应用场景里,Veth Pair的一端在容器网络栈内,充当eth0网卡,另一端在宿主机上充当一块虚拟网卡,这块虚机网卡实际是”插“在docker0网桥上的一个从设备,可以理解为docker0网桥的一个端口,这块内容后续会介绍。

我们可以看一下Veth Pair的应用场景,加深理解:

注意以下命令都是在宿主机执行

我们启动一个busybox容器:

docker run -itd busybox:latest

得到运行的容器id:

f2ef1bc23ff2

然后查看当前容器的网卡情况:

 docker exec f2ef1bc23ff2 ifconfig

返回内容:

eth0      Link encap:Ethernet  HWaddr 02:42:AC:1F:00:03  

          inet addr:172.31.0.3  Bcast:172.31.0.255  Mask:255.255.255.0

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:8 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:0 

          RX bytes:656 (656.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  

          inet addr:127.0.0.1  Mask:255.0.0.0

          UP LOOPBACK RUNNING  MTU:65536  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000 

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

可以看到容器内有一块eth0网卡,这就是Veth Pair设备在容器的这一端,另一端在宿主机上,我们看看容器的这块eth0网卡在宿主机上对应的是什么设备:

docker exec f2ef1bc23ff2 cat /sys/class/net/eth0/iflink

返回内容:

121

121的意思是宿主机的虚机网卡的序号是121,那么我们根据这个信息去查询宿主机上序号为121的网卡设备:

ip link | grep 121

返回内容:

121: veth3486f3d@if120: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 

可以看到这块虚拟网卡名称是 veth3486f3d

通过在宿主机执行ifconfig命令可以看到这块网卡的详细信息:

veth3486f3d: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet6 fe80::c805:10ff:feb8:35dc  prefixlen 64  scopeid 0x20<link>

        ether ca:05:10:c8:55:dc  txqueuelen 0  (Ethernet)

        RX packets 0  bytes 0 (0.0 B)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 8  bytes 656 (656.0 B)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

现在我们以容器内的eth0网卡为线索,找到了它在宿主机上对应的虚拟网卡设备。

那么在哪里查看这个虚拟网卡和网桥的关联关系呢?

我们可以执行brctl show查看网桥状态:

可以看到veth3486f3d 这块网卡是”插“在docker0网桥上。

这样容器内eth0网卡和宿主机上的虚拟网卡veth3486f3d的映射关系就很清楚了。它们是一对Veth Pair设备,一端在docker容器内充当容器的eth0网卡,另一端”插“在docker0网桥上。

对Veth Pair设备的介绍就告一段落了,为了理解后续数据包的传递过程,我们来看看容器内的路由情况:

docker exec f2ef1bc23ff2 route

返回内容:

 可以看到eth0网卡是容器内的默认路由设备,并且第二条规则是直连规则(网关是*),匹配这条规则的请求应该经过本机的eth0网卡通过二层网络直接发给目的主机。

三、容器通信原理分析

介绍了网桥和容器内的路由情况,我们来看看单个宿主机上不同的docker容器是怎么通信的。

梳理了过程,画了一张图:

环境说明:

容器1 ip: 172.31.0.3 

容器2 ip: 172.31.0.4

虚拟网卡1:veth3486f3d

虚拟网卡2:veth39f81a5

容器1的eth0网卡对应虚拟网卡1 ,这是一对Veth Pair设备

容器2的eth0网卡对应虚拟网卡2 ,这是一对Veth Pair设备

通信过程说明:

1.容器1尝试访问容器2的IP地址

2.这条请求匹配到了容器1路由中的第二条地址,即:

172.31.0.0      *               255.255.255.0   U     0      0        0 eth0

前文已经提到,这是一条直连规则,数据包应该通过二层网络直接发往目标机器

3.要通过二层网络将数据包发往容器2,需要知道容器2 172.31.0.4 这个ip地址对应的MAC地址,此时容器1的网络协议栈通过eth0网卡发送一个ARP(地址解析协议)广播,目的是通过容器2的IP地址查找对应的MAC地址

4.由于eth0网卡是一个Veth Pair设备,它的另一端”插“在docker0网桥上,当虚拟网卡插在网桥上之后,就变成了网桥的从设备,从设备无权调用网络协议栈处理数据包,实质上降级为网桥上的一个端口。端口的唯一作用是接收流入的数据包,将这些数据包交给网桥处理(转发/丢弃)。

所以此时网桥收到了ARP请求,将请求转发给插在网桥上的各个虚拟网卡,虚拟网卡2此时收到了ARP请求,将容器2:172.31.0.4 这个IP地址对应的MAC地址回复给容器1。

5.容器1收到了 容器2的MAC地址之后,就可以通过eth0发出数据包了,根据Veth Pair设备原理,数据包出现在虚拟网卡1:veth3486f3d上。

6.由于虚拟网卡1:veth3486f3d只是 docker0网桥的从设备,因此容器1发出的数据包最终流入了docker0网桥,网桥来处理后续的转发。

7.docker0根据数据包的目的MAC地址,在CAM表中查到对应的目的端口是虚拟网卡2:veth39f81a5,将数据包发往该端口

8.虚拟网卡2和容器2的eth0网卡也是一对Veth Pair设备,此时数据包进入了容器2的 Network Namespace。容器2接收到了这个由容器1发出的数据包。

从以上过程我们可以归纳一下几个重要信息:

1.从容器1访问容器2,需要先知道容器2的MAC地址。

2.查询MAC地址靠ARP广播。

3.docker0网桥扮演的是二层交换机的角色。

4.容器网络栈和docker0网桥的交互,依赖Veth Pair设备。

理解单宿主机容器网络通信原理对于后续理解kubernetes多宿主机容器通信机制有重要的帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值