“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
kubernetes的网络通信可以分为以下几个部分:
- pod内部的容器间通信(本次讲解)
- pod间通信(本次讲解)
- pod与service之间网络通信(下次)
- kubernetes外部与service之间的网络通信(下次)
理论:
1、pod内部的容器间通信
kubernetes为每个pod分配了一个IP地址,且同一个pod内的容器共享pod的网络命名空间(包括IP地址和网络端口)
同一个pod内的所有容器之间共享端口,可直接通过【localhost+端口】来访问。
由于每个pod有单独的IP,所以不需要考虑容器端口与主机端口映射以及端口冲突问题。
2、pod间通信
pod有可能在同一个node上运行,也有可能在不同的node上运行,所有pod间的通信分为两类:同一个node的pod之间通信;不同node上的pod之间通信。
再说pod之间怎么访问的时候,先说一下pod 的IP怎么来的,和pause容器相关。
k8s调用CNI组件的流程:
- kubelet先创建pause容器生成对应的netns网络命名空间
- 根据配置调用具体的CNI插件,可以配置成CNI插件链来进行链式调用,
- 当CNI插件被调用时,它根据环境变量以及命令行参数来获得网络命名空间netns、容器的网络设备等必要信息,然后执行ADD操作
- CNI插件给pause容器配置正确的网络,pod中其他的容器都是用pause容器的网络
比如组件是flannel的gw-host形式:
flannel首先会创建一个cni0的网桥,
当创建pod时,k8s先在k8s.io的命名空间中创建pause容器。
flannel去建立网络命名空间netns,虚拟网卡eth0和IP地址等信息
pause容器负责获取网络命名空间netns,获取虚拟网卡eth0以及IP地址,然后将pod里的容器加入到命名空间中。
pause容器起到一个协调网络的工作。
现在开始说pod和pod之间是怎么通信的
- 同一个node内的pod之间
不同的CNI之间可能有细微的差别。但是大体的逻辑是一致的,现在以flannel模式进行描述,方便大家理解。
在一个node中,flannel会将node当做为一个子网看到,分配一个IP地址段。
同时flannel创建一个cni0的网桥,在这个node上的所有pod都会绑定在这个网桥上,使用的linux的veth pair对进行绑定,如下图
flannel分为很多中模式,比较多的vxlan和gw-host模式,当使用vxlan模式,需要额外创建一个flannel.1的组件,用于封包解包。如果是使用gw-host,直接使用主机的路由表作为流量转发渠道。
由于Pod1和Pod2都关联在同一个cni0网桥上,位于同一个网段,它们之间是能直接通信的。如图红色的线是pod1和pod2之间的流量访问:pod的网卡eth0数据包到达cni0网桥的接口veth上,泛红cni的所有veth接口,只有对的目的MAC会保留数据包,其他veth都会把数据包扔掉。
- 不同node上的pod之间(CNI的组件完成的是跨主机通信)
不同Node上的Pod间通信,要达到两个条件:
- 在整个Kubernetes集群中对Pod的IP地址分配进行规划,不能有冲突。
- 找到一种办法,将Pod的IP地址和所在Node的IP地址关联起来,让Pod之间可以互相访问。
根据第一个条件,需要在部署Kubernetes时,对网桥(根据CNI组件不一样,使用的网桥名称可能不同,如docker0、cni0)的IP地址进行规划,保证每一个Node上的网桥地址没有冲突。
根据第二个条件,Pod中的数据在发出时,需要有一个机制能够知道对方Pod的IP地址挂载在哪个具体的Node上。也就是说先要找到Node对应的宿主机IP地址,将数据发送到宿主机的网卡上,然后在宿主机上将应用的数据转到具体的网桥上。一旦数据到达宿主机Node,则Node内部的网桥便知道如何将数据发送到Pod。
如下图,Pod1访问Pod3。首先要将数据从源Node1的eth0发送出去,找到并到达Node 2的eth0。除了部署Kubernetes和Docker,还需要额外的网络配置,甚至通过一些软件或插件来实现Kubernetes对网络的要求。之后,Pod之间才能无差别地透明通信。
实践:
k8s容器网络模型:
单独node中pod的网络
kubectl -n default get pod -owide//查看所有的pod
在default的命名空间中有三个pod,centos的那个pod有两个容器,两个容器都是正常的状态(READY 2/2)。
ip addr//在node1上使用查看IP命令,可以看到node1上所有的网卡,其中cni0是网桥,其他veth是挂载在这个网桥上veth pair,另一端是在pod上的eth0,且没创建创一个pod,flannel会创建一个cni-开头的netns
一个pod会对应建一个netns,查看netns 的网卡,在netns中的路由,全部指向cni的网关地址,通过eth0
ip net exec cni-63bd8b8b-c0cf-6003-9cad-7e81e3c7541e ip addr
ip net exec cni-63bd8b8b-c0cf-6003-9cad-7e81e3c7541e ip route
在node的默认网络命名空间中查看路由表,发现:
- 172.28.96.0/26内的IP地址互通,通过cni0能够通
- 169和172.17暂时不关心
- 表示在10.0.0.0/24子网内的IP地址,通过eth0可以访问,二层通,不需要下一跳——这个是node所在的子网进行访问
- 目的地址不在上述段中的,走eth0,下一跳为10.0.0.253
这个路由表,没有向网上说的,目的地址是另一个node节点上IP段,下一跳是另一个node的eth0的IP,于是登录到阿里云上的VPC和VSwitch去看VSwitch的路由表。
但是登录到vswitch的页面后,可以看到路由条目,目的IP来自172.28.96.0/26的IP包,下一跳地址是10.0.0.1的IP(实例node2的eth网卡);目的IP来自172.28.96.64/26的IP包,下一跳地址是10.0.0.252的IP(实例node1的eth网卡);
所以多个node之间pod的访问其实是这样的图形: