K8S中Pod内部容器通信原理

原文地址: https://blog.csdn.net/xyz_dream/article/details/103179128

要了解和分析Pod中的container容器网络,那么首先肯定是要了解docker容器网络的几种模式以及常见用法。之后我们再带着这个问题一步一步地去进行实操验证一下我们的推断是否正确。

1.docker容器网络模式

1.1 默认bridge桥接网络

默认分配docker0网桥网段上的ip给容器, 每个容器的network namespace都是相互隔离的。docker自身生成一个veth pair(虚拟网卡对) 一端放在docker0网桥上, 一端放在容器内部。
图片

通过docker inspect 容器查看容器网络模式信息:
图片
网络详情:
图片

1.2 共享宿主机host的网络栈

容器的直接使用宿主机的网络栈 以及端口port范围。

通过docker inspect 容器查看容器网络模式信息:
图片

1.3 container共享模式

可以指定容器,与另外某个已存在的容器共享它的网络栈。 这个和上面共享宿主机网络栈类似 ,不同点是这次共享的对象由宿主机转变成了某个容器而已。
K8S里面多个container共享一个pod的网络栈以及端口范围也是基于此模式实现的。 每当一个Pod被创建,那么首先创建一个 “pause” 容器, 之后这个pod里面的其他容器通过共享这个pause容器的网络栈,实现与外部pod进行通信 , 然后通过localhost进行pod内部的container的通信。如下图所示:
图片
我们可以发现共享的这种模式, 相比bridge桥接模式有3个eth0网卡而言, 只有一个eth0网卡只出现在了”pause容器”里面,nginx和php-fpm复用了”pause容器”的network namespace,简单理解就是使用了”pause容器”的eth0网卡与外部进行通信。

通过docker inspect 容器查看容器网络模式信息:
图片
如果NetworkMode是conatiner共享类型,则Networks字段为空。应该再去找到共享conatiner的详细信息才能查看。

图片

1.4 none模式

此模式只会给容器分配隔离的network namespace, 不会分配网卡 ip地址等。默认假设我们没有手动配置网络信息, 那么这样网络模式下的容器不能上网。 那么该模式给我了我们配置容器网络的很大的发挥自由度, 也就意味着我们可以自己配置这个容器的 网卡信息 ip地址等。简单理解,docker给了你裸机容器,后面网卡veth 网卡信息 ip地址等等你可以自行分配。
图片
网络详情:
图片

docker netwokr ls // 显示当前docker存在的网络模式列表:
图片
k8s网络插件也是基于此去做网络分配,插件负责分配ip地址 网卡等给”pause容器”即可,继续往下看会详细分析。

2.举例分析

2.1 验证一个Pod中多个conatiner是否共享这个Pod的ip以及端口范围

运行一个deployemnt, 这个deployment里面的Pod要运行3个容器:

  1. nginx
  2. busybox
  3. php-fpm

用这个例子类验证一下K8S容器之间通信以及使用的docker网络模式是否和我们上面预想的一致。

docker ps 看到这个pod运行了4个容器:
图片
第一个: 8bf44ff2b133是Pod里面运行的php容器
第二个: 2e10bc93d413是这个Pod的nginx容器
第三个: ed2fc5bf201e 是这个Pod的busybox容器
第四个: 9bafb86e521c 是这个Pod的pause容器

我们分别进入这个3容器看看分配的内部eth0的ip地址:

php-fpm容器内部ip信息: 10.244.0.34
图片
nginx容器内部ip信息: 10.244.0.34
图片

busybox容器内部ip信息: 10.244.0.34
图片
我们发现了3个容器内部的ip地址都是一样, 那现在来看看K8S Pod分配的ip是不是也是和上面3个容器内部的ip地址一样?
图片
完全一致。这也证实了同一个Pod里面确实多个container共享这个Pod的ip地址。 其实可以再验证共享Pod的端口范围, 在这个Pod基础上再运行一个nginx容器会报错。 因为80端口已经被占用 , 再运行的第二个nginx默认还是监听80端口所以报错 ,可以自行尝试一下。

2.2 验证多个container是否是共享了pause容器的network namespace?

pause容器的id简写是: 9bafb86e521c

分别执行docker inspect -f ‘.HostConfig.NetworkMode’ 上面3个容器的(NetworkMode)这个字段可以看到当前这个容器的网络模式:

php容器docker inspect:
图片

可以看到当前这个php容器的networkMode使用的是 “容器共享network namespace”的模式, 共享的是下面这个容器的network namepsace

container: 9bafb86e521c43d9cc33757ecef2d4ef8342c17cce032fabe0c9d7a543ac7960

nginx容器inspect:
图片

container: 9bafb86e521c43d9cc33757ecef2d4ef8342c17cce032fabe0c9d7a543ac7960

busybox容器inspect:

图片
container: 9bafb86e521c43d9cc33757ecef2d4ef8342c17cce032fabe0c9d7a543ac7960

由此可见 这3个container的都采用了”共享conatiner network namespace”的模式,来与外部通信。 并且这个被共享的容器就是Pod里面的pause容器。

2.3 在busybox容器中查看端口监情况

图片

由此看出,busybox以及nginx php-fpm 3个容器通过localhost通信, 并且共享这个Pod的端口范围,其实也会是”pause容器”的ip和端口范围。(K8S中的Pod只是一个逻辑概念, 这个4个容器逻辑上组成了一个逻辑Pod,这一点要明确,底层还是一个个容器而已)。

2.4 查看pause容器的网络模式

那么我们现在来看看”pause容器”的docker inspect 详细信息:

图片
我们发现当前这个puase使用的docker网络模式是:

none类型且模式的id是

9b9cd04e8d5742aaea058d8d72a450641058cbd71e13219c17564960b7b3c747

查看当前docker的网络模式列表:

1
docker  network  ls

图片
9b9cd04e8d57 这个网络模式是none类型。

两者一致。

3.总结

(本环境使用flannel网络插件).由此验证K8S网络插件负责管理ip地址分配以及veth pair(虚拟网卡对)以及网桥连接工作, pause容器采用了docker的none网络模式, 网络插件再将ip和veth虚拟网卡分配给Pod的pause容器, 其它的容器再采用”共享container”的网络模式来共享这个pause的网络即可与外部进行通信。
k8s只是提供了CNI接口,只要任何overlay网络分配解决方案或者具体实现,屏蔽了底层网络分配实现,只要去对接或者说实现了CNI接口规范,那么k8s就能使用该插件为k8s中的pod进行网络配置。市面上目前有常见的一些CNI插件例如Flannel, Calico等。