KubeRay + vLLM:大模型推理的 K8s 分布式方案

KubeRay 架构全景解析:RayCluster/RayJob/RayService 三层 CRD、vLLM + Ray Serve 多节点 TP 推理、PD 分离编排,以及 9 个 K8s LLM 推理项目的横向对比与选型建议。

一、为什么需要 KubeRay

1.1 原生 K8s Deployment 的局限

你已经知道 vLLM 可以用标准 Deployment 跑在 K8s 上。但多节点 TP(Tensor Parallelism)时,原生 Deployment 有一个致命的短板:

原生 Deployment 启动 4 个 Pod 做 TP=4:
  Pod-0: vllm serve --tensor-parallel-size 4  # 我是 rank 0,rank 1/2/3 在哪?
  Pod-1: vllm serve --tensor-parallel-size 4  # 我是 rank 1,rank 0 在哪?
  Pod-2: vllm serve --tensor-parallel-size 4  # 我是 rank 2,谁告诉我地址?
  Pod-3: vllm serve --tensor-parallel-size 4  # 我是 rank 3,同上

问题:4 个 Pod 互不知道对方的 IP。
      需要手动注入 rank 地址表,或依赖 NCCL 环境变量。
      K8s StatefulSet 可以给固定 hostname,但 NCCL 初始化仍然脆弱。

1.2 Ray 的解法

Ray 从第一天就是为分布式计算设计的。它的核心能力正好解决这个问题:

问题原生 K8sRay
多节点 rank 发现手动或 hackRay 内置(GCS 全局调度)
GPU 分配感知只知道数量,不知道拓扑Ray 了解 GPU 拓扑(NVLink/PCIe)
进程生命周期管理Pod = 进程Ray Actor = 进程(更灵活)
失败重试Pod 重启Actor 重启(可保留 GPU 分配)
多服务编排手写多个 DeploymentRay Serve 原生支持

1.3 KubeRay 在栈中的位置

┌─────────────────────────────────────┐
│           你的推理请求               │
├─────────────────────────────────────┤
│  vLLM (推理引擎)                     │
├─────────────────────────────────────┤
│  Ray Serve (服务层:HTTP 路由/扩缩)   │
├─────────────────────────────────────┤
│  Ray Core (分布式调度:rank 协调)     │
├─────────────────────────────────────┤
│  KubeRay (K8s Operator: 管理 Pod)    │
├─────────────────────────────────────┤
│  Kubernetes (容器编排)               │
└─────────────────────────────────────┘

KubeRay 不是替代 vLLM,而是给 vLLM 提供 K8s 原生的分布式运行环境。


二、KubeRay 三层 CRD

KubeRay 在 K8s 中注册了三种自定义资源:

RayCluster  → 定义一个 Ray 集群(Head + Workers)
RayJob      → 一次性任务(跑完就停)
RayService  → 长期运行的服务(Ray Serve + 滚动更新)

2.1 RayCluster — 基础

apiVersion: ray.io/v1
kind: RayCluster
metadata:
  name: raycluster-vllm
spec:
  rayVersion: '2.40.0'
  headGroupSpec:
    rayStartParams:
      dashboard-host: '0.0.0.0'
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.40.0
          resources:
            limits:
              cpu: "4"
              memory: "16Gi"
  workerGroupSpecs:
  - groupName: gpu-workers
    replicas: 4
    minReplicas: 4
    maxReplicas: 8
    rayStartParams: {}
    template:
      spec:
        containers:
        - name: ray-worker
          image: rayproject/ray:2.40.0
          resources:
            limits:
              nvidia.com/gpu: "1"
              memory: "80Gi"

关键概念

  • headGroupSpec:Head 节点,运行 GCS(全局调度器)和 Dashboard
  • workerGroupSpecs:Worker 节点,实际执行推理的 GPU 节点
  • minReplicas/maxReplicas:支持自动扩缩(根据 GPU 利用率)

2.2 RayJob — 一次性任务

apiVersion: ray.io/v1
kind: RayJob
metadata:
  name: vllm-benchmark
spec:
  entrypoint: python /scripts/benchmark.py
  rayClusterSpec:
    # ... 同 RayCluster 定义
  submissionMode: HTTPMode

RayJob 创建一个临时的 RayCluster,执行完 entrypoint 后自动销毁。

2.3 RayService — 长期推理服务(你的场景)

apiVersion: ray.io/v1
kind: RayService
metadata:
  name: vllm-serve
