fix: 收敛 WebProbe 物理内存与资源上限

This commit is contained in:
Codex
2026-07-13 08:42:44 +02:00
parent 0c22a05cee
commit 2e38d690e7
31 changed files with 3882 additions and 1772 deletions
+100 -37
View File
@@ -1,16 +1,35 @@
# Disk GC And Retention Reference
# 磁盘 GC 与留存参考
UniDesk 的磁盘治理入口是 `bun scripts/cli.ts gc ...`。该入口用于短期一次性止血和低风险防膨胀策略,所有清理动作都必须先有结构化 plan,再通过显式确认执行。GC 不是通用 `rm -rf` 或原生命令集合;当目标磁盘水位无法在保护边界内下降到阈值以下时,应停止并升级为 retention/capacity 决策,而不是扩大清理范围。
- 治理入口:
- UniDesk 磁盘治理统一使用 `bun scripts/cli.ts gc ...`
- 所有清理动作必须先生成结构化 plan,再通过显式确认执行;
- GC 不是通用 `rm -rf` 或原生命令集合;
- 保护边界内无法达到目标时,应升级为留存或容量决策,不得扩大清理范围。
所有主 server GC 可调策略都由 `config/unidesk-cli.yaml#gc` 拥有,包括默认 include 开关、保留窗口、输出 limit、Codex session root、worktree main/root/baseRef、worktree 扫描预算、`.state` allowlist roots 和是否在 plan 阶段估算 worktree size。CLI 参数只作为一次性显式覆盖;代码只校验 YAML 字段存在、类型正确和路径可渲染,不把这些策略值写成隐藏默认。
- 配置真相:
- 主 server GC 可调策略由 `config/unidesk-cli.yaml#gc` 唯一拥有;
- YAML 包含默认 include 开关、保留窗口、输出 limit、session/worktree/state root 和扫描预算;
- CLI 参数只作为一次性显式覆盖;
- 代码只校验和渲染,不得写入隐藏策略默认值。
## Command Boundary
## 命令边界
- `gc plan`:只读生成主 server 清理候选、估算收益、风险等级、保护对象和数据库诊断摘要。
- `gc run --confirm`:只执行当前 plan 可见候选页,默认不执行分页隐藏候选;用 `--limit``--result-limit``--full|--raw` 控制披露和执行范围。
- `gc policy plan|install`:从 `config/unidesk-cli.yaml#gc.policyTimer` 渲染或安装低风险长期策略,例如 journald cap、每日 allowlisted 文件/tmp 清理 timer、VPN 诊断 pcap retention、`.state` artifact retention 和 VS Code CachedExtensionVSIXs 下载缓存 retention。
- `gc db-trace plan|run --confirm --before-date YYYY-MM-DD --vacuum-full`:显式 trace 遥测留存入口;涉及数据库重写时按维护窗口处理。
- `gc remote <providerId> plan|run --confirm|status --job-id <id>`通过 UniDesk SSH 透传在 provider host 上执行受控 GC。远端长任务必须使用异步 job 和 `status` 短查询,不应让单次 SSH 等待完整 registry GC 或其他长清理。
- `gc remote <providerId> plan|run --confirm|status --job-id <id>`
- 通过 UniDesk SSH 透传执行受控远端 GC
- 长任务必须使用异步 job 和 `status` 短查询;
- 不得让单次 SSH 等待完整 registry GC 或其他长清理。
- `gc remote <providerId> plan --memory-pressure-only` / `gc remote <providerId> run --confirm --memory-pressure-only`
- 只规划或执行 WebProbe observer/Chrome 内存压力候选;
- 不得混入通用磁盘 GC 候选;
- 执行入口必须显式携带 `--confirm`
- `gc remote <providerId> memory`
- 只读读取 `/proc/meminfo``MemAvailable`
- 不执行 cluster preflight、候选扫描或 mutation
- 固定披露 `metric=MemAvailable``source=/proc/meminfo``swapExcluded=true` 和可用字节数。
- `--include-tool-caches`:本机和远端都必须是显式 opt-in,只清理固定 allowlist 的可重建 npm/npx/Bun 缓存;不得扩大成清理 `~/.npm``~/.bun``node_modules`、auth/config 或运行面依赖。
所有成功和失败输出都必须是 JSON。`plan` 必须标记 `dryRun=true``mutation=false``run` 必须要求 `--confirm` 并报告 `diskBefore``diskAfter``summary``results``protected`。远端 GC 可用 `--target-use-percent N` 显式表达目标根盘水位;`summary.target` 必须给出目标所需释放量、候选估算、预计水位、缺口和 `safeStop` 决策,避免靠人工心算判断是否应该继续扩大清理范围。
@@ -40,7 +59,7 @@ Worktree 清理由 `--include-merged-worktrees` 显式启用,只扫描 `config
主 server VS Code 下载缓存默认不清理。`/root/.vscode-server/data/CachedExtensionVSIXs` 只用于 VS Code extension VSIX 下载缓存,可通过显式 `--include-vscode-cached-vsix` 进入候选;执行时只允许删除该目录下符合 extension-version 命名的顶层普通文件,并按 `--vscode-cached-vsix-keep-days` 保留近期缓存。执行前必须重新校验路径、文件名、非 symlink/regular file,并用 active-file 检查确认没有进程仍打开该文件。该入口不得触碰 `/root/.vscode-server/extensions``/root/.vscode-server/cli/servers`、VS Code user data 或任意 session/auth state。
## Protected Data
## 受保护数据
默认 GC 不得删除或 prune 以下对象:
@@ -66,7 +85,7 @@ Worktree 清理由 `--include-merged-worktrees` 显式启用,只扫描 `config
`gc policy install` 的每日 timer 按 `config/unidesk-cli.yaml#gc.policyTimer` 启用 `.state` artifact retention、VPN pcap retention 和 VS Code CachedExtensionVSIXs retention,用来限制历史诊断/部署产物、tcpdump ring 文件与 VS Code 下载缓存长期增长;手动 `gc plan/run` 仍默认不清 `.state` 或 VSIX 缓存,必须显式 `--include-state-artifacts` / `--include-vscode-cached-vsix` 才会列出或执行这些候选。policy timer 仍保护上表对象,并把输出限制在 `.state/gc/last-run.json``.state/gc/last-run.stderr`
## Remote G14 Policy
## G14 远端策略
`gc remote G14 ...` 必须先确认目标是 G14 原生 k3s 节点,且 preflight 中节点名包含 `ubuntu-rog-zephyrus-g14-ga401iv-ga401iv`。G14 默认候选只允许:
@@ -79,13 +98,13 @@ Worktree 清理由 `--include-merged-worktrees` 显式启用,只扫描 `config
受限 core dump 只匹配 `/root/unidesk/core.<pid>` 普通文件。执行前必须重新校验路径 allowlist、Git 未跟踪、非 symlink、无 `fuser` 活跃引用。估算收益必须按实际分配块数计算,并可另行披露 `apparentSizeBytes`;不能把 sparse core dump 的表观大小当成可回收磁盘空间。
## Remote PK01 Policy
## PK01 远端策略
PK01 是腾讯云 Docker provider,不是 G14 k3s/registry 节点;长期运维边界见 `docs/reference/pk01.md``gc remote PK01 ...` 可用于通用低风险候选(allowlisted `/tmp`、Docker json-file 日志、BuildKit cache、apt cache、受限 core dump 和 journald 计划),但 pikanode 的主要增长源由 PK01 节点本地 retention 机制管理,而不是 G14 registry/PVC retention。
PK01 pikanode temp retention 只允许清理 `/home/ubuntu/pikanode/html/temp` 下超过保留窗口的直接子目录,并必须保护 `html/download/``html/upload/``files/`、证书、Git state、直接日志文件和近期 temp workspace。该策略已固化为 PK01 节点本地 systemd timer 与 logrotate;人工排障时优先查看 `systemctl status unidesk-pk01-pikanode-temp-gc.timer``/var/log/unidesk-pk01/pikanode-temp-gc.log`。如果 PK01 高水位仍无法通过 temp retention 和通用低风险 GC 降下来,必须停止并进入 pikanode 下载产物留存、Docker image retention 或容量扩容决策,不能把 `download/``files/` 或 Docker overlay 当作普通临时目录删除。
## Remote JD01 Policy
## JD01 远端策略
JD01 是 YAML-first k3s provider,承载 AgentRun、HWLAB v0.3、Web probe sentinel 和相关 PVC/state artifact。`gc remote JD01 ...` 的目标节点、lane、namespace 集合、state root、保护路径、扫描预算、artifact cap、retention 窗口、observer stop 策略和输出 limit 必须来自 `config/unidesk-cli.yaml#gc.remote.targets.JD01` 或等价 source of truth;CLI 参数只作为一次性覆盖,不得把 JD01 namespace、路径或阈值写成脚本隐藏默认。
@@ -107,31 +126,75 @@ JD01 Web observe artifact 是一等 GC 对象。state root 必须来自 YAML
### Web observe 与 Chrome 逃逸进程
- 启动资格真相源:
- 唯一配置位于 `config/hwlab-node-lanes.yaml#templates.hwlabV03.webProbeWorkbench.resourcePolicy.memoryStartGuard`
- 当前阈值合同为 `4 GiB`,即 `4294967296` 字节;
- 代码只负责读取、比较和结构化披露,不得另设隐藏阈值。
- 唯一内存指标:
- 只使用 `/proc/meminfo``MemAvailable`
- 输出必须披露 `metric=MemAvailable``source=/proc/meminfo``swapExcluded=true`
- `SwapFree``MemFree + swap`、cgroup 虚拟空间或其他 virtual memory 指标不得合并进可用量;
- swap 只能作为独立压力信号,不能改变启动资格。
- 人工 `web-probe observe start`
- `MemAvailable <= 4 GiB` 时返回结构化 blocked
- 不创建 observer,也不启动 Chromium
- blocked 后只能按以下 scoped 流程人工处置。
```bash
bun scripts/cli.ts gc remote <node> plan --memory-pressure-only
bun scripts/cli.ts gc remote <node> run --confirm --memory-pressure-only
bun scripts/cli.ts gc remote <node> status --job-id <id>
bun scripts/cli.ts gc remote <node> memory
```
- 人工处置顺序:
- 先复核 `plan --memory-pressure-only` 的候选、保护对象和预计回收量;
- 只执行参数完全匹配的 `run --confirm --memory-pressure-only`
- `run` 返回 job id 后,重复执行 `status --job-id <id>` 直到明确终态;
- job 未终态时,禁止提前执行 `memory` 或重试 WebProbe
- job 终态后执行 `memory`,以新的 `/proc/meminfo` 快照重新判定;
- `MemAvailable` 仍不大于 `4 GiB` 时,下一次人工 start 继续 blocked
- 不得用未带 `--memory-pressure-only` 的通用 plan/run 代替本流程。
- sentinel cadence
- 每轮在 provider、凭据和 observer 启动前读取 `MemAvailable`
- `MemAvailable < 4 GiB` 时返回 `skipped-wait-next-round`
- 不创建 observer、不启动 Chromium、不自动执行 GC
- 等待下一轮 cadence 重新判定;
- 恰好等于 `4 GiB` 时,按 sentinel 的 `<` 比较器允许继续。
- 运行时上限:
- TTL、sample、screenshot、PerformanceObserver buffer、browser process history、freeze signal history 和 response body 上限均来自 owning YAML
- terminal、error 和 signal 路径必须显式关闭 observer page、control page、browser context 和 browser
- cap 只能收敛 raw 证据增长,不得删除 report、manifest、heartbeat 或必要摘要。
- 正常生命周期:
- Chrome 内存治理优先管理 observer runner 生命周期;
- Web probe sentinel 和 quick-verify 启动 observer 后,全部终态都必须执行受控 stop
- 全部终态包括成功、blocked、失败、timeout 和异常;
- sentinel 和 quick-verify 的成功、blocked、失败、timeout、异常终态都必须执行受控 stop
- stop 流程由 YAML 控制,并使用 `web-probe observe stop`/force stop
- stop 后必须验证对应 runner 与 Chrome process tree 已退出;
- observe runner 必须从 scenario/YAML 获得最大运行时长或 max samples 兜底,即使调用方退出也会停止采样并关闭 browser
- browser freeze policy 只能作为异常保护,不能替代正常终态 stop。
- 逃逸判定:
- 长时间存活的 Playwright `cliDaemon`/Chrome 进程树是高度可疑对象
- 存活窗口必须来自 owning YAML,不能写成脚本隐藏默认
- 必须先证明该进程树不属于 active observer 或其他在途受控任务
- 归属证据包括受控 observe/sentinel 状态、manifest、heartbeat、PID/session 记录和完整 process tree
- PID、PPID、session/process group 和后代进程不能闭合为唯一进程树,或任一归属证据存在歧义时,继续标记 protected
- 只有证据闭合的无归属进程树才能分类为 `suspected-orphan-browser`,并直接进入精确终止流程。
- 精确终止:
- 先结构化记录 session、root PID、后代 PID、命令摘要、存活时间、归属判定和终止前的 `MemAvailable`/swap
- 只向已核实的 session/process tree 发送 TERM,并按 YAML 定义的有界等待时间复核;
- TERM 后仍存活时,重新确认残留 PID 仍属于同一棵树,再仅向这些残留进程发送 KILL
- 禁止使用 `pkill -f chrome``killall chrome` 或任何按 `chrome`/`chromium` 名称批量终止的入口。
- stop 后必须验证 runner 与 Chrome process tree 已退出;
- runner 必须从 scenario/YAML 获得最大运行时长或 max samples 兜底;
- browser freeze policy 只能用于异常保护,不能替代正常终态 stop。
- 独立无归属 Chrome 判定:
- 存活窗口必须来自 owning YAML
- 必须证明进程树不属于 active observer 或其他在途受控任务
- 归属证据包括 observe/sentinel 状态、manifest、heartbeat、PID 记录和完整 process tree
- 候选快照必须记录 root 与后代的 `PID``PPID``SID``/proc/<pid>/stat` `startTicks`
- `startTicks` 用于排除 PID 复用
- 任一读取失败、扫描截断、身份不闭合或归属歧义都必须保持 protected;
- stale observer 进程树标记为 `stale-web-observer-process-tree`
- 证据闭合的独立无归属浏览器树标记为 `browser-orphan-process-tree`
- 精确 TERM→KILL
- TERM 前重新核对候选的 `PID/PPID/SID/startTicks`
- 只向身份完全匹配的候选发送 TERM
- 按 YAML 有界等待后重新枚举原候选;
- TERM 后残留复核只使用不可变的 `PID/startTicks`
- 重挂载场景同时披露原始与当前 `PPID/SID`,不得把可变化的归属字段继续当身份匹配条件;
- 只对复核后仍属于原身份的残留进程发送 KILL;
- 成功条件是原始 `PID/startTicks` 身份全部消失,而不是只看进程名或原 PPID;
- 禁止使用 `pkill -f chrome``killall chrome` 或任何按名称批量终止入口。
- 收口验证:
- 目标 root PID、全部后代 PID 和 session/process group 必须为空
- 受控 observe/sentinel 状态必须确认 active observer 未受影响;
- 必须重新采集 `MemAvailable`、swap、Chrome/observer process summary 和 web-observe 状态;
-果必须结构化记录分类、归属证据、TERM/KILL 动作、残留 PID、终止前后资源数据和复核结论
- root 与后代的 `PID/startTicks` 身份必须全部消失
- active observer 必须未受影响;
- 重新采集 `/proc/meminfo``MemAvailable`独立 swap 信号、Chrome/observer 摘要和 web-observe 状态;
- 结构化记录归属证据、TERM/KILL 动作、PPID 变化、残留身份和终止前后资源数据。
JD01 plan 和 status 应同时披露内存压力摘要:active observer 数、Chrome process 数、Chrome RSS、stale observer 数、state root artifact bytes、last cleanup、last stop failure 和 drill-down 命令。GC run 只能执行 plan 中明确标记 safe 的低风险动作,例如 apt/journal/tmp allowlist、dead web-observe raw artifact retention,以及通过受控 observe stop 处理 stale observer;对 PVC、k3s runtime、containerd、Docker volume、Secret 和 auth/config 状态一律保持 protected 或转交专用 retention 入口。
@@ -139,7 +202,7 @@ JD01 的 npm/npx/Bun 缓存可作为中低风险边界候选,但必须通过 `
Web probe sentinel 的 `quick-verify`、Playwright 或浏览器验证不得作为 CI/CD 发布 gate 自动周期触发。若某个 sentinel 需要周期 quick-verify,必须在 YAML 中显式 opt-in cadence scheduler;默认发布验证只看配置的 health endpointquick-verify 只作为人工或独立 post-deploy evidence 入口。这样避免 CI/CD 周期性重启 observer/Chrome 并重新制造内存和 artifact 增长。
## HWLAB Registry Retention
## HWLAB 镜像仓库留存
G14 HWLAB registry 清理必须显式使用 `--include-hwlab-registry`,默认 `gc remote G14 plan` 不进入 registry。策略必须保守,不能只留 latest,也不能只删除 tag link 后误判已经释放空间。
@@ -169,7 +232,7 @@ Registry 执行必须以远端异步 job 完成,并具备以下维护保护:
- 状态查询使用 `gc remote G14 status --job-id <id>`,不使用长 SSH 会话等待。
- `gc remote` 的 stdout 是有界 JSON:当 `--full` 或大量候选导致结果过大时,完整结果会写入远端 `/tmp/unidesk-gc-remote/jobs/<job>.json`stdout 只返回摘要、`jobId``statePath``statusCommand`,再用 `gc remote G14 status --job-id <job>` 渐进查询,避免输出爆炸被误判为 JSON 失败。
## G14 CI Workspace Retention
## G14 CI 工作区留存
G14 的 Tekton workspace retention 不能通过原生 `kubectl delete`、直接删除 `/var/lib/rancher/k3s/storage` 或手工清 PV host path 完成。所有完成态 PipelineRun workspace 清理都必须走对应高层 UniDesk CLI,先 dry-run 输出候选和保护对象,再 confirm 执行。
@@ -201,7 +264,7 @@ Retention 入口的长期合同:
CI workspace retention 是 registry retention 之前的低风险步骤。若它清不出足够空间,不能扩大到 raw PV/path 删除;应继续进入 registry retention plan,或进入 safe-stop 决策。
## Safe Stop Line
## 安全停止线
磁盘目标可以设为 `<70%` 或维护窗口临时目标如 `<50%`,但安全边界优先级高于目标百分比。目标必须直接传给 CLI,使输出带有可机读缺口:
@@ -231,7 +294,7 @@ G14 进入 50% 这类维护窗口目标时,标准顺序是:
维护窗口目标接近整数边界时,验收必须同时报告 `df -h /``df -B1 /``summary.target` 使用字节计算,`df` 使用整数展示,二者在 49/50 这类边界可能看起来不一致;收口时以最终 `diskAfter.usePercent``df -h` 展示和字节视图共同说明,不得因为四舍五入差异绕过 protected boundary。
## G14 Space Attribution Baseline
## G14 空间归因基线
G14 当前只有一个本机 k3s cluster;空间归因时不要把 `hwlab-dev``hwlab-prod``hwlab-v02` 理解成独立 cluster,它们是同一 k3s 上的 namespace/runtime slice。长期诊断应按三层归因:
@@ -274,7 +337,7 @@ Registry 的长期判读口径:
Registry 报告必须区分 `uniqueBlobBytes``sharedBlobBytes`。多个 repo/tag 复用同一 layer 时,expanded tag size 会重复计算;判断可回收空间应以 official registry GC 后的根目录变化为准。
## Diagnosis Commands
## 诊断命令
G14 空间审计默认只读。需要报告时优先采集以下摘要,避免全量 dump 大 JSON:
@@ -299,7 +362,7 @@ rsyslog 文件日志不属于当前 `gc remote` 默认可变更对象。若 `/va
JD01 空间和 Chrome 压力审计同样默认只读。需要深挖时,优先通过 `gc remote JD01 snapshot|plan|status` 暴露有界摘要;必要的远端探测只用于补 CLI 证据,不作为长期手工流程。报告字段至少包括 root 水位、inode、水位目标缺口、YAML 配置引用、k8s PVC namespace 归因、web-observe run artifact topN、observer/Chrome process tree 摘要、protected sizeState 和 safe/blocked 分类。Web observe run 的 stale 判定必须说明 manifest/heartbeat/pid/open-fd 依据;不能把目录 mtime 当作唯一证据。
## Validation Checklist
## 验证清单
G14 GC 后必须验证:
@@ -333,7 +396,7 @@ bun scripts/cli.ts web-probe sentinel control-plane status --node JD01 --lane v0
若本次变更涉及关闭 cadence scheduler,还应确认 GitOps objects 不再包含 quick-verify CronJob,并确认 runtime/Argo/health endpoint 仍为 ready。
## Long-Term Anti-Bloat Plan
## 长期防膨胀计划
| 措施 | 默认策略 | 预期收益 |
|---|---|---|