Ray 集群多用户资源隔离在企业级 AI 平台中多个团队共享同一个 Ray 集群是常见场景。如果缺乏有效的资源隔离机制一个用户的大规模训练任务可能吞掉整个集群的 GPU导致其他团队的推理服务直接瘫痪。本文将系统介绍 Ray 集群中实现多用户资源隔离的几种核心手段并给出一个多租户 ML 训练平台的完整实践方案。一、为什么需要资源隔离共享 Ray 集群面临的典型问题资源抢占用户 A 提交大量任务占满 CPU/GPU用户 B 的任务排队等待命名冲突不同用户的 Actor 同名导致互相覆盖环境污染用户 A 安装的依赖版本与用户 B 冲突故障传播一个用户的 OOM 导致整个节点上的任务全部失败Ray 从 1.x 版本开始逐步引入了 Namespace、Placement Group、Runtime Environment 等机制组合使用可以实现较为完善的多租户隔离。二、Namespace 隔离Namespace 是 Ray 中最基础的逻辑隔离单元。不同 Namespace 下的 Named Actor 互不可见。import ray # 用户 A 连接到集群使用自己的命名空间 ray.init(addressauto, namespaceteam_alpha) # 用户 B 连接到同一集群使用不同命名空间 # ray.init(addressauto, namespaceteam_beta) ray.remote class ModelServer: def __init__(self, model_name): self.model_name model_name def predict(self, data): return fPrediction from {self.model_name} # team_alpha 命名空间下创建 Actor server ModelServer.options(namemodel_server, lifetimedetached).remote(resnet50) # team_beta 中即使也创建同名 Actor也不会冲突注意事项Namespace 只隔离 Named Actor 的可见性不隔离计算资源。用户 A 仍然可以消耗掉用户 B 的 CPU/GPU 配额。三、资源配额控制3.1 连接时限制资源通过ray.init()的参数限制单个 Job 可使用的资源上限# 限制该 Job 最多使用 8 个 CPU 和 2 个 GPU ray.init(addressauto, num_cpus8, num_gpus2)但这种方式仅在本地模式非集群模式下生效。在集群模式下需要通过 Task/Actor 级别的资源声明来控制。3.2 Task/Actor 级别资源声明# 精确声明每个 Task 需要的资源 ray.remote(num_cpus2, num_gpus1, memory4 * 1024 * 1024 * 1024) # 4GB def train_model(config): import torch model torch.nn.Linear(512, 10).cuda() # ... 训练逻辑 return {status: done} # 精确声明 Actor 的资源占用 ray.remote(num_cpus4, num_gpus2) class DistributedTrainer: def __init__(self): pass def train(self, dataset_path): # 该 Actor 独占 4 CPU 2 GPU pass四、Placement Group 资源预留Placement Group 是 Ray 中实现资源预留和亲和性调度的核心机制。它允许你预先锁定一组资源确保这些资源只被特定任务使用。from ray.util.placement_group import placement_group, remove_placement_group from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy # 为 team_alpha 预留资源2 个 bundle每个 bundle 4 CPU 1 GPU bundles [{CPU: 4, GPU: 1} for _ in range(2)] pg placement_group(bundles, strategyPACK) # 等待资源分配完成 ray.get(pg.ready()) print(Placement group 资源预留成功) # 将任务调度到预留的资源上 ray.remote(num_cpus4, num_gpus1) def train_on_reserved(data_shard): import time time.sleep(5) return fTrained on {len(data_shard)} samples # 使用 PlacementGroupSchedulingStrategy 绑定到预留资源 strategy PlacementGroupSchedulingStrategy( placement_grouppg, placement_group_bundle_index0 # 使用第一个 bundle ) result ray.get( train_on_reserved.options(scheduling_strategystrategy).remote([1, 2, 3]) ) print(result) # 任务完成后释放资源 remove_placement_group(pg)调度策略说明策略说明适用场景PACK尽量将 bundle 放在同一节点需要高带宽通信的分布式训练SPREAD将 bundle 分散到不同节点容错优先的推理服务STRICT_PACK必须放在同一节点否则失败单机多卡训练STRICT_SPREAD必须分散到不同节点否则失败跨节点冗余部署五、自定义资源标签Ray 支持自定义资源标签可以用来实现用户级别的资源隔离。在启动 Worker 节点时声明自定义资源# 启动节点时声明该节点属于 team_alpha配额 100 个单位 ray start --addresshead_ip:6379 \ --resources{team_alpha: 100, team_beta: 0}# team_alpha 的任务声明需要自定义资源 ray.remote(resources{team_alpha: 1}) def team_alpha_task(x): return x * 2 # 该任务只会被调度到拥有 team_alpha 资源的节点上 results ray.get([team_alpha_task.remote(i) for i in range(10)]) print(results)这种方式可以将物理节点按团队划分实现硬隔离。六、Runtime Environment 隔离不同用户可能依赖不同版本的 Python 包。Ray 的 Runtime Environment 机制可以为每个 Job 或 Task 创建独立的运行环境runtime_env_alpha { pip: [torch2.1.0, transformers4.35.0], env_vars: {TEAM: alpha, CUDA_VISIBLE_DEVICES: 0,1}, working_dir: /data/team_alpha/workspace } runtime_env_beta { pip: [torch2.0.1, transformers4.30.0], env_vars: {TEAM: beta, CUDA_VISIBLE_DEVICES: 2,3}, working_dir: /data/team_beta/workspace } # 用户 A 的任务使用 alpha 环境 ray.remote(runtime_envruntime_env_alpha) def alpha_inference(text): from transformers import pipeline pipe pipeline(sentiment-analysis) return pipe(text) # 用户 B 的任务使用 beta 环境 ray.remote(runtime_envruntime_env_beta) def beta_inference(text): from transformers import pipeline pipe pipeline(text-generation) return pipe(text)七、实战多租户 ML 训练平台下面给出一个整合以上所有机制的多租户平台调度示例import ray from ray.util.placement_group import placement_group from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy from dataclasses import dataclass from typing import Dict dataclass class TenantConfig: name: str namespace: str cpu_quota: int gpu_quota: int runtime_env: Dict TENANT_CONFIGS { alpha: TenantConfig( namealpha, namespaceteam_alpha, cpu_quota16, gpu_quota4, runtime_env{pip: [torch2.1.0]} ), beta: TenantConfig( namebeta, namespaceteam_beta, cpu_quota8, gpu_quota2, runtime_env{pip: [torch2.0.1]} ), } def submit_tenant_job(tenant_id: str, train_func, data_shards: list): 为指定租户提交训练任务自动应用资源隔离策略 config TENANT_CONFIGS[tenant_id] # 1. 连接到租户命名空间 ray.init(addressauto, namespaceconfig.namespace, ignore_reinit_errorTrue) # 2. 创建 Placement Group 预留资源 gpu_per_worker 1 num_workers min(config.gpu_quota, len(data_shards)) bundles [{CPU: config.cpu_quota // num_workers, GPU: gpu_per_worker} for _ in range(num_workers)] pg placement_group(bundles, strategyPACK) ray.get(pg.ready()) # 3. 提交任务到预留资源使用租户专属运行环境 futures [] for i, shard in enumerate(data_shards[:num_workers]): strategy PlacementGroupSchedulingStrategy( placement_grouppg, placement_group_bundle_indexi ) remote_func train_func.options( scheduling_strategystrategy, runtime_envconfig.runtime_env, num_cpusconfig.cpu_quota // num_workers, num_gpusgpu_per_worker ) futures.append(remote_func.remote(shard)) results ray.get(futures) return results # 使用示例 ray.remote def distributed_train(data_shard): import torch # 模拟训练 model torch.nn.Linear(768, 10) return {shard_size: len(data_shard), status: completed} # submit_tenant_job(alpha, distributed_train, [list(range(1000)) for _ in range(4)])八、踩坑提示Namespace 不等于资源隔离很多人误以为设置了 Namespace 就万事大吉实际上 Namespace 只隔离 Actor 命名不隔离计算资源。Placement Group 超时如果集群资源不足pg.ready()会一直阻塞。建议加上ray.get(pg.ready(), timeout60)设置超时。Runtime Environment 冷启动首次使用某个 runtime_env 时需要安装依赖可能耗时较长。建议提前预热或使用 Docker 镜像。自定义资源是软限制自定义资源标签依赖节点启动时的声明如果节点重启忘记加参数隔离就会失效。建议通过 Kubernetes Operator 管理节点配置。内存隔离不完善Ray 的memory参数是逻辑限制不是物理隔离。真正的内存隔离需要配合 cgroup 或容器化方案。总结Ray 的多用户资源隔离需要组合使用多种机制Namespace 做逻辑隔离Placement Group 做资源预留自定义资源标签做节点级隔离Runtime Environment 做依赖隔离。在生产环境中建议结合 Kubernetes 的 ResourceQuota 和 Ray on K8s Operator 来实现更完善的多租户管理。推荐标签Ray分布式计算资源隔离多租户机器学习平台Placement GroupMLOps