2.8 Kubernetes的网络
前面介绍了很多Kubernetes的概念以及架构方面的知识,本节我们学习Kubernetes的网络。云计算领域的网络向来是复杂的,因为牵扯到硬件网络和虚拟网络的交互。尤其是虚拟网络比较抽象,如果理解不清,许多问题排障将寸步难行。
2.8.1 虚拟化网络基础
我们知道计算有虚拟化,这是因为现在的CPU有VT-X技术,存储也可以虚拟化,比如Ceph技术。那么同样的,网络也有虚拟化,我们一般称之为SDN技术(Software Defined Network,软件定义网络)。
传统硬件网络里有网线、网口、交换机、路由器、防火墙等概念,虚拟化网络里同样也有对应的概念。因为虚拟环境的模型参考现实环境的模型来设计,这有利于架构的对接和理解,节约相关成本。
同时,虚拟网络底层也需要物理网络来支撑,中间通过网络虚拟化平台把物理层网络软件定义成了虚拟组网,如图2-17所示。网络虚拟化的出现,促进了云计算的发展,毕竟每一种IT资源都要具备弹性的优点。
那么网络虚拟化平台是通过哪些技术手段让物理网络变成虚拟网络的呢?当然这里面有很多复杂的技术,就不展开介绍了,我们对照表2-9就能了解其中一些要点。
表2-9 物理层设备虚拟化实现
图2-17 网络虚拟化示意图
表2-9中很多虚拟层的技术都是Linux本身提供的,这也很容易理解,毕竟“万物基于Linux”。当然,有些高并发场景直接用Linux自带的实现技术,是达不到性能要求的,因此产生了很多其他开源项目,比如Open vSwitch、DPDK、NFV。
2.8.2 Docker的网络
在物理机环境下,传统网络机制的接入和理解都相对简单,因为网线、网卡还有交换机这样的设备都是实实在在看得到的。而在虚拟环境下,虚拟机模式和容器模式相对难理解一些,因为它们比较抽象,而且虚拟机模式和容器模式本来也不太一样。
2.8.3 Kubernetes网络详解
Kubernetes的技术架构是由master+node组成的。节点里面运行着Pod,而Pod又是Kubernetes里最小的控制单元。因此,我们学习Kubernetes的网络可以从Pod、节点、Service、外界这几个对象间的网络连接关系入手。
我们先通过图2-18对Pod、节点和Service之间的关系有个大概的认识。
图2-18中Pod1和Pod2在不同的节点上,它们都在Service1里,内部之间存在网络连接;Pod3在Service2里,Service2与Pod1连接;Pod4和Pod5同在一个节点里,且内部连通,同时Pod2和Service3又存在连接;最后,Service1又与外部LB存在着连接。其实图2-18就是一个Kubernetes基本的网络连接图,涵盖了通用的网络连接情况,下面我们具体说说每种连接具体的实现机制。
1.Pod里容器的互通
Pod内部的容器共享一份网络和存储资源,因此内部的容器能一起享受和使用Kubernetes给Pod分配的IP地址和网络端口。
图2-18 Pod、节点和Service之间的关系
我们知道Linux除了有eth0这样的网口,还有本地环路接口(lo),这个lo其实就是localhost的缩写,在Linux系统里进程之间就是通过本地环路接口相互发包通信的。
同样的道理,Pod内部的容器是通过本地环路接口容器用到的端口互通的。
我们可以用图2-19表示Pod里容器互通。
图2-19 Pod里容器的互通
2.Pod之间的互通
Pod之间存在两种形式的互通:一种是同一个节点里Pod间的互通,另外一种是不同节点里的Pod之间相互连通。
(1)同一节点,Pod之间的互通
安装部署了Kubernetes的节点如果使用了Docker作为运行时,默认会创建一个叫作Docker0的网桥,这个Docker0就是Linux Bridge模式。每个Pod都会分配一个固定的IP,这个IP跟这个Docker0是同一个网段,而且每个Pod都会通过veth-pair方式连接到这个Docker0上。可以把每个Pod想象成一个VM虚拟机,然后每个虚拟机连接在这个Docker0的二层交换机上,如图2-20所示。
(2)不同节点、Pod之间的互通
Pod A要跟另外一节点的Pod B通信,那么很显然这两个节点之间首先要网络互通,这是先决条件。其次,每个Pod的数据要确保能从每个节点的网卡出去。最后,从整个Kubernetes集群来看,每个Pod的IP不能有冲突。所以,这种互通情况总结起来有3点需要注意。
●节点之间网络需要保证互通。
●Pod数据包能通过本节点的网卡发送出去。
●Kubernetes集群里每个Pod分配的IP不能有冲突。
不同节点、Pod之间的互通可用图2-21表示。
图2-20 同节点上的Pod之间互通
图2-21 不同节点、Pod之间的互通
通常需要一些网络插件来保证Kubernetes集群的网络规划有条理又不失性能。上面这个例子中是两个节点和3个Pod之间的互联关系,如果是N个节点和N个Pod,再加上业务场景,网络的关系就复杂了,需要一种更为专业的管理软件来主导。下面会提到Kubernetes的一些专业网络插件。
3.Pod与Service之间的互通
我们知道Kubernetes里Pod是不稳定的,随着业务量的增加或减少,必然会有扩容和缩容,因此随时可以创建或销毁Pod。Pod销毁了,它上面的IP肯定会变,因此通过访问Pod的IP来获取服务资源肯定是不行的。
我们介绍Kubernetes Service的概念时,将Service理解为Pod的前端,标签Selector可以把Service和Pod绑定,这样无论Pod如何变化,需要访问其服务的客户端都不受影响,客户端只要访问确定的Service就行了。
Service也是一个抽象的实体概念,分配给Service的都是虚拟IP,Client对其访问的请求转发都是靠kube-proxy来实现的。所以,Pod跟Service之间的互通,真正的操作者是kube-proxy,那么kube-proxy都是通过哪些方式做请求转发的呢?总的来说有3种模式:userspace、iptables和ipvs。
(1)userspace模式
在这种模式下,kube-proxy会实时监控Service和Endpoints的添加和删除操作,如果发现有创建Service的操作,kube-proxy会随机开发一个代理端口,然后为其新建一个对应的iptables规则,这个iptables就会对Service的<IP:端口>与代理端口之间的流量进行转发,最后再从Endpoints里选择后端的一个Pod,把代理端口的流量转给这个Pod。
(2)iptables模式
在这个模式下,创建Service的时候会同时建立两个iptables规则,一个给Service,另一个给Endpoints。给Service的iptables规则是为了转发流量给后端的,给Endpoints的iptables规则是为了选择Pod。
(3)ipvs模式
ipvs(IP Virtual Server,IP虚拟服务器)模式下,节点必须安装好ipvs内核模块,不然默认使用iptables模式。ipvs模式调用netlink接口创建相应的ipvs规则,同时会定期跟Service和Endpoints同步ipvs规则,以保证状态一致。流量访问Service后会重新定向到后端某一个Pod里,ipvs模式下有多种算法可以实现这个重定向过程(算法有轮询调度算法、最少连接数调度算法、目的地址散列调度算法、源地址散列调度算法),因此ipvs模式的效率和性能都很高。
下面我们总结这3种模式的优劣,如表2-10所示。
表2-10 kube-proxy三种请求转发模式对比
从表2-10可以看出,生产环境应该采用ipvs或者iptables模式,那么这两种模式该如何区分呢?我们可以看下它们之间的性能测试对比,可以参考这篇文章:
https://www.projectcalico.org/comparing-kube-proxy-modes-iptables-or-ipvs/
4.Service与外部的互通
Pod作为后端,提供资源服务给外界使用,这些资源都是通过Service来访问的。如果是集群内部访问,直接访问Service的ClusterIP即可;如果是外部访问,将通过Kubernetes Ingress组件结合一些LB(负载均衡器)做服务的外部暴露。另外还有NodePort模式,外界能通过节点的外部端口直接访问服务。总结起来有下面4种访问方式。
1)ClusterIP:通过内部IP地址暴露服务的方式,只能用于集群内访问,这也是默认类型。
2)NodePort:通过<NodeIP>:<NodePort>在集群外访问Service。
3)LoadBalancer:结合负载均衡器,给服务分配一个固定的外部地址。目前很多云厂商用这个方式访问资源服务。
4)ExternalName:结合kube-dns将相关资源映射成域名的形式,然后相互之间通过域名访问。
5.Kubernetes CNI
前面学习Docker的时候提到过,Docker为了规范网络提出了CNM模型,CNM提供了用于开发的多种网络驱动。模型里面定义很多标准的接口,供开发者使用。Kubernetes的CNI也是类似的效果和作用。CNI(Container Network Interface)的推出让Kubernetes更加关注自己内部原生机制的实现,而外部的一些网络规范设计和开发交给专业的网络厂商和开源社区去做,CNI只提供一套标准的接口。Kubernetes CNI可用图2-22表示。
图2-22 容器网络接口CNI驱动
Kubernetes CNI中,有如下几个常见的插件。
1)Loopback Plugin:用于创建一个回环接口(lo)。
2)Bridge Plugin:创建虚拟网桥,并添加容器到该虚拟网桥上。
3)PTP Plugin:用于创建veth对,在容器和主机之间建立通道。
4)MACvlan Plugin:使用macvlan技术,从物理网卡虚拟出多个网卡,虚拟网卡IP和mac地址具有唯一性。
5)IPvlan Plugin:在容器中添加一个ipvlan接口,虚拟网卡有着相同的mac地址。
除了以上几个常见插件,CNI还有很多种流行的插件,下面分别进行介绍。
(1)Flannel
Flannel是CoreOS公司开源的CNI网络插件,它的功能是让Kubernetes集群中的不同节点创建的容器都具有全集群唯一的虚拟IP地址。Flannel基于Linux TUN/TAP,使用UDP封装IP包来创建overlay网络,IP分配信息会存于ETCD中。我们上面提到不同节点和Pod间的互通的时候就提到过节点间的Pod如何通信,设计Flannel的目的就是为了更好地解决这个问题,它使不同节点上的Pod能够获得同属一个内网且不重复的IP地址。
Flannel也是目前生产中用的比较多的插件之一,简单而且性能稳定。
(2)Calico
Calico这个插件也是比较常用的,功能丰富且性能稳定。在大型Kubernetes集群里,Calico会采用最新的技术和过去积累的经验持续优化Kubernetes网络,它是一个主打三层BGP路由的解决方案,路由模式不需要封包解包,而Flannel是隧道模式,需要封包解包。
(3)Canal
Canal不是某个组件的名称,而是Tigera跟CoreOS一起合作的项目,它将Flannel的性能和Calico的网络策略管理功能进行结合。但是Calico和Flannel都在不断完善和更新,目前Calico的性能已经很不错了,Canal的优势不再明显。不过如果有需求,比如想同时用Flannel和Calico,就可以选择Canal。
(4)Cilium
Cilium也是开源的软件,它是BPF的内核技术。BPF这项技术可以保证API和进程的安全性,以保护容器或Pod内的通信。
(5)WeaveNet
WeaveNet在安全性方面能够提供企业级的服务,通常应用于金融银行领域。采用了WeaveNet的网络加密模式,安全性能会得到很大的保证,但是性能方面会降低很多,后面会有相关的数据测试对比,大家可以参考。
这些CNI插件各有优劣,目前用的比较多的是Calico和Flannel。那么它们的性能到底如何?如图2-23所示是CNI插件性能对比,大家可以参考。
图2-23 CNI插件连接方式性能对比
除了最关键的性能,我们也要考虑资源消耗(毕竟消耗意味着成本),图2-24所示是内存和CPU两种资源消耗情况对比。第一条是裸金属环境条件(用作基准对比),第二条是没用任何CNI插件的数据(也是用于基准测试对比)资源消耗测试对比。
图2-24 CNI插件资源消耗测试对比
从图2-24中我们可以看出,消耗内存最少的是Flannel,为427MB(排除kube-router),Calico表现也还不错,消耗441MB;在CPU占用这项,Flannel和Calico不相上下,Flannel占5.7%,Calico占5.9%。
从性能和资源消耗来看,Flannel和Calico选择哪个都可以。Flannel的优势就是精简,但是不支持NetworkPolicies(网络策略),Calico功能相对齐全,但是不支持自动设置MTU值(MTU值对网络性能影响还是很大的)。
如果你的环境对于安全要求非常高,不在乎性能和资源消耗,可以选择Cilium和WeaveNet插件,它们都有加密模式。相比于Cilium,WeaveNet的资源消耗少一些。
上面的测试结果都来自ITNEXT的文章“Benchmark results of Kubernetes network plugins (CNI) over 10Gbit/s network (Updated: April 2019)”,这篇文章做了专业的压测分析(测试时间是2019年4月)。
本节从基本的网络虚拟化概念讲起,依次介绍了Kubernetes的网络实现方式和CNI网络插件。网络虚拟化是云计算领域的一个难点,较为复杂,有兴趣的读者可以找相关主题图书深入研究。