19

Kubernetes网络介绍

 4 years ago
source link: https://www.tuicool.com/articles/uaEnyiN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

NbQje2N.png!web

图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所示。

VNBBzuy.png!web

图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所示。

2qyUn2z.png!web

图3 Kubernetes bridge网络模型

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

Kubernetes跨节点组网模型

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

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

FFJbIjM.png!web

图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网络的拓扑图。

aYfeum6.png!web

图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 网络权威指南


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK