在 Kubernetes 上通过 Linkerd 及 Istio 进行 gRPC 连接的负载均衡
国外案例
作者:Jakub Scholz
译者:何小龙
2017-07-08 11:53

现在,以 Kubernetes 为代表的各类新技术成果的快速崛起使得我们能够轻松建立应用程序的微服务架构。本文讨论了 Kubernetes 上搭建微服务过程中 gRPC 负载均衡的技术选型过程。

概述

现代的应用程序通常由许多小型的微服务构成,微服务之间使用 API 进行交互。为了充分利用这样的架构,不同的服务之间需要能够单独扩展。正在加载的服务应该在多个实例中运行。然而,其他的服务可能并不需要扩展。

在这种情况下,您需要一些机制来平衡 N 个服务实例 A 到 M 个服务实例 B 之间的流量。没有负载均衡,可能会发生目标服务的某些实例不会收到任何请求的情况。如果不能给新的实例分配任务,那么服务的扩展性有什么意义?负载均衡如何取决于通信协议?

Kubernetes Service 与 Ingress

Kubernetes Service 似乎是自然的服务入口, Service 将提供虚拟 IP 地址和 DNS 名称。它们使用 Iptables 将您的连接转发到与 Service 链接不同的 Pod 上面。

Kubernetes Service 的问题是它们只能作为 L4(即四层负载均衡)工作 – 只能在 TCP 连接级别上进行负载均衡。这对于使用 HTTP 1.1/REST 的 API 来说可能并不重要。基于 HTTP 1.1 协议的应用程序经常使用许多长时间不使用的连接, 对于很多的连接,较低级别的负载均衡可能会更好:

但是,每个应用程序可能都有所不同。这取决于很多因素。例如:

  •  HTTP 客户端在重用连接方面有多好?
  •  可以并行处理多少个请求?
  •  单个请求在后端会产生多少的负载?

所以即使使用 REST API,重要的仍是检查 Kubernetes Service 是否适合您的服务。
Kubernetes 也支持 Ingress。Ingress 可以用作理解 HTTP 的 L7(即七层负载均衡)。它可以把单个连接解构为单独的请求,并独立地路由请求以分发负载。它还可以提供更智能的请求路由 — 例如基于请求 URI。这对于 REST API 来说是非常有用的。

gRPC 协议

gRPC 是基于 HTTP2 和 Google Protocol Buffer 的 RPC 协议。gRPC 使用长生命周期的 HTTP2 连接,和 HTTP1.1/ REST API 相比,它能够提供更好的性能。然而,使用长时间的连接会使负载均衡更加复杂。

当使用一个客户端实例和多个后端实例进行部署时,所有的调用仅路由到单个后端实例。当部署第二个客户端时,它可能被路由到另一个后端实例。这不是所需的那种负载均衡,因为它不允许独立地扩展客户端和服务器。当客户端实例比服务器实例少时,一些服务器实例将处于空闲状态,所以 Kubernetes Service 不太适合 gRPC 负载均衡。

那么 Ingress 又如何呢?如果你的 Ingress 支持 HTTP2,那可能会运行得很好。但是通常情况下 Ingress 只支持 HTTP 1.1,这对 gRPC 负载均衡没有任何帮助。
那么,如何在 Kubernetes 集群中负载均衡 gRPC 呢?

Linkerd

一种可行的解决方案是采用 Linkerd。Linkerd 是一种云原生应用的服务网格。它可以用来路由和均衡 HTTP 流量,同时它也支持 HTTP2 和 gRPC(也有一些名为 Linkerd-tcp 的 TCP 连接,但我从没尝试过)。
Linkerd 有两种基本模式可以在 Kubernetes 中操作,您可以将其部署为 Sidecar 放入您的 Pod 中(在 Pod 中运行的其他容器)或者可以将其部署为 DaemonSet(每个节点上都有一个 Linkerd 实例/Pod)。DaemonSet 部署似乎是首选。原因是有了很多 Pod,您将需要运行许多 Linkerd Sidecar,这就会花费大量资源。而采用 DaemonSet 部署,您每个节点上只运行单个实例。但是,DaemonSet 也不完美。它需要使用主机网络在不同节点之间进行通信,并且当您使用诸名 Weave 或 Calico 这样的 CNI 网络时,似乎会引起并发症。尽管如此,我还是决定用 DaemonSet 进行测试。

Linkerd 工作的方式是在每个节点上运行的守护程序互相连接以创建网格。当客户端想要访问您的后端时,它将连接到在其节点上运行的 Linkerd 守护程序。此守护程序将请求路由到目标节点上运行的 Linkerd 的守护程序,最终此 Linkerd 守护程序将其转发到后端的服务。

在您的应用程序中,这似乎很容易使用。当与 HTTP 一起使用时,您只需定义一个 HTTP 代理来自动将 HTTP 流量路由到 Linkerd 守护进程,该守护进程根据您调用的 HTTP URI 决定在哪里路由它。IT 仍然使用 Kubernetes 服务来了解流量应该在哪里路由,但不会通过服务路由流量,而是通过 Linkerd 网格路由,并将服务仅用作*配置*。


使用 gRPC 似乎有点复杂。至少在用于我的演示服务的 Vert.x 工具包中,似乎没有办法定义代理服务器(说实话,我不知道 HTTP2 中的 HTTP 代理服务器支持状况)。相反,通过告知我的客户端直接连接到 Linkerd 守护进程,gRPC 流量被路由到 Linkerd。必须将 Linkerd 网格配置为了解哪些 Kubernetes Service gRPC 调用应当路由 – 这是使用路由表完成的,该路由表是 Linkerd 配置的一部分,还有一些在 HTTP2 Header 上执行的其他信息。它并不像普通的 HTTP 那么简单,但是我仍然不需要对我的客户端或服务器做任何的更改。只需要配置就够了。Linkerd 能够处理后端实例的负载均衡。它还应该支持更多的高级算法。因此即使基本的轮询调度负载均衡不足也是合适的。我的客户端和服务器的 YAML 定义可以在 GitHub 上找到。

对我来说最大的障碍就是文件编制。例如对于调用的路由就是采用 dtab 配置的。但是我还没有找到任何优秀的 dtab 指南。我自己也是通过反复试验配置它的。至少有些不错的例子,为我提供了一个起点。

更新:dtab 文件编制指南(文件链接:https://linkerd.io/in-depth/dtabs/ ;https://twitter.github.io/finagle/guide/Names.html)

Istio

另一个可用项目就是 Istio。这个项目还很新,几周前发布,发展也还处于相对早期的发展阶段。Istio 似乎提供了一些有趣的功能,应该有一个光明的未来。Istio 将自己描述为一个能够链接、管理并且保障微服务的开放平台。它似乎为服务之间的通信基础设施提供了类似的功能,但在其上添加了一些安全和管理功能。当然,Istio 也可以解决 HTTP 1.1/REST 的问题,它不局限于 HTTP2 或 gRPC。


Istio 根据 Sidecar 原则构建,Sidecar 我们已经在说到 Linkerd 时提到了。它能够部署一些独立的管理、Ingress、Egress、安全等的服务。此外,它还在你所有的 Pod 中注入了一个初始化容器和 Sidecar 容器。Pod 上所有对外的流量都会被路由到 Envoy 代理服务器(服务器的作用就好像是 Sidecar 容器)Envoy 代理服务器会将流量路由到更远的部分。因此,Pod 上的实际应用只涉及代理服务器沟通,代理服务器和另一个目标 Pod 交谈,该代理将本地的流量转发到后端应用程序。有趣的是,Istio 选择了 Sidecar 方法,这种方法同样也有 Linkerd 支持,但似乎不是首选的。

在哪里发送流量,哪个服务被允许与哪个服务交谈……这是通过中央 Istio 服务控制的。流量不通过它们,它们只做个人代理的编排。Kubernetes 服务被用作配置资源。 Istio 使用它们来了解哪个 Pod 应该处理哪些调用。除了通信网络之外,Istio 可以帮助发布用于加密代理之间的流量和访问控制的 TLS 证书。它也可以用于分割几种 Service 之间的流量,用于 AB 测试和许多类似的路由技巧。


我不喜欢 Istio 的地方是:你必须使用他们的 istioctl 命令行工具来将 Sidecar 和初始化容器注入你的部署。你必须使用这种命令:

kubectl apply -f <(istioctl kube-inject -f client.yaml)

当您想使用 Istio 与其他工具共同进行部署时,可能会更复杂。如何将 Istio 与 Helm 等整合?希望这个问题在将来的版本中能够得到解决,注入应该在 Kubernetes 集群中自动完成。


好的地方是除了将 Init 和 Sidecars 注入到我的部署中,我还没有需要任何其他更改。甚至不需要配置。  

Istio 的文件编制似乎已经很好。作为一个全新的工具,这非常让人惊喜。但问题是并不是所有的工作都和文件编制中一样。当我在 Minikube 上尝试时,似乎每隔一秒尝试一次。在使用 AWS Kubernetes Quickstart(基于 kubeadm)设置的 Amazon AWS 上运行的集群中,它完全没有工作。我必须修改 Istio 需要的角色(有关我需要更改的更多细节,请参阅这个链接:https://github.com/istio/istio/pull/333/files)。鉴于 Istio 还是很新的,我希望这样的问题在下一个版本中不会出现。

结论

那么哪一个方法是最好的?这个选择很艰难。两者都有一些优点和缺点。Linkerd 似乎现在更成熟一点。然而,Istio 似乎是一个观察者。看看这两个项目是否会在区分特征方面收敛或分岐(例如更多关注 Istio 中的安全性,Linkerd 中的 DaemonSets),这非常有趣,因为它们似乎有很多共同之处。 使用 Istio 和 Linkerd 进行一些性能测试也很有意思。到目前为止,我只是像玩一样试用它们,检查有趣的功能等等。也许有一些有趣的表现差异。也许会是我下一篇文章的主题……

源代码

我在调查期间使用的所有源代码和示例(包括 Linkerd 和 Istio)都可以在我的 GitHub 上找到:


·https://github.com/scholzj/vertx-grpc-echo(gRPC)

·https://github.com/scholzj/vertx-http-echo(HTTP / REST)

单纯用 Kubernetes,Linkerd 和 Istio 做的部署文件可以在 Kubernetes 目录中找到。 在 Docker Hub 上可以找到 Docker 镜像。

1451 comCount 0