spec:
  serveConfigV2: |
    applications:
    - name: vllm
      import_path: vllm_serve:app
      route_prefix: /
  rayClusterConfig:
    rayVersion: '2.40.0'
    headGroupSpec:
      # ... Head 配置
    workerGroupSpecs:
    - groupName: gpu-prefill
      replicas: 4
      template:
        spec:
          containers:
          - name: ray-worker
            image: rayproject/ray:2.40.0
            resources:
              limits:
                nvidia.com/gpu: "8"
    - groupName: gpu-decode
      replicas: 2
      template:
        spec:
          containers:
          - name: ray-worker
            image: rayproject/ray:2.40.0
            resources:
              limits:
                nvidia.com/gpu: "8"

RayService 的核心价值

  • 自动滚动更新:修改 serveConfigV2 → RayService 自动做零停机更新
  • 健康检查:Serve 应用挂了自动重启
  • 多版本部署:可以做 A/B 测试(两个版本同时在线)

三、vLLM + Ray Serve 部署实战

3.1 最小示例

# vllm_serve.py
from ray import serve
from vllm import LLM, SamplingParams

@serve.deployment(
    ray_actor_options={"num_gpus": 1},
    autoscaling_config={
        "min_replicas": 1,
        "max_replicas": 4,
        "target_num_ongoing_requests_per_replica": 10,
    }
)
class VLLMDeployment:
    def __init__(self):
        self.llm = LLM(
            model="Qwen/Qwen3-72B",
            tensor_parallel_size=4,   # 每个 replica 占 4 张 GPU
            max_model_len=32768,
        )

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

app = VLLMDeployment.bind()
# 部署到 KubeRay
serve run vllm_serve:app

3.2 多节点 TP(4 卡跨 2 节点)

@serve.deployment(
    ray_actor_options={"num_gpus": 4},    # 每个 replica 4 张 GPU
)
class VLLMTPDeployment:
    def __init__(self):
        self.llm = LLM(
            model="Qwen/Qwen3-72B",
            tensor_parallel_size=4,       # TP=4
            pipeline_parallel_size=1,
        )

Ray 会自动找到有 4 张空闲 GPU 的节点,如果单节点不够 4 张,Ray 会跨节点分配并自动配置 NCCL 通信。

3.3 PD 分离在 KubeRay 上的实现

# prefill_serve.py
@serve.deployment(
    ray_actor_options={"num_gpus": 8},
    name="prefill"
)
class PrefillDeployment:
    def __init__(self):
        self.llm = LLM(
            model="GLM-5.2-w8a8",
            tensor_parallel_size=8,
            enforce_eager=True,
            # Prefill 模式
            max_num_batched_tokens=4096,
        )
    async def prefill(self, prompt: str):
        # 只做 Prefill,返回 KV Cache
        ...

# decode_serve.py
@serve.deployment(
    ray_actor_options={"num_gpus": 4},
    name="decode",
    autoscaling_config={"min_replicas": 2, "max_replicas": 8}
)
class DecodeDeployment:
    # 从 KV Cache 继续生成
    ...

# 组合
@serve.deployment
@serve.ingress(app)
class Gateway:
    def __init__(self, prefill, decode):
        self.prefill = prefill
        self.decode = decode

    async def __call__(self, request):
        kv = await self.prefill.prefill.remote(prompt)
        result = await self.decode.generate.remote(kv)
        return result

app = Gateway.bind(
    PrefillDeployment.bind(),
    DecodeDeployment.bind()
)

注意:这个 PD 分离示例是 Ray Serve 层面的编排,不是 vLLM 原生的 PD 分离(--disaggregation-mode)。vLLM 原生 PD 分离 + KubeRay 的组合仍在早期阶段。


四、KubeRay vs 原生 Deployment vs AIBrix

4.1 三方案对比

方案多节点 TPPD 分离GPU 自动扩缩Ascend社区
原生 K8s Deployment❌ 手动✅ 手写✅ HPA通用 K8s
KubeRay✅ 自动⚠️ 半自动✅ Ray 原生2.6k ★
AIBrix5k ★
docker-compose你现在的

4.2 选型决策

你的场景:
  单节点推理 (TP=1-8) → 原生 Deployment 足够
  多节点 TP (TP>8)    → KubeRay(NVIDIA)或 手写 StatefulSet(Ascend)
  PD 分离 (Ascend)    → 你现在的 docker-compose 方案是最成熟的
  PD 分离 (NVIDIA)    → KubeRay + Ray Serve 编排
  追求 K8s 原生体验    → AIBrix(目前仅 NVIDIA)

五、KubeRay 在你的场景中的现实评估

5.1 能用上的

特性你的场景
RayService 服务管理替代 docker-compose 的 restart: unless-stopped 和健康检查
GPU 自动扩缩Decode 节点根据 QPS 自动扩缩(如果有 NVIDIA GPU)
滚动更新模型版本升级零停机

5.2 用不上的(Ascend 限制)

  • Ascend NPU 的 Device Plugin 不如 NVIDIA GPU Operator 成熟
  • Ray 的 GPU 感知调度在 Ascend 上未适配
  • Mooncake RDMA 在 KubeRay 中没有现成支持
  • 多节点 HCCL 初始化需要 hostNetwork 或手写 NCCL 环境变量

5.3 务实路线

阶段1(现在):继续 docker-compose + hostNetwork
  → Ascend NPU + PD 分离 + Mooncake 的最稳定方案

阶段2(学习):用 KubeRay 跑 NVIDIA GPU 的 vLLM(如果有多余 GPU)
  → 理解 KubeRay 的工作方式,积累经验

阶段3(未来):等 AIBrix 或华为 Ascend K8s Operator 成熟
  → 再考虑 Ascend 上的 K8s 化

六、KubeRay 部署速查

6.1 安装

# Helm 安装 KubeRay Operator
helm repo add kuberay https://ray-project.github.io/kuberay-helm/
helm install kuberay-operator kuberay/kuberay-operator

# 验证
kubectl get crd | grep ray
# rayclusters.ray.io
# rayjobs.ray.io
# rayservices.ray.io

6.2 部署 vLLM 推理服务

# 1. 写 RayService YAML(见 2.3 节)
# 2. 写 vLLM Serve 脚本(见 3.1 节)
# 3. 打包成 ConfigMap
kubectl create configmap vllm-serve-script --from-file=vllm_serve.py

# 4. 部署
kubectl apply -f rayservice-vllm.yaml

# 5. 查看状态
kubectl get rayservice vllm-serve
kubectl logs -l ray.io/cluster=raycluster-vllm

# 6. 端口转发测试
kubectl port-forward service/vllm-serve-serve-svc 8000:8000
curl http://localhost:8000/

6.3 监控

# Ray Dashboard
kubectl port-forward service/raycluster-vllm-head-svc 8265:8265
# 浏览器打开 http://localhost:8265 → 查看 GPU 利用率/任务队列/Serve 状态

# KubeRay 状态
kubectl describe rayservice vllm-serve

七、AIBrix 速览

AIBrix(vllm-project/aibrix)是 vLLM 官方推出的 K8s-native 方案:

AIBrix 组件:
  Gateway    → 统一推理入口(路由 + 限流 + 负载均衡)
  Scheduler  → GPU 感知调度(拓扑感知 + 碎片整理)
  ModelMgr   → 模型缓存管理(PVC + 预加载)
  Router     → PD 分离路由(Prefill/Decode 自动分发)

一句话:AIBrix 想做的是 “vLLM 的 K8s Operator”,但目前仅 NVIDIA。如果你以后有 NVIDIA GPU 集群,AIBrix 比 KubeRay 更轻量。


八、面试要点

Q: vLLM 怎么部署到 K8s?多节点 Tensor Parallelism 怎么解决?

单节点 TP ≤ 8:原生 Deployment + GPU limits + /dev/shm 扩容,足够。
多节点 TP > 8:需要分布式 rank 协调。
  方案A(推荐):KubeRay + Ray Serve。
    Ray 内置 GCS 全局调度,自动分配 GPU,自动 NCCL 初始化。
    定义 RayService CRD → 写 @serve.deployment → serve run。
  方案B:AIbrix(vLLM 官方 K8s Operator)。
    更轻量,但社区还在早期(仅 NVIDIA)。
  方案C:手写 StatefulSet + headless Service + NCCL 环境变量。
    最原始但最可控,Ascend NPU 上用这个。

Q: KubeRay 的核心 CRD 是什么?

RayCluster  → 定义一个 Ray 集群(Head + Workers)
RayJob      → 一次性任务(跑完销毁,如 benchmark)
RayService  → 长期推理服务(Ray Serve + 滚动更新 + A/B 测试)