架构#
本节将探讨 Serve 的关键架构概念和组件。它将提供对以下方面的见解和概述:
Serve 中每个组件的作用及工作方式
构成 Serve 应用的不同类型的 actor
高层视图#
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 使用有效的
port
和grpc_servicer_functions
启动,则 gRPC proxy 会与 HTTP proxy 一同启动。此 Actor 运行一个 grpcio 服务器。gRPC 服务器接收传入请求,将它们转发给副本,并在完成后响应。副本 (Replicas):实际执行代码以响应请求的 actor。例如,它们可能包含 ML 模型的一个实例。每个副本处理来自 proxy 的单个请求。副本可以使用
@serve.batch
对请求进行批处理。请参阅批处理文档。
请求的生命周期#
当 HTTP 或 gRPC 请求发送到相应的 HTTP 或 gRPC proxy 时,会发生以下情况:
请求被接收并解析。
Ray Serve 根据 HTTP URL 路径或应用程序名称元数据查找正确的部署。Serve 将请求放入队列。
对于部署队列中的每个请求,会查找可用的副本并将请求发送到该副本。如果没有可用副本(即每个副本上未完成的请求数超过
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 的自动扩缩容功能根据部署的负载自动增加或减少其副本数量。
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+)被写入对象存储,副本可以通过零拷贝读取它们。