佳人彩云里
欲赠隔远天
前言
最近在使用K8S集群部署服务和项目时,发现集群网络问题很是复杂,因此想系统了解一下K8S的网络原理。实际使用时发现pod似乎是可以直接使用宿主机网络配置的,但是集群的设置又会影响到各pod,被搞的有些迷糊。
K8S网络模型
K8S集群网络模型的一个基础原则就是:每个pod都拥有一个独立的IP,并且假定所有pod都在同一个扁平的网络空间中,因此所有pod(不管在哪个物理节点上)都可以直接通过IP进行互相访问,因此用户不必额外考虑如何建立pod之间的连接,也不需要考虑将容器端口映射到主机。
K8S集群里,IP是以pod为单位分配的;而在pod中,内部所有容器共享一个网络堆栈(一个网络命名空间,包括IP地址、网络设备、配置等)。此模型叫作IP-per-Pod模型。在pod内部的容器看到的自己的IP和端口与外部所见一致,由此规避NAT进行内外部转换。同时,由于内部的容器共享同一个网络堆栈,因此它们可以通过localhost进行互相访问。
因此K8S网络的要求如下:
- 所有容器可以不通过NAT进行互相访问
- 所有节点也可以不通过NAT访问容器
- 容器地址内外部所见一致
Linux网络基础
Docker技术依赖于Linux内核虚拟化技术,其网络的依赖于此。
- 网络命名空间
为了支持网络协议栈的多个实例,Linux在网络栈中引入命名空间,实现对不同协议栈的隔离,两个不同命名空间彼此无法通信,同时也可以拥有独立的路由表、防火墙、NAT等。 - Veth设备对
若要使不同的命名空间互相通信,则需要使用Veth设备对,打个比方,Veth设备对就像一根管子,联通两个不同的网络命名空间 - 网桥
网桥负责Linux端口之间的多对多通信 - 路由
负责转发
Docker网络实现
Docker网络支持4中模式:
- host模式
- container模式
- none模式
- bridge模式(K8S默认)
在bridge模式下,Docker Daemon第一次启动时会创建一个虚拟网桥docker0,并分配给其一个子网。针对每一个由docker创建的容器,都会创建一个虚拟Veth设备对,一端关联网桥,一端映射到容器内部的eth0设备上。
如上图所示,这么一来,同一节点上的容器之间就可相互通信,不同节点上的容器就得通过宿主机端口的映射才能通信了,哪怕其实它们的网路地址范围相同。
K8S网络实现
K8S面临以下几个场景:
- 容器与容器之间直接通信
共享同一网络栈,不需要多做修改,直接使用localhost进行通信 - pod到pod之间通信
- 同一node
通过docker0网桥进行通信 - 不同node
需要一个机制发现目标pod在哪个node上,转发过去之后,在分发给对应的虚拟网桥,这就需要网络组件的帮助了
- 同一node
- pod与service之间的通信
通过给service分配内部IP和端口实现 - 集群外部和内部组件通信
将内部组件映射到物理机端口上