grpc-java k8s下的负载均衡

原创
03/16 11:43
阅读数 3.5K

前言

grpc 因为是长连接的,所以负载均衡处理起来没有 rest 接口那么容易。常见的 grpc 负载均衡方法分为两类,一类是客户端侧实现负载逻辑,一类是代理侧实现负载逻辑,对客户端侧是透明的。在容器化的网络环境里, grpc-java 客户端侧的负载均衡有两种常见的实现路径。1、基于 dns 实现,2、基于外部的服务注册中心实现(ZooKeeper/Etcd/Consul/Eureka)。本文旨在,在容器化的网络环境下,通过测验寻找一种改造成本最小的实现负载均衡的途径

现状

在 k8s 的网络环境下,一个 grpc 的服务,同一个 namespace 下,可以直接通过 service 访问,不同的 namespace 可以通过 service.namespace 访问。但是,经验证,这种直连的方式没法做到负载均衡,也就意味着 server 端无论开启了多少个 pod 实例,客户端也只能连接一个pod 。所以,在客户端和服务端数量不对等时,打到 server 侧的流量会非常的不均衡,如果数量对等,情况稍微好些。本次测验只测试了 java 链接 java 的 grpc 服务,生产环境的实际调用场景会更复杂,包含了 php 、go、java 三种 grpc 服务的相互调用

负载均衡的方案

一、客户端 dns 模式

dns 的模式是 grpc-java 实现复杂均衡改造成本最小的。应该也是最通用的,各个语言的 grpc  应该都有支持。主要改动两个地方,

1、修改 Service 的 spec.clusterIP 为 ”None“,如:

apiVersion: v1
kind: Service
metadata:
  namespace: tap-prod
  name: queuing-rpc
  labels:
    app: queuing-rpc
spec:
  clusterIP: None
  ports:
    - port: 8030
      targetPort: 8030
      name: grpc
  selector:
    app: queuing-rpc

改动后,可以通过 service 的名称解析到 pod 的 ip 列表

2、配置的 grpc 链接协议头加上 dns 协议,如:

grpc.client.store.address = dns:///store-rpc:8020

 二、客户端注册中心模式

客户端注册中心模式相比较 dns 模式,实现方式上相对复杂点,但是灵活度更高了,有了注册中心后,服务治理相关的也就都可以做了。但是在多语言的场景下,这种方式的普及难度会更高,无论选择哪个注册中心实现,都必须要求其他语言也要对应实现。这里只简要阐述 grpc-java 的实现途径。grpc-java 客户端提供了 NameResolver 、NameResolverProvider 、NameResolverRegistry 等实现服务注册发现的扩展类。结合注册中心 ZooKeeper/Etcd/Consul/Eureka ,很容易实现一个基于注册中心的带服务治理的 grpc 。

三、代理端走 ingress

nginx-ingress-controller 从 0.30.0 版本开始支持 grpc 的流量代理,经测验,在 nginx-ingress 代理模式下,grpc 的流量是负责均衡的。这种改动方式也比较简单,服务方只需要新增一个 ingress 代理 grpc 流量即可,客户端链接是无感的,不需要做任何改动。因为走了一层代理,性能上会比dns 模式差点

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: tap-prod
  name: store-rpc
  annotations:
    kubernetes.io/ingress.class: nginx-intranet-grpc
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
spec:
  rules:
    - host: store-rpc.xx.com
      http:
        paths:
          - backend:
              serviceName: store-rpc
              servicePort: 8020

四、代理端 service mesh

需要引入 istio 等服务网格架构。这种模式,对于多语言微服务环境是非常友好的,可以屏蔽各种语言基础服务治理的实现细节,应该是最终目标方案。

结语

短期而言,需要解决 grpc 负载均衡问题,最快速、最无感的方案是基于 ingress 的代理负载模式。改动小、性能好的方案应该是客户端基于 dns 的模式。最复杂、最灵活、可控度最高的应该是基于客户端注册中心的实现方式。综合起来看,service mesh 的方式才是最终的目标,不仅解决服务负载问题,流量观测、服务治理也统统解决了

参考:

  1. https://grpc.io/blog/grpc-load-balancing/
  2. https://kubernetes.github.io/ingress-nginx/examples/grpc/
  3. https://medium.com/getamis/istio-%E5%9F%BA%E7%A4%8E-grpc-%E8%B2%A0%E8%BC%89%E5%9D%87%E8%A1%A1-d4be0d49ee07
展开阅读全文
打赏
0
3 收藏
分享
加载中
多组deply+svc不必那么复杂
03/16 14:20
回复
举报
KL博主博主
有可以参考的链接么
03/16 14:21
回复
举报
同一个服务部署多个副本不采用多pod而是采用多个deploy,每个deploy对多个svc就行了
03/16 15:30
回复
举报
KL博主博主
额,这个不是更复杂么,为了负载突破了原有的架构模式了
03/16 15:34
回复
举报
或者采用headless的svc+多副本
03/16 15:38
回复
举报
KL博主博主
采用headless 后,就不需要多副本了,直接在连接url加上 dns 协议就ok了。这不就是我写的:客户端 dns 模式么
03/16 15:47
回复
举报
回复 @KL博主 : 666
03/16 15:53
回复
举报
回复 @KL博主 : 还是交流起来思路打开的快
03/16 16:08
回复
举报
KL博主博主
回复 @_snake_ :欢迎多多交流
03/16 16:14
回复
举报
更多评论
打赏
9 评论
3 收藏
0
分享
返回顶部
顶部