架构#

在本节中,我们将探讨 Serve 的关键架构概念和组件。它将深入介绍

  • 每个组件在 Serve 中的作用以及它们如何协同工作

  • 构成 Serve 应用程序的不同类型的 actor

../_images/architecture-2.0.svg

高层概览#

Serve 运行在 Ray 之上,并使用 Ray actors

构成 Serve 实例的 actor 有三种类型:

  • Controller:每个 Serve 实例独有的全局 actor,负责管理控制平面。Controller 负责创建、更新和销毁其他 actor。Serve API 调用,如创建或获取部署,会向 Controller 发起远程调用。

  • HTTP Proxy:默认情况下,head 节点上有一个 HTTP proxy actor。该 actor 运行一个 Uvicorn HTTP 服务器,用于接受传入请求,将其转发给副本,并在请求完成后做出响应。为了提高可扩展性和高可用性,您还可以通过 serve.start() 中的 proxy_location 字段或 serve.start()配置文件 在集群中的每个节点上运行代理。

  • gRPC Proxy:如果 Serve 是使用有效的 portgrpc_servicer_functions 启动的,那么 gRPC proxy 将与 HTTP proxy 一起启动。该 Actor 运行一个 grpcio 服务器。gRPC 服务器接受传入请求,将其转发给副本,并在请求完成后做出响应。

  • Replicas:实际执行代码以响应请求的 actor。例如,它们可能包含一个 ML 模型的实例化。每个副本处理来自代理的单个请求。副本可以使用 @serve.batch 对请求进行批处理。请参阅 批处理 文档。

请求的生命周期#

当 HTTP 或 gRPC 请求被发送到相应的 HTTP 或 gRPC 代理时,会发生以下情况:

  1. 接收并解析请求。

  2. Ray Serve 会查找与 HTTP URL 路径或应用程序名称元数据关联的正确部署。Serve 将请求放入队列。

  3. 对于部署队列中的每个请求,会查找一个可用的副本并将请求发送给它。如果没有可用的副本(即,每个副本的处理中的请求数超过 max_ongoing_requests),请求将保留在队列中,直到副本可用为止。

每个副本维护一个请求队列,并一次执行一个请求,可能使用 asyncio 并发处理它们。如果处理程序(部署函数或部署类的 __call__ 方法)声明为 async def,则副本将不会等待处理程序运行。否则,副本会阻塞直到处理程序返回。

当通过 DeploymentHandle 发送请求而非 HTTP 或 gRPC(用于 模型组合)时,请求将被放入 `DeploymentHandle` 的队列中,然后我们跳到上面的第 3 步。

容错#

应用程序错误,如模型评估代码中的异常,会被捕获并包装。将返回具有 traceback 信息的 500 状态码。副本将能够继续处理请求。

Ray Serve 如下处理机器错误和故障:

  • 当副本 Actor 失败时,Controller Actor 会用新的副本替换它们。

  • 当代理 Actor 失败时,Controller Actor 会重启它。

  • 当 Controller Actor 失败时,Ray 会重启它。

  • 当使用 KubeRay RayService 时,KubeRay 会恢复崩溃的节点或集群。您可以通过使用 GCS FT 功能 来避免集群崩溃。

  • 如果您不使用 KubeRay,当 Ray 集群失败时,Ray Serve 无法恢复。

当托管任何 actor 的机器崩溃时,这些 actor 会在另一台可用机器上自动重启。Controller 中的所有数据(路由策略、部署配置等)都会被 checkpoint 到 head 节点上的 Ray Global Control Store (GCS)。路由器和副本中的瞬态数据(如网络连接和内部请求队列)将在这种类型的故障中丢失。有关 actor 崩溃如何被检测到的更多详细信息,请参阅 端到端容错指南

Ray Serve 自动扩展#

Ray Serve 的自动扩展功能会根据部署的负载自动增加或减少其副本数量。

pic

  • Serve Autoscaler 运行在 Serve Controller actor 中。

  • 每个 DeploymentHandle 和每个副本会定期将它们的指标推送到自动扩展器。

  • 对于每个部署,自动扩展器会定期检查 DeploymentHandle 队列和副本上的正在进行的查询,以决定是否扩展副本数量。

  • 每个 DeploymentHandle 会持续轮询控制器以检查新的部署副本。一旦发现新副本,它会将任何缓冲或新的查询发送到副本,直到达到 max_ongoing_requests。查询以轮询方式发送到副本,但受限于没有副本同时处理的请求数超过 max_ongoing_requests

注意

当控制器死亡时,请求仍然可以通过 HTTP、gRPC 和 DeploymentHandle 发送,但自动扩展会暂停。当控制器恢复时,自动扩展会继续,但所有先前收集的指标都会丢失。

Ray Serve API 服务器#

Ray Serve 提供了一个 CLI 用于管理您的 Ray Serve 实例,以及一个 REST API。您 Ray 集群中的每个节点都提供了一个 Serve REST API 服务器,该服务器可以连接到 Serve 并响应 Serve REST 请求。

常见问题解答#

Serve 如何确保水平可扩展性和可用性?#

您可以通过在 serve.start() 中的 proxy_location 字段或 serve.start()配置文件 中设置,配置 Serve 为每个节点启动一个代理 Actor。每个代理绑定到相同的端口。您应该能够通过任何服务器访问 Serve 并向任何模型发送请求。您可以在 Ray Serve 之上使用自己的负载均衡器。

这种架构确保了 Serve 的水平可扩展性。通过添加更多节点,您可以扩展您的 HTTP 和 gRPC 入口。您还可以通过增加部署的 num_replicas 选项来扩展您的模型推理。

DeploymentHandles 是如何工作的?#

DeploymentHandles 封装了对同一节点上的“路由器”的句柄,该路由器将请求路由到部署的副本。当一个请求通过句柄从一个副本发送到另一个副本时,请求将经过与传入的 HTTP 或 gRPC 请求相同的数据路径。这使得相同的部署选择和批处理过程得以发生。DeploymentHandles 通常用于实现 模型组合

大型请求会怎样处理?#

Serve 利用 Ray 的 共享内存对象存储 和进程内内存存储。小型请求对象通过网络调用直接在 actor 之间发送。较大的请求对象(100KiB 以上)会被写入对象存储,副本可以通过零拷贝读取来读取它们。