![]()
2020 还没来得及品味就即将过去一个季度,愿剩下的时光不被辜负。进入正题,docker container是单进程模式,能够解决一些单一的问题,在现实中,我们常常需要多个进程放在一个「盒子」里、或者多个节点共同完成通信过程,接下来,说下这个过程的网络通信是如何实现的?
[root@localhost ~]# docker network lsNETWORK ID NAME DRIVER SCOPEc250329fad3c bridge bridge localc7c3d1f77969 compose_extnetwork bridge local199b85fbf2fa host host localb488be9da3d6 none null local
host指的是共享主机的网络和端口,但是破坏了 container 的隔离性;
其中无网络模式是指加入此模式下的容器都不能通信,比较鸡肋;
自定义主要用于实现DNS解析和服务发现,特殊场景下定制使用;
网桥是 docker 默认网络模式,也是平时用的最多的一种,这里主要对 docker 的 bridge 模式做详细讲解。
2、docker 桥接如何实现同一个宿主机不同容器之间的通信
-
docker启动后建立名为docker0的虚拟网桥。
-
容器启动时在主机上创建一对虚拟网卡veth pair设备。这一对虚拟设备完成一组数据完整流通的链路,数据从一个设备进入,从另一个设备出来。容器中重命名为eth0,宿主机上的以veth*显示并插在docker0网桥上。可以通过如下命令查看,docker0上插了 veth42f3f11 和 vethe8589bd 两张虚拟设备,见(a)图。
[root@localhost ~]# brctl showbridge name bridge id STP enabled interfacesbr-c7c3d1f77969 8000.02429160f0dd no docker0 8000.02420a13dd3a no veth42f3f11 vethe8589bd
(a)
那么你可能会有疑问,多个容器之间又是如何通信的呢?如下图所示:
![]()
(b)
其原理也非常简单,如图(b)所示,container1 ping container2(172.0.0.3)网络时,同一宿主机中的两个容器网络默认是互通的,其中docker0扮演二层交换机的角色。
通过命令查看container路由信息表,如下所示:
bash-4.4# routeKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Ifacedefault 172.17.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 * 255.255.0.0 U 0 0 0 eth0bash-4.4#
container1 访问172.17.0.3匹配到第二条路由规则;网关是0.0.0.0,这是一条直连规则,意思是匹配到该规则的网络地址经过本机eth0网卡,再通过二层网络doceker0 直接发送到目的主机。docker0之所以能够做到从veth虚拟设备中接受数据和发送数据,是因为veth相当于docker0网桥的从设备,故docker0能够直接处理来自于veth上网络数据包,进而直接转发到container2,就完成从一个容器到另外一个容器的通信。见(b)图所示。
如果访问外部网络,也非常简单,数据包先经过docker0网卡,根据宿主机路由规则连接到eth0网卡,转发到外部网络。见(c)图所示。
![]()
(c)
docker 在默认网络设置情况下,节点A 的docker0 跟节点B 的docker0 没有任何关联,网络也是不通的,这就导致不能满足我们跨节点通信要求。Kubernetes 的 Pod 又是通过何种方式实现的呢?
3、pod 通信机制
如果要说明 pod 的通信机制,要从一个镜像说起,在 kubectl 安装kubernetes 的时候一定会看到 k8s.gcr.io/pause 这个镜像,不知道有没有疑问,这个到底是干嘛的?其它几个镜像顾名思义,但是这个【暂停】是什么?没错,他就是用来 hold 一个 Pod 内部多个 Container 网络通信。
[root@k8s-client1 ~]# docker ps |grep sp-nginxe34adacf9be1 0a81924719d1 "/usr/local/nginx/b…" 3 seconds ago Up 2 seconds k8s_sp_nginx-deployment-84b5d9cb66-hkfp6_default_5c27af26-6b7e-11ea-8d03-70fd45ac3f1f_0f245174b9a51 0a81924719d1 "/usr/local/nginx/b…" 5 seconds ago Up 4 seconds k8s_sp_nginx-deployment-84b5d9cb66-zfbhr_default_5c281ef0-6b7e-11ea-8d03-70fd45ac3f1f_0
如上所示,你可以看到在创建 nginx pod 过程中,不仅创建了一个nginx 容器,并附带了一个 pause 容器,而且 pause 容器是在 nginx容器之前创建的,这个被暂停的容器把所有的容器收纳到一起,一个基础容器,唯一目的就是保存所有的命名空间。容器中 pod 共享同一个 IP 地址。故同一个 Pod 中 Container 可以做到直接通过 localhost 直接通信,那么同一个节点多个 Pod 之间如何通信的呢?
![]()
(d)
pause 容器启动之前,会为容器创建虚拟一对 ethernet 接口,一个保留在宿主机 vethxxx(插在网桥上),一个保留在容器网络命名空间内,并重命名为eth0。两个虚拟接口的两端,从一端进入,另一端出来。任何 Pod 连接到该网桥的 Pod 都可以收发数据。如(d)图所示。
跨节点 Pod 通信,相当于创建一个整个集群公用的【 网桥 】然后把集群中所有的 Pod 连接起来,就可以通信了。
![]()
(e)
其中跨整个集群的 Pod ip 是唯一的,当报文从一个节点转发到另外一个节点时,报文首先通过 veth,然后通过网桥,转发到物理适配器网卡,最后转发到其它节点的虚拟网桥,进而到达 veth 目标容器。如(e)图所示。
其实现方式有 Flannel、calico、weave 等。
注意 k8s 的网桥跟 docker0 网桥功能类似,但是 k8s 并没有复用 docker0 网桥,其原因是
Kubernetes 为了连接 infra 容器更加方便,而是重新实现了 CNI 网络接口功能,它允许网络插件使用 CNI 接口,比如 flannel,它本身实现也经过几个过程,其本质上来说,是基于「隧道」机制实现。示意图(f)所示:
![]()
(f)
5、总结
本文由浅到深的讲解了 docker 网络模式实现以及 Kubernetes Pod 跨节点之间通信原理和实现方式。简单介绍了dockers、Kubernetes网络通信方式,其内部通过网卡、回环设备、路由表、iptables等实现,如果你是个喜欢深究的人,可以研究下组网过程。
原创不易,随手关注或者”在看“,诚挚感谢!