基于IPVS的集群内负载均衡深入解读

在Kubernetes官方博客之前的文章《Kubernetes1.11: In-Cluster Load Balancingand CoreDNS Plugin Graduate to General Availability中我们宣布了基于IPVS的集群内负载均衡已经实现了GA(General Availability),在这篇文章中我们将详细介绍该特性的实现细节。

什么是IPVS

IPVS (IP Virtual Server)是基于Netfilter的、作为linux内核的一部分实现传输层负载均衡的技术。

IPVS集成在LVS(Linux Virtual Server)中,它在主机中运行,并在真实服务器集群前充当负载均衡器。IPVS可以将对TCP/UDP服务的请求转发给后端的真实服务器,因此IPVS天然支持Kubernetes Service。

为什么选择IPVS

随着kubernetes使用量的增长,其资源的可扩展性变得越来越重要。特别是对于使用kubernetes运行大型工作负载的开发人员或者公司来说,service的可扩展性至关重要。

kube-proxy是为service构建路由规则的模块,之前依赖iptables来实现主要service类型的支持,比如(ClusterIP和NodePort)。但是iptables很难支持上万级的service,因为iptables纯粹是为防火墙而设计的,并且底层数据结构是内核规则的列表。

kubernetes早在1.6版本就已经有能力支持5000多节点,这样基于iptables的kube-proxy就成为集群扩容到5000节点的瓶颈。举例来说,如果在一个5000节点的集群,我们创建2000个service,并且每个service有10个pod,那么我们就会在每个节点上有至少20000条iptables规则,这会导致内核非常繁忙。

基于IPVS的集群内负载均衡就可以完美的解决这个问题。IPVS是专门为负载均衡设计的,并且底层使用哈希表这种非常高效的数据结构,几乎可以允许无限扩容。

基于IPVS的kube-proxy

IPVS (IP Virtual Server)是基于Netfilter的、作为linux内核的一部分实现传输层负载均衡的技术。

IPVS集成在LVS(Linux Virtual Server)中,它在主机中运行,并在真实服务器集群前充当负载均衡器。IPVS可以将对TCP/UDP服务的请求转发给后端的真实服务器,因此IPVS天然支持Kubernetes Service。

1. 启动参数变更

启动参数:--proxy-mode。除了userspace模式和iptables模式外,现在用户可以通过--proxy-mode=ipvs使用IPVS模式。它默认使用IPVS的NAT模式,用于实现service端口映射。

启动参数:--ipvs-scheduler。引入新的kube-proxy参数以支持IPVS的负载均衡算法,用户可以通过--IPVS-scheduler配置,默认使用轮询模式(rr)。下面是另外支持的几种负载均衡算法:

• rr:轮询

• lc:最少连接数

• dh:目的地址哈希

• sh:源地址哈希

• sed:最短期望延时

• nq:从不排队

未来还可以实现通过service定义调度策略(可能是基于annotation),覆盖kube-proxy默认的调度策略。

启动参数:-cleanup-ipvs。与iptables的--cleanup-iptables参数类似,如果设置为true,清理IPVS相关配置,以及IPVS模式创建的iptables规则。

启动参数:--ipvs-sync-period。刷新IPVS规则的最大时间间隔,必须大于0。

启动参数:--ipvs-min-sync-period。刷新IPVS规则的最小时间间隔,必须大于0。

启动参数:--ipvs-exclude-cidrs。指定一个用逗号分隔的CIDR列表,这个列表规定了IPVS刷新时不能清理的规则。因为基于IPVS的kube-proxy不能区分自身创建的规则和系统中用户自带的规则,因此如果您要使用基于IPVS的kube-proxy并且系统中原来存在一些IPVS规则,那么应该加上这个启动参数,否则系统中原来的规则会被清理掉。

2. 设计细节

a) IPVS service网络拓扑

当我们创建ClusterIP类型的service时,IPVS模式的kube-proxy会做下面几件事:

• 确认节点中的虚拟网卡,默认是kube-ipvs0

• 绑定service IP地址到虚拟网卡

• 为每个service IP地址创建IPVS虚拟服务器

下面是一个示例:

# kubectl describe svc nginx-service Name: nginx-service ... Type: ClusterIP IP: 10.102.128.4 Port: http 3080/TCP Endpoints:10.244.0.235:8080,10.244.1.237:8080 Session Affinity: None # ip addr ... 73:kube-ipvs0:<BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff inet 10.102.128.4/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever # ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.102.128.4:3080 rr -> 10.244.0.235:8080 Masq 1 0 0 -> 10.244.1.237:8080 Masq 1 0 0

需要注意的是,service与IPVS虚拟服务器应该是1:N的对应关系,因为service可能有多个IP地址(比如ExternalIP类型的service,就有clusterIP和ExternalIP两个地址)。而endpoint与IPVS真实服务器的对应关系应该是1:1。

删除kubernetes的service将会触发删除相应的IPVS虚拟服务器、IPVS真实服务器并且解绑虚拟网卡上的IP。

b)端口映射

IPVS有三种代理模式:NAT(masq),IPIP 和DR,其中只有NAT模式支持端口映射。kube-proxy使用NAT模式进行端口映射,以下示例是IPVS映射service端口3080到pod端口8080:

TCP 10.102.128.4:3080 rr

-> 10.244.0.235:8080 Masq 1 0 0

-> 10.244.1.237:8080 Masq 1 0

c) 会话保持

IPVS支持会话保持,当kubernetes的service指定会话保持时,IPVS会设置超时时间,默认180分钟,下面是会话保持示例:

# kubectl describe svc nginx-service

Name: nginx-service

...

IP: 10.102.128.4

Port: http 3080/TCP

Session Affinity: ClientIP

# ipvsadm -ln

IP Virtual Server version 1.2.1 (size=4096)

Prot LocalAddress:Port Scheduler Flags

-> RemoteAddress:Port Forward Weight ActiveConn InActConn

TCP 10.102.128.4:3080 rr persistent 10800

d) IPVS模式中的iptables和ipset

IPVS是专门为负载均衡设计的,因此IPVS自身不能实现kube-proxy的其他功能,比如包过滤、hairpin、源地址转换等。

IPVS模式会在上述场景中使用iptables,具体来说分为下面四种场景:

• kube-proxy启动参数中带有--masquerade-all=true,即所有流量作源地址转换。

• kube-proxy启动参数中指定CIDR。

• 支持LoadBalancer类型的service。

• 支持NodePort类型的service。

但是IPVS模式不会像iptables模式,创建太多iptables规则。所以我们引入了ipset来减少iptables规则。以下是IPVS模式维护的ipset表:

 

ipset名称set成员功能
KUBE-CLUSTER-IP 所有Service IP + port 如果启动参数中加了masquerade-all=true或clusterCIDR,用来做masquerade
KUBE-LOOP-BACK 所有Service IP + port + IP 针对hairpin问题做masquerade
KUBE-EXTERNAL-IP Service External IP + port 对方问外部IP的流量做masquerade
KUBE-LOAD-BALANCER lb型service的ingress IP + port 对访问lb类型service的流量做masquerade
KUBE-LOAD-BALANCER-LOCAL 规定了externalTrafficPolicy=local的lb型的service的ingress IP + port 接收规定了externalTrafficPolicy=local的lb型service
KUBE-LOAD-BALANCER-FW 规定了loadBalancerSourceRanges的lb型的service的ingress IP + port 针对规定了loadBalancerSourceRanges的lb型service,用于过滤流量
KUBE-LOAD-BALANCER-SOURCE-CIDR lb型的service的ingress IP + port + source CIDR 针对规定了loadBalancerSourceRanges的lb型service,用于过滤流量
KUBE-NODE-PORT-TCP NodePort型Service TCP port 对访问NodePort(TCP)的流量作masquerade
KUBE-NODE-PORT-LOCAL-TCP 规定了externalTrafficPolicy=local的NodePort型Service TCP port 接收规定了externalTrafficPolicy=local的NodePort型service
KUBE-NODE-PORT-UDP NodePort型Service UDP port 对访问NodePort(UDP)的流量作masquerade
KUBE-NODE-PORT-LOCAL-UDP 规定了externalTrafficPolicy=local的NodePort型Service UDP port 接收规定了externalTrafficPolicy=local的NodePort型service

 

通常来说,对于IPVS模式的kube-proxy,无论有多少pod/service,iptables的规则数都是固定的。

e)使用基于IPVS的kube-proxy

目前,local-up-cluster脚本,GCE部署集群脚本,kubeadm都已经支持通过环境变量KUBE_PROXY_MODE=ipvs自动部署IPVS模式的集群。

另外可以通过在kube-proxy的启动参数中添加--proxy=mode=ipvs启动IPVS模式的kube-proxy,不过需要保证事先加载了IPVS依赖的内核模块。

ip_vs

ip_vs_rr

ip_vs_wrr

ip_vs_sh

nf_conntrack_ipv4

最后,在kubernetes之前的版本中,需要通过设置特性开关SupportIPVSProxyMode来使用IPVS。在kubernetes v1.10版本中,特性开关SupportIPVSProxyMode默认开启,在1.11版本中该特性开关已经被移除。但是如果您使用kubernetes 1.10之前的版本,需要通过--feature-gates=SupportIPVSProxyMode=true开启SupportIPVSProxyMode才能正常使用IPVS

posted @ 2021-09-01 17:09  技术颜良  阅读(1845)  评论(0编辑  收藏  举报