代理与负载均衡
代理与负载均衡
在 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,因此它取决于具有工作准备就绪探测。