K8S中iptables和ipvs区别比较
从k8s的1.8版本开始,并在v1.9中进入了beta,kube-proxy
引入了IPVS模式,IPVS模式与iptables
同样基于Netfilter
,但是ipvs采用的hash表,iptables
采用一条条的规则列表。
iptables
又是为了防火墙设计的,集群数量越多iptables
规则就越多,而iptables
规则是从上到下匹配,所以效率就越是低下。因此当service
数量达到一定规模时,hash
查表的速度优势就会显现出来,从而提高service
的服务性能。
每个节点的kube-proxy
负责监听API server
中service
和endpoint
的变化情况。将变化信息写入本地userspace、iptables、ipvs
来实现service
负载均衡,使用NAT将vip流量转至endpoint
中。由于userspace
模式因为可靠性和性能(频繁切换内核/用户空间)早已经淘汰,所有的客户端请求svc,先经过iptables
,然后再经过kube-proxy
到pod,所以性能很差。
ipvs和iptables
都是基于netfilter
的,两者差别如下:
- ipvs 为大型集群提供了更好的可扩展性和性能
- ipvs 支持比 iptables 更复杂的负载均衡算法(最小负载、最少连接、加权等等)
- ipvs 支持服务器健康检查和连接重试等功能
在集群中不超过1000个服务的时候,iptables 和 ipvs
并无太大的差异。而且由于iptables
与网络策略实现的良好兼容性,iptables
是个非常好的选择。当你的集群服务超过1000个时,而且服务之间链接大多没有开启keepalive
,IPVS模式可能是一个不错的选择。
Iptables模式
kube-proxy
就可以通过 Service
的Informer
感知到API Server
中service
和endpoint
的变化情况。
而作为对这个事件的响应,它就会在宿主机上创建这样一条iptables
规则(你可以通过 iptables-save
看到它)。这些规则捕获到service的clusterIP
和port的流量,并将这些流量随机重定向到service后端Pod。对于每个endpoint
对象,它生成选择后端Pod的iptables
规则。
如果选择的第一个Pod没有响应,kube-proxy
将检测到到第一个Pod的连接失败,并将自动重试另一个后端Pod。
拓扑图
iptables
是一个 Linux 内核功能,是一个高效的防火墙,并提供了大量的数据包处理和过滤方面的能力。
它可以在核心数据包处理管线上用 Hook 挂接一系列的规则。
iptables
模式中 kube-proxy
在 NAT pre-routing Hook
中实现它的 NAT 和负载均衡功能。这种方法简单有效,依赖于成熟的内核功能,并且能够和其它跟 iptables 协作的应用融洽相处。
因为它纯粹是为防火墙而设计且基于内核规则列表,kube-proxy
使用的是一种 O(n) 算法,其中的 n 随集群规模同步增长,所以这里的集群规模越大,更明确的说就是服务和后端 Pod 的数量越大,查询的时间就会越长。
一个例子是,在5000节点集群中使用
NodePort
服务,如果我们有2000个服务并且每个服务有10个 pod,这将在每个工作节点上至少产生20000个 iptable 记录,这会使内核非常繁忙。
结论
kube-proxy
通过 iptables
处理 Service
的过程,其实需要在宿主机上设置相当多的iptables
规则。而且kube-proxy
还需要在控制循环里不断地刷新这些规则来确保它们始终是正确的。
不难想到,当你的宿主机上有大量 Pod 的时候,成百上千条iptables
规则不断地被刷新,会大量占用该宿主机的 CPU 资源,甚至会让宿主机“卡”在这个过程中。所以说,一直以来,基于iptables
的 Service
实现,都是制约 Kubernetes
项目承载更多量级的 Pod 的主要障碍。
ipvs模式
在 IPVS 模式下,kube-proxy
监视Kubernetes服务和端点,调用 netlink 接口创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。
访问服务时,IPVS 将流量定向到后端Pod之一;IPVS代理模式基于类似于iptables
模式的 netfilter
挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。
这意味着,与 iptables 模式下的kube-proxy
相比,IPVS 模式下的 kube-proxy
重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 模式的工作原理,其实跟iptables
模式类似。当我们创建了前面的Service
之后,kube-proxy
首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0
),并为它分配 Service VIP
作为 IP 地址。接下来,kube-proxy
就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略。
拓扑图
IPVS 是一个用于负载均衡的 Linux 内核功能。IPVS 模式下,kube-proxy
使用 IPVS 负载均衡代替了iptable
。这种模式同样有效,IPVS 的设计就是用来为大量服务进行负载均衡的,它有一套优化过的 API,使用优化的查找算法,而不是简单的从列表中查找规则。
这样一来,kube-proxy
在 IPVS 模式下,其连接过程的复杂度为 O(1)。换句话说,多数情况下,他的连接处理效率是和集群规模无关的。
另外作为一个独立的负载均衡器,IPVS 包含了多种不同的负载均衡算法,例如轮询、最短期望延迟、最少连接以及各种哈希方法等。而iptables
就只有一种随机平等的选择算法。IPVS一个潜在缺点是,与正常情况下的数据包相比,由IPVS处理的数据包通过iptables
筛选器hook
的路径不同。如果打算将IPVS与其他使用iptables
的程序一起使用,则需要研究它们是否可以一起正常工作。不过Ipvs代理模式已经推出很久了,很多组件已经适配的很好了,比如Calico
。
结论
IPVS是专门设计用来做内核四层负载均衡的,由于使用了hash表的数据结构,因此相比iptables
来说性能会更好。基于IPVS实现Service
转发,Kubernetes
几乎能够具备无限的水平扩展能力。随着Kubernetes
的部署规模越来越大,应用越来越广泛,IPVS必然会取代iptables
成为Kubernetes Service
的默认实现后端。
总结
IPVS (IP Virtual Server
,IP虚拟服务器)是基于Netfilter
的、作为linux内核的一部分实现传输层负载均衡的技术,通常称为第4层LAN交换。IPVS集成在LVS(Linux Virtual Server
)中,它在主机中运行,并在真实服务器集群前充当负载均衡器。IPVS可以将对TCP/UDP服务的请求转发给后端的真实服务器,并使真实服务器的服务在单个IP地址上显示为虚拟服务。因此IPVS天然支持Kubernetes Service
。
- 随着
kubernetes
使用量的增长,其资源的可扩展性变得越来越重要。特别是对于使用kubernetes
运行大型工作负载的开发人员或者公司来说,service
的可扩展性至关重要。kube-proxy
是为service
构建路由规则的模块,之前依赖iptables
来实现主要service
类型的支持,比如(ClusterIP和NodePort
)。但是iptables
很难支持上万级的service
,因为iptables
纯粹是为防火墙而设计的,并且底层数据结构是内核规则的列表。 - 而相比于
iptables
,IPVS 在内核中的实现其实也是基于 Netfilter 的 NAT 模式,所以在转发这一层上,理论上 IPVS 并没有显著的性能提升。但是,IPVS 并不需要在宿主机上为每个 Pod 设置iptables
规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价。 kubernetes
早在1.6版本就已经有能力支持5000多节点,这样基于iptables的kube-proxy
就成为集群扩容到5000节点的瓶颈。举例来说,如果在一个5000节点的集群,我们创建2000个service,并且每个service有10个pod,那么我们就会在每个节点上有至少20000条iptables
规则,这会导致内核非常繁忙。基于IPVS的集群内负载均衡就可以完美的解决这个问题。IPVS是专门为负载均衡设计的,并且底层使用哈希表这种非常高效的数据结构,几乎可以允许无限扩容。不过需要注意的是,IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的Service
流程正常工作所需要的包过滤、SNAT 等操作,还是要靠iptables
来实现。只不过,这些辅助性的 iptables 规则数量有限,也不会随着 Pod 数量的增加而增加。