ディスク水位 · DerivedData / CocoaPods / Gradle · 3 層成果物 · 6 ステップ Runbook · ハードしきい値
運用、モバイル基盤、共有 Mac ビルドプールにディスク SLO を契約する Tech Leadは、金曜の夜に同型のアラートを受け取ることが多いです。Runner はオンラインだが Job が「No space left」で失敗し、DerivedData がシステムボリュームを占有し、CocoaPods と Gradle のグローバルキャッシュが無主になり、rsync 成功後も成果物がローカルに残る。本稿はまず誰がどんな問題に直面するかを定義します。Mac Mesh のマルチテナントローテーションでは、ディスクは「満杯になってから手動 rm」ではなく、観測可能な水位と層別回収契約が欠けています。次に結論:L1 DerivedData / L2 依存キャッシュ / L3 CI 成果物の 3 層水位線と 6 ステップ Runbook で、清掃を火消しから監査可能な定例へ移します。構成は5 つの隠れコスト、清掃戦略対照表、水位スクリプト項目、6 ステップ実装、3 つのハードしきい値、FAQです。シートとリースは 並行シートとミューテックス、イメージドリフトは ゴールデンイメージ一覧、成果物は rsync とオブジェクトストレージ、プール容量は 3 プール SLO マトリックス、多ブランチは Git worktree 分離を参照してください。
2026 年の Mac Mesh チケットでは、ディスク問題は「100GB 足りない」だけではありません。テナントローテーション、キャッシュ局所性、成果物ライフサイクルの間に統一契約がなく、APFS 上は空きがあるのに Xcode の一時書き込みが失敗するケースが多いです。
DerivedData の無境界共有:複数リポジトリが ~/Library/Developer/Xcode/DerivedData を共有し、インデックスと ModuleCache がブランチで交錯。clean 1 回で隣接テナントの ModuleCache を誤削除し、ディスク満杯ではなくランダムな link 失敗として現れます。
CocoaPods / Gradle グローバルキャッシュに TTL なし:~/Library/Caches/CocoaPods と ~/.gradle/caches が増え続ける。Pods 更新後も旧 tarball が数 GB 残り、worktree 多ブランチと並行すると争用が拡大します。
「アップロード済みだがローカル残留」:オブジェクトストレージ側は成功したのに $CI_ARTIFACTS_DIR に保持ポリシーがなく、rsync 完了フック未連携で IPA/dSYM がディスクを消費します。
APFS スナップショットと「空き」の錯覚:ローカルスナップショットで df は余裕に見えるが、コンパイルピークで実書き込みが破綻。ボリューム・層別の waterline_used_pct が欠けます。
清掃とシートロックの競合:リース未解放でディレクトリを掃引、または シートロック TTL と衝突し、「ディスクは空いたがビルドは赤」の二次障害になります。
成果物は 3 層ディレクトリ辞書、warn/hard 二重水位、リース終了時 LRU、ゴールデンイメージ週次ドリフト検査との分離です。いずれか欠ければ共有プールで「任意 monorepo 並行」を約束すべきではありません。次節は 3 つの清掃哲学を対照し、「毎週金曜に全員 ssh で rm -rf」を避けます。
ディスク運用は「強ければよい」わけではなく、ビルドヒット率、清掃の監査可能性、テナント分離のバランスです。変更レビューに下表を貼り、各層(L1/L2/L3)でデフォルト戦略は 1 つだけにしてください。
| 戦略 | 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%」だけに依存しません。
以下 6 ステップは Runner が Mac Mesh ラベルに接続済みで、Job 前にシート acquire、後に release することを前提とします。順序を飛ばさないでください。指標のない水位線は盲削除です。
3 層辞書とパスを固定:L1/L2/L3 ルート、warn(82%)/ hard(92%)を mesh-disk-policy.yaml に書き、イメージ一覧 にデフォルトマウントを登録します。
disk-waterline プローブを配置:60 秒ごとにボリューム使用率と各層バイト数を収集し Prometheus/OpenTelemetry へ送信。hard 到達時は Runner を drain し新規 Job を fail-fast します。
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 パネルの先頭行に置き、OS アラートのみに依存しないでください。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 システムボリュームで mesh に約 60% を割く現場では、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 を報告し、半書き込み成果物を防ぎます。スケジューラは空きノードへルーティングするか Burst を起動します。シート意味論は 並行シート記事 を参照してください。
必要です。ディスク清掃はランタイムゴミの回収のみで、スナップショットドリフト一覧の代替にはなりません。導入は ヘルプセンター、プラン比較は 価格ページ をご覧ください。