代理与负载均衡
在Kubernetes中,我们可能会遇到许多不同的代理的概念,主要包含以下几类:
-
kubectl代理:在用户的桌面或pod中运行,代理从本地主机地址到Kubernetes apiserver;客户端到代理将使用HTTP,代理到apiserver使用HTTPS,定位apiserver,添加身份验证header。
-
apiserver代理:内置于apiserver中,将集群外部的用户连接到集群IP,否则这些IP可能无法访问。运行在apiserver进程中,客户端代理使用HTTPS(也可配置为http),代理将根据可用的信息决定使用HTTP或者HTTPS代理到目标。可用于访问节点、Pod或服务,在访问服务时进行负载平衡。
-
kube proxy:运行在每个节点上,代理UDP和TCP,不能代理HTTP。提供负载均衡,只能用来访问服务。
-
位于apiserver之前的Proxy/Load-balancer:存在和实现因集群而异(例如nginx),位于所有客户和一个或多个apiserver之间,如果有多个apiserver,则充当负载均衡器。
-
外部服务上的云负载均衡器:由一些云提供商提供(例如AWS ELB,Google Cloud Load Balancer),当Kubernetes服务类型为LoadBalancer时自动创建,只使用UDP/TCP,具体实现因云提供商而异。
Kubernetes在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了Service资源,并通过kube-proxy配合Cloud Provider来适应不同的应用场景。随着kubernetes用户的激增,用户场景的不断丰富,又产生了一些新的负载均衡机制。目前,Kubernetes中的负载均衡大致可以分为以下几种机制,每种机制都有其特定的应用场景:
-
Service:直接用Service提供Cluster内部的负载均衡,并借助Cloud Provider提供的LB提供外部访问。
-
Ingress Controller:还是用Service提供Cluster内部的负载均衡,但是通过自定义LB提供外部访问。
-
Service Load Balancer:把LB直接跑在容器中,实现Bare Metal的Service Load Balancer。
-
Custom Load Balancer:自定义负载均衡,并替代kube-proxy,一般在物理部署Kubernetes时使用,方便接入公司已有的外部服务。
VIP和Service代理
在Kubernetes集群中,每个Node运行一个kube-proxy进程。kube-proxy负责为Service实现了一种VIP(虚拟IP)的形式,而不是ExternalName的形式。在Kubernetes v1.0版本,代理完全在userspace。在Kubernetes v1.1版本,新增了iptables代理,但并不是默认的运行模式。从Kubernetes v1.2起,默认就是iptables代理。
在Kubernetes v1.0版本,Service是“4层”(TCP/UDP over IP)概念。在Kubernetes v1.1版本,新增了Ingress API(beta版),用来表示“7层”(HTTP)服务。
userspace代理模式
这种模式,kube-proxy会监视Kubernetes master对service对象和Endpoints对象的添加和移除。对每个Service,它会在本地Node上打开一个端口(随机选择)。任何连接到“代理端口”的请求,都会被代理到Service的backend Pods中的某一个上面(如Endpoints所报告的一样)。使用哪个backend Pod,是基于Service的SessionAffinity来确定的。最后,它安装iptables规则,捕获到达该Service的clusterIP(是虚拟IP)和Port的请求,并重定向到代理端口,代理端口再代理请求到backend Pod。
网络返回的结果是,任何到达Service的IP:Port的请求,都会被代理到一个合适的backend,不需要客户端知道关于Kubernetes,service或pod的任何信息。默认的策略是,通过round-robin算法来选择backend Pod。实现基于客户端IP的会话亲和性,可以通过设置service.spec.sessionAffinity的值为“C
iptables代理模式
这种模式,kube-proxy会监视Kubernetes master对象和Endpoinnts对象的添加和移除。对每个Service,它会安装iptables规则,从而捕获到达该Service的clusterIP(虚拟IP)和端口的请求,进而将请求重定向到Service的一组backend中某个上面。对于每个Endpoints对象,它也会安装iptables规则,这个规则会选择一个backend Pod。
默认的策略是,随机选择一个backend。实现基于客户端IP的会话亲和性,可以将service.spec.sessionAffinity的值设置为“ClientIP”(默认值为“None”)
和userspace代理类似,网络返回的结果是,任何到达Service的IP:Port的请求,都会被代理到一个合适的backend,不需要客户端知道关于Kubernetes,service或Pod的任何信息。这应该比userspace代理更快,更可靠。然而,不想userspace代理,如果始出选择的Pod没有响应,iptables代理不能自动地重试另一个Pod,所以它需要依赖readiness probes;
ipvs代理模式
这种模式,kube-proxy会监视Kubernetes service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。
与iptables类似,ipvs基于netfilter的hook功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多的选项,例如:
- rr:轮询调度
- lc:最小连接数
- dh:目标哈希
- sh:源哈希
- sed:最短期望延迟
- nq:不排队调度
注意:ipvs模式假定在运行kube-proxy之前在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式。
负载均衡
在Kubernetes集群中微服务的负载均衡是由kube-proxy实现的。kube-proxy是Kubernetes集群内部的负载均衡器。它是一个分布式代理服务器,在Kubernetes的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。与之相比,我们平时在服务器端做个反向代理做负载均衡,还要进一步解决反向代理的负载均衡和高可用问题。
kubernetes中通过service概念来对应用做多POD间的负载均衡,Service是一组Pod的服务抽象,相当于一组Pod的LB,负责将请求分发给对应的Pod;Service会为这个LB提供一个IP,一般称为ClusterIP。Service Cluster IP是Kubernetes系统中的虚拟IP地址,由系统动态分配;Kubernetes集群中的每个节点都会运行kube-proxy,其负责为ExternalName以外的服务实现虚拟IP形式,v1.2版本后默认使用的是iptables。
Kube-proxy是一个简单的网络代理和负载均衡器,它的作用主要是负责Service的实现,具体来说,就是实现了内部从Pod到Service和外部的从NodePort向Service的访问。
kubernetes中的每个node都会运行一个kube-proxy。他为每个service都映射一个本地port,任何连接这个本地port的请求都会转到backend后的随机一个pod,service中的字段SessionAffinity决定了使用backend的哪个pod,最后在本地建立一些iptables规则,这样访问service的cluster ip以及对应的port时,就能将请求映射到后端的pod中。
在这种模式下,kube-proxy监视Kubernetes主服务器添加和删除服务和端点对象。对于每个服务,它安装iptables规则,捕获到服务的clusterIP(虚拟)和端口的流量,并将流量重定向到服务的后端集合之一。对于每个Endpoints对象,它安装选择后端Pod的iptables规则。默认情况下,后端的选择是随机的。可以通过将service.spec.sessionAffinity设置为“ClientIP”(默认为“无”)来选择基于客户端IP的会话关联。与用户空间代理一样,最终结果是绑定到服务的IP:端口的任何流量被代理到适当的后端,而客户端不知道关于Kubernetes或服务或Pod的任何信息。这应该比用户空间代理更快,更可靠。然而,与用户空间代理不同,如果最初选择的Pod不响应,则iptables代理不能自动重试另一个Pod,因此它取决于具有工作准备就绪探测。