2020 还没来得及品味就即将过去一个季度,愿剩下的时光不被辜负。进入正题,docker container是单进程模式,能够解决一些单一的问题,在现实中,我们常常需要多个进程放在一个「盒子」里、或者多个节点共同完成通信过程,接下来,说下这个过程的网络通信是如何实现的?
1、docker 网络模式
可以通过如下命令行查看docker网络模式
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c250329fad3c bridge bridge local
c7c3d1f77969 compose_extnetwork bridge local
199b85fbf2fa host host local
b488be9da3d6 none null local
host指的是共享主机的网络和端口,但是破坏了 container 的隔离性;
其中无网络模式是指加入此模式下的容器都不能通信,比较鸡肋;
自定义主要用于实现DNS解析和服务发现,特殊场景下定制使用;
网桥是 docker 默认网络模式,也是平时用的最多的一种,这里主要对 docker 的 bridge 模式做详细讲解。
2、docker 桥接如何实现同一个宿主机不同容器之间的通信
其实主要用到两个技术知识点:
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-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# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 * 255.255.0.0 U 0 0 0 eth0
bash-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 网络通信。
如果在计算节点上运行 docker ps 命令
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c250329fad3c bridge bridge local
c7c3d1f77969 compose_extnetwork bridge local
199b85fbf2fa host host local
b488be9da3d6 none null local
如上所示,你可以看到在创建 nginx pod 过程中,不仅创建了一个nginx 容器,并附带了一个 pause 容器,而且 pause 容器是在 nginx容器之前创建的,这个被暂停的容器把所有的容器收纳到一起,一个基础容器,唯一目的就是保存所有的命名空间。容器中 pod 共享同一个 IP 地址。故同一个 Pod 中 Container 可以做到直接通过 localhost 直接通信,那么同一个节点多个 Pod 之间如何通信的呢?
(d)
pause 容器启动之前,会为容器创建虚拟一对 ethernet 接口,一个保留在宿主机 vethxxx(插在网桥上),一个保留在容器网络命名空间内,并重命名为eth0。两个虚拟接口的两端,从一端进入,另一端出来。任何 Pod 连接到该网桥的 Pod 都可以收发数据。如(d)图所示。
4、跨 node pod 通信
跨节点 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等实现,如果你是个喜欢深究的人,可以研究下组网过程。