299 lines
13 KiB
Markdown
299 lines
13 KiB
Markdown
---
|
||
name: unidesk-gc
|
||
description: >-
|
||
UniDesk 磁盘 GC 与主机压力处置流程。用于 `bun scripts/cli.ts gc ...`、
|
||
根盘高水位、worktree/缓存/日志清理、Web observe/Chrome 内存压力和安全停止判定。
|
||
---
|
||
|
||
# UniDesk GC
|
||
|
||
- 处理 UniDesk 磁盘或内存压力时,优先使用受控 UniDesk CLI。
|
||
- 到达保护边界后停止,不得扩大为临时 `rm -rf`、Docker prune、数据库清理、原生 Kubernetes 删除或运行时状态删除。
|
||
|
||
- 长期规则以 `docs/reference/gc.md` 为准。
|
||
- 执行远端 GC、k3s/PVC 归因、Web observe/Chrome 压力处置、registry/CI 留存或安全停止判定前,必须先读该文档。
|
||
|
||
## 本机流程
|
||
|
||
先执行只读归因:
|
||
|
||
```bash
|
||
df -h /
|
||
df -BG /
|
||
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 50
|
||
```
|
||
|
||
默认计划存在缺口时,显式启用候选类别:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 2000 \
|
||
--include-tool-caches \
|
||
--include-stale-tmp \
|
||
--include-vscode-stale-servers \
|
||
--include-vscode-stale-extensions \
|
||
--include-vscode-cached-vsix \
|
||
--include-baidu-staging \
|
||
--include-state-artifacts \
|
||
--include-state-stale-scratch \
|
||
--include-codex-sessions \
|
||
--include-merged-worktrees \
|
||
--include-vpn-diagnostic-logs
|
||
```
|
||
|
||
复核计划后,只执行相同候选范围:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc run --confirm --target-use-percent 69 --limit 2000 \
|
||
--include-tool-caches \
|
||
--include-stale-tmp \
|
||
--include-vscode-stale-servers \
|
||
--include-vscode-stale-extensions \
|
||
--include-vscode-cached-vsix \
|
||
--include-baidu-staging \
|
||
--include-state-artifacts \
|
||
--include-state-stale-scratch \
|
||
--include-codex-sessions \
|
||
--include-merged-worktrees \
|
||
--include-vpn-diagnostic-logs
|
||
```
|
||
|
||
worktree 因 merge/cherry 超时而受保护时,只提高 worktree 临时预算:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 2000 \
|
||
--include-merged-worktrees \
|
||
--worktree-scan-budget-ms 120000 \
|
||
--worktree-cherry-check-timeout-ms 10000 \
|
||
--no-file-logs --no-docker-logs --no-journal --no-build-cache --no-tmp --no-db-summary
|
||
```
|
||
|
||
- 只对计划仍显示的候选执行匹配的 `run --confirm`。
|
||
- dirty、近期、活跃、未合并或超时保护的 worktree 必须继续受保护。
|
||
|
||
## 缓存与日志
|
||
|
||
单独检查 Docker 镜像候选:
|
||
|
||
```bash
|
||
bun scripts/cli.ts server cleanup plan --min-age-hours 24 --limit 80
|
||
```
|
||
|
||
- 返回零个过期镜像候选时,不得使用 `docker image prune` 或 `docker system prune`。
|
||
- 受保护镜像可能仍是当前运行或回滚真相。
|
||
|
||
默认 BuildKit 清理可能因缓存均较新而实际释放 `0B`。只有明确止压时才使用 `--build-cache-all`:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc plan --target-use-percent 69 --build-cache-all --limit 50 \
|
||
--no-file-logs --no-docker-logs --no-journal --no-tmp --no-db-summary
|
||
bun scripts/cli.ts gc run --confirm --target-use-percent 69 --build-cache-all --limit 50 \
|
||
--no-file-logs --no-docker-logs --no-journal --no-tmp --no-db-summary
|
||
```
|
||
|
||
Journald 上限也通过同一 CLI 管理:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc plan --target-use-percent 69 --journal-target-size 128M --limit 50 \
|
||
--no-file-logs --no-docker-logs --no-build-cache --no-tmp --no-db-summary
|
||
```
|
||
|
||
计划可接受时,使用参数完全匹配的 `run --confirm`。
|
||
|
||
## 临时转储
|
||
|
||
- `/tmp/unidesk-cli-output` 保存超大 JSON/stdout。
|
||
- 截断的 plan/run 会持续写入新转储,因此 GC 诊断时可能增长到 GiB 级。
|
||
- 提取必要证据并确认没有活跃写入者后,可以删除该目录:
|
||
|
||
```bash
|
||
fuser -v /tmp/unidesk-cli-output 2>&1 || true
|
||
rm -rf -- /tmp/unidesk-cli-output
|
||
```
|
||
|
||
重复出现时应补受控 CLI 留存策略,不得把手工删除变成常规入口。
|
||
|
||
- 其他 `/tmp` 目录先检查大小、mtime 和活跃 fd。
|
||
- 未明确归属和可重建性时,不得删除可能属于并行任务的当日 source/workspace scratch。
|
||
|
||
## 远端节点
|
||
|
||
- provider host 统一使用 `bun scripts/cli.ts gc remote <providerId> ...`。
|
||
- 远端长任务必须异步执行,并用 `status --job-id` 短查询,不得维持长 SSH 会话。
|
||
|
||
### 已合并 worktree
|
||
|
||
先读取 YAML 策略并只生成计划:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote <providerId> merged-worktrees plan
|
||
```
|
||
|
||
- 仅枚举 Git 登记且位于 `workspaceRoots` 的 worktree;
|
||
- dirty、活跃、近期、未被 `baseRef` 吸收、cherry 检查超时和主 worktree 必须显示为 protected;
|
||
- 手动入口与 policy timer 复用同一筛选器,批量上限分别读取 YAML 的手动和自动字段;
|
||
- 执行前逐项重新计划,只允许 `git worktree remove`,不得删除 branch 或使用 `rm -rf`。
|
||
|
||
复核候选后才允许执行:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote <providerId> merged-worktrees run --confirm
|
||
```
|
||
|
||
周期 stage 通过以下入口核对 YAML 字段、stage 和 `inSync`:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote <providerId> policy plan
|
||
bun scripts/cli.ts gc remote <providerId> policy status
|
||
```
|
||
|
||
### 内存分布与 Kubernetes 留存
|
||
|
||
先用紧凑 overview 归因真实内存压力:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote <providerId> memory-distribution
|
||
```
|
||
|
||
- 默认一次输出物理内存、PSS、进程 swap、cgroup、zombie 和 Kubernetes 对象分布;
|
||
- 同一结果必须直接披露 `processes.topSwap`,以及 zombie 父进程的 Kubernetes Pod 归属、
|
||
泄露分类和处置类别,避免额外执行 `ps`、cgroup 映射和 `kubectl` 拼接;
|
||
- swap 单独披露,不能计入 WebProbe 启动资格;
|
||
- 需要完整明细时才使用 `--full`。
|
||
|
||
发现大量 zombie 时不得尝试终止 zombie。先按 overview 的 owner 定位长期父进程,
|
||
修复子进程回收路径,再通过正常自动交付滚动 owner。运行面重启只能清除既有 zombie,
|
||
不能替代源码修复。
|
||
|
||
Kubernetes 历史对象手动回收固定使用:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote <providerId> retention plan --limit 500
|
||
bun scripts/cli.ts gc remote <providerId> retention run --confirm --limit 500
|
||
bun scripts/cli.ts gc remote <providerId> status --job-id <id>
|
||
```
|
||
|
||
- 筛选、保留窗口、分组、批量和级联策略只来自目标节点的 `kubernetesObjectRetention` YAML;
|
||
- 多 namespace 必须由共享 `defaults` 与显式 `policies` 表达:
|
||
- 一个 `retention plan` 聚合显示全部 policy;
|
||
- 每个 policy 使用唯一 `configId` 和不会随 commit 变化的稳定 owner 分组标签;
|
||
- CLI `--limit` 是每个 policy 的上限,并继续受对应 YAML 单批上限约束;
|
||
- 根级 `aggregatePreviewLimit` 控制聚合样例数,默认 overview 不得依赖第二次 `--full` 才能判断。
|
||
- 手动入口与 `policyTimer.includeKubernetesObjectRetention` 自动阶段必须复用同一筛选器;
|
||
- 活跃、过新、未知分组、时间不可解析、扫描截断和 Table 形状漂移全部保护;
|
||
- 只删除 YAML 指定的根对象,通过 ownerRef 级联,不直接删除下游 TaskRun、Pod、Secret 或 Event;
|
||
- 自动回收由 `gc remote <providerId> policy plan|install --confirm|status` 管理,周期和单批上限由 YAML 控制。
|
||
- `policy status` 必须一次披露期望与安装态的配置/runner 指纹、policy 数量、各 namespace 关键留存字段和 `inSync`;
|
||
- owning YAML 变化后只通过 `policy install --confirm` 收敛安装态,禁止直接修改 host JSON、runner 或 systemd unit。
|
||
|
||
## WebProbe 低内存人工处置
|
||
|
||
- 唯一资格指标:
|
||
- 只读取 `/proc/meminfo` 的 `MemAvailable`;
|
||
- 输出必须披露 `metric=MemAvailable`、`source=/proc/meminfo` 和 `swapExcluded=true`;
|
||
- swap 只作为独立压力信号,绝不计入启动可用量;
|
||
- 启动门槛和比较语义只读取 `config/hwlab-node-lanes.yaml#templates.hwlabV03.webProbeWorkbench.resourcePolicy.memoryStartGuard`;
|
||
- 文档、skill、源码和测试不得复制 `thresholdBytes` 的当前数值;
|
||
- 除非用户明确授权修改 WebProbe 启动门槛,否则不得改变 owning YAML 的 `thresholdBytes`。
|
||
- 人工处置命令必须按顺序执行:
|
||
|
||
```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
|
||
```
|
||
|
||
- 顺序约束:
|
||
- 默认 scoped plan 必须直接披露 YAML 限量候选摘要和 `runEligibility`,一次输出即可判断;
|
||
- 只有 `runEligibility.allowed=true` 时才允许执行明确标记安全的内存压力候选;
|
||
- `--full` 只用于下钻完整身份明细,不得作为 scoped run 的默认前置步骤;
|
||
- `run` 返回 job id 后,重复执行 `status --job-id <id>` 直到终态;
|
||
- job 未终态时不得提前执行 `memory` 或重试 WebProbe;
|
||
- 终态后执行 `memory`,只有不再命中 owning YAML 的 `manualComparator` 时才可重试人工启动;
|
||
- 内存处置不得改用未带 `--memory-pressure-only` 的通用 plan/run。
|
||
- sentinel cadence:
|
||
- 命中 owning YAML 的 `sentinelComparator` 时返回 `skipped-wait-next-round`;
|
||
- 不创建 observer、不启动 Chromium、不自动执行 GC;
|
||
- 等待下一轮 cadence 重新读取 `MemAvailable`。
|
||
|
||
以下远端场景必须先读 `docs/reference/gc.md`:
|
||
|
||
- G14 registry/CI workspace 留存、k3s/PVC 归因和安全停止判定;
|
||
- PK01 pikanode 临时目录留存与 Docker provider 安全边界;
|
||
- JD01 k3s/PVC 归因,以及 NC01/JD01 Web observe artifact、Chrome/observer 内存增长与 YAML-first 真相源核对。
|
||
|
||
## Web observe 与 Chrome 逃逸进程
|
||
|
||
- 正常生命周期:
|
||
- 远端节点的 Chrome 内存增长应先按 observer 生命周期问题处理;
|
||
- sentinel/quick-verify 的全部终态必须停止对应 observer;
|
||
- runner 的 TTL、maxSamples 和 artifact cap 必须来自 YAML;
|
||
- 归属尚未判清时不得按名称终止 Chrome,也不得删除 web-observe 目录。
|
||
- 保护边界:
|
||
- active observer、受控任务仍引用的浏览器会话及其完整进程树始终受保护。
|
||
- 只有独立且无归属的 Playwright `cliDaemon`/Chrome 进程树才可进入精确终止:
|
||
- 存活时间超过 owning YAML 定义的异常存活窗口;
|
||
- 受控 observe/sentinel 状态未显示该进程树归属于 active observer 或在途受控任务;
|
||
- manifest、heartbeat、PID/session 记录均不能把它归属到有效 run;
|
||
- `PID`、`PPID`、`SID`、`startTicks` 与后代进程能闭合为唯一身份;
|
||
- 任一证据缺失或存在歧义时继续标记 protected。
|
||
- 精确终止:
|
||
- TERM 前重新核对 `PID/PPID/SID/startTicks`,防止 PID 复用;
|
||
- 只向已核实的唯一进程树发送 TERM;
|
||
- 按 YAML 定义的有界等待时间重新枚举;
|
||
- 仍未退出时,只以不可变的 `PID/startTicks` 复核残留身份;
|
||
- 同时披露原始与当前 `PPID/SID`,允许 TERM 后重挂载但不放宽 PID 复用保护;
|
||
- 只对复核后的同一残留进程树发送 KILL。
|
||
- 禁止批量终止:
|
||
- 不得使用 `pkill -f chrome`、`killall chrome` 或其他按进程名批量终止方式;
|
||
- 证据不完整或归属存在歧义时保持 protected,不得猜测。
|
||
- 终止后验证:
|
||
- 确认目标 PID、后代 PID 和 session/process group 全空;
|
||
- 确认 active observer 未受影响;
|
||
- 重新采集 `/proc/meminfo` 的 `MemAvailable`、独立 swap 信号、observer 状态和 Chrome/runner 摘要;
|
||
- 结构化记录判定证据、TERM/KILL 结果、残留进程与终止前后资源数据。
|
||
|
||
JD01 等远端节点只能显式启用可重建工具缓存:
|
||
|
||
```bash
|
||
bun scripts/cli.ts gc remote JD01 plan --target-use-percent 49 --include-tool-caches --limit 100
|
||
bun scripts/cli.ts gc remote JD01 run --confirm --target-use-percent 49 --include-tool-caches --limit 100
|
||
```
|
||
|
||
- 必须先复核 plan。
|
||
- 候选仅限固定 allowlist 的 npm/npx/Bun 缓存。
|
||
- 不得改用 `rm -rf ~/.npm ~/.bun`,也不得触碰 `node_modules`、auth/config、k3s/containerd、PVC 或 Docker volume。
|
||
|
||
## 保护边界
|
||
|
||
以下对象不得作为通用磁盘止压目标:
|
||
|
||
- `docker system prune`、`docker image prune`、Docker/Compose volume 删除;
|
||
- 未经 `gc db-trace`、备份和维护窗口的 PostgreSQL PGDATA/trace 清理;
|
||
- `/var/lib/containerd`、`/var/lib/rancher/k3s`、`/var/lib/kubelet`、PVC、registry blob、runtime snapshot;
|
||
- Codex auth/config/profile;session 仅能通过 `--include-codex-sessions` 清理,活跃 SQLite 日志还需 `fuser` 与专门留存决策;
|
||
- 以下 Web observe 与 Chrome 对象:
|
||
- active run 和 live observer runner;
|
||
- 归属或 stale 分类未闭合的 Chrome process tree;
|
||
- 缺少 manifest/heartbeat/pid/open-fd 证据的 web-observe state root;
|
||
- 只有按“Web observe 与 Chrome 逃逸进程”完成无归属判定的唯一进程树才可精确终止。
|
||
- dirty、active、unmerged、recent 或 timeout-protected worktree;
|
||
- 用户未明确要求时的 `backend-core` rebuild/restart/replacement。
|
||
|
||
- 低风险候选执行后仍为 `summary.target.safeStop=true` 时,停止并报告剩余压力源与决策选项。
|
||
- 不得绕过 CLI 追求目标百分比。
|
||
|
||
## 验证
|
||
|
||
使用以下有界证据收口:
|
||
|
||
```bash
|
||
df -h /
|
||
df -BG /
|
||
docker system df
|
||
du -sh /root/unidesk/.worktree /root/unidesk/.state /tmp /var/log 2>/dev/null || true
|
||
```
|
||
|
||
- 远端内存/Chrome 压力还需记录 `free -h`、Chrome/observer 摘要与 web-observe state 大小。
|
||
- 总结起止 `df` 百分比、主要清理类别、受保护失败和剩余高风险压力源。
|