基于 EMR Serverless Ray 实现 Qwen 模型批量推理实践

0 阅读5分钟

概述: 本文介绍如何在阿里云 EMR Serverless Ray 集群上,使用 Ray Serve LLM 部署 Qwen3.5 系列模型,并通过 Ray Data 的 ai_query API 实现集群内高吞吐批量推理。其他自建模型可按同样的方式提供模型服务

适用场景:

  • 大规模文本标注、分类、摘要等离线批量推理任务
  • RAG 管道中的批量生成环节
  • 模型评估与数据合成

架构设计

image

核心组件说明

Ray Serve LLM(模型服务层)

Ray Serve LLM 是 Ray 引入的高层 LLM 部署 API。它在内部编排了以下组件:

  1. OpenAI Ingress:HTTP 入口,提供/v1/chat/completions/v1/models等标准端点,内置 SSE streaming 和 token 用量统计。

  2. LLMServer:编排层,管理推理引擎生命周期(加载配置转换、引擎启停),将请求路由到 EngineCore。

  3. vLLM EngineCore:运行在 GPU 节点上的推理引擎 Worker,负责模型加载、KV Cache 管理和 continuous batching。

  4. CompiledDAG(通信机制):在 LLMServer 与 EngineCore 之间建立零拷贝 IPC 通道(共享内存/NCCL),绕过常规 Ray task 调度的序列化和 GCS 查找开销。

请求路径:HTTP → OpenAiIngress → LLMServer → (CompiledDAG) → EngineCore

这种分层设计使得模型服务可以独立扩缩(通过autoscaling_config),而 Ingress 层自动负载均衡。

Ray Data ai_query(批量推理层)

ray.data.ai.ai_query是 Ray Data 提供的声明式批量推理 API:

  • 将数据集按batch_size分片,分发给concurrency个 Actor 并行处理

  • 每个 Actor 内部使用 OpenAI SDK 异步并发请求模型服务

  • 内置 AIMD(加性增/乘性减)流控算法,根据服务端响应延迟动态调节并发度

  • 自动处理请求失败重试和 Actor 故障恢复

两层解耦的好处是:模型服务的副本数和推理任务的并发数可以独立调优,互不影响。

前置条件

项目要求
EMR Serverless Ray 集群err-1.3.0(ray llm镜像)
GPU 节点至少 1 张 A10/A100/V100等
模型Qwen3.5 系列,可放在OSS上挂载到集群可访问路径(如/mnt/Qwen3.5-4B
Python 依赖无,镜像已涵盖

步骤一:部署模型服务

1.1 编写模型配置

创建serve_app.py

from ray.serve.llm import LLMConfig, build_openai_app

llm_config = LLMConfig(
    model_loading_config=dict(
        model_id="Qwen3.5-4B",
        model_source="/mnt/Qwen3.5-4B",  # 集群上的模型路径(需集群启动时挂载)
    ),
    deployment_config=dict(
        ray_actor_options={"num_cpus": 1},
    ),
    engine_kwargs=dict(
        trust_remote_code=True,
        tensor_parallel_size=1,
        gpu_memory_utilization=0.9,
        max_model_len=4096,
    ),
)

app = build_openai_app({"llm_configs": [llm_config]})

重要: ray_actor_options 中不要设置 num_gpus。Ray Serve LLM 内部的 vLLM EngineCore 会根据 tensor_parallel_size 自动申请 GPU 资源。如果同时设置了 num_gpus,两者会相加,导致单卡集群因资源不足而无法调度。

1.2 编写 Serve 配置

创建serve_config.yaml

http_options:
  host: 0.0.0.0
  port: 8000

applications:
  - name: default
    import_path: serve_app:app
    route_prefix: /

1.3 提交部署

from ray.job_submission import JobSubmissionClient

client = JobSubmissionClient(
    "https://<your-gateway>",
    headers={"ray-token": "<your-token>"},
)

job_id = client.submit_job(
    entrypoint="serve run serve_config.yaml",
    runtime_env={"working_dir": "."},  # 打包上传 serve_app.py / serve_config.yaml
)

部署成功后,集群内可通过http://<head-node-ip>:8000/v1访问 OpenAI 兼容 API。

1.4 验证服务

ray job submit -- python -c "
from openai import OpenAI
client = OpenAI(base_url='http://<head-node-ip>:8000/v1', api_key='dummy')
resp = client.chat.completions.create(
    model='Qwen3.5-4B',
    messages=[{'role': 'user', 'content': '你好'}],
    max_tokens=64,
)
print(resp.choices[0].message.content)
"

步骤二:批量推理

2.1 基本用法

import ray
import ray.data.ai  # 注册 .ai accessor

ray.init()

# 构造数据集(实际场景中可从 OSS/HDFS/数据库读取)
ds = ray.data.from_items([
    {"text": "解释什么是分布式计算"},
    {"text": "比较 MapReduce 和 Spark 的优劣"},
    # ... 更多数据
])

result_ds = ds.ai.ai_query(
    "Qwen3.5-4B",                              # 模型 ID(与 LLMConfig 中一致)
    prompt_col="text",                          # 输入列
    base_url="http://<serve-nlb-ip>:8000/v1",   # 模型服务地址
    api_key="dummy",
    output_col="generated_text",                # 输出列
    options={
        "temperature": 0.7,
        "max_tokens": 256,
        "extra_body": {
            "chat_template_kwargs": {"enable_thinking": False}
        },
    },
    concurrency=32,   # 推理 Actor 数量
    batch_size=8,     # 每个 Actor 的异步并发度
)

# 消费结果
for row in result_ds.iter_rows():
    print(row["generated_text"]["content"])

2.2 Qwen3.5 Thinking 模式控制

Qwen3.5 系列支持"思考模式"(thinking),模型会先输出推理链再给出最终答案。在批量推理场景中,关闭 thinking 可以大幅减少 token 消耗及节省 GPU 资源,同时提高吞吐。

传参方式因服务端而异:

服务端写法
自建 vLLM(本文方案)"extra_body": {"chat_template_kwargs": {"enable_thinking": False}}
ray ai_query内置模型"enable_thinking": False直接放 options

2.3 结果写入外部存储

# 写入 Parquet(OSS)
result_ds.write_parquet("oss://<bucket>/inference_results/")

# 或逐行处理
result_ds.map_batches(
    lambda batch: [post_process(row) for row in batch],
    batch_format="pandas",
).write_parquet(...)

性能调优

关键参数

参数作用调优建议
concurrency推理 Actor 数量,每个占 1 CPU设为集群可用 CPU 数的 50%~70%
batch_size每个 Actor 内部的异步并发请求数按需调整
max_tokens最大生成 token 数按业务需要设置,越小吞吐越高
enable_thinking是否开启思考模式批量场景建议关闭
gpu_memory_utilizationvLLM KV Cache 显存占比0.9(单模型部署时)

总结

通过 EMR Serverless Ray + Ray Serve LLM + Ray Data ai_query 的组合,可以在 10 行代码内完成 Qwen 模型的生产级部署,并以声明式 API 实现高吞吐批量推理。上述方案也再次验证ServerlessRay以下价值:

  1. 极简部署:Ray极简API 封装了 vLLM 引擎管理、OpenAI 协议适配和 streaming 支持

  2. 自动流控:Serverless Ray AIMD 算法动态调节并发,无需手动调参即可逼近硬件极限

  3. 架构解耦:模型服务与推理管道独立扩缩,适配从单机到多节点的弹性需求

  4. 生态兼容:标准 OpenAI API 接口,可无缝对接现有 OpenAI SDK 生态