当前位置: 首页 > news >正文

Ray 集群多用户资源隔离实践

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(address="auto", namespace="team_alpha") # 用户 B 连接到同一集群,使用不同命名空间 # ray.init(address="auto", namespace="team_beta") @ray.remote class ModelServer: def __init__(self, model_name): self.model_name = model_name def predict(self, data): return f"Prediction from {self.model_name}" # team_alpha 命名空间下创建 Actor server = ModelServer.options(name="model_server", lifetime="detached").remote("resnet50") # team_beta 中即使也创建同名 Actor,也不会冲突

注意事项:Namespace 只隔离 Named Actor 的可见性,不隔离计算资源。用户 A 仍然可以消耗掉用户 B 的 CPU/GPU 配额。

三、资源配额控制

3.1 连接时限制资源

通过ray.init()的参数限制单个 Job 可使用的资源上限:

# 限制该 Job 最多使用 8 个 CPU 和 2 个 GPU ray.init(address="auto", num_cpus=8, num_gpus=2)

但这种方式仅在本地模式(非集群模式)下生效。在集群模式下,需要通过 Task/Actor 级别的资源声明来控制。

3.2 Task/Actor 级别资源声明

# 精确声明每个 Task 需要的资源 @ray.remote(num_cpus=2, num_gpus=1, memory=4 * 1024 * 1024 * 1024) # 4GB def train_model(config): import torch model = torch.nn.Linear(512, 10).cuda() # ... 训练逻辑 return {"status": "done"} # 精确声明 Actor 的资源占用 @ray.remote(num_cpus=4, num_gpus=2) 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, strategy="PACK") # 等待资源分配完成 ray.get(pg.ready()) print("Placement group 资源预留成功") # 将任务调度到预留的资源上 @ray.remote(num_cpus=4, num_gpus=1) def train_on_reserved(data_shard): import time time.sleep(5) return f"Trained on {len(data_shard)} samples" # 使用 PlacementGroupSchedulingStrategy 绑定到预留资源 strategy = PlacementGroupSchedulingStrategy( placement_group=pg, placement_group_bundle_index=0 # 使用第一个 bundle ) result = ray.get( train_on_reserved.options(scheduling_strategy=strategy).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 --address=head_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": ["torch==2.1.0", "transformers==4.35.0"], "env_vars": {"TEAM": "alpha", "CUDA_VISIBLE_DEVICES": "0,1"}, "working_dir": "/data/team_alpha/workspace" } runtime_env_beta = { "pip": ["torch==2.0.1", "transformers==4.30.0"], "env_vars": {"TEAM": "beta", "CUDA_VISIBLE_DEVICES": "2,3"}, "working_dir": "/data/team_beta/workspace" } # 用户 A 的任务使用 alpha 环境 @ray.remote(runtime_env=runtime_env_alpha) def alpha_inference(text): from transformers import pipeline pipe = pipeline("sentiment-analysis") return pipe(text) # 用户 B 的任务使用 beta 环境 @ray.remote(runtime_env=runtime_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( name="alpha", namespace="team_alpha", cpu_quota=16, gpu_quota=4, runtime_env={"pip": ["torch==2.1.0"]} ), "beta": TenantConfig( name="beta", namespace="team_beta", cpu_quota=8, gpu_quota=2, runtime_env={"pip": ["torch==2.0.1"]} ), } def submit_tenant_job(tenant_id: str, train_func, data_shards: list): """为指定租户提交训练任务,自动应用资源隔离策略""" config = TENANT_CONFIGS[tenant_id] # 1. 连接到租户命名空间 ray.init(address="auto", namespace=config.namespace, ignore_reinit_error=True) # 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, strategy="PACK") ray.get(pg.ready()) # 3. 提交任务到预留资源,使用租户专属运行环境 futures = [] for i, shard in enumerate(data_shards[:num_workers]): strategy = PlacementGroupSchedulingStrategy( placement_group=pg, placement_group_bundle_index=i ) remote_func = train_func.options( scheduling_strategy=strategy, runtime_env=config.runtime_env, num_cpus=config.cpu_quota // num_workers, num_gpus=gpu_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)])

八、踩坑提示

  1. Namespace 不等于资源隔离:很多人误以为设置了 Namespace 就万事大吉,实际上 Namespace 只隔离 Actor 命名,不隔离计算资源。
  2. Placement Group 超时:如果集群资源不足,pg.ready()会一直阻塞。建议加上ray.get(pg.ready(), timeout=60)设置超时。
  3. Runtime Environment 冷启动:首次使用某个 runtime_env 时需要安装依赖,可能耗时较长。建议提前预热或使用 Docker 镜像。
  4. 自定义资源是软限制:自定义资源标签依赖节点启动时的声明,如果节点重启忘记加参数,隔离就会失效。建议通过 Kubernetes Operator 管理节点配置。
  5. 内存隔离不完善:Ray 的memory参数是逻辑限制,不是物理隔离。真正的内存隔离需要配合 cgroup 或容器化方案。

总结

Ray 的多用户资源隔离需要组合使用多种机制:Namespace 做逻辑隔离,Placement Group 做资源预留,自定义资源标签做节点级隔离,Runtime Environment 做依赖隔离。在生产环境中,建议结合 Kubernetes 的 ResourceQuota 和 Ray on K8s Operator 来实现更完善的多租户管理。


推荐标签Ray分布式计算资源隔离多租户机器学习平台Placement GroupMLOps

http://www.jsqmd.com/news/463444/

相关文章:

  • MySQL 进阶:库与表的DDL核心操作全指南(含实战案例)
  • 工业 + AI 落地实践:JBoltAI在工业场景的应用解析
  • 打卡信奥刷题(2938)用C++实现信奥题 P5800 [SEERC 2019] Life Transfer
  • 单片机高阻态:数字电路中的“隐形守护者”
  • Qt开发与MySQL数据库教程(一)——配置MySQL
  • 数据|非rag的类人检索
  • Java团队转型AI应用开发:挑战与JBoltAI的破局之道
  • 打卡信奥刷题(2939)用C++实现信奥题 P5810 [SCOI2004] 文本的输入
  • 化学绘图效率革命:InDraw五大核心功能全解析,从OCR识别到CAS号检索的实战指南
  • JBoltAI视频SOP:让“工业+AI”更高效直观
  • Python爬虫实战:监控贝壳找房小区均价与挂牌增量!
  • 物联网毕业设计效率提升指南:基于STM32原理图的模块化设计与快速验证方法
  • Spring Boot WebClient性能比RestTemplate高?看完秒懂!
  • 打卡信奥刷题(2940)用C++实现信奥题 P5815 [CQOI2010] 扑克牌
  • MTools教育应用:智能批改系统开发实战
  • 次元画室生成网络拓扑图:运维与网络教学的AI助手
  • 1.9 电子商城核心链路质量保障:从下单到支付的测试实战拆解
  • 使用IDEA开发RVC模型Java调用客户端:工程化配置与调试技巧
  • Leaflet与turf.js实战:动态生成等值线图并实现精准值交互展示
  • ArcGIS坐标系实战:从基础概念到投影变换全解析
  • Clawdbot汉化版企业微信实战:消息模板开发、事件回调处理、菜单集成
  • QGC地面站集成NTRIP网络差分:从原理到稳定配置实战
  • DDD分层架构的实践指南:从理论到落地
  • SwAV:在线聚类与对比学习的融合——无监督视觉表征学习新范式
  • 嵌入式系统多协议融合实战:从IIC温湿度采集到CAN总线通信的完整链路解析
  • OpenStack实战:从零搭建私有云平台
  • 从零到一:基于Cloudreve构建企业级私有云存储平台
  • 墨语灵犀GPU算力适配:华为昇腾910B+MindSpore框架移植全流程详解
  • 【密码学】从MD5到SM3:哈希函数演进与实战应用解析
  • Tao-8k前端交互应用:集成微信小程序的AI对话功能开发