运行在每个Node节点的kube-proxy会实时的watch
Services
和 Endpoints对象。
当用户在kubernetes集群中创建了含有label的Service之后,同时会在集群中创建出一个同名的Endpoints对象,用于存储该Service下的Pod IP. 它们的关系如下图所示:
2.每个运行在Node节点的kube-proxy感知到Services和Endpoints的变化之后,会在各自的Node节点设置相关的iptables或IPVS规则,用于之后用户通过Service的ClusterIP去访问该Service下的服务。
3.当kube-proxy把需要的规则设置完成之后,用户便可以在集群内的Node或客户端Pod上通过ClusterIP经过iptables或IPVS设置的规则进行路由和转发,最终将客户端请求发送到真实的后端Pod。
对于kube-proxy如何设置Iptables和IPVS策略后续会讲。接下来先介绍下每种不同类型的Service的使用场景。
CoreDNS
服务(旧版本的kubernetes集群使用的是kubeDNS), 来达到集群内部的Pod通过DNS的方式进行集群内部各个服务之间的通讯。
当前kubernetes集群默认使用CoreDNS作为默认的DNS服务,主要原因是CoreDNS是基于Plugin的方式进行扩展的简单,灵活。并且不完全被Kubernetes所捆绑。
Iptables
)是一个用户态程序,通过配置Netfilter规则来构建Linux内核防火墙。Netfilter是Linux内核的网络包管理框架,提供了一整套的hook函数的管理机制,使得诸如数据包过滤,网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能,Netfilter在内核中的位置如下图所示。
本文不会更多的介绍关于Iptales和Netfilter的细节,如果想更多的了解请查看:
Iptables
)和
什么是Netfilter
接下来介绍kube-proxy是如何利用Iptables做负载均衡的。数据包在Iptables中的匹配流程如下图所示:
在Iptables模式下,kube-proxy通过在目标node节点上的Iptables中的NAT表的PREROUTIN和POSTROUTING链中创建一系列的自定义链(这些自定义链主要是”KUBE-SERVICE”链, “KUBE-POSTROUTING”链,每个服务对应的”KUBE-SVC-XXXXXX”链和”KUBE-SEP-XXXX”链),然后通过这些自定义链对流经到该Node的数据包做DNAT和SNAT操作从而实现路由,负载均衡和地址转化,如下图所示:
kube-proxy中,客户端的请求数据包在Iptables规则中具体的匹配过程为:
1.PREROUTING链或者OUTPUT链(集群内的Pod通过clusterIP访问Service时经过OUTPUT链, 而当集群外主机通过NodePort方式访问Service时,通过PREROUTING链,两个链都会跳转到KUBE-SERVICE链)
2.KUBE-SERVICES链(每一个Service所暴露的每一个端口在KUBE-SERVICES链中都会对应一条相应的规则,当Service的数量达到一定规模时,KUBE-SERVICES链中的规则的数据将会非常的大,而Iptables在进行查找匹配时是线性查找,这将耗费很长时间,时间复杂度O(n))。
3.KUBE-SVC-XXXXX链 (在KUBE-SVC-XXXXX链中(后面那串 hash 值由 Service 的虚 IP 生成),会以一定的概率匹配下面的某一条规则执行,通过statistic模块为每个后端设置权重,已实现负载均衡的目的,每个KUBE-SEP-XXXXX链代表Service后面的一个具体的Pod(后面那串 hash 值由后端 Pod 实际 IP 生成),这样便实现了负载均衡的目的)
4.KUBE-SEP-XXXX链 (通过DNAT,将数据包的目的IP修改为服务端的Pod IP)
5.POSTROUTING链
6.KUBE_POSTROUTING链 (对标记的数据包做SNAT)
通过上面的这个设置便实现了基于Iptables实现了负载均衡。但是Iptbles做负载均衡存在一些问题:
规则线性匹配时延:
KUBE-SERVICES链挂了一长串KUBE-SVC-*链,访问每个service,要遍历每条链直到匹配,时间复杂度O(N)
规则更新时延:
非增量式,需要先iptables-save拷贝Iptables状态,然后再更新部分规则,最后再通过 iptables-restore写入到内核。当规则数到达一定程度时,这个过程就会变得非常缓慢。
可扩展性:
当系统存在大量的Iptables规则链时,增加/删除规则会出现kernel lock,这时只能等待。
可用性: 服务扩容/缩容时, Iptables规则的刷新会导致连接断开,服务不可用。
为了解决Iptables当前存在的这些问题,华为开源团队的同学为社区贡献了IPVS模式,接下来介绍下IPVS是如何实现负载均衡的。
IPVS
是
LVS
项目的一部分,是一款运行在Linux kernel当中的4层负载均衡器,性能异常优秀。使用调优后的内核,可以轻松处理每秒10万次以上的转发请求。
IPVS具有以下特点:
传输层Load Balancer, LVS负载均衡器的实现。
与Iptables同样基于Netfilter, 但是使用的是hash表。
支持TCP, UDP, SCTP协议,支持IPV4, IPV6。
支持多种负载均衡策略:
rr: round-robin
lc: least connection
dh: destination hashing
sh: source hashing
sed: shortest expected delay
nq: never queue
支持会话保持
LVS的工作原理如下图所示:
1.当客户端的请求到达负载均衡器的内核空间时,首先会达到PREROUTING链。
2.当内核发现请求的数据包的目的地址是本机时,将数据包送往INPUT链。
3.当数据包达到INPUT链时, 首先会被IPVS检查,如果数据包里面的目的地址及端口没有在IPVS规则里面,则这条数据包将被放行至用户空间。
4.如果数据包里面的目的地址和端口在IPVS规则里面,那么这条数据报文的目的地址会被修改为通过负责均衡算法选好的后后端服务器(DNAT),并发往POSROUTING链。
5.最后经由POSTROUTING链发往后端的服务器。
LVS主要由三种工作模式, 分别是NAT DR, Tunnel模式,而在kube-proxy中,IPVS工作在NAT模式,所以下面主要对NAT模式进行介绍:
还是分析上面的那种图:
客户端将请求发往前端的负载均衡器,请求报文源地址是CIP(客户端IP), 目的地址是VIP(负载均衡器前端地址)
负载均衡器收到报文之后,发现请求的是在规则里面存在的地址,那么它将请求的报文的目的地址改为后端服务器的RIP地址,并将报文根据响应的负责均衡策略发送出去。
报文发送到Real Server后,由于报文的目的地址是自己,所有会响应请求,并将响应的报文返回给LVS。
然后LVS将此报文的源地址修改本机的IP地址并发送给客户端。
介绍完基本的工作原理之后,下面我们看看如何在kube-proxy中使用IPVS模式进行负载均衡。
首先需要在启动kube-proxy的参数中指定如下参数:
1 2 3 4 5 6 7
--proxy-mode=ipvs //将kube-proxy的模式设置为IPVS --ipvs-scheduler=rr //设置ipvs的负载均衡算法,默认是rr --ipvs-min-sync-period=5 s // 刷新IPVS规则的最小时间间隔 --ipvs-sync-period=30 s // 刷新IPVS规则的最大时间间隔
设置完这些参数之后,重启启动kube-proxy服务即可。当创建ClusterIP类型的Service时,IPVS模式的kube-proxy会做下面几件事儿:
创建虚拟网卡,默认是kube-ipvs0。
绑定service IP地址到虚拟网卡kube-ipvs0.
同时IPVS还支持会话保持功能,通过在创建Srevice对象时,指定
service.spec.sessionAffinity
参数为
ClusterIP
默认是
None
和 指定
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
参数为需要的时间,默认是
10800s
。
下面是一个创建Service,指定会话保持的一个具体的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
kind : ServiceapiVersion : v1metadata : name : nginx-service spec : type : ClusterIP selector : app : nginx sessionAffinity : ClientIP sessionAffinityConfig : clientIP : timeoutSeconds : 50 ports : -name : http protocol : TCP port : 80 targetPort : 80
之后通过
ipvsadm -L
即可查看会话保持功能是否设置成功。
这样kube-proxy便可以通过IPVS的模式实现负载均衡。
https://zhuanlan.zhihu.com/p/37230013
https://zhuanlan.zhihu.com/p/39909011
https://www.projectcalico.org/comparing-kube-proxy-modes-iptables-or-ipvs/
https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0
https://wiki.archlinux.org/index.php/Iptables_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)