Kubernetes网络介绍


Kubernetes网络包括网络模型、CNI、Service、Ingress、DNS等。

在Kubernetes的网络模型中,每台服务器上的容器有自己独立的IP段,各个服务器之间的容器可以根据目标容器的IP地址进行访问,如图1所示。
1.png

图1 Kubernetes网络模型概览

为了实现这一目标,重点解决以下两点:
  • 各台服务器上的容器IP段不能重叠,所以需要有某种IP段分配机制,为各台服务器分配独立的IP段;
  • 从某个Pod发出的流量到达其所在服务器时,服务器网络层应当具备根据目标 IP地址,将流量转发到该IP所属IP段对应的目标服务器的能力。


总结起来,实现Kubernetes的容器网络重点需要关注两方面:IP地址分配和路由。

Kubernetes 网络基础

在开始对Kubernetes网络进行探讨之前,我们先从Pod网络入手,简单介绍 Kubernetes网络的基本概念和框架。

IP地址分配

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


Pod出站流量

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


Kubernetes网络架构综述

谈到Kubernetes的网络模型,就不能不提它著名的“单Pod单IP”模型,即每个Pod都有一个独立的IP,Pod内所有容器共享network namespace(同一个网络协议栈和IP)。

“单Pod单IP”网络模型为我们勾勒了一个Kubernetes扁平网络的蓝图,在这个网络世界里:容器是一等公民,容器之间直接通信,不需要额外的NAT,因此不存在源地址被伪装的情况;Node与容器网络直连,同样不需要额外的NAT。扁平化网络的优点在于:没有NAT带来的性能损耗,而且可追溯源地址,为后面的网络策略做铺垫,降低网络排错的难度等。

总体而言,集群内访问Pod,会经过Service;集群外访问Pod,经过的是Ingress。Service和Ingress是Kubernetes专门为服务发现而抽象出来的相关概念,后面会做详细讨论。

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

当然也有例外,Weave就是一个会处理 docker0的CNI 插件,具体分析请看后面章节的内容。

Kubernetes网络总体架构如图2所示。
2.png

图2 Kubernetes网络总体架构

图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主机内容器的默认组网方案是bridge。flannel、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.png

图3 Kubernetes bridge网络模型

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

Kubernetes跨节点组网模型

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

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

图4 Kubernetes的bridge跨机通信网络模型

如图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.png

图5 典型的overlay网络的拓扑图

和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也支持虚拟路由等方式。

本文节选自:《Kubernetes 网络权威指南

0 个评论

要回复文章请先登录注册