> ## Documentation Index
> Fetch the complete documentation index at: https://starforge.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 角色与配额

> 谁能做什么，以及他们能占住多少集群。

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf admin user-add alice
sf admin set-role alice operator
sf admin set-quota alice --max-concurrent-gpus 8 --max-concurrent-jobs 4
```

## 角色

只有两种。

| 角色         | 能做                                           |
| ---------- | -------------------------------------------- |
| `operator` | 提交、观察、对比、发布插件和环境、管理自己的数据集和模型。控制台上显示为「Member」 |
| `admin`    | 以上全部，再加用户、配额、硬件、设置、定时任务、审计，以及禁用已发布的扩展        |

管理员专属接口对 `operator` 的 token 返回 `403` 而不是 `401`——token 没问题，权限有问题。

新账号默认是 `operator`；启用单点登录时，`FORGE_OIDC_DEFAULT_ROLE` 决定首次登录的人拿到什么角色。

## 配额的几个维度

配额按用户设置，五项互相独立：

<ParamField path="max_concurrent_gpus" type="int" default="8">
  这个用户同时最多能占多少张卡，跨他所有运行中的作业统计。精确——准入在下发前就核算。
</ParamField>

<ParamField path="max_concurrent_jobs" type="int" default="4">
  同时能跑多少个作业，不看大小。防止一个人用小作业把队列占满。
</ParamField>

<ParamField path="daily_gpu_hours" type="int" default="0">
  每日 GPU-时。0 表示不限。天的边界按服务时区计算。
</ParamField>

<ParamField path="max_storage_gb" type="int" default="0">
  这个用户的 run 目录最多能占多少。0 表示不限。是测量而不是预留，所以最终一致。
</ParamField>

<ParamField path="allowed_profiles" type="逗号分隔" default="全部">
  这个用户可以申请哪些硬件 profile。留空表示全部。
</ParamField>

另外还有 `priority`，在 `priority-fifo` 策略下决定队列顺序；
以及一份按卡型的映射——`{卡型: {max_concurrent_gpus, daily_gpu_hours}}`——
用于异构集群上一个全局数字不够用的情况。

## 团队配额

多个部门共用一套集群时，个人配额不够用：八个人各占四张卡，每个人都在自己额度内，
合起来是一个部门三十二张卡。团队配额管的是后者。

**一次运行的预算由它所属项目的团队决定，不由提交人决定。** 这条是整个模型的关键：
一个人可以同时属于多个团队，只有「项目」能唯一回答「这笔算力算谁的」。作业提交时
就把团队记在作业行上，之后把项目转到别的团队也不会改写已经跑过的账——和 `gpu_seconds`
在终态冻结是同一条规则。

在控制台 **管理 → 团队** 建团队、配额度、管成员；项目的归属在项目设置里改（仅管理员）。

两条与个人配额相反的语义，值得单独记住：

* **团队没配配额 = 不限**，不是禁止。个人配额是反过来的（没配 = 不许提交），
  那是刻意的 deny-by-default；照搬到团队上，就会变成「建一个团队立刻把里面所有人卡死」。
* **管理员不豁免团队配额。** 个人配额豁免的含义是「这个人可以信任」；跳过团队预算
  花的是别的部门的钱，管理员身份不构成对它的主张。

被团队配额拦下的作业**排队**而不是被拒：这是同事的作业跑完就会解除的共享状况，
拒了等于逼人手动重提一次平台自己就能等的事。

## 计费

平台一直精确地计量 GPU 卡时，并把它归到项目、人和团队上。计费在这之上**只多一个单价**。

它不重新计量任何东西：卡时来自和用量页同一份台账，归期规则也和 `cost_breakdown` 一致——
**按作业结束的时间段计入**。跨月的长作业整段算在它结束的那个月；仍在跑的作业按已跑部分
计入当期并单独标出。一份和用量页对不上的账单，比没有账单更糟：它是一场谁也说不清的争论，
而用量页是大家已经信的那个数。

在控制台 **管理 → 计费** 看账单，可按团队 / 项目 / 用户三个维度归集，导出 Excel。

### 价目表

单价按**卡型**配，带生效时间。规则两条：

* **某卡型的第一条价格覆盖全部历史**——此前没有任何账单存在，没有可被重写的已结账期。
  刚开启计费的部署因此可以给已经跑过的季度定价，那是所有人问的第一件事。
* **之后的价格不能往回改**，那会重写已经给人看过、并且已经被据以行动的账。调价 = 加一条
  生效时间更晚的新记录；价目只增不改。

产生了卡时但没有生效单价的卡型会**被点名**（`unpriced_series`），那部分按 0 计。
一个悄悄漏掉某个卡型的确定数字，比一个说清自己漏了什么的数字糟得多。

金额全程用**整数微单位**（百万分之一货币单位）计算，只在渲染时取整——浮点不是用来加钱的，
每次运行漂一分钱，最后就是一个谁也对不上的总额。

### 月度预算

团队可以设一个**按自然月**的金额预算（服务时区的月界）。超了之后新作业**排队**而不是被拒——
月初自己就恢复了，而被告知"十一天后再提一次"的人不会真的去提。

预算读的是**已花**，不是预测：没人知道一个还没跑的作业会花多少。所以越线的那个作业总是被
允许跑完——超出的部分是它跑出来的，不是它被放行时就注定的。与日卡时预算是同一个形状，
只是上了一层、并且以钱计。

## 借用与回收（抢占）

静态配额会把集群切碎，然后让它闲置——这是所有共享集群最后都会有的抱怨，也是
Kueue（`reclaimWithinCohort`）、Volcano（`deserved` + reclaim）、YARN（guaranteed vs
maximum capacity）、Slurm（QOS 抢占）不约而同长出**借用**的原因。

开启 `FORGE_SCHED_PREEMPTION` 后，团队的并发 GPU 配额从「上限」变成「**保底**」：

* 集群有空时，团队可以超出保底额度运行（容量本身仍由 `capacity_gate` 按物理卡数兜住）
* 当**低于自己保底额度**的团队在排队且被容量拦下时，超出部分被回收

**借用和回收是同一个开关。** 只借不收，会让一个团队无限期占住集群、而别的团队在自己的
配额之内干等——那比它取代的硬上限更糟。

### 什么可以被回收，尽可能窄地说

五个条件全部成立才算候选，每一条都对应一种「没有它就会让人有理由生气」的情况：

| 条件          | 没有它会怎样                                       |
| ----------- | -------------------------------------------- |
| 索取方低于自己的保底  | 这就不是回收，是一个团队把另一个顶掉——没有团队能相信给它的那个数字           |
| 受害方高于自己的保底  | 从保底额度内的团队手里拿，就破坏了配额唯一的承诺                     |
| 不是同一个团队     | 团队抢占自己，等于队列重排一次、代价是一次 checkpoint             |
| 已运行超过最小时长   | 没有下限，繁忙队列会回收一个、放行一个、再回收一个，集群一天都在写 checkpoint |
| 持有的是短缺的那个卡型 | 回收一个 H200 作业，帮不了等 H100 的人                    |

取**最年轻的**，因为那是丢掉最少的活。**刻意不用优先级**：优先级排的是「接下来轮到谁」，
拿它来选受害者会让一个高优先级的长作业安全、而一分钟前刚起的高优先级作业被拿掉——
两个问题用了同一个数字。

### 回收做的事就是暂停

平台早就有这条路，而且语义正好：`PAUSED(auto_resume)` 是**系统侧暂停**（维护排空和
时段执法用的就是它），计量在暂停那一刻就停止计费，工作目录保留，队列 worker 会自动从最近
checkpoint 续跑。**被回收的人不需要做任何事**，也会收到站内通知说明原因。

代价是诚实且有界的：最近一次 checkpoint 之后的进度。NeMo-RL 默认每 `save_period` 步落盘，
所以是分钟级。

## 切分 GPU（MIG / 时分）

**只需要在硬件注册表里加一行。** NVIDIA GPU Operator 会把 MIG 切片作为独立的扩展资源上报
（`nvidia.com/mig-3g.40gb`），时分切片报成 `nvidia.com/gpu.shared`。在**管理 → 平台设置 →
硬件**里把它注册成一个卡型，并填上 `k8s_resource`，它就自动获得独立的容量、配额、计量和
时段窗口——因为这四样本来就是按卡型 id 组织的。

**调度器一行都不用改。** 切卡是硬件注册表的一条记录，不是一个调度特性。

仅 kuberay 后端读取这个字段：`local` 和 `node` 后端的分配器按索引发整卡，不认识切片，
在那里声明一个切片等于宣告一份放不下去的容量。

## 审批：一条升级路径，不是流程引擎

团队在 3 号就花光当月预算，作业会排 28 天。排队是对的**默认**——条件会自己消失，拒了等于
让人三周后再提一次——但总得有办法说"这个真的等不了"，并且留下是谁说的。

**它不接在准入里。** 显而易见的做法是给 `_SUBMIT_DISPOSITION` 加第三个动词：超预算就转审批。
那是错的，而且理由可以推广：团队配额往往几分钟内就随同事作业结束而解除，把每一次这样的拒绝
都送给人，只会得到一队还没被读就自己有答案的请求，以及一个学会无视这个列表的审批人。

所以升级是**主动发起**的：作业照常排队，提交人在确实等不了时去作业详情页申请一次例外。

**一次批准只放行一个作业。** 不是这个团队，不是这个月，也不是"直到有人想起来撤销"。
一个活得比它的理由更久的授权，就是预算不再是预算的开始；而一个范围很窄的授权，才是审批人
在他会花的那十秒里真的能想清楚的东西。

审批人是**拥有那笔预算的团队的负责人**（管理员兜底——总得有人能答，当团队没有负责人、
或唯一的负责人正是申请人时）。**没有人能批准自己的申请**；驳回自己的可以，撤回不需要第二个人。

配额变更申请一律由管理员批：团队负责人批准自己的加额，那不叫额度。

### 刻意不做的

一级审批，加一个有效期。没有会签、加签、转办、代理。那些属于组织已经在用的 OA 系统——
在这里重造一遍只会得到一个更差的副本，而且没有哪个合规部门会认。平台欠那套系统的是
**一条记录和一个回调**，不是一套流程引擎。

没人回应的请求会在有效期后自动过期。一个只增不减的待办列表没有人会读，而真正要紧的那条
就埋在里面。

## 从单点登录同步团队

配置 `FORGE_OIDC_GROUP_CLAIM` 后，每次登录用目录里的组覆盖此人的团队成员关系——
**加也删**：在 IdP 里离开了某个部门，这里也就离开了。留空（默认）表示平台完全不碰
成员关系，手工维护团队的部署必须留空，否则下次登录会把所有人清出去。

目录里有、平台上没有的组会被跳过，不会自动建团队：一个 IdP 组不构成「创建一个预算
承担方」的授权。`FORGE_OIDC_GROUP_PREFIX` 用来去掉目录下发的路径前缀。

## 默认是软的

`FORGE_QUOTA_ENFORCE=false` 只记录用量，不拒绝任何东西。
在你还在摸索这些数字该设多少时它很有用，摸索完还开着就很危险。

即使打开了执法，裸金属 Ray 也没有 cgroup 或 GPU 硬隔离，所以准入是作业启动**之前**的一道闸。
[运行时看门狗](/zh-Hans/ops/observability)是第二层：它把集群实际用量与台账对账，
并且可以停掉能可靠归因的超额作业。硬配额需要 Kubernetes 或 Kueue 一类的基础设施。

## 读懂一次拒绝

| 消息点名的       | 含义                            |
| ----------- | ----------------------------- |
| 并发 GPU      | 这个用户到上限了。不是集群的问题              |
| 存储配额        | run 目录超预算。对已导出的实验跑 `sf clean` |
| 每日 GPU-时    | 今天的预算花完了，服务时区的午夜重置            |
| profile 不允许 | `allowed_profiles` 里没有他申请的那个  |
| 集群容量        | 真的没有空闲卡了。这一种是排队，不是拒绝          |

最后一行是重要的区分：**在队列里排队不是配额失败**。排队的作业是已经被准入的。

## 审计

创建用户、设置角色、启用或禁用账号、设置配额，全都会连同操作的管理员一起记进
[审计日志](/zh-Hans/console/audit)。每一次因配额被拒的提交也会记——
这就是为什么错误信息滚走之后，「我的作业为什么没跑」仍然回答得出来。
