gRPC是用于进程间通信的最流行的现代RPC框架之一。对于微服务体系结构来说,这是一个很好的选择。毫无疑问,部署微服务应用程序最流行的方法之一是Kubernetes。
Kubernetes部署可以有相同的后端实例来服务许多客户端请求。Kubernetes的ClusterIP服务提供负载平衡的IP地址。但是,这种默认的负载平衡在gRPC中无法立即实现。如果您将gRPC与许多部署在Kubernetes上的后端一起使用,则本文档适用于您。
为什么要进行负载平衡?
大规模部署具有许多相同的后端实例和许多客户端。每个后端服务器都有一定的容量。负载平衡用于将来自客户端的负载分布到可用服务器上。
在您开始详细了解Kubernetes中的gRPC负载平衡之前,让我们试着了解负载平衡的好处。
负载平衡有很多好处,其中一些好处是:
- 故障容忍度:如果您的一个副本出现故障,那么其他服务器可以为该请求提供服务。
- 提高了可伸缩性:您可以跨多个服务器分发用户流量,从而提高了可伸缩性。
- 提高吞吐量:您可以通过在各种后端服务器上分布流量来提高应用程序的吞吐量。
- 无缺点部署:您可以使用滚动部署技术实现无停机部署。
gRPC中的负载平衡选项
gRPC中有两种类型的负载平衡选项—代理和客户端。
代理负载平衡
在代理负载平衡中,客户端向负载平衡器(LB)代理发出RPC。LB将RPC调用分发给一个可用的后端服务器,该后端服务器实现为调用提供服务的实际逻辑。LB跟踪每个后端上的负载,并实现公平分配负载的算法。客户端本身不知道后端服务器。客户端可能不受信任。此体系结构通常用于面向用户的服务,其中来自开放internet的客户端可以连接到服务器
客户端负载平衡
在客户端负载平衡中,客户端知道许多后端服务器,并为每个RPC选择一个。如果客户端希望,它可以根据服务器的负载报告实现负载平衡算法。对于简单的部署,客户端可以在可用服务器之间循环请求。
与gRPC负载平衡相关的挑战
gRPC在HTTP/2上工作。HHTP/2上的TCP连接是长期的。一个连接可以多路传输多个请求。这减少了与连接管理相关的开销。但这也意味着连接级负载平衡不是很有用。Kubernetes中的默认负载平衡基于连接级负载平衡。因此,Kubernetes的默认负载平衡不适用于gRPC。
为了证实这个假设,让我们创建一个Kubernetes应用程序。此应用程序包括——
- 服务器pod:Kubernetes部署有三个gRPC服务器pod。
- 客户端pod:Kubernetes部署,带有一个gRPC客户端pod。
- 服务:集群服务,它选择所有服务器pod。
创建服务器部署
要创建部署,请将以下代码保存在YAML文件中,比如deployment-server.yaml
,然后运行命令kubectl apply -f deployment-server.yaml
。
apiVersion: apps/v1
kind: Deployment
metadata:
name: grpc-server
labels:
app: grpc-server
spec:
replicas: 3
selector:
matchLabels:
app: grpc-server
template:
metadata:
labels:
app: grpc-server
spec:
containers:
- name: grpc-server
image: techdozo/grpc-lb-server:1.0.0
这将创建一个具有三个副本的gRPC服务器。gRPC服务器正在端口8001
上运行。
要验证pod是否已成功创建,请运行命令kubectl get pods
。
NAME READY STATUS RESTARTS AGE
grpc-server-6c9cd849-5pdbr 1/1 Running 0 1m
grpc-server-6c9cd849-86z7m 1/1 Running 0 1m
grpc-server-6c9cd849-mw9sb 1/1 Running 0 1m
您可以运行命令kubectl logs --follow grpc-server-<>
查看日志。
创建服务
要创建服务,请将以下代码保存在YAML文件中,比如service.yaml
,然后运行命令kubectl apply-f service.yaml
。
apiVersion: v1
kind: Service
metadata:
name: grpc-server-service
spec:
type: ClusterIP
selector:
app: grpc-server
ports:
- port: 80
targetPort: 8001
ClusterIP服务提供负载平衡的IP地址。它负载平衡通过标签选择器匹配的pod端点之间的流量。
Name: grpc-server-service
Namespace: default
Selector: app=grpc-server
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.28.234
IPs: 10.96.28.234
Port: <unset> 80/TCP
TargetPort: 8001/TCP
Endpoints: 10.244.0.11:8001,10.244.0.12:8001,10.244.0.13:8001
Session Affinity: None
如上所述,POD的IP地址为–10.244.0.11:8001
,10.244.0.12:8001
,10.244.0.13:8001
。若客户端在端口80上调用服务,那个么它将跨端点(POD的IP地址)进行负载平衡调用。但这对于gRPC是不正确的,您很快就会看到。
创建客户端部署
要创建客户端部署,请将以下代码保存在YAML文件中,比如deployment-client.yaml
,然后运行命令kubectl apply-f deployment-client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: grpc-client
labels:
app: grpc-client
spec:
replicas: 1
selector:
matchLabels:
app: grpc-client
template:
metadata:
labels:
app: grpc-client
spec:
containers:
- name: grpc-client
image: techdozo/grpc-lb-client:1.0.0
env:
- name: SERVER_HOST
value: grpc-server-service:80
gRPC客户端应用程序在启动时使用一个通道在10个并发线程中对服务器进行1000000次调用。SERVER_HOST
环境变量指向服务grpc服务器服务的DNS。在gRPC客户端上,通过将SERVER_HOST
(serverHost)传递为以下内容来创建通道:
ManagedChannelBuilder.forTarget(serverHost)
.defaultLoadBalancingPolicy("round_robin")
.usePlaintext()
.build();
如果查看服务器日志,您会注意到所有客户端调用都只由一个服务器pod提供服务。
使用headless服务的客户端负载平衡
您可以使用Kubernetes headless服务进行客户端循环负载平衡。这种简单的负载平衡与gRPC一起开箱即用。缺点是它没有考虑服务器上的负载。
什么是Headless服务?
幸运的是,Kubernetes允许客户端通过DNS查找来发现pod IP。通常,当您对服务执行DNS查找时,DNS服务器返回单个IP—服务的群集IP。但是,如果您告诉Kubernetes您的服务不需要群集IP(您可以通过在服务规范中将clusterIP字段设置为None来实现),DNS服务器将返回pod IP而不是单个服务IP。DNS服务器不会返回单个DNS a记录,而是返回服务的多个a记录,每个记录都指向当时支持服务的单个pod的IP。因此,客户端可以进行简单的DNS a记录查找,并获取作为服务一部分的所有POD的IP。然后,客户机可以使用该信息连接到其中一个、多个或全部。
将服务规范中的clusterIP字段设置为None会使服务无头,因为Kubernetes不会为其分配群集IP,客户端可以通过该群集IP连接到支持它的POD。
Kubernetes在行动——Marko Lukša
将Headless服务定义为:
apiVersion: v1
kind: Service
metadata:
name: grpc-server-service
spec:
clusterIP: None
selector:
app: grpc-server
ports:
- port: 80
targetPort: 8001
要使服务成为headless服务,唯一需要更改的字段是将.spec.clusterIP
字段设置为None
。
验证DNS
要确认headless服务的DNS,请创建一个pod,其图像tutum/dnsutils
为:
kubectl run dnsutils --image=tutum/dnsutils --command -- sleep infinity
然后运行命令
kubectl exec dnsutils -- nslookup grpc-server-service
此headless服务的返回FQDN为:
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: grpc-server-service.default.svc.cluster.local
Address: 10.244.0.22
Name: grpc-server-service.default.svc.cluster.local
Address: 10.244.0.20
Name: grpc-server-service.default.svc.cluster.local
Address: 10.244.0.21
正如您所见,headless服务解析为通过该服务连接的所有POD的IP地址。将此与非headless服务返回的输出进行对比。
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: grpc-server-service.default.svc.cluster.local
Address: 10.96.158.232
配置客户端
剩下的唯一更改是在客户端应用程序上,指向带有服务器pods端口的headless服务,如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: grpc-client
labels:
app: grpc-client
spec:
replicas: 1
selector:
matchLabels:
app: grpc-client
template:
metadata:
labels:
app: grpc-client
spec:
containers:
- name: grpc-client
image: techdozo/grpc-lb-client:1.0.0
env:
- name: SERVER_HOST
value: grpc-server-service:8001
注意,SERVER_HOST现在指向无头服务grpc服务器服务和服务器端口8001
。
您还可以将SERVER_HOST用作FQDN,如下所示:
name: SERVER_HOST
value: "grpc-server-service.default.svc.cluster.local:8001"
如果通过首先删除客户端部署来再次部署客户端,请执行以下操作:
kubectl delete deployment.apps/grpc-client
然后再次将客户端部署为:
kubectl apply -f deployment-client.yaml
代码示例
本文的工作代码示例列在GitHub上:https://github.com/techdozo/grpc-lb
总结
gRPC中有两种负载平衡选项—代理和客户端。由于gRPC连接的寿命较长,Kubernetes的默认连接级别负载平衡不适用于gRPC。Kubernetes headless服务是一种可以实现负载平衡的机制。Kubernetes headless服务DNS解析为支持pods的IP。
原文地址:https://techdozo.dev/grpc-load-balancing-on-kubernetes-using-headless-service/
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2492.html
暂无评论