架构#

本节将探讨 Serve 的关键架构概念和组件。它将提供对以下方面的见解和概述:

  • Serve 中每个组件的作用及工作方式

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

../_images/architecture-2.0.svg

高层视图#

Serve 运行在 Ray 之上,并利用 Ray actor

构建 Serve 实例时会创建三种类型的 actor:

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

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

  • gRPC Proxy:如果 Serve 使用有效的 portgrpc_servicer_functions 启动,则 gRPC proxy 会与 HTTP proxy 一同启动。此 Actor 运行一个 grpcio 服务器。gRPC 服务器接收传入请求,将它们转发给副本,并在完成后响应。

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

请求的生命周期#

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

  1. 请求被接收并解析。

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

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

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

当通过 DeploymentHandle 而非 HTTP 或 gRPC 进行请求,用于模型组合时,请求会被放入 DeploymentHandle 的队列中,并直接跳到上面的步骤 3。

容错#

应用程序错误,例如模型评估代码中的异常,会被捕获并封装。将返回一个 500 状态码并附带堆栈跟踪信息。副本将能够继续处理请求。

机器错误和故障由 Ray Serve 按如下方式处理:

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

  • 当 proxy Actor 失败时,Controller Actor 会重新启动它。

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

  • 使用 KubeRay RayService 时,KubeRay 会恢复崩溃的节点或崩溃的集群。您可以使用 GCS FT 特性 来避免集群崩溃。

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

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

Ray Serve 自动扩缩容#

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

pic

  • Serve Autoscaler 运行在 Serve Controller actor 中。

  • 每个 DeploymentHandle 和每个副本会定期将其指标推送到 autoscaler。

  • 对于每个部署,autoscaler 会定期检查 DeploymentHandle 队列和副本上正在处理的查询,以决定是否扩缩容副本数量。

  • 每个 DeploymentHandle 持续轮询 controller 以检查新的部署副本。一旦发现新的副本,它就会将任何缓冲或新的查询发送到该副本,直到达到 max_ongoing_requests。查询以轮询方式发送到副本,前提是每个副本一次处理的请求不超过 max_ongoing_requests

注意

当 controller 死亡时,仍然可以通过 HTTP、gRPC 和 DeploymentHandle 发送请求,但自动扩缩容会暂停。当 controller 恢复时,自动扩缩容恢复,但之前收集的所有指标都会丢失。

Ray Serve API 服务器#

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

常见问题#

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

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

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

DeploymentHandle 如何工作?#

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

如何处理大型请求?#

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