Ray Serve:基于 Ray 的可扩展模型服务框架

FreeGuideOnline 最新 2026-06-29

Ray Serve 部署完全指南:从零搭建可扩展模型服务

目录

  1. Ray Serve 是什么?
  2. 环境准备与安装
  3. 快速入门:第一个 Serve 应用
  4. 核心概念详解
  5. 部署模式:单节点与集群
  6. 高级配置:自动伸缩与请求批处理
  7. 生产化部署最佳实践
  8. 常见问题与排障

1. Ray Serve 是什么?

Ray Serve 是基于分布式计算框架 Ray 构建的模型服务子系统。它能够将任意 Python 函数或类转化为可弹性伸缩的在线推理 API,从根本上解决了模型服务中常见的三个痛点:

  • 高可用性:每个副本独立运行,单个副本失效不会影响整体服务。
  • 弹性扩展:可手动或根据请求负载自动增加/减少副本数量。
  • 异构部署:同一个 Serve 应用内可以同时部署多个不同模型,彼此独立扩缩容。

与传统模型服务框架(如 Flask + Gunicorn)相比,Ray Serve 原生具备分布式调度能力,无需额外集成负载均衡器或服务发现组件;同时它深度嵌入 Ray 生态,能直接复用 Ray 的 Actor 模型与内存存储,非常适合需要低延迟、高吞吐的在线推理场景。


2. 环境准备与安装

2.1 基础依赖

  • Python 3.8 及以上
  • pip 或 conda 包管理器
  • Linux/macOS 系统(Windows 支持受限,建议使用 WSL2)

2.2 安装 Ray 与 Serve

# 安装包含 Serve 的 Ray 完整包
pip install "ray[serve]"

# 验证安装
python -c "import ray; from ray import serve; print(ray.__version__)"

提示:若仅需最小化安装,可使用 pip install ray 后再安装 ray[serve],但推荐直接安装完整版以避免依赖缺失。

2.3 启动 Ray 集群(可选)

单机开发可直接使用 Ray 的隐式启动,无需显式配置:

import ray
ray.init()  # 自动检测本地资源

如需多节点集群,请先在所有节点安装相同版本的 Ray,然后在主节点执行 ray start --head,工作节点执行 ray start --address='主节点IP:6379'


3. 快速入门:第一个 Serve 应用

我们从一个极简文本翻译模型开始,展示完整的部署流程。

3.1 定义服务

# my_serve.py
from ray import serve
from starlette.requests import Request

@serve.deployment(num_replicas=2, ray_actor_options={"num_cpus": 0.1})
class Translator:
    def __init__(self):
        self.translate_dict = {"hello": "你好", "world": "世界"}
    
    async def __call__(self, request: Request):
        payload = await request.json()
        text = payload.get("text", "")
        results = [self.translate_dict.get(w, w) for w in text.split()]
        return " ".join(results)

translator_app = Translator.bind()

3.2 启动 Serve 并部署

import ray
from ray import serve

ray.init()
serve.start(detached=True)
serve.run(translator_app, name="translator", route_prefix="/translate")

3.3 测试服务

import requests
resp = requests.post("http://127.0.0.1:8000/translate", json={"text": "hello world"})
print(resp.text)  # 输出: 你好 世界

至此,一个具备双副本的高可用推理服务已经运行。所有请求会自动分发到两个副本上,且每个副本仅使用 0.1 个 CPU 资源。


4. 核心概念详解

4.1 Deployment(部署单元)

一个 Deployment 是对某个 Python 类或函数的声明式封装。通过 @serve.deployment 装饰器,可以指定副本数、资源需求、自动伸缩策略等。

关键参数:

  • num_replicas:静态副本数。
  • ray_actor_options:每个副本在 Ray 集群中的资源申请(CPU/GPU/内存)。
  • max_concurrent_queries:每个副本可同时处理的最大请求数(默认 100)。

4.2 Application(应用)

一个 Application 由一个或多个 Deployment 通过 .bind() 组合而成。它是 serve.run() 接收的基本部署单元。

@serve.deployment
class Preprocessor:
    pass

@serve.deployment
class Model:
    pass

app = Model.bind(Preprocessor.bind())

这种链式绑定会自动构建处理流水线,前一个 Deployment 的输出成为后一个的输入。

4.3 Ingress(入口)

直接暴露为 HTTP 的 Deployment 称为 Ingress。它可以通过 __call__ 方法接收 Starlette 的 Request 对象,并返回 JSON 兼容的响应。

4.4 Backend(已废弃)

在旧版本中 Serve 有 Backend 与 Endpoint 分离的概念,目前统一为 Deployment,请直接使用 Deployment 设计服务。


5. 部署模式:单节点与集群

5.1 单节点开发模式

使用 ray.init() 启动本地 Ray 运行环境,所有副本运行在单机上。适合开发与测试。

5.2 生产集群部署

  1. 启动 Ray 集群(前文已述)
  2. 使用 serve.start(detached=True) 启动 Serve 控制器,它会将配置持久化到 Ray 的全局状态中。
  3. 提交应用serve.run(app)
  4. 更新应用:重新运行脚本或调用 serve.run(new_app) 即可实现零停机滚动更新。

示例:在集群中提交任务

# 主节点执行
python deploy_script.py

脚本内容:

ray.init(address="auto")
serve.start(detached=True)
# 假设 app 已定义
serve.run(app)

5.3 使用 Serve CLI(实验性)

Ray 2.8+ 提供了命令行工具:

serve deploy config.yaml   # 从 YAML 文件部署
serve status               # 查看服务状态
serve shutdown             # 关闭所有 Serve 服务

6. 高级配置:自动伸缩与请求批处理

6.1 自动伸缩

通过在 @serve.deployment 中设置 autoscaling_config,可以根据实时 QPS 或延迟动态调整副本数。

@serve.deployment(
    autoscaling_config={
        "min_replicas": 1,
        "max_replicas": 8,
        "target_num_ongoing_requests_per_replica": 10,
        "upscale_delay_s": 30,
        "downscale_delay_s": 300,
    }
)
class MyModel:
    ...
  • target_num_ongoing_requests_per_replica:每个副本的理想并发请求数,超出即扩容。
  • upscale_delay_s:扩容冷却时间。
  • downscale_delay_s:缩容冷却时间。

6.2 请求批处理

Serve 支持将多个请求合并为一个批次以提高 GPU 利用率。

from ray.serve.batching import batch

@serve.deployment
class BatchModel:
    @batch(max_batch_size=16, batch_wait_timeout_s=0.2)
    async def handle_batch(self, inputs: list):
        results = self.model.predict(np.array(inputs))
        return results
    
    async def __call__(self, request):
        data = await request.json()
        return await self.handle_batch(data["input"])

当请求在 batch_wait_timeout_s 内聚集足够数量时,它们会被合并调用 handle_batch,大幅提升计算密度。

6.3 多模型组合与路由

Serve 支持根据请求路径或参数动态路由到不同 Deployment。

@serve.deployment
class ModelA: ...
@serve.deployment
class ModelB: ...

@serve.deployment
class Router:
    def __init__(self, model_a, model_b):
        self.model_a = model_a
        self.model_b = model_b
    
    async def __call__(self, request):
        data = await request.json()
        if data["type"] == "A":
            return await self.model_a.remote(data)
        else:
            return await self.model_b.remote(data)

app = Router.bind(ModelA.bind(), ModelB.bind())

7. 生产化部署最佳实践

7.1 资源配置细粒度控制

  • 为每个 Deployment 指定真实的资源需求,避免过度分配。例如 NLP 模型通常需要 GPU,使用 ray_actor_options={"num_gpus": 1} 可确保每个副本独占一张 GPU。
  • 使用 num_cpus 的小数(如 0.1)来允许多个轻量模型共享 CPU 核心。

7.2 健康检查与优雅退出

  • Serve 内置副本健康检查,一旦副本挂掉会自动重启。
  • 如需优雅关闭,可发送 SIGTERM 至 Raylet,Serve 会等待该副本的进行中请求完成再退出。

7.3 监控与日志

  • 通过 Ray Dashboard(默认 http://127.0.0.1:8265)查看 Serve 状态、副本数量、请求延迟等。
  • 日志默认输出到 /tmp/ray/session_latest/logs/,可在部署信息中查看每个副本的日志。
  • 使用 serve.status() API 获取当前所有 Deployment 的状态。
  • 集成 Prometheus 监控:设置 serve.start(proxy_http_options={"metrics_port": 8001}),指标端点暴露在 :8001

7.4 安全与访问控制

  • 生产环境建议在前面放置反向代理(如 Nginx,Caddy)处理 HTTPS 和请求限流。
  • Serve 默认不进行鉴权,需要在路由内实现或使用 API 网关。

7.5 模型加载优化

  • 将模型初始化放在 __init__ 方法中,每个副本只加载一次,避免每次请求重新加载。
  • 对于大型模型,可使用 Ray 的对象引用将自己加载的模型存入 Ray Plasma,多个副本共享只读内存。

7.6 版本管理与滚动更新

  • 重新运行 serve.run 即触发滚动更新,新副本逐步替换旧副本,期间不会中断服务。
  • 可通过 serve.run(app, name="v2") 部署多个版本,再通过路由逐步切换流量。

8. 常见问题与排障

Q:启动 Serve 后访问 8000 端口无响应? A:检查是否调用了 serve.run(),以及是否有 Ingress Deployment 绑定到该路由。可使用 serve.list_deployments() 确认。

Q:副本一直处于 RECOVERING 状态? A:通常是资源不足导致,检查集群是否有可用 CPU/GPU/内存,或降低资源需求。

Q:如何调试 Deployment 中的错误? A:查看对应副本的日志文件,或使用 ray.get(deployment.get_logs.remote()) 获取特定副本日志。

Q:GPU 推理时性能很差? A:确认 ray_actor_options={"num_gpus": 1} 正确设置,且模型在 __init__ 中加载,必要时启用批处理。

Q:是否支持 gRPC 或 WebSocket? A:Serve HTTP 入口基于 Starlette,可以封装 ASGI 应用支持 WebSocket;gRPC 目前需通过自定义 Ray Actor 实现,未原生集成。


通过本教程,你已掌握从安装到生产级部署的完整 Ray Serve 知识。建议在本地尝试组合多个模型并启用自动伸缩,亲身体验其分布式服务编排的灵活性。更多高级特性可查阅 Ray Serve 官方文档