Docker容器网络-实现篇

前面介绍了:Docker容器网络-基础篇html

前文说到容器网络对Linux虚拟化技术的依赖,这一篇章咱们将一探究竟,看看Docker到底是怎么作的。一般,Linux容器的网络是被隔离在它本身的Network Namespace中,其中就包括:网卡(Network Interface)、回环设备(Loopback Device)、路由表(Routing Table)和iptables规则。对于一个进程来讲,这些要素,就构成了它发起和响应网络请求的基本环境。docker

管中窥豹

咱们在执行docker run -d --name xxx以后,进入容器内部:segmentfault

## docker ps 可查看全部docker
## 进入容器
docker exec -it 228ae947b20e /bin/bash

并执行 ifconfig:bash

$ ifconfig
eth0 Link encap:Ethernet HWaddr 22:A4:C8:79:DD:1A
 inet addr:192.168.65.28 Bcast:

咱们看到一张叫eth0的网卡,它正是一个Veth Pair设备在容器的这一端。网络

咱们再经过 route 查看该容器的路由表:oop

$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use If

咱们能够看到这个eth0是这个容器的默认路由设备。咱们也能够经过第二条路由规则,看到全部对 169.254.1.1/16 网段的请求都会交由eth0来处理。学习

而Veth Pair 设备的另外一端,则在宿主机上,咱们一样也能够经过查看宿主机的网络设备来查看它:url

$ ifconfig
......
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
 inet 172.16.241.192 netmask 255.255.240.0 broadcast 172.16.255.255
 ether 00:16:3e:0a:f3:75 txqueuelen 1000 (Ethernet)
 RX packets 3168620550 bytes 727592674740 (677.6 GiB)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 2937180637 bytes 8661914052727 (7.8 TiB)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
......
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
 ether 02:42:16:58:92:43 txqueuelen 0 (Ethernet)
 RX packets 0 bytes 0 (0.0 B)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 0 bytes 0 (0.0 B)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
......
vethd08be47: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
 ether 16:37:8d:fe:36:eb  txqueuelen 0 (Ethernet)
 RX packets 193 bytes 22658 (22.1 KiB)
 RX errors 0 dropped 0 overruns 0 frame 0
 TX packets 134 bytes 23655 (23.1 KiB)
 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
......

在宿主机上,容器对应的Veth Pair设备是一张虚拟网卡,咱们再用brctl show命令查看网桥:spa

$ brctl show
bridge name    bridge id        STP enabled    interfaces
docker0 8000.0242afb1a841 no vethd08be47

能够清楚的看到Veth Pair的一端 vethd08be47 就插在 docker0 上。设计

我如今执行docker run 启动两个容器,就会发现docker0上插入两个容器的 Veth Pair的一端。若是咱们在一个容器内部互相ping另一个容器的IP地址,是否是也能ping通?

$ brctl show
bridge name    bridge id        STP enabled    interfaces
docker0 8000.0242afb1a841 no veth26cf2cc
 veth8762ad2

容器1:

$ docker exec -it f8014a4d34d0 /bin/bash
$ ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:0

容器2:

$ docker exec -it 9a6f38076c04 /bin/bash
$ ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:0

从一个容器ping另一个容器:

# -> 容器1内部 ping 容器2
$ ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17

咱们看到,在一个容器内部ping另一个容器的ip,是能够ping通的。也就意味着,这两个容器是能够互相通讯的。

容器通讯

咱们不妨结合前文时所说的,理解下为何一个容器能访问另外一个容器?先简单看如一幅图:
image.png
当在容器1里访问容器2的地址,这个时候目的IP地址会匹配到容器1的第二条路由规则,这条路由规则的Gateway是0.0.0.0,意味着这是一条直连规则,也就是说凡是匹配到这个路由规则的请求,会直接经过eth0网卡,经过二层网络发往目的主机。而要经过二层网络到达容器2,就须要127.17.0.3对应的MAC地址。因此,容器1的网络协议栈就须要经过eth0网卡来发送一个ARP广播,经过IP找到MAC地址。

所谓ARP(Address Resolution Protocol),就是经过三层IP地址找到二层的MAC地址的协议。这里说到的eth0,就是Veth Pair的一端,另外一端则插在了宿主机的docker0网桥上。eth0这样的虚拟网卡插在docker0上,也就意味着eth0变成docker0网桥的“从设备”。从设备会降级成docker0设备的端口,而调用网络协议栈处理数据包的资格所有交给docker0网桥。

因此,在收到ARP请求以后,docker0就会扮演二层交换机的角色,把ARP广播发给其它插在docker0网桥的虚拟网卡上,这样,127.17.0.3就会收到这个广播,并把其MAC地址返回给容器1。有了这个MAC地址,容器1的eth0的网卡就能够把数据包发送出去。这个数据包会通过Veth Pair在宿主机的另外一端veth26cf2cc,直接交给docker0。

docker0转发的过程,就是继续扮演二层交换机,docker0根据数据包的目标MAC地址,在CAM表查到对应的端口为veth8762ad2,而后把数据包发往这个端口。而这个端口,就是容器2的Veth Pair在宿主机的另外一端,这样,数据包就进入了容器2的Network Namespace,最终容器2将响应(Ping)返回给容器1。在真实的数据传递中,Linux内核Netfilter/Iptables也会参与其中,这里再也不赘述。

CAM就是交换机经过MAC地址学习维护端口和MAC地址的对应表

这里介绍的容器间的通讯方式就是docker中最多见的bridge模式,固然此外还有host模式、container模式、none模式等,对其它模式有兴趣的能够去阅读相关资料。

跨主通讯

好了,这里不由问个问题,到目前为止只是单主机内部的容器间通讯,那跨主机网络呢?在Docker默认配置下,一台宿主机的docker0网桥是没法和其它宿主机连通的,它们之间没有任何关联,因此这些网桥上的容器,天然就没办法多主机之间互相通讯。可是不管怎么变化,道理都是同样的,若是咱们建立一个公共的网桥,是否是集群中全部容器均可以经过这个公共网桥去链接?

固然在正常的状况下,节点与节点的通讯每每能够经过NAT的方式,可是,这个在互联网发展的今天,在容器化环境下未必适用。例如在向注册中心注册实例的时候,确定会携带IP,在正常物理机内的应用固然没有问题,可是容器化环境却未必,容器内的IP极可能就是上文所说的172.17.0.2,多个节点都会存在这个IP,大几率这个IP是冲突的。

若是咱们想避免这个问题,就会携带宿主机的IP和映射的端口去注册。可是这又带来一个问题,即容器内的应用去意识到这是一个容器,而非物理机,当在容器内,应用须要去拿容器所在的物理机的IP,当在容器外,应用须要去拿当前物理机的IP。显然,这并非一个很好的设计,这须要应用去配合配置。因此,基于此,咱们确定要寻找其余的容器网络解决方案。

image.png
在上图这种容器网络中,咱们须要在咱们已有的主机网络上,经过软件构建一个覆盖在多个主机之上,且能把全部容器连通的虚拟网络。这种就是Overlay Network(覆盖网络)。

关于这些具体的网络解决方案,例如Flannel、Calico等,我会在后续篇幅继续陈述。

_连接: https://www.cnblogs.com/sally... 做者Mr_Zack_