Ray × Kubernetes × HAMi 多‑Agent 架构中的资源感知与自动伸缩机制
关键词:Ray、多 Agent、Kubernetes、HAMi、GPU 切分、自动伸缩、资源调度
一、为什么多‑Agent 场景下“资源感知”是核心问题
在多‑Agent 系统中(例如多智能体推理、协同规划、仿真环境),系统往往呈现出以下特征:
- Agent 数量动态变化
- Agent 生命周期长、状态复杂
- GPU 等异构资源昂贵且稀缺
- 单个 Agent 往往需要独占或半独占算力
因此,一个核心问题是:
系统如何在不知道“真实算力消耗”的情况下,依然做到可预测、可伸缩、可控的资源调度?
Ray × Kubernetes × HAMi 的组合,正是围绕这个问题形成的一套“分层解耦”的工程答案。
二、整体分层:谁负责什么?
在进入细节前,先明确三者的职责边界:
- Ray:负责 计算调度(谁来算、算什么)
- Kubernetes:负责 运行调度(算在哪、Pod 生命周期)
- HAMi:负责 算力兑现(GPU 如何切分并绑定)
一句话总结:
Ray 决定“需要多少算力”,K8s 决定“算力放在哪”,HAMi 决定“算力怎么给”。
三、Ray 是如何“知道”需要多少资源的?
3.1 声明式资源模型(Ray 的核心前提)
Ray 并不会分析代码、也不会感知 GPU 利用率,它只依赖用户显式声明的资源需求:
@ray.remote(num_cpus=2, num_gpus=1)
class Agent:
...
在 Ray 内部,这会被转换为一个不可变的结构:
ActorCreationSpec
├─ ActorID
├─ RequiredResources:
│ ├─ CPU: 2
│ └─ GPU: 1
└─ RuntimeEnv
这是 Ray 判断资源需求的唯一确定性输入。
3.2 Ray 的“逻辑资源视图”
Ray Head 节点(GCS + Scheduler)维护一张逻辑资源表:
Worker-1 (Pod-1): CPU=4, GPU=1
Worker-2 (Pod-2): CPU=4, GPU=1
这张表来自:
- Worker Pod 启动后主动向 Head 注册
- 注册的是“逻辑资源容量”,而非真实硬件指标
3.3 Pending 状态:自动伸缩的真实触发器
当 Ray 无法为 Task / Actor 分配所需资源时:
Actor → PENDING (GPU shortage)
Ray 并不会“降级运行”或“抢占”,而是将其放入 Pending 队列。
Pending 数量 × 资源声明 = Ray 对“还差多少算力”的全部认知。
四、多‑Agent 状态流(从创建到执行)
下面以一个典型的多‑Agent 场景为例:
agents = [Agent.remote() for _ in range(4)]
每个 Agent:num_gpus=1
4.1 Agent 创建阶段(Ray 内部)
ActorCreationSpec × 4
each requires GPU=1
此时:
- Agent 只是“逻辑对象”
- 尚未绑定任何 Pod / Node / GPU
4.2 Ray 调度阶段(逻辑调度)
假设当前集群状态:
Worker Pods × 2
Total GPU = 2
调度结果:
Agent-1 → RUNNING (Worker-1)
Agent-2 → RUNNING (Worker-2)
Agent-3 → PENDING
Agent-4 → PENDING
4.3 Autoscaler 推导“需要多少 Worker”
Ray Autoscaler 周期性执行:
Pending GPU = 2
Per Worker GPU = 1
→ Need 2 more Workers
注意:
- 这是算术推导,不是资源监控
- 不涉及 Node、GPU 型号、HAMi
五、Ray 自动伸缩是如何驱动 Kubernetes 的?
5.1 KubeRay 模式(主流)
Ray 并不直接创建 Pod,而是:
修改 RayCluster CR 中的 worker replicas
例如:
workerGroupSpecs:
- replicas: 2 → 4
KubeRay Operator 监听到变化后:
- 创建新的 Ray Worker Pod
5.2 Kubernetes 接管后的职责
Kubernetes 对 Ray 是“无感知”的,它只看到:
kind: Pod
spec:
containers:
- name: ray-worker
resources:
requests:
cpu: 4
memory: 16Gi
hami.io/gpu: 1
K8s 负责:
- Pod 调度到某个 Node
- 管理 Pod 生命周期
六、HAMi 在什么时间点介入?
HAMi 只在 Pod 级别、节点本地介入。
6.1 HAMi 的真实位置
- 每个 Node 上运行一个 HAMi Device Plugin(DaemonSet)
- 只管理“本节点的 GPU”
6.2 GPU 切分与绑定时机
当 Worker Pod 被调度到 Node 并启动容器时:
kubelet → HAMi Device Plugin → Allocate(GPU=1)
HAMi 做的事情:
- 感知 Node 上的 GPU 拓扑
- 切分成逻辑算力单元
- 绑定给当前 Pod
Ray 与 HAMi 之间没有直接通信。
七、完整执行链路(不可省略 Pod 的工程视角)
Agent (Actor)
↓
Ray Worker Process
↓
Container
↓
Pod
↓
Node
↓
HAMi GPU Unit
Task 并不直接“占用 GPU”,而是通过所在的 Worker Pod 间接使用。
八、缩容是如何发生的?
当:
- Agent 结束
- Task 队列清空
- Worker Pod 长时间 idle
Ray Autoscaler 会:
标记 Worker idle → 请求 K8s 删除 Pod
Pod 删除后:
容器退出 → HAMi 回收 GPU → Node 资源释放
整个过程无需额外通知机制。
九、为什么 Ray 不看 GPU / CPU 利用率?
这是一个刻意的设计选择:
- 利用率 ≠ 可调度性(显存、上下文占用)
- 跨节点、跨异构环境不可比
- 声明式资源带来调度确定性
代价是:
- 可能存在“资源空转”
收益是:
- 多‑Agent 系统行为可预测
- 调度逻辑简单且稳定
十、总结:多‑Agent 架构下的三层确定性
- Ray:通过声明式资源 + Pending 状态,确定“需要多少算力”
- Kubernetes:通过 Pod 调度,确定“算力放在哪”
- HAMi:通过 Device Plugin,确定“算力如何兑现”
正是这种分层解耦,使得 Ray × K8s × HAMi 能够支撑复杂、多变、长期运行的多‑Agent 系统。