gRPC客户端负载均衡

时间:2016-09-22 16:03:40

标签: python http dns kubernetes grpc

我正在使用gRPC和Python作为kubernetes pods中的客户端/服务器... 我希望能够启动多个相同类型的pod(gRPC服务器)并让客户端(随机)连接它们。

我发送了10个服务器的pod并设置了一个'服务'来定位它们。然后,在客户端,我连接到服务的DNS名称 - 意味着kubernetes应该进行负载平衡并将我引导到随机服务器pod。 实际上,客户端调用gRPC函数(效果很好)但是当我查看日志时,我看到所有调用都转到同一个服务器pod。

我认为客户端正在进行某种DNS缓存,这会导致所有呼叫被发送到同一服务器。是这样的吗?无论如何要禁用它并设置相同的存根客户端进行“新”呼叫并通过DNS每次呼叫获取新的IP?

我知道如果它每次都会查询DNS服务器,我可能会产生的开销,但是分发负载对我来说更重要。

修改

可能不是缓存问题......可能只是gRPC的工作方式。 HTTP / 2和持久可重用连接。每次通话后有什么方法可以“断开连接”吗?

3 个答案:

答案 0 :(得分:14)

让我借此机会通过描述事情的运作方式来回答。

客户端LB在gRPC C核心(除Java和Go版本或gRPC之外的所有基础)的工作方式如下(权威文档可以找到here):

客户端LB保持简单且“愚蠢”#34;故意。我们选择实施复杂LB策略的方式是通过外部LB服务器(如上述文档中所述)。您不关心这种情况。相反,您只是创建一个频道,它将使用(默认)先选择 LB策略。

LB策略的输入是已解析地址的列表。使用DNS时,如果foo.com解析为[10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4],策略将尝试建立与所有这些连接的连接。成功连接的第一个将成为所选的,直到它断开连接。因此名称" pick-first"。一个较长的名字本来可以先选择并尽可能长时间地坚持使用#34;,但这样做的文件名很长:)。如果/当被挑选的一个断开连接时,选择优先策略将移动以返回下一个成功连接的地址(内部称为"连接的子信道"),如果有的话。再一次,只要它保持连接,它就会继续选择这个连接的子通道。如果所有这些都失败了,则呼叫将失败。

这里的问题是,基于内在拉取的DNS解析仅在信道创建时触发1)和2)在所选连接子信道断开时触发。

截至目前,一个hacky解决方案是为每个请求创建一个新的频道(效率非常低,但它可以根据您的设置进行操作)。

鉴于2017年第一季度的变化(见https://github.com/grpc/grpc/issues/7818)将允许客户选择不同的LB政策,即Round Robin。另外,我们可能会考虑引入"随机化"位到那个客户端配置,它会在对它们进行Round-Robin之前对地址进行洗牌,从而有效地实现您的意图。

答案 1 :(得分:3)

通常的K8S负载平衡不适用于gRPC。以下链接说明了原因。 https://kubernetes.io/blog/2018/11/07/grpc-load-balancing-on-kubernetes-without-tears/

  

这是因为gRPC基于HTTP / 2构建,并且HTTP / 2旨在   有一个长期存在的TCP连接,所有请求都通过该连接   多路复用-意味着多个请求可以在同一请求上处于活动状态   在任何时间点连接。通常,这很好,因为它   减少了连接管理的开销。但是,这也意味着   (您可能会想到)连接级别的平衡不是很好   有用。建立连接后,就再也没有平衡了   要做。所有请求都将固定到单个目标窗格。

大多数现代的入口控制器都可以处理此问题,但是它们要么很热(nginx),要么是Alpha版本(traefik),或者需要最新版本的K8S(Linkerd)。您可以执行客户端负载平衡,其中可以找到Java解决方案here

答案 2 :(得分:1)

如果您已经创建了一个vanilla Kubernetes服务,该服务应该有自己的负载均衡虚拟IP(检查kubectl get svc your-service是否为您的服务显示CLUSTER-IP。如果是这种情况,DNS缓存应该不是问题,因为单个虚拟IP应该在实际后端之间分割流量。

尝试kubectl get endpoints your-service确认您的服务确实知道您的所有后端。

如果您有headless service,则DNS查找将返回包含10个IP的A记录(每个Pod一个)。如果您的客户总是选择A记录中的第一个IP,那么这也可以解释您所看到的行为。