01
引言
在分布式计算领域,高效编排多节点推理任务变得日益重要。Kubernetes 是管理容器化应用程序的领先平台,提供强大的资源管理和可扩展性。Ray 是构建和运行分布式应用程序的强大框架,尤其适合处理复杂的机器学习工作流程。然而,现有的编排方法在灵活性和简便性方面存在不足。Kubernetes 运算符虽然功能强大,但在处理分布式应用程序的细粒度编排时可能会变得复杂。分布式 Ray 推理通过高效的任务调度、资源管理以及自动扩缩容能力,为大规模推理任务提供了强大的支持。其架构设计充分考虑了分布式系统的可扩展性和可靠性,结合 Kubernetes 的强大生态,能够轻松部署和管理Ray集群。KubeRay Operator 提供了原生的 Kubernetes 方式来管理 Ray 集群,包括 RayJob、RayCluster、RayService 等自定义资源定义(CRD),支持自动扩缩、异构计算节点以及在同一Kubernetes 集群中运行多个不同版本的Ray集群。
通过这种协同方法,用户可以在 Kubernetes 上高效部署和管理Ray集群, 由此我们集成了Deepinfer项目,简化分布式应用程序的开发和运维流程,提升资源利用率和任务执行效率。
02
分布式推理的构建
Ray是UC Berkeley RISELab 推出的开源分布式计算框架,旨在简化机器学习和数据处理任务的并行执行。在大模型推理场景中,Ray 提供了高效的任务调度、资源管理和弹性伸缩能力,支持模型在多节点、多 GPU 上并行运行,显著提升推理效率。
以下是基于图中的架构,参考Kubernetes中Deployment-ReplicaSet-Pod三级机制构建了RayClusterFleet-RayClusterReplicaSet-RayCluster机制实现多Ray集群的管理,对分布式 Ray 推理的介绍、使用方法以及原理的详细分析:
根据提供的架构图,分布式 Ray 推理的核心组件如下:
(1)Ray 集群管理
RayClusterFleet :负责管理RayClusterReplicaSet的部署和生命周期,支持多Ray集群统一管理。
RayClusterReplicaSet :确保指定数量的Ray集群副本,管理RayCluster的生命周期。
RayCluster :表示一个完整的 Ray 集群,包含Head节点和Worker节点。
Ray-Operator :Kubernetes Operator,监听RayCluster、RayJob,用于自动化管理和扩展 Ray集群,支持动态调整资源和节点数量;创建并维护 Ray Head 和 Worker Pod;根据 RayCluster spec 创建、更新、删除底层资源;设置 Service、PVC、ConfigMap、PodTemplate 等。
(2)Head 节点
Driver Process :运行用户编写的主程序逻辑,负责任务提交和结果收集。Driver 可以提交任务,但不能自己执行任务。
Worker Process :执行实际的计算任务。
Raylet :每个节点上的核心组件,负责本地任务调度和资源管理。主要包含调度器和共享存储Object Store
Scheduler :全局调度器,负责将任务分配到合适的 Worker 节点。整个集群中所有的调度器组成Ray的分布式调度系统
Object Store :分布式对象存储,用于存储和共享数据(如模型权重、中间结果),负责存储、传输和落盘大对象,集群中所有对象存储进程共同构成 Ray 的分布式对象存储。
Global Control Store :全局控制存储,用于协调集群状态和元数据。例如 actor 的位置,这些元数据以键值对形式存储,并可被工作进程本地缓存。此外GCS 还管理少量集群级操作,例如:placement group 和 actor 的调度;集群节点成员资格的判定通常,GCS 管理那些访问频率较低但全局重要的元数据,以避免其性能瓶颈对应用性能产生影响。从Ray 2.0 开始,GCS 支持容错机制,可运行在任意节点甚至多个节点上,而不局限于头节点。
Autoscaler :自动扩缩容模块,根据负载动态调整集群规模。RayCluster开启自动扩缩后head节点将增加Autoscaler容器,监听节点,它可以根据资源需求自动扩容或缩容集群。自动扩缩如下图所示:
具体过程如下:
a. 用户提交 Ray 工作负载
b. Ray Head 容器聚合工作负载资源需求,并将其传递给 Ray 自动扩缩sidecar容器(Autoscaler sidecar)
c. 自动扩缩容器决定添加 Ray Worker Pod 以满足工作负载资源需求,例如目前head和worker总的CPU资源只有3C,而用户提交任 务需要4C才能完成,这时需要增加Ray Worker Pod才能满足负载要求。
d. 自动扩缩容器通过增加 RayCluster CR(自定义资源)的 replicas
字段值来请求新增 Worker Pod
e. KubeRay Operator 创建 Ray Worker Pod 以满足新的副本数要求
f. Ray 调度器将用户工作负载调度到新 Worker Pod 上
(3)Worker 节点
Worker Process :执行具体的推理任务,分为无状态的(stateless)和有状态的(actor),其中无状态的(stateless)的可复用,用于执行任意的
@ray.remote
函数;有状态的(actor)只能执行与其绑定的@ray.remote
类中的方法。Raylet :与 Head 节点的 Raylet 协作,负责本地任务调度和资源分配。每个工作进程和 raylet 都会被分配一个唯一的 28 字节标识符(ID)以及 IP 地址和端口。尽管地址和端口在进程终止后可以被后续组件重用,但唯一 ID 永不复用。工作进程与其所在节点的 raylet 进程命运共享(fate-share),即 raylet 挂了它也挂
Scheduler :辅助 Head 节点的调度器,处理本地任务调度。
Object Store :存储本地计算结果,支持跨节点的数据共享。
(4)主要流程如下:
a. 用户提交 RayClusterFleet
任务后,DeepInfer 中的 RayClusterFleet Controller
监听该资源的创建事件。
Controller 检查该 RayClusterFleet
是否已关联对应的 RayClusterReplicaSet
资源;如未找到,则根据配置新建一个 RayClusterReplicaSet
自定义资源对象(CRD)。
apiVersion: orchestration.aibrix.ai/v1alpha1kind: RayClusterFleetmetadata: annotations: creationTimestamp: "2025-05-26T02:23:36Z" generation: 1 labels: app: nodetest371 ... name: nodetest371-6833c71c5b0e5dbd66d6c6ee namespaces: ray-cardsspec: replicas: 1 selector: matchLabels: ... strategy: rollingUpdate: maxSurge: 2 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: nodetest371 ... namespace: prdsafe spec: autoscalerOptions: idleTimeoutSeconds: 60 imagePullPolicy: IfNotPresent resources: limits: cpu: 500m memory: 512Mi requests: cpu: 500m memory: 512Mi upscalingMode: Default enableInTreeAutoscaling: true headGroupSpec: rayStartParams: dashboard-host: 0.0.0.0 template: metadata: annotations: ... labels: app: nodetest371 ... namespace: prdsafe spec: containers: - args: - ulimit -n 65536;echo head;$KUBERAY_GEN_RAY_START_CMD;python3 -m vllm.entrypoints.openai.api_server --port 8000 --model /deepseek-ai/DeepSeek-R1-Distill-Llama-8B --tensor-parallel-size 1 --pipeline-parallel-size 2 --gpu-memory-utilization 0.95 --max_model_len 4096 --served-model-name nodetest371-6oow2j44 --uvicorn-log-level warning --trust-remote-code; command: - /bin/bash - -c - -- env: - name: HBOX_NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: RANK valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['leaderworkerset.sigs.k8s.io/worker-index'] image: vllm-openai:v0.8.4 imagePullPolicy: IfNotPresent name: ray-head ports: - containerPort: 6379 name: gcs-server protocol: TCP - containerPort: 8265 name: dashboard protocol: TCP - containerPort: 10001 name: client protocol: TCP - containerPort: 8000 name: service protocol: TCP resources: limits: cpu: "4" memory: 32Gi nvidia.com/gpu: "1" requests: cpu: "4" memory: 32Gi nvidia.com/gpu: "1" volumeMounts: ... enableServiceLinks: false imagePullSecrets: ... schedulerName: volcano tolerations: ... volumes: ... rayVersion: 2.40.0 workerGroupSpecs: - groupName: small-group maxReplicas: 5 minReplicas: 1 numOfHosts: 1 rayStartParams: {} replicas: 1 scaleStrategy: {} template: metadata: annotations: ... labels: app: nodetest371 ... namespace: ray-cards spec: containers: - args: - ulimit -n 65536; echo worker; $KUBERAY_GEN_RAY_START_CMD; command: - /bin/bash - -c - -- env: ... image: vllm-openai:v0.8.4 imagePullPolicy: IfNotPresent name: ray-worker resources: limits: cpu: "4" memory: 32Gi nvidia.com/gpu: "1" requests: cpu: "4" memory: 32Gi nvidia.com/gpu: "1" volumeMounts: ... enableServiceLinks: false imagePullSecrets: ... schedulerName: volcano tolerations: ... volumes: ... name: cachevol
b. RayClusterReplicaSet Controller
监听到新建的 RayClusterReplicaSet
后,检查是否已存在关联的 RayCluster
资源。若未查询到,且当前副本数低于 RayClusterReplicaSet
中定义的期望副本数(replicas
),则创建新的 RayCluster
资源对象以满足副本要求。RayClusterReplicaSet
CRD 结构如下:
type RayClusterReplicaSet struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec RayClusterReplicaSetSpec `json:"spec,omitempty"` Status RayClusterReplicaSetStatus `json:"status,omitempty"`}type RayClusterReplicaSetSpec struct { Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"` MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"` Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,2,opt,name=selector"` Template RayClusterTemplateSpec `json:"template,omitempty"`}
c. Ray Operator
监听 RayCluster
的创建事件后,校验其状态与 Spec
配置,并据此创建对应的 Head 和 Worker Pod 以及 Headless Service。若 RayCluster
中启用了自动扩缩容功能(autoscaling),则在 Head Pod 中注入 AutoScaler
容器,持续监听资源使用情况并根据策略动态调整 Worker 副本数。其中Head Pod YAML如下所示:
containers: - args: - ulimit -n 65536;echo head;$KUBERAY_GEN_RAY_START_CMD;python3 -m vllm.entrypoints.openai.api_server --port 8000 --model /models/deepseek-ai/DeepSeek-R1-Distill-Llama-8B --tensor-parallel-size 1 --pipeline-parallel-size 2 --gpu-memory-utilization 0.95 --max_model_len 4096 --served-model-name nodetest129-3emb2a8o --uvicorn-log-level warning --trust-remote-code --distributed-executor-backend ray; command: - /bin/bash - -c - -- env: - name: RANK valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['leaderworkerset.sigs.k8s.io/worker-index'] - name: RAY_CLUSTER_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['ray.io/cluster'] - name: RAY_CLOUD_INSTANCE_ID valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.name - name: RAY_NODE_TYPE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['ray.io/group'] - name: KUBERAY_GEN_RAY_START_CMD value: 'ray start --head --dashboard-agent-listen-port=52365 --dashboard-host=0.0.0.0 --memory=60129542144 --metrics-export-port=8080 --num-cpus=7 --num-gpus=1 ' - name: RAY_PORT value: "6379" - name: RAY_ADDRESS value: 127.0.0.1:6379 - name: RAY_USAGE_STATS_KUBERAY_IN_USE value: "1" - name: RAY_USAGE_STATS_EXTRA_TAGS value: kuberay_version=v1.3.2;kuberay_crd=RayCluster - name: RAY_DASHBOARD_ENABLE_K8S_DISK_USAGE value: "1" image: vllm-openai:v0.8.4 imagePullPolicy: IfNotPresent name: ray-head ports: - containerPort: 6379 name: gcs-server protocol: TCP - containerPort: 8265 name: dashboard protocol: TCP - containerPort: 10001 name: client protocol: TCP - containerPort: 8000 name: service protocol: TCP - containerPort: 8080 name: metrics protocol: TCP
d. Head Pod 创建后,首先启动 Ray Head 节点,常见启动命令如下:
ray start --head \ --dashboard-agent-listen-port=52365 \ --dashboard-host=0.0.0.0 \ --memory=60129542144 \ --metrics-export-port=8080 \ --num-cpus=7 \ --num-gpus=1
f. Ray Head 节点启动成功后,Worker 节点通过 Ray Head 的 Service 地址加入集群。Worker 的典型启动命令如下:
ray start \ --address=st129-68400db06b18f87d9b0e00bc-6767bb826f-head-svc.xx.svc:6379 \ --block \ --dashboard-agent-listen-port=52365 \ --memory=34359738368 \ --metrics-export-port=8080 \ --num-cpus=5 \ --num-gpus=1
03
分布式异构推理
异构 GPU 推理指在同一模型部署中混合使用不同类型 GPU(如 NVIDIA A100 与 T4、H100 与 RTX 4090 等),通过动态调度或模型切分技术,突破单一 GPU 类型的资源限制并优化成本。其核心目标包括:资源弹性扩展,利用不同区域 / 集群的可用 GPU 类型(如 A100 短缺时启用 T4),保障模型服务的连续性;成本分层优化,对计算密集型任务使用高性能 GPU(如 H100),对低负载任务降级使用低成本 GPU(如 RTX 3060),实现性价比平衡。
Deepinfer利用KubeRay 的多 WorkerGroup 机制允许在一个 RayCluster 中定义多个 WorkerGroupSpec
,每个 WorkerGroup 可以绑定不同的资源请求、容器规格和副本数,实现分布式异构部署。结合 Kubernetes 的调度能力,可以实现在调度层面指定不同类型的 GPU 节点。这允许你在同一个 Ray 集群中同时运行需要不同 GPU 算力或显存的任务。实现支持模型在不同类型 GPU 上运行、不依赖单一类型 GPU、自动扩缩与调度策略支持异构资源、满足成本优化与推理稳定性的双重目标。
以下是一个完善的 KubeRay RayCluster YAML 设计,以实现这种混合 GPU的使用:
apiVersion: orchestration.aibrix.ai/v1alpha1kind: RayClusterFleetmetadata: annotations: prometheus.io/custom: "true" prometheus.io/path: /metrics prometheus.io/port: "8000" prometheus.io/scrape: "true" creationTimestamp: "2025-05-19T06:04:46Z" generation: 1 labels: app: mix-ray-cards-review-v3 global/index: 682aa920d5cdaaab68ccae82 k8s.io/priority: P3 k8s.io/product.type: perception k8s.io/trace.env: test model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b name: review-v3-hg-sc-682aa920d5cdaaab6 namespaces: ray-cardsspec: replicas: 1 selector: matchLabels: model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b strategy: rollingUpdate: maxSurge: 2 maxUnavailable: 1 type: RollingUpdate template: metadata: annotations: prometheus.io/custom: "true" prometheus.io/path: /metrics prometheus.io/port: "8000" prometheus.io/scrape: "true" ray.io/overwrite-container-cmd: "true" labels: app: mix-ray-cards-review-v3 global/index: 682aa920d5cdaaab68ccae82 k8s.io/priority: P3 model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b name: review-v3-hg-sc-682aa920d5cdaaab6 spec: autoscalerOptions: idleTimeoutSeconds: 60 imagePullPolicy: IfNotPresent resources: limits: cpu: 500m memory: 512Mi requests: cpu: 500m memory: 512Mi upscalingMode: Conservative enableInTreeAutoscaling: true headGroupSpec: rayStartParams: block: "false" dashboard-host: 0.0.0.0 template: metadata: annotations: .... labels: .... model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b spec: containers: - args: - ulimit -n 65536;echo head;$KUBERAY_GEN_RAY_START_CMD;python3 -m vllm.entrypoints.openai.api_server --port 8000 --model /models/deepseek-ai/DeepSeek-R1-Distill-Llama-8B --tensor-parallel-size 1 --pipeline-parallel-size 4 --gpu-memory-utilization 0.95 --max_model_len 4096 --served-model-name mix-ray-cards-review-v3-uuzj6l0b --uvicorn-log-level warning --trust-remote-code; command: - /bin/bash - -c - -- env: - name: HBOX_NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: RANK valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['leaderworkerset.sigs.k8s.io/worker-index'] image: vllm-openai:v0.7.2 imagePullPolicy: IfNotPresent name: ray-head ports: - containerPort: 6379 name: gcs-server protocol: TCP - containerPort: 8265 name: dashboard protocol: TCP - containerPort: 10001 name: client protocol: TCP - containerPort: 8000 name: service protocol: TCP resources: limits: cpu: "11" memory: 120Gi nvidia.com/l20: "1" requests: cpu: "11" memory: 120Gi nvidia.com/l20: "1" volumeMounts: .... - args: - |- until curl --max-time 5 --fail http://127.0.0.1:8000 > /dev/null 2>&1; do echo "[WAITING] model is not ready yet..."; sleep 5; done && aibrix_runtime --port 8080 command: - /bin/bash - -lc - -- env: - name: INFERENCE_ENGINE value: vllm - name: INFERENCE_ENGINE_ENDPOINT value: http://localhost:8000 - name: PYTORCH_CUDA_ALLOC_CONF value: expandable_segments:True image: aibrix-runtime:v0.2.1 name: aibrix-runtime ports: - containerPort: 8080 protocol: TCP readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: "1" memory: 1Gi requests: cpu: "1" memory: 1Gi enableServiceLinks: false imagePullSecrets: .... schedulerName: volcano tolerations: ... volumes: ... rayVersion: 2.40.0 workerGroupSpecs: - groupName: small-group maxReplicas: 3 minReplicas: 0 numOfHosts: 1 rayStartParams: {} replicas: 0 scaleStrategy: {} template: metadata: annotations: prometheus.io/custom: "true" prometheus.io/path: /metrics prometheus.io/port: "8000" prometheus.io/scrape: "true" ray.io/overwrite-container-cmd: "true" labels: app: mix-ray-cards-review-v3 global/index: 682aa920d5cdaaab68ccae82 model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b namespace: ray-cards spec: containers: - args: - ulimit -n 65536; echo worker; $KUBERAY_GEN_RAY_START_CMD; command: - /bin/bash - -c - -- env: - name: HBOX_NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: RANK valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['leaderworkerset.sigs.k8s.io/worker-index'] - name: HF_ENDPOINT value: https://hf-mirror.com image: vllm-openai:v0.7.2 imagePullPolicy: IfNotPresent lifecycle: preStop: exec: command: - /bin/sh - -c - ray stop name: ray-worker resources: limits: cpu: "11" memory: 120Gi nvidia.com/gpu: "1" requests: cpu: "11" memory: 120Gi nvidia.com/gpu: "1" volumeMounts: .... enableServiceLinks: false imagePullSecrets: schedulerName: volcano nodeSelector: ... tolerations: ... volumes: ... - groupName: small-group-2 maxReplicas: 3 minReplicas: 1 numOfHosts: 1 rayStartParams: {} replicas: 1 scaleStrategy: {} template: metadata: annotations: ... prometheus.io/path: /metrics prometheus.io/port: "8000" prometheus.io/scrape: "true" labels: app: mix-ray-cards-review-v3 global/index: 682aa920d5cdaaab68ccae82 model.deepinfer.ai/name: mix-ray-cards-review-v3-uuzj6l0b namespace: ray-cards spec: containers: - args: - ulimit -n 65536; echo worker; $KUBERAY_GEN_RAY_START_CMD; command: - /bin/bash - -c - -- env: - name: NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - name: RANK valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.labels['leaderworkerset.sigs.k8s.io/worker-index'] - name: HF_ENDPOINT value: https://hf-mirror.com image: vllm-openai:v0.7.2 imagePullPolicy: IfNotPresent lifecycle: preStop: exec: command: - /bin/sh - -c - ray stop name: ray-worker resources: limits: cpu: "11" memory: 116Gi nvidia.com/gpu: "1" requests: cpu: "11" memory: 116Gi nvidia.com/gpu: "1" volumeMounts: ... enableServiceLinks: false imagePullSecrets: ... schedulerName: volcano nodeSelector: ... tolerations: ... volumes: ...
rayVersion
: 确保指定你的 Ray 版本。KubeRay 的功能和配置可能因 Ray 版本而异。enableInTreeAutoscaling: true
: 启用 KubeRay 的自动扩缩功能。这是利用不同 WorkerGroup 的关键,因为它允许 KubeRay 根据 Ray 任务的资源需求自动创建或销毁不同类型的 Worker Pod。headGroupSpec
:-
rayStartParams: num-cpus: "0"
: 这是一个最佳实践,通常将 Head 节点的 CPU 资源设置为 0,以防止 Ray 任务调度到 Head 节点上,确保 Head 节点主要用于集群管理和调度。image
: 确保使用支持 GPU 的 Ray 或者vllm镜像。资源限制 (
resources
): 根据你的 Head 节点实际需求配置 CPU 和内存。
workerGroupSpecs
: 这是实现混合 GPU 的核心。-
这是实现混合 GPU 类型调度的最重要部分。你需要根据你的 Kubernetes 集群中 GPU 节点的标签来配置,也可以根据亲和性设置调度到哪种类型的GPU节点。
通过为每个 WorkerGroup 指定不同的
nodeSelector
,KubeRay 会确保该 WorkerGroup 的 Pod 只调度到满足这些标签条件的节点上,从而实现不同 GPU 类型的分离。
Kubernetes 的 GPU 驱动通常会在 GPU 节点上添加
nvidia.com/gpu:NoSchedule
污点 (taint),以防止非 GPU 工作负载调度到这些节点上。为了让 Ray Worker Pod 能够调度到 GPU 节点上,你需要在 Worker Pod 的
tolerations
中添加对应的容忍度。
这是 Kubernetes 识别并分配 GPU 资源的关键。
nvidia.com/gpu
: "1"
表示每个 Worker Pod 需要一块 GPU。limits
和requests
应该根据你希望每个 Worker Pod 占用的 GPU 数量和物理资源(CPU、内存)来设置。
多个
workerGroupSpecs
: 定义多个workerGroupSpecs
数组元素,每个元素代表一种不同类型的 Worker 组。groupName
: 为每个 Worker 组提供一个有意义的名称,例如a100-workers
和v100-t4-workers
,便于管理和识别。replicas
,minReplicas
,maxReplicas
: 配置每个 Worker 组的初始、最小和最大 Pod 数量。当启用自动扩缩时,KubeRay 会在此范围内动态调整 Worker 数量。rayStartParams: num-gpus: "1"
: 在 Ray 内部,num-gpus
参数告诉 Ray 这个 Worker 节点有多少可用的 GPU。即使 Kubernetes 层面分配了 GPU 资源,Ray 调度器也需要知道这些信息才能正确调度 GPU 任务。template.spec.containers.resources.limits/
requests.nvidia.com/gpu
:template.spec.tolerations
:template.spec.nodeSelector
:
工作流程如下所示:
结合提供的架构图和 KubeRay 多 WorkerGroup 的机制,我们可以深入分析如何在 Deepinfer 项目中实现异构 GPU 卡部署,特别是对于 vLLM 这类需要高性能 GPU 的应用场景。
Client (客户端):这是用户或应用程序发起请求的入口。
Load Balancer (负载均衡器):负责将客户端请求分发到 vLLM Head 节点,Head节点处理请求或者转发到Worker中处理。
vLLM Head Node (vLLM 头节点):这个节点很可能是 vLLM 服务的入口点,负责接收请求,进行模型加载、请求排队、分发到 Ray 集群进行实际推理等。它可能包含 vLLM 的 API 服务器和一些调度逻辑。
Ray Cluster (Ray 集群):这是核心的分布式计算层,vLLM 头节点将推理任务发送到此集群进行处理。
Worker Group 1: A100 (工作组 1:A100):这是一个 Ray Worker 组,专门配置了 NVIDIA A100 GPU。A100 GPU 以其卓越的算力、显存带宽和 FP16/BF16 性能,非常适合处理大型语言模型 (LLM) 的训练、微调或高性能推理任务,特别是那些对延迟和吞吐量要求极高的场景。
Worker Group 2: T4 (工作组 2:T4):这是另一个 Ray Worker 组,专门配置了 NVIDIA T4 GPU。T4 GPU 虽然在绝对性能上不如 A100,但其出色的性价比和 INT8 推理能力使其成为轻量级推理、边缘部署或对成本敏感的场景的理想选择。当Head作为Worker节点时,单独A100不能满足资源需求或者A100资源不足,增加T4能够有效缓解GPU显存压力,一般推荐相近的资源。
KubeRay 多 WorkerGroup 实现异构卡部署的优势和具体结合:
a. 资源优化与成本效益:
结合图分析: 架构图中清晰地展示了两种不同类型的 Worker Group (A100 和 T4)。
KubeRay 实现: 通过在 KubeRay 的
RayCluster
YAML 中定义两个workerGroupSpecs
:-
一个
workerGroupSpec
专门用于 A100 GPU 节点,配置较高的资源限制和请求,以及对应的nodeSelector
来匹配 A100 节点。另一个
workerGroupSpec
用于 T4 GPU 节点,配置适合 T4 的资源,并使用不同的nodeSelector
来匹配 T4 节点。
优势: 这允许你根据任务的实际需求,灵活地使用不同性能和成本的 GPU。例如,若head不作为worker节点时大型、复杂的 LLM 推理可以路由到 A100 节点,而小型模型或对延迟不那么敏感的推理可以路由到 T4 节点。这样可以避免昂贵的 A100 GPU 资源被低性能任务浪费,从而实现成本效益最大化。若head作为worker节点时,当部署模型使用A100资源不足了,可以共享T4的GPU显存,从而实现分布式推理。
b. 自动扩缩容:
结合图分析: 架构图暗示了对可伸缩性的需求,特别是在处理大量并发请求时。
KubeRay 实现: KubeRay 的内置自动扩缩功能 (
enableInTreeAutoscaling: true
) 在异构卡部署中尤为重要。你可以为每个 WorkerGroup 设置独立的minReplicas
和maxReplicas
。优势: 当 A100 任务负载增加时,KubeRay 可以自动扩容 A100 Worker Group;当 T4 任务负载增加时,KubeRay 可以独立扩容 T4 Worker Group。这使得资源利用更加动态和高效,避免了在空闲时占用过多昂贵的 GPU 资源,并在高峰期及时满足需求。
c. 独立管理和隔离:
结合图分析: 两个 Worker Group 的独立性通过分支箭头清晰地表示。
KubeRay 实现: 每个
workerGroupSpec
都是一个独立的配置单元,拥有自己的 Pod 模板、资源限制、节点选择器等。优势: 这允许你独立地管理和维护不同类型的 GPU Worker。例如,你可以对 A100 Worker 执行滚动更新,而不会影响 T4 Worker。同时,通过 Kubernetes 的资源限制和节点隔离,可以确保不同 Worker Group 之间的资源不会相互干扰。
d. 灵活的软件栈:
结合图分析: 尽管图中没有直接体现,但不同的 GPU 可能需要不同版本的 CUDA、cuDNN 或其他深度学习库。
KubeRay 实现: 可以在每个 WorkerGroup 的 Pod 模板中指定不同的 Docker 镜像。例如,A100 Worker 可以使用一个预装了最新 CUDA/cuDNN 和 PyTorch/TensorFlow 版本的镜像,而 T4 Worker 可以使用一个针对其优化过的、可能包含 INT8 优化库的镜像。
优势: 确保每个 GPU 类型都能运行在最适合其硬件和应用需求的软件环境下。
通过 KubeRay 的多 WorkerGroup 机制,Deepinfer 项目能够构建一个高度灵活、成本效益高、且具备强大调度能力的异构 GPU 集群。这使得 vLLM 服务能够智能地利用不同性能的 GPU 资源来处理各种 LLM 推理请求,从而在满足性能需求的同时,优化整体运营成本。架构图清晰地展现了这种分层的设计理念,而 KubeRay 则提供了在 Kubernetes 上实现这一复杂分布式系统的强大能力。
04
平台实践
为满足大规模并发推理场景下的低延迟、高吞吐需求,平台在线服务中引入了高效部署模式,结合Ray分布式执行框架 与 vLLM 高性能推理引擎,实现异构GPU推理服务的高效调度与资源利用。
a. 核心实践点
启用 Ray 分布式执行后端: 在 vLLM 启动命令中通过参数
--distributed-executor-backend ray
,启用 Ray 作为推理任务的底层调度与执行引擎,支持多节点、多副本的分布式运行。根据实际副本数动态调整并行度: 启动命令中通过 --tensor-parallel-size 参数指定并行副本数量,该参数应与部署时 RayCluster 中的 Worker 副本总数一致,确保张量切分与通信逻辑正确匹配。
b. vLLM 启动命令配置示例:
python3 -m vllm.entrypoints.openai.api_server \ --port 8000 \ --model /deepseek-ai/DeepSeek-R1-Distill-Llama-8B \ --tensor-parallel-size 1 \ --pipeline-parallel-size 2 \ --gpu-memory-utilization 0.95 \ --max_model_len 8192 \ --served-model-name nodetest371-6oow2j44 \ --trust-remote-code
c. 平台选择高效部署模型、启动分布式,勾选异构资源然后创建任务即可,如下所示:
平台通过深度融合 KubeRay 的异构 WorkerGroup 管理能力、Ray 的分布式调度框架与 vLLM 的高性能推理引擎,实现了异构 GPU 资源的智能调度与高效利用。平台根据模型规模与推理负载特征,自动将任务分发至不同性能和成本的 GPU 资源上,在性能与成本之间实现最佳平衡。
🚀 极致推理性能:借助 vLLM 的 PagedAttention 与 KV Cache 优化机制,结合 Ray 的并行计算能力,即便在大规模并发场景下,仍能保持极低延迟与高吞吐表现。
🔁 弹性伸缩与高可用性:依托 KubeRay 提供的自动扩缩容机制与 Ray 的原生分布式架构,平台可根据流量动态伸缩计算资源,保障服务的稳定性与高可用。
⚙️ 部署与运维简化:KubeRay 提供声明式 API 接口,实现对复杂 Ray 集群的便捷部署与统一管理,极大降低了分布式推理服务的运维复杂度。
更多技术干货,
请关注“360智汇云开发者”👇
360智汇云官网:https://zyun.360.cn(复制在浏览器中打开)
更多好用又便宜的云产品,欢迎试用体验~
添加工作人员企业微信👇,get更快审核通道+试用包哦~