• Kubernetes上的gRPC负载均衡服务


    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。

     

  • 相关阅读:
    基于 Python/Vue/D2 实现的CRM管理系统(客户管理,产品管理,商机管理,合同管理,客户公海,权限管理等业务模块)
    【顺序表和链表】
    SpringBoot中使用Apache Kafka客户端
    ElementuiPlus的table组件实现行拖动与列拖动
    10 索引优化与查询优化
    IT学习笔记--Flink
    JavaScript小技能:Array
    记一次 Golang 的奇怪错误
    Debezium系列之:2.0.0.Beta1的重大变化和新特性
    jstack分析cpu占用100%
  • 原文地址:https://blog.csdn.net/JavaShark/article/details/125523488