磁盘水位 · DerivedData / CocoaPods / Gradle · 产物三层 · 六步 Runbook · 硬阈值
运维、移动端基建与要为共享 Mac 构建池签磁盘 SLO 的 Tech Lead常在周五晚上收到同一类告警:Runner 在线但 Job 因「No space left」失败,DerivedData 占满系统卷,CocoaPods 与 Gradle 全局缓存无人认领,产物目录在 rsync 成功后仍堆在本地。本文先界定谁遇到什么问题:Mac Mesh 多租户轮换下,磁盘不是「满了再手工 rm」,而是缺可观测水位 + 分层回收契约;再给出结论:用 L1 DerivedData / L2 依赖缓存 / L3 CI 产物 三层水位线与六步 Runbook,把清理从救火变成可审计例行;结构上交付五条隐性税、清理策略对照表、水位脚本字段、六步落地、三条硬阈值与 FAQ。席位与租约见 并发席位与互斥;镜像漂移见 黄金镜像清单;产物外送见 rsync 与对象存储;池型容量见 三池 SLO 矩阵;多分支隔离见 Git worktree 隔离。
2026 年 Mac Mesh 工单里,磁盘问题很少是「少买了 100GB」这么简单。更多是在轮换租户、缓存局部性与产物生命周期之间缺统一契约,导致 APFS 看起来还有空间,Xcode 却已经在写临时文件时失败。
DerivedData 无界共享:多仓库共用 ~/Library/Developer/Xcode/DerivedData,索引与模块缓存按分支交错,一次 clean 误删邻居正在用的 ModuleCache,表现为随机 link 失败而非磁盘满。
CocoaPods / Gradle 全局缓存无 TTL:~/Library/Caches/CocoaPods 与 ~/.gradle/caches 只增不减;Pods 版本升级后旧 tarball 仍占数 GB,且与 worktree 多分支 并行时放大争用。
产物「已上传仍留本地」:Job 在对象存储侧已成功,但 $CI_ARTIFACTS_DIR 未挂保留策略,与 rsync 完成钩子 未绑定,磁盘被 IPA/dSYM 慢慢吃光。
APFS 快照与「可用」误导:本地快照让 df 显示余量充足,真实可写空间在编译峰值时击穿;缺少按卷、按层的 waterline_used_pct 指标。
清理与席位锁竞态:租约未释放就扫目录,或与 席位锁 TTL 冲突,造成「磁盘清了、构建却红」的二次事故。
交付物应是:三层目录字典、warn/hard 双水位、按租约结束的 LRU、与黄金镜像漂移周检解耦。缺任一,就不应在共享池上承诺「任意 monorepo 可并行」。下一节用对照表比较三种常见清理哲学,避免「每周五全员 ssh 上去 rm -rf」。
磁盘治理不是越狠越好,而是要在构建命中率、清理可审计性与租户隔离之间取平衡。请把下表贴在变更评审页:每一层(L1/L2/L3)只允许勾选一种默认策略。
| 策略 | L1 DerivedData | L2 Pods/Gradle | L3 产物 | 适合 | 主要风险 |
|---|---|---|---|---|---|
| 手工 cron | 周末 rm 全局目录 | 偶发 pod cache prune | 按天数 find 删除 | 极小团队、低并行 | 误删邻居、不可审计 |
| 水位守护进程 | 按 workspace 哈希 LRU | 容量触线 evict | rsync 成功后 48h | 共享池默认 | 需指标与锁契约 |
| 镜像重置 | 快照回滚清空 | 随镜像刷新 | 卷级替换 | 漂移失控、合规快照 | 冷启动编译变慢 |
选型底线:共享池默认应选「水位守护」;镜像重置只配合 黄金镜像漂移清单 做季度兜底,不能替代日常 LRU。
当 Dedicated 独占池 与 Shared 轮换并存时,L1 缓存键必须带池型标签,否则独占机的局部性收益会被共享池清扫脚本误伤。
L1:/var/mesh/cache/deriveddata/{workspace_hash},绑定 Xcode DERIVED_DATA_DIR。L2:/var/mesh/cache/cocoapods、/var/mesh/cache/gradle,禁止写回用户主目录全局缓存。L3:/var/mesh/artifacts/{job_id},上传成功后仅保留校验旁路文件。这样监控可以按层汇报 layer_*_bytes,而不是只有一个模糊的「/ 分区 85%」。
以下六步假设 Runner 已接入 Mac Mesh 标签,且席位在 Job 开始前 acquire、结束后 release。顺序不要跳:没有指标的水位线等于盲删。
冻结三层字典与路径:把 L1/L2/L3 根目录、warn(82%)/ hard(92%)阈值写入仓库 mesh-disk-policy.yaml,并在 镜像清单 中登记默认挂载点。
部署 disk-waterline 探针:每 60s 采集卷使用率与各层字节数,上报 Prometheus/OpenTelemetry;hard 触线时 Runner 进入 drain 并 fail-fast 新 Job。
隔离 DerivedData:CI 注入 DERIVED_DATA_DIR 指向 workspace 哈希桶;租约结束触发该桶 LRU,禁止扫全局 DerivedData。
L2 依赖缓存 evict:CocoaPods 用 pod cache clean 封装为「按容量」而非「按时间」;Gradle 启用 GRADLE_USER_HOME 指向 mesh 目录并限制 max-cache-size。
产物与 rsync 钩子:对象存储 multipart 完成事件回调删除本地 L3;失败重试保留至 7 天,字段与 产物 Runbook 对齐。
周检与演练:对照黄金镜像 checksum、模拟 90% 水位下 Job 拒绝、记录清理审计日志;与 Burst 溢出 联动时先清 L3 再接纳可中断 Job。
hostname pool_type volume_mount waterline_used_pct waterline_warn_threshold waterline_hard_threshold layer_l1_deriveddata_bytes layer_l2_cocoapods_bytes layer_l2_gradle_bytes layer_l3_artifacts_bytes seat_lease_id last_cleanup_ts_unix cleanup_evicted_bytes_1h disk_waterline_hard_stop
提示:探针输出应作为 Grafana 面板的第一行,而不是仅依赖系统告警。把 cleanup_evicted_bytes_1h 与成功构建数同图,可区分「真清理」与「构建变少所以磁盘看似下降」。
磁盘告警与 队列 SLO 症状常重叠。先用下表定位是容量、缓存键还是产物堆积,再决定清扫范围。
| 症状 | layer_* 主导 | 可能根因 | 优先动作 |
|---|---|---|---|
| 仅 Xcode 步骤失败 | L1 高 | DerivedData 串味或索引损坏 | 按 workspace 哈希清桶 |
| Android/iOS 混合池慢 | L2 高 | Pods/Gradle 无 evict | 收紧 L2 容量上限 |
| 上传成功仍满 | L3 高 | rsync 钩子未绑 | 补对象存储回调 |
| df 正常但写入失败 | 快照 | APFS 本地快照 | 减快照保留 + 探针 |
注意:不要在持有 席位锁 时执行卷级 rm -rf。清理脚本必须检查 seat_lease_id 为空或租约已过期,否则会把正在编译的 ModuleCache 一并删掉。
若 L1 在清桶后 24 小时内再次顶满,优先审查是否缺少 worktree 隔离 导致同一节点并行多份全量 DerivedData,而不是继续加盘。
下列数值来自多个 16GB/24GB 共享池现场的折中,写入变更单后可作为对外 SLO 的附件;独占池可把 warn 下调 5 个百分点以换取更稳定的索引热缓存。
waterline_warn_threshold=82 触发 L3→L2→L1 顺序 evict;waterline_hard_threshold=92 拒绝新 Job 并打 disk_waterline_hard_stop=1。在 512GB 系统卷、约 60% 预留给 mesh 的现场,L2 合计建议封顶 80GB(CocoaPods 与 Gradle 各 40GB 软上限),L3 单 Job 目录软上限 12GB(含 dSYM)。把「仅 cron 周末清扫」或「全员 ssh 手工删缓存」当作长期方案时,往往缺少审计字段与席位契约,误删邻居、编译冷启动激增、产物半写入会在发布周集中爆发。对于要把 iOS/Android CI 与磁盘 SLO 落在可合同化云端 Mac Mini 的团队,VpsMesh 的 Mac Mini 云端租赁通常是更优解;套餐见 价格页,接入与水位脚本样例见 帮助中心。
默认按 workspace 哈希分桶 并绑定席位租约,租约结束触发该桶 LRU。多分支并行见 worktree 隔离文;勿让全局 ~/Library/Developer/Xcode/DerivedData 无界共享。
Runner 应 fail-fast 并上报 disk_waterline_hard_stop,避免半写入产物;调度层把 Job 路由到有余量节点或触发 Burst。席位语义见 并发席位文。