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

Posted 容器时代

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]基于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:最少连接数

•   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

下面是一个示例:

# 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



删除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模式会在上述场景中使用iptables,具体来说分为下面四种场景:

•   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。






以上是关于[转]基于IPVS的集群内负载均衡深入解读的主要内容,如果未能解决你的问题,请参考以下文章

Linux负载均衡--LVS(IPVS)

IPVS(LVS)负载均衡简介及实验测试

基于lvs实现4层负载均衡

LVS部署测试

Linux集群之高可用负载均衡lvs+keepalived

【swarm】Docker swarm 的负载均衡