【转载】 Kubernetes网络介绍

olivee 5年前 ⋅ 1125 阅读

参考

IP地址分配

Kubernetes使用各种IP范围为节点、Pod和服务分配IP地址。

  1. 系统会从集群的VPC网络为每个节点分配一个IP地址。该节点IP用于提供从控制组件(如Kube-proxy和Kubelet)到Kubernetes Master的连接;
  2. 系统会为每个Pod分配一个地址块内的IP地址。用户可以选择在创建集群时通过--pod-cidr指定此范围;
  3. 系统会从集群的VPC网络为每项服务分配一个IP地址(称为ClusterIP)。大部分情况下,该VPC与节点IP地址不在同一个网段,而且用户可以选择在创建集群时自定义VPC网络。

Pod出站流量

Kubernetes处理Pod的出站流量的方式主要分为以下三种:

  1. Pod到Pod,在Kubernetes集群中,每个Pod都有自己的IP地址,运行在Pod内的应用都可以使用标准的端口号,不用重新映射到不同的随机端口号。所有的Pod之间都可以保持三层网络的连通性,比如可以相互ping对方,相互发送TCP/UDP/SCTP数据包。CNI就是用来实现这些网络功能的标准接口。
  2. Pod到Service,Pod的生命周期很短暂,但客户需要的是可靠的服务,因此Kubernetes引入了新的资源对象Service,其实它就是Pod前面的4层负载均衡器。Service总共有 4 种类型,其中最常用的类型是ClusterIP,这种类型的Service会自动分配一个仅集群内部可以访问的虚拟IP。Kubernetes通过Kube-proxy组件实现这些功能,每台计算节点上都运行一个 kube-proxy进程,通过复杂的iptables/IPVS规则在Pod和Service之间进行各种过滤和NAT。
  3. Pod到集群外,从Pod内部到集群外部的流量,Kubernetes会通过SNAT来处理。SNAT做的工作就是将数据包的源从Pod内部的IP:Port替换为宿主机的IP:Port。当数据包返回时,再将目的地址从宿主机的IP:Port 替换为Pod内部的IP:Port,然后发送给Pod。当然,中间的整个过程对Pod来说是完全透明的,它们对地址转换不会有任何感知。

Kubernetes网络架构综述

与CRI之于Kubernetes的runtime类似,Kubernetes使用CNI作为Pod网络配置的标准接口。需要注意的是,CNI并不支持Docker网络,也就是说,docker0网桥会被大部分CNI插件“视而不见”

图2 Kubernetes网络总体架构: k8s-network.png

图2描绘了当用户在Kubernetes里创建了一个Pod后,CRI和CNI协同创建Pod 所属容器,并为它们初始化网络协议栈的全过程。具体过程如下:

  1. 当用户在Kubernetes的Master里创建了一个Pod后,Kubelet观察到新Pod 的创建,于是首先调用CRI(后面的runtime实现,比如dockershim、containerd 等)创建Pod内的若干个容器。
  2. 在这些容器里,第一个被创建的pause容器是比较特殊的,这是Kubernetes系统“赠送”的容器,也称pause容器。里面运行着一个功能十分简单的C程序,具体逻辑是一启动就把自己永远阻塞在那里。一个永远阻塞而且没有实际业务逻辑的pause容器到底有什么用呢?用处很大。我们知道容器的隔离功能利用的是 Linux内核的namespace机制,而只要是一个进程,不管这个进程是否处于运行状态(挂起亦可),它都能“占”用着一个namespace。因此,每个Pod内的第一个系统容器pause的作用就是占用一个Linux的network namespace
  3. Pod内其他用户容器通过加入这个network namespace的方式共享同一个 networknamespace。用户容器和pause容器之间的关系有点类似于寄居蟹和海螺。因此,Container runtime创建Pod内的用户容器时,调用的都是同一个命令:docker run --net= none。意思是只创建一个network namespace,不初始化网络协议栈。如果这个时候通过nsenter方式进入容器,会看到里面只有一个本地回环设备lo。
  4. 容器的eth0是怎么创建出来的呢?答案是CNI。CNI主要负责容器的网络设备初始化工作。Kubelet目前支持两个网络驱动,分别是Kubenet和CNI。Kubenet是一个历史产物,即将废弃,因此本节不过多介绍。CNI有多个实现,官方自带的插件就有p2p、bridge等,这些插件负责初始化pause容器的网络设备,也就是给pause容器内的eth0分配IP等,到时候,Pod内其他容器就使用这个IP与其他容器或节点进行通信。Kubernetes主机内容器的默认组网方案bridgeflannel、Calico这些第三方插件解决容器之间的跨机通信问题,典型的跨机通信解决方案有bridge和overlay等。

Kubernetes主机内组网模型

Kubernetes经典的主机内组网模型是veth pair+bridge的方式。

当Kubernetes调度Pod在某个节点上运行时,它会在该节点的Linux内核中为Pod创建network namespace,供Pod内所有运行的容器使用。从容器的角度看,Pod是具有一个网络接口的物理机器Pod中的所有容器都会看到此网络接口。因此,每个容器通过localhost就能访问同一个Pod内的其他容器

Kubernetes使用veth pair容器与主机的网络协议栈连接起来,从而使数据包可以进出Pod。容器放在主机根network namespace中veth pair的一端连接到Linux网桥,可让同一节点上的各Pod之间相互通信,如图3所示。

图3 Kubernetes bridge网络模型: host-pod-network.png

如果Kubernetes集群发生节点升级、修改Pod声明式配置、更新容器镜像或节点不可用,那么Kubernetes就会删除并重新创建Pod。在大部分情况下,Pod创建会导致容器IP发生变化。也有一些CNI插件提供Pod固定IP的解决方案,例如Weave、Calico等。

Kubernetes跨节点组网模型

Kubernetes典型的跨机通信解决方案有bridge、overlay等,下面我们将简单介绍这两种方案的基本思路。

Kubernetes的bridge跨机通信网络模型如图4所示。

图4 Kubernetes的bridge跨机通信网络模型: node-breadge.png

如图4所示,Node1上Pod的网段是10.1.1.0/24,接的Linux网桥是10.1.1.1,Node2上Pod的网段是10.1.2.0/24,接的Linux网桥是10.1.2.1,接在同一个网桥上的Pod通过局域网广播通信。我们发现,Node1上的路由表的第二条是:

10.1.1.0/24 dev cni0

意思是,所有目的地址是本机上Pod的网络包,都发到cni0这个Linux网桥,进而广播给Pod。

注意看第三条路由规则:

10.1.2.0/24 via 192.168.1.101

10.1.2.0/24是Node2上Pod的网段,192.168.1.101又恰好是Node2的IP。意思是,目的地址是10.1.2.0/24的网络包,发到Node2上。这时,我们观察Node2上面的第二条路由信息:

10.1.2.0/24 dev cni0

就会知道,这个包会被接着发给Node2上的Linux网桥cni0,再广播给目标Pod。回程报文同理(走一条逆向的路径)。因此,我们可以得出一个结论:bridge网络本身不解决容器的跨机通信问题,需要显式地书写主机路由表,映射目标容器网段和主机IP的关系,集群内如果有N个主机,需要N - 1条路由表项。

至于overlay网络,它是构建在物理网络之上的一个虚拟网络,其中VXLAN是主流的overlay标准。VXLAN就是用UDP包头封装二层帧,即所谓的MAC in UDP。图5所示为典型的overlay网络的拓扑图。

图5 典型的overlay网络的拓扑图: overlay.png

和bridge网络类似,Pod同样接在Linux网桥上,目的地址落在本机Pod网段的网络包同样发给Linux网桥cni0。不同的是,目的Pod在其他节点上的路由表规则,例如:

10.1.0.0/16 dev tun0

这次是直接发给本机的tun/tap设备tun0,而tun0就是overlay隧道网络的入口。我们注意到,集群内所有机器都只需要这么一条路由表,不需要像bridge网络那样,写N-1条路由表项。如何将网络包正确地传递到目标主机的隧道口另一端呢?以flannel的实现为例,它会借助一个分布式的数据库,记录目的容器IP与所在主机的IP的映射关系,而且每个节点上都会运行一个agent。例如,flanneld 会监听在tun0上进行的封包和解包操作。例如,Node1上的容器发包给Node2上的容器,flanneld会在tun0处将一个目的地址是 192.168.1.101:8472的UDP包头(校验和置成0)封装到这个包的外层,然后借着主机网络的东风顺利到达 Node2。监听在Node2的tun0上的flanneld捕获这个特殊的UDP包(检验和为 0),知道这是一个overlay的封包,于是解开UDP包头,将它发给本机的Linux网桥 cni0,进而广播给目的容器。

bridge和overlay是Kubernetes最早采用的跨机通信方案,但随着集成Weave和Calico等越来越多的CNI 插件,Kubernetes也支持虚拟路由等方式。