Ray Serve:把 LLM 推理当"分布式 Actor"调度——不是 Kubernetes,是 Python 原生

LLM 推理服务系列 · 第 5 篇

KServe 的思路是把推理包装成 Kubernetes 原语------Deployment、Service、HPA、Istio VirtualService。这套方案在"模型即微服务"的场景里非常成熟,但它有一个隐含假设:推理是独立的、无状态的、请求-响应的。

Ray Serve 推翻了这一套------它不做 YAML 配置,不依赖 Kubernetes(虽然可以跑在 K8s 上),不用 Knative。它用 Python 装饰器把一个函数或类变成可水平扩展的推理服务,然后由一个分布式调度器在集群里分配计算资源。


Ray:更底层的分布式计算引擎

Ray Serve 是 Ray 生态的一部分。先理解 Ray:

ini 复制代码
Ray = 分布式 Python 运行时

核心抽象:
  Task  (Ray Task):   无状态函数,在集群中任意节点执行
  Actor (Ray Actor):  有状态对象,在集群中固定节点常驻

Ray 本身不是为"推理服务"设计的------它是为"分布式 Python 计算"设计的。它最著名的用途是训练大模型(RLHF 中的 Ray Train)、超参调优(Ray Tune)、数据处理(Ray Data)。推理服务(Ray Serve)是后来加上的能力。

但正是因为这个出身------Ray Serve 做 LLM 推理时有一些其他框架做不到的灵活性。


Ray Serve 的核心抽象:Deployment

不需要 YAML,不需要 config.pbtxt,不需要 InferenceService CRD------一个 Python 类加一个装饰器就是完整的推理服务:

python 复制代码
from ray import serve
from vllm import LLM, SamplingParams

@serve.deployment(
    name="llama-8b",              # 服务名
    num_replicas=2,               # 2 个副本
    ray_actor_options={
        "num_gpus": 1             # 每个副本用 1 个 GPU
    }
)
class LlamaInference:
    def __init__(self):
        self.llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct")

    async def __call__(self, request):
        prompt = await request.json()
        result = self.llm.generate(
            prompt["text"],
            SamplingParams(temperature=0.7, max_tokens=512)
        )
        return result[0].outputs[0].text

# 部署
serve.run(LlamaInference.bind())

@serve.deployment 背后发生的事情:

  1. 类被序列化,分发到 Ray Cluster 中所有带 GPU 的节点
  2. 每个节点根据 num_replicas 启动对应数量的 Actor 进程
  3. 每个 Actor 调用 __init__ 加载模型(只加载一次)
  4. HTTP 端点自动注册------默认 http://localhost:8000/LlamaInference
  5. 请求自动负载均衡到所有副本

跟 KServe 的差异一目了然:Ray Serve 是代码驱动的,KServe 是 YAML 驱动的。 你不需要理解 Kubernetes,你只要写 Python。


毫秒级调度 vs 容器级调度

这是 Ray Serve 最底层的差异:

arduino 复制代码
Kubernetes 调度粒度:Pod(容器)
  扩缩一个副本 = 启动一个新容器 = 拉镜像 + 容器启动 + 模型加载 = 30s ~ 2min​
  适合:模型作为微服务、流量变化缓慢、可以接受秒级扩容延迟

Ray Serve 调度粒度:Actor(进程)
  扩缩一个副本 = 在当前或相邻节点启动一个进程 = 1-2 秒
  适合:动态推理负载、实时特征计算、需要在<5s 内响应流量突发

因为 Ray Cluster 里所有节点都已经运行了 Ray Runtime------启动一个 Actor 只需要 fork 一个进程------不需要拉镜像、不需要创建容器、不需要网络配置------纯进程级别的调度。


Ray Serve 真正独特的功能:多模型编排在代码里

前面 Triton 用 config.pbtxt 声明 DAG,KServe 用 InferenceGraph 声明。Ray Serve 直接用 Python 函数的调用关系:

python 复制代码
@serve.deployment(name="intent-classifier")
class IntentClassifier:
    def __init__(self):
        self.model = load_bert_model()
    
    async def classify(self, text: str) -> str:
        return self.model(text)  # 返回 "query_order" | "chat" | "support"

@serve.deployment(name="llm-generator")
class LLMGenerator:
    def __init__(self):
        self.llm = LLM(model="llama-3.1-8b")
    
    async def generate(self, intent: str, text: str) -> str:
        if intent == "query_order":
            order = query_database(text)  # 查数据库
            prompt = f"根据订单信息回复:{order}\n用户问题:{text}"
        else:
            prompt = text
        return self.llm.generate(prompt)

# 组装 Pipeline
classifier = IntentClassifier.bind()
generator = LLMGenerator.bind()

@serve.deployment(name="orchestrator")
class Orchestrator:
    def __init__(self, classifier, generator):
        self.classifier = classifier
        self.generator = generator
    
    async def __call__(self, request):
        text = await request.body()
        intent = await self.classifier.classify.remote(text)
        response = await self.generator.generate.remote(intent, text)
        return response

