Ray × Kubernetes × HAMi 多‑Agent 架构中的资源感知与自动伸缩机制

7 阅读4分钟

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 负责:

  1. Pod 调度到某个 Node
  2. 管理 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 利用率?

这是一个刻意的设计选择

  1. 利用率 ≠ 可调度性(显存、上下文占用)
  2. 跨节点、跨异构环境不可比
  3. 声明式资源带来调度确定性

代价是:

  • 可能存在“资源空转”

收益是:

  • 多‑Agent 系统行为可预测
  • 调度逻辑简单且稳定

十、总结:多‑Agent 架构下的三层确定性

  • Ray:通过声明式资源 + Pending 状态,确定“需要多少算力”
  • Kubernetes:通过 Pod 调度,确定“算力放在哪”
  • HAMi:通过 Device Plugin,确定“算力如何兑现”

正是这种分层解耦,使得 Ray × K8s × HAMi 能够支撑复杂、多变、长期运行的多‑Agent 系统。