系统设置
HeteroFlow 是本站的异构算力与大模型调度产品。系统设置。系统设置 系统设置页( )是 HeteroFlow 平台管理员的运维控制台:负载调度阈值、任务默认内存、告警外发渠道三类全局配置在此集中管理,配套一个随时可跑的系统自检面板把"平台现在健康吗"变成六项可核对的检查结果。页面上每一项配置都遵循同一条纪律—— 改完即生效、落盘可持久、…
系统设置
系统设置页(/settings)是 HeteroFlow 平台管理员的运维控制台:负载调度阈值、任务默认内存、告警外发渠道三类全局配置在此集中管理,配套一个随时可跑的系统自检面板把"平台现在健康吗"变成六项可核对的检查结果。页面上每一项配置都遵循同一条纪律——改完即生效、落盘可持久、部分更新不误伤未提及的键:负载阈值保存后立刻同步进运行中的调度器并写回 config.yaml,任务默认内存与告警渠道存入数据库配置表,告警渠道更是保存后下一次告警推送就走新配置,全程不需要重启服务。
整个页面挂在前端 AdminRoute 守卫之后(非平台 admin 角色访问即重定向回首页),后端每条读写接口再各自校验权限,形成页面级与接口级的双层收口。本页讲的是平台侧全局设置;节点环境与镜像检测请见《环境检测》篇。
功能总览
| 能力 | 一句话说明 | 状态 |
|---|---|---|
| 负载调度阈值 | GPU 显存/算力/温度、CPU、内存五项准入阈值,超阈值的节点不再接收新任务 | 已实现 |
| 阈值即时生效与持久化 | 保存即同步运行中的调度器并落盘 config.yaml,重启不丢 | 已实现 |
| 任务默认内存 | 推理/训练/Docker 三类任务创建未填内存时的全局兜底值,按类型独立配置 | 已实现 |
| 告警外发渠道 | 企业微信/钉钉/飞书群机器人 webhook 配置,推送级别两档可选 | 已实现 |
| 凭据脱敏回显 | webhook 与密钥从不明文回显,留空保存即保持原值 | 已实现 |
| 渠道测试发送 | 每渠道一键真实发送测试告警,配置对不对当场验证 | 已实现 |
| 告警配置热更新 | 渠道配置保存后下一次告警推送即走新配置,无需重启 | 已实现 |
| 系统自检 | 后端服务/插件/节点/GPU/分片/任务队列六项检查 + 总体状态聚合 | 已实现 |
| 一键修复 | 插件系统重初始化、残留分片清理、僵死任务裁决三项修复动作,二次确认后执行 | 已实现 |
| 权限双层收口 | 前端 AdminRoute 页面守卫 + 后端逐接口读写权限校验 | 已实现 |
负载调度阈值:逐项说明
这是页面的第一个区块,由五个滑块组成。当节点资源使用率超过阈值时,调度器不再向该节点分配新任务——这是集群过载保护的第一道闸门,判定发生在每次调度绑定前。
| 配置项 | 配置键 | 默认值 | 滑块范围 | 作用范围 |
|---|---|---|---|---|
| GPU 显存使用率 | gpu_memory_percent | 90% | 50–99% | 节点单卡真实显存用量达到该百分比后,该卡不再接收新任务 |
| GPU 计算利用率 | gpu_utilization_percent | 90% | 50–99% | 单卡算力利用率超阈值后不再接收新任务 |
| GPU 温度阈值 | gpu_temperature_threshold | 85°C | 40–95°C | 单卡温度超阈值后不再接收新任务;节点不支持温度采集(上报温度为 0)时自动跳过该项检查 |
| CPU 利用率 | cpu_percent | 85% | 50–99% | 节点级 CPU 使用率超阈值后不再接收新任务 |
| 系统内存使用率 | memory_percent | 85% | 50–99% | 节点级内存使用率超阈值后不再接收新任务 |
几个关键行为细节:
- 判定是逐卡的三合一检查:GPU 三项阈值在调度器里是一个整体判定(
core/pkg/scheduler/scheduler_core.go的nodeHasAvailableGPU)——一张卡必须显存、算力、温度三项同时达标才算可用,任何一项越界这张卡就出局;三项阈值任一大于 0 该检查才启用。CPU 与内存则是节点级判定,同样以大于 0 为启用条件。 - 保存语义是严格的部分更新:前端保存时刻意只提交
thresholds一个对象,不带页面加载时顺带读到的 GPU 保留配置——依赖后端"请求里没出现的字段保持原值"的指针字段语义。这样即使另一个管理员刚在别处改过别的配置项,这次保存也不会用过期快照把它悄悄回滚。该语义由 API 层性质测试钉住(未传字段不许被改、改动必须真的落盘)。 - 0 = 关闭检查是接口级语义:直接调 API 可以把某项阈值显式设为 0(关闭该项检查)、百分比上限 100、温度上限 120°C;前端滑块不提供这个入口,界面操作范围内始终为启用状态。
- 改完的去向:保存 → 后端先改运行中调度器的内存态(下一轮调度立即生效)→ 再写回 config.yaml(重启后由启动流程回填,配置不丢)。落盘失败会如实返回 500 并注明"内存已生效,重启会丢",绝不假装成功。
- 与推理显存预算是两回事:这里的 GPU 显存阈值是调度准入判断(卡快满了就别再塞),与推理引擎的 KV Cache 显存预算(
gpu_memory_utilization)语义无关、方向相反,2026-08-29 起已拆分为两个独立字段,互不牵连。
页面顶部的"刷新"按钮重新拉取当前生效配置;保存成功后出现"配置已保存"提示,3 秒自动消失。
任务默认内存:创建任务的兜底值
第二个区块回答一个问题:创建任务时没填内存(memory_mb)怎么办? 答案是按任务类型读这里配的全局默认值。三个键互相独立、逐项可配:
| 配置项 | 配置键 | 未配置时回落 | 说明 |
|---|---|---|---|
| 推理服务默认内存 | default_memory_mb_inference | 512 MB | 创建推理服务未指定内存时使用。推理创建表单本身没有内存输入项,实际全部走这个兜底值 |
| 训练任务默认内存 | default_memory_mb_training | 512 MB | 创建训练任务未指定内存时使用。训练表单同样没有内存输入项 |
| Docker 任务默认内存 | default_memory_mb_docker | 512 MB | 创建普通 Docker 任务未指定内存时使用。前端表单自带 4096 MB 预填值,此键主要覆盖直接调 API 不传 memory_mb 的场景 |
校验与生效规则:
- 下限 64 MB:前端在输入框就地拦截(空值、非整数、低于 64 MB 都无法点保存),后端 PUT 同样拒绝低于 64 MB 的值——过小的默认值会让默认创建的容器直接 OOM。即使有人绕过 API 手改数据库塞进非法值,读取侧也会忽略它回落 512 MB。
- 作用于创建时,不追溯存量:这三个键只在任务创建请求缺省
memory_mb时参与解析,已创建任务的内存值在创建瞬间已经落库定型,之后改这里不会影响它们。 - 任务类型判定吃权威结果:inference / training 之外的一切类型(含未标注的普通任务)一律走 docker 键,不另立一套分类。
- 部分更新语义与整区块保存:PUT 接口没携带的键不动(全指针字段);本区块的三个键都归这个表单所有,保存时整表提交,响应体返回当前生效值供界面回显。
告警外发渠道:把告警推进群里
第三个区块配置告警的多渠道外发,支持三家有官方 webhook 的群机器人。每渠道一组配置 + 一个真实的测试发送按钮;保存后即时生效——下一次告警推送就走新配置,无需重启服务。渠道列表:
| 渠道 | 加签密钥 | 获取方式(页面内附图文指引) |
|---|---|---|
| 企业微信群机器人 | 无需密钥 | 群设置 → 添加群机器人 → 复制 Webhook 地址 |
| 钉钉群机器人 | 需要(SEC 开头的加签密钥) | 群设置 → 智能群助手 → 自定义机器人,安全设置勾选"加签" |
| 飞书群机器人 | 可选(开启签名校验才需要) | 群设置 → 群机器人 → 自定义机器人,复制 Webhook 地址 |
逐字段说明:
| 字段 | 控件 | 行为 |
|---|---|---|
| 推送级别 | 单选(全部告警 / 仅 ERROR 及以上) | 全局过滤:选"仅 ERROR 及以上"时低级别告警不外发,只在平台内落库 |
| 启用开关 | 每渠道一个复选框 | 关闭后该渠道不外发,配置保留 |
| Webhook 地址 | 文本框 | 已配置时 placeholder 显示脱敏串(如 https://qyapi.weixin.qq.com/.../send?key=abcd****)并注明"留空保持不变";启用中的渠道保存时必须已配置且以 http(s) 开头,否则 400 拒绝 |
| 加签密钥 | 密码框(钉钉/飞书) | 同样脱敏回显、留空保持原值;要改就整段重新粘贴 |
| 发送测试 | 每渠道一个按钮 | 真实走网络发一条测试告警到目标群;输入框里有新值就用新值测(可测尚未保存的配置),留空则用已保存值。发送失败按"用户配置/网络问题"如实返回失败原因,不算服务器错误 |
| 保存渠道配置 | 按钮 | 提交后回读脱敏配置刷新界面,Toast 提示"即时生效,无需重启" |
凭据安全约定(与后端一致):GET 从不明文回显 webhook URL 与密钥——webhook URL 里的 key/access_token 查询参数本身就是凭据,明文回显等于泄密;后端只返回脱敏展示串(token 保留前 4 位便于识别)加"是否已配置"标志。前端因此拿不到原值,"留空 = 保持原值"是唯一安全的修改语义。此外页面还提供可展开的"如何获取 Webhook"分步指引,照着点就能配好。
渠道配置以单个 JSON 键存进数据库配置表,告警推送时按需热读取;后端为非 PostgreSQL 存储时该区块显示"未启用"提示(告警仅落库不外发)。告警的类型体系、生命周期与推送触发机制见《告警管理》篇,本篇只讲配置面。
系统自检:六项检查与一键修复
第四个区块是运维巡检面板,进入页面自动跑一轮,之后可随时点"重新检测"。
逐检查项与判据(后端 core/pkg/api/system.go):
| 检查项 | 正常 | 警告 | 异常 | 可修复 |
|---|---|---|---|---|
| 后端服务 | 能响应本接口即正常 | — | — | 否 |
| 插件系统 | 已加载 ≥1 个插件(文案报数量) | — | 没有加载任何插件 | 是 |
| 节点状态 | 有节点且至少一个在线 | 没有注册任何节点 | 所有节点离线 | 否(节点离线只能由 Agent 上线或人工注册解决,服务端没有对应修复动作,契约如实标不可修复) |
| GPU 资源 | "n/m 个 GPU 可用"(可用/总数) | 无 GPU;或存在判定丢失的卡(仍计入总数、不计可用,文案点明张数并指引到节点页) | — | 否 |
| GPU 分片 | 报当前已分配分片数 | — | — | 是(清理残留) |
| 任务队列 | 待处理任务 ≤10 | 积压超 10 个 pending(全平台口径,显式跨全租户统计,不受请求者所在租户收窄) | — | 是 |
总体状态按最差项聚合:任一项异常 → 总体异常;否则任一项警告 → 总体警告;全正常 → 正常。界面形态为顶部总体状态灯 + 逐项列表(状态点、名称、状态标签、判据文案)+ 底部检测时间戳。
一键修复(可修复项异常时出现"修复..."按钮):
| 修复项 | 实际动作 |
|---|---|
| 插件系统 | 以插件管理器现有配置打底、config.yaml 配置覆盖,重新初始化插件系统,并同步重建调度器实际生效的插件列表 |
| GPU 分片 | 释放并删除所有处于 releasing 状态的残留分片记录,同时对已释放分片下发 MIG 分区销毁指令,避免"账上删了、卡上还在" |
| 任务队列 | 将排队超过 1 小时仍未调度的待处理任务标记为失败(终态,不可逆) |
修复的交互刻意做了两道防护:点击"修复..."先展开内联确认面板,明示"将执行"的具体操作再二次确认;修复请求本身做了去取消化——批量清理中途断连不会只清一半就停,保证内存与数据库一致。修复完成后自动重跑一轮自检,让结果当场刷新。某项是否显示修复按钮由后端返回的 auto_fixable 契约字段决定,前端不做白名单绕过,契约与实际修复能力一一对应。
配置持久化:双轨与单一持久源原则
本页管理的配置按性质分两条持久化轨道,各自纪律明确:
| 轨道 | 持久层 | 覆盖配置 | 生效方式 |
|---|---|---|---|
| 运行时键值配置 | PostgreSQL system_config 表(key 主键 / value 文本 / 更新时间,UPSERT 写入) | 任务默认内存三键、告警渠道 JSON、推理全局配置等 | 保存即落库,消费方按需读取(告警渠道为每次推送热读取) |
| 调度行为配置 | config.yaml 的 scheduler 段 | 五项负载阈值、GPU 保留配置 | 保存时先改运行中调度器内存态(下一轮调度生效),再落盘;启动时从 yaml 回填进调度器 |
第二条轨道遵循单一持久源原则:同一字段绝不允许两个持久化来源并存。这不是纸面洁癖——GPU 温度阈值曾经就是双源形态:一个接口写数据库、另一个接口写 yaml,而启动流程只读 yaml,于是库里的值每次重启必被覆盖,界面上"保存成功"、重启后配置原地消失,且无从排查。2026-08-29 起收敛为只写 yaml 一个来源,保存新值时顺带清理历史遗留的库键(清理刻意安排在 yaml 落盘成功之后,落盘失败时旧值至少还留在一处)。
写入路径的并发安全也有交代:修改内存中的配置对象与整份序列化落盘之间用同一把锁互斥,避免并发保存与配置导出互相踩踏;落盘失败按"内存已生效、重启会丢"如实报 500,绝不提示假成功。
页面功能介绍
/settings 系统设置页(平台 admin 专属)
页面加载时并行拉取调度器配置、任务默认内存、告警渠道配置并自动跑一轮系统自检;四个区块各自独立保存、互不牵连。
| 区块 | 操作 / 控件 | 行为 | 背后 API |
|---|---|---|---|
| 页头 | 刷新按钮 | 重新拉取负载阈值当前生效值 | GET /api/v1/scheduler/config |
| 负载调度阈值 | 五个滑块(百分比 50–99 / 温度 40–95,步进 1,右侧实时显示当前值与单位) | 拖动即时预览;保存时只提交 thresholds 对象 | PUT /api/v1/scheduler/config |
| 负载调度阈值 | 保存配置按钮 | 同步调度器内存态 + 落盘 config.yaml;成功提示 3 秒自动消失 | 同上 |
| 任务默认内存 | 三个数字输入框(MB,最小 64) | 非法值就地标红并禁用保存按钮,不发请求 | GET / PUT /api/v1/system/task-defaults |
| 任务默认内存 | 保存配置按钮 | 整表提交三键,响应回显生效值 | 同上 |
| 告警外发渠道 | 推送级别单选、三渠道启用开关、Webhook/密钥输入框、获取指引折叠面板 | 留空字段保持原值;启用渠道必须有合法 webhook | GET / PUT /api/v1/alerts/notify-config |
| 告警外发渠道 | 发送测试按钮(每渠道) | 真实发送测试告警;新值优先、留空用已存值 | POST /api/v1/alerts/test-notify |
| 告警外发渠道 | 保存渠道配置按钮 | 保存后回读脱敏配置刷新,即时生效 | 同 PUT |
| 系统自检 | 重新检测按钮 | 重跑六项检查 | GET /api/v1/system/check |
| 系统自检 | 修复...→ 确认执行 / 取消 | 内联二次确认后触发修复,完成自动复检 | POST /api/v1/system/fix |
后端逻辑概念表
| 概念 | 定义 | 代码出处 |
|---|---|---|
| THRESHOLD_CONFIGS | 前端五项阈值的单一真相源:键、文案、默认值一处派生,改默认值即全局生效 | web/src/pages/SettingsPage.tsx:17 |
| 阈值部分更新语义 | PUT 请求全指针字段:nil = 本次不改,显式 0 = 关闭该项检查,杜绝"没传"与"传 0"混淆 | core/pkg/api/plugin.go:348 |
| nodeHasAvailableGPU | 调度器逐卡准入判定:显存、算力、温度三项同时达标才算可用;温度为 0 视为不支持采集跳过 | core/pkg/scheduler/scheduler_core.go:718 |
| NewScheduler 默认阈值 | 五项默认 90/90/85/85/85,调度器未注入时 GET 接口回落同款默认 | core/pkg/scheduler/types.go:256 |
| resolveDefaultMemoryMB | 创建请求缺省 memory_mb 时按任务类型读全局默认键,非法/未配置一律回落 512 | core/pkg/api/task_default_memory.go:60 |
| MinDefaultTaskMemoryMB | 默认内存下限 64 MB:PUT 校验拒收,读取侧对绕过 API 的非法库值同样忽略 | core/pkg/api/task_default_memory.go:41 |
| system_config 表 | 全局配置键值表(key 主键 / value TEXT / updated_at),任务默认内存、告警渠道等运行时配置的持久层 | core/pkg/storage/postgres/schema.go:766 |
| ConfigStore | system_config 的读写访问器,UPSERT 语义写入 | core/pkg/storage/postgres/config.go:18 |
| alert_notify_channels | 三渠道配置在 system_config 的单一 JSON 键,推送时热读取实现免重启生效 | pkg/plugin/plugins/group_bot_notifier.go:44 |
| maskToken / maskWebhookURL | 密钥与 webhook URL 中 token 参数的脱敏:保留前 4 位与 host+path,凭据绝不明文回显 | core/pkg/api/alert.go:1671 |
| mergeChannel | 渠道配置合并规则:留空的 webhook/密钥保持原值——脱敏回显下唯一安全的"不修改"信号 | core/pkg/api/alert.go:1765 |
| handleSystemCheck | 六项检查的判定与总体状态聚合;任务队列项显式全租户口径 | core/pkg/api/system.go:31 |
| systemGPUCheck | GPU 资源项判定:丢失卡(lost_at)单独计数、计入总数不计可用 | core/pkg/api/system.go:695 |
| handleSystemFix | 三项修复分支;detachedWriteContext 去取消化保证批量清理不半途而废 | core/pkg/api/system.go:154 |
| auto_fixable 契约 | 检查项可否修复由后端据实上报(节点状态曾谎报可修复已纠正),前端直接信任该字段 | core/pkg/api/system.go:27 |
| requireAdminAuth | 登录 + 权限校验之外强制平台管理员角色;system/check 与 system/fix 挂此门禁 | core/pkg/api/middleware.go:348 |
| AdminRoute | 前端页面守卫:未登录跳登录页,非平台 admin 重定向首页 | web/src/auth/PrivateRoute.tsx:27 |
API 参考
| 接口 | 说明 |
|---|---|
GET /api/v1/scheduler/config | 读调度器配置:五项负载阈值 + GPU 保留配置。挂 settings:read(普通用户也持有——分片页轮询共用此接口,刻意保持读开放) |
PUT /api/v1/scheduler/config | 部分更新:nil 字段不动、显式 0 关闭对应检查(百分比 0–100、温度 0–120);先同步运行中调度器再落盘 config.yaml。路由级 settings:read + 处理器内二次校验 settings:write(仅 admin 持有) |
GET /api/v1/system/task-defaults | 三类任务默认内存当前生效值(未配置键回落 512) |
PUT /api/v1/system/task-defaults | 部分更新三键;值域 ≥64 MB,未知字段 400;system:write |
GET /api/v1/alerts/notify-config | 告警渠道配置(脱敏视图 + 已配置标志 + 推送级别);非 PG 后端 503 |
PUT /api/v1/alerts/notify-config | 保存渠道配置:留空字段保持原值,启用渠道强制校验 webhook;保存后热生效;system:write |
POST /api/v1/alerts/test-notify | 渠道真实测试发送;channel 仅认 wecom/dingtalk/feishu;发送失败回 success:false 而非 5xx |
GET /api/v1/system/check | 系统自检:六项检查 + overall_status + 时间戳;requireAdminAuth(平台管理员专属) |
POST /api/v1/system/fix | 按检查项名修复(插件系统/GPU分片/任务队列);未知项名返回 fixed:false 并点名认识的键;requireAdminAuth |
已知边界
1. GPU 保留配置不在本页管理:GET /api/v1/scheduler/config 返回的 gpu_reserve_fixed_mb / gpu_reserve_percent 由分片页的配置表单负责编辑,本页保存时刻意只提交阈值对象,避免用过期快照覆盖他处刚改的保留值。
2. 推理全局配置另有入口:推理休眠超时、KV Cache 显存预算、单节点推理服务数上限等键走独立的 PUT /api/v1/system/inference-config(见《推理服务》篇),不在本页面。
3. 告警外发渠道依赖 PostgreSQL 后端:非 PG 部署下通知器不装配,本区块显示"未启用",告警仅平台内落库。
4. 推送级别非法值静默回落:min_level 传非 all/error 的值时不报错,回落"全部告警"。
5. 前端滑块不含"关闭检查"入口:阈值设 0(关闭该项过载检查)是 API 级语义,需要直接调 PUT 接口;界面滑块始终落在启用区间。
6. 任务队列积压阈值固定:自检的"pending > 10 判积压"与修复的"排队超 1 小时判僵死"是代码常量,不可配置。
7. "后端服务"检查项恒为正常:该检查的判据就是"能响应本接口",不做更深的依赖健康判定(数据库、连接池等另由服务器监控覆盖,见《服务器监控》篇)。
8. 任务队列修复不可逆:将排队超 1 小时的 pending 任务标记为 failed 是终态写入,确认面板已明示该操作会修改系统状态,执行前请确认积压原因(例如节点全部离线时应先恢复节点而非清理队列)。
9. Email / Slack 渠道未实现:多渠道外发当前只覆盖企业微信/钉钉/飞书三家群机器人(另有环境变量形态的 legacy 通用 webhook 兜底),其他渠道形态不在当前承诺内。
<!-- 状态标注说明: 已实现=代码+测试钉住并在开发环境长期运行;本篇各能力均无"真机 GPU 验证"范畴的独立验证记录,故统一标"已实现" -->