serve.run(Orchestrator.bind(classifier, generator))

这放在 KServe 里需要:一个 Classification 的 InferenceService + 一个 LLM 的 InferenceService + 一个 Istio VirtualService 做路由 + 一个中间服务做编排。Ray Serve 里------全在一个 Python 文件里。


跟 vLLM 原生结合:Ray Serve + vLLM 是官方推荐架构

vLLM 本身支持 Ray 作为分布式通信后端------当 vLLM 以 Tensor Parallel 模式跨多张 GPU 部署时,用 Ray 做 GPU 间通信比 NCCL 更灵活。

bash 复制代码
# vLLM 启动时指定后端为 Ray
vllm serve meta-llama/Llama-3.1-70B-Instruct \
    --tensor-parallel-size 8 \   # 8 张 GPU
    --distributed-executor-backend ray  # Ray 后端

Ray Serve + vLLM 组合的作用:

  • Ray 管理 GPU 间的通信拓扑(vLLM 的张量并行)
  • Ray Serve 管理请求的路由和负载均衡
  • 当集群拓扑变化(GPU 节点增减),Ray 自动重调度 Actor

什么时候选 Ray Serve

场景 Ray Serve 适合?
全 Python 团队,不想写 YAML 首选
LLM + 在线特征计算 + 召回等同一 Pipeline 最佳选择------代码级别的编排
已有 Ray Cluster(做训练/数据处理) 零成本复用,推理做在同集群上
需要 1-2 秒级别的快速扩缩 Actor 级别调度,不是 Pod
多模型动态编排(不属于简单的 request/response) Python 代码灵活性碾压配置驱动
需要 Kubernetes 级别的多租户+配额+RBAC 不适用------KServe 更适合
团队没有 Python 背景(Go/Java 为主) 不适用------Ray 是 Python-first
需要 Scale to Zero 不内置支持------Ray 的设计假设常驻集群

五个框架的选择速查

复制代码
你要做的事                          → 用什么
─────────────────────────────────────────────────
单机极致 LLM 推理吞吐               → vLLM
HuggingFace 模型 + LoRA + 多模态    → TGI
多种模型类型(LLM + CV + 树模型)    → Triton
多团队多模型的 K8s 平台             → KServe
Python 原生、代码驱动编排           → Ray Serve

一句话总结

Ray Serve 不把推理当成"服务"来管理------而当成"分布式 Actor"来调度。它的优势不在 LLM 推理性能(那是 vLLM/TGI 的活),而在编排灵活性------Python 纯代码编排、毫秒级扩缩、跟 Ray 生态(Train/Tune/Data)的原生整合。如果你对 YAML 驱动的方式感到不耐------Ray Serve 是让你回到"写代码部署"节奏的选项。


推理服务系列完结。五篇文章覆盖了 LLM 推理服务的完整版图:

  1. vLLM:PagedAttention + Continuous Batching------单机推理吞吐的天花板
  2. TGI:HuggingFace 生态原生------LoRA 热切换 + Chunked Prefill + 多模态
  3. Triton:通用推理平台------Backend 架构 + Model Ensemble + TensorRT-LLM
  4. KServe:Kubernetes 原生------Serverless 扩缩容 + 金丝雀发布 + ModelMesh
  5. Ray Serve:Python 原生------Actor 调度 + 代码编排 + 毫秒级扩缩

从头到尾的递进:单进程 → 单服务 → 多模型 → 平台化 → 分布式编程模型。选什么框架,取决于你的团队在"自己做基础设施"和"用平台"之间的偏好。但无论选哪个------底层做推理计算的,基本还是 vLLM。

相关推荐
玉鸯1 小时前
界面用完即消失:Agent 生成式 UI 的短暂性哲学与前端工程的未来
前端·llm·agent
组合缺一1 小时前
SolonCode v2026.8.4 发布:界面字体可调、22 种语言、记忆搜索增强
llm·agent·ai编程·solon·claudecode·opencode
ShineWinsu1 小时前
对于Linux:五种IO模型以及非阻塞IO的详细解析
linux·c++·面试·io·阻塞·非阻塞·fcntl
echoVic2 小时前
Orca:一个 DeepSeek 原生的终端 Coding Agent
开源·ai编程·deepseek
黄敬峰2 小时前
前端路由到底是个啥?React Router v7 从零到一,大白话一次讲透
前端·面试
用户469368483202 小时前
kimi-code 深度掌握系列文章-Agent 类编排者(四)
llm·agent
牧艺2 小时前
Knowledge Studio 本地开源版:对标百炼 RAG 控制台的架构、重难点与差异
llm·agent·next.js
程序员天天困2 小时前
我用Doubao-Seed-Evolving做GitHub+Gitee编码档案
ai编程·豆包marscode
晨米酱2 小时前
Matt Pocock Skills v1.2:控制从主流程深入到每一步
面试·架构·agent