シンガポール/日本/韓国/香港/米国東海岸/米国西海岸に置いたベアメタル remote Mac mini M4でリリース列車が回り始めたあと、本当に痛いのは CPU 不足ではなく、複数の Xcode と iOS SDK を数か月単位で固定しながらも起きるツールチェーンの取り違えとDerivedData の膨張、そして画面共有での GUI 調査と夜間 Archive の競合です。本文は DEVELOPER_DIR をジョブ単位で注入するのが正攻法である理由、xcode-select をメンテナンス窓に閉じ込めるべき理由、Archives と DerivedData を分けて予算化する方法、ジョブラベルで Xcode メジャーを固定する手順を一つの設計表にまとめます。価格と在庫は 料金ページ、発注は 注文ページ、接続まわりは ヘルプセンターを参照してください。ハイブリッド CI 記事や SSH と VNC の記事と併読すると全体像が揃います。
読了後に答えられるようになるのは次の三つです。第一に、並列 Runner ではなぜ ジョブごとの DEVELOPER_DIR がデフォルトであるべきか。第二に、リリースピークで一日二回 Archive する場合、Archives と DerivedData にそれぞれ何ギガバイトの余白を確保すべきか。第三に、10〜14 日だけ尖る負荷に対して 日次/週次レンタルで検証し、M4 16GB/256GB、M4 24GB/512GB、M4 Pro 64GB/2TB と並列リソースへ段階的に上げる順序をどう組むかです。コマンド例は構造説明であり、パスは貴社のインストールに合わせて置き換えてください。
[ SECTION_01 ] // FAILURE_MODES まず表面化する三つの事故モードと隠れコスト
第一はステップ間のツールチェーンのズレです。ログ上は Xcode 16.2 でも、後段のスクリプトが別の developer ディレクトリを参照し、Swift やリンカの実体が期待と異なるパスから解決されることがあります。ローカル Mac では単一ユーザーのセッションが状態を隠してくれますが、リモート Runner はシェルの再利用、ビルドディレクトリの再利用、並列スケジューリングが重なり、問題が顕在化します。第二はディスク勾配です。ブランチごとに増分状態が必要になるのに、すべてのパイプラインが同一の DerivedData ルートへ書き込むと、256GB 構成では数日以内に空き容量が赤ゾーンに入り、codesign のタイムアウトや一見ネットワーク起因に見えるフリーズを誘発します。第三は人と自動化の競合です。同じ macOS ユーザーで画面共有から Xcode を操作しながら夜間に xcodebuild archive を走らせると、索引ロックや共有 Archives パスがボトルネックになり、インシデント調査が時間単位に伸びます。
さらに、キューの意味論が Xcode メジャーラベルに束縛されていないこと、notarytool 移行期に挙動差が残ること、Git リモートやコンテナレジストリが Runner から地理的に離れているために依存取得だけで壁時計を浪費することが、CPU を積んでも解消しないコストとして積み上がります。これらを変更審査の冒頭に書き留めることは、コア数を増やす議論より安価です。
- SDK の取り混ぜ:ラッパーだけが
DEVELOPER_DIRをエクスポートし、別ジョブはデフォルトパスを使う。 - 共有 DerivedData:増分キャッシュの前提が壊れ、モジュールキャッシュが汚染される。
- Archives の肥大:保持ポリシーなしの日次二重 Archive が数十ギガバイトを消費する。
- SwiftPM とモジュールキャッシュ:巨大な
.buildがシステムキャッシュと同一ボリュームで競合する。 - GUI と CI の重ね:前景の索引処理が夜間 Archive をブロックする。
- リージョン不一致:アーティファクトが欧州、Runner がアジア太平洋にあると取得待ちだけでキューが空転する。
複数 Xcode の共存はアイコンが二つあることではなく、すべての xcodebuild 呼び出しに対するルーティング決定です。
[ SECTION_02 ] // ROUTING_MATRIX DEVELOPER_DIR 注入・グローバル xcode-select・ラッパー方式の比較
グローバルツールチェーンを誰が変えられるかと、単一プロセス環境を誰が変えられるかを分離しないと、ローカル検証のために sudo xcode-select -s を実行したエンジニアが、同一ホスト上の夜間ジョブを静かに別バージョンへ切り替えてしまいます。下表は典型シナリオ、隔離強度、ロールバック負債、ディスク方針を並べたものです。
| 観点 | A・ジョブ単位 DEVELOPER_DIR | B・sudo xcode-select | C・ラッパーと明示 SDKROOT |
|---|---|---|---|
| 典型シナリオ | GitHub Actions、Jenkins、同一ユーザーで並列ジョブ | 単一オーナーの保守ホストで直列スクリプト | レガシーシェルが多く CI YAML をすぐに変えられない |
| 隔離強度 | 高い:プロセス環境が独立 | 低い:グローバル切替は並列前提を壊す | 中程度:スクリプト規律とレビューに依存 |
| DerivedData 指針 | パイプライン ID でサブディレクトリ分割 | 切替後も増分再利用の前提が崩れるため分割推奨 | A と同様にラッパー内で DERIVED_DATA_DIR をエクスポート |
| ロールバック | 低:環境変数を戻す | 中:切替前後のパスを二名で確認 | 中:ラッパーをバージョン管理 |
| 六地域との関係 | リージョンプールで同一イメージを配るのに最適 | 単用途の短い検証レンタル向き | 単一コマンド完結の納品物向き |
256GB 単一ボリュームでは DerivedData、Archives、SwiftPM キャッシュを別ルートに分け、保持ポリシーを必須にしてください。512GB〜1TB では IPA 出力と Archives を同一ボリュームに置きコピーを減らす構成が現場では扱いやすいです。二系統の同時 Archive と画面共有の余白を両立するなら M4 Pro 64GB/2TB と並列リソースの組み合わせが、手作業のキャッシュ削除より人件費を削りやすいです。
メンテナンス窓の
xcode-selectは速いですが、本番プールのデフォルトは DEVELOPER_DIR に置くべきです。
[ SECTION_03 ] // GATES_AND_LAYOUT ゲートスクリプト、ディスク分割、モジュールキャッシュの基線
すべての Archive の先頭三行に Xcode のビルド番号、swift --version、xcrun --find swift を出してください。三行で環境を証明できない成果物は昇格を止める、という規律が監査に効きます。
Apple 公式ドキュメントは Xcode の更新後に必ず再確認してください。
https://developer.apple.com/documentation/xcode
https://developer.apple.com/library/archive/technotes/tn2339/_index.html
#!/bin/bash
set -euo pipefail
export DEVELOPER_DIR="/Applications/Xcode_16_2.app/Contents/Developer"
echo "DEVELOPER_DIR=${DEVELOPER_DIR}"
xcodebuild -version
xcrun swift --version
if [[ "${PIPELINE_XCODE_MAJOR:-}" != "16" ]]; then echo "major mismatch"; exit 2; fi
DERIVED_DATA_DIR を /Volumes/build/dd/${PIPELINE_ID} のようにパイプライン単位に向け、ジョブ終了後に非同期削除または LRU で回収します。ARCHIVE_PATH と一時的な IPA 出力は DerivedData の親ディレクトリと共有させず、誤削除で並列増分が壊れる余地を潰します。CLANG_MODULE_CACHE_PATH をツールチェーンごとに分けるとディスクは五〜十五ギガバイトほど増えますが、ヘッダ時刻に起因する幽霊リビルドを減らせます。
[ SECTION_04 ] // RUNBOOK 監査可能な複数 Xcode CI に近づける八ステップ
- マトリクス凍結:最小 iOS バージョン、必須 Xcode メジャー、notarytool の経路を一枚にまとめ、ダウングレード禁止を明文化します。
- ジョブと Runner のラベル:
xcode-16-2のようなタグをジョブ定義と Runner 登録の両方に入れます。 - ゲートスクリプト:すべての入口で
source ci-xcode-gate.shを強制し、裸のxcodebuildを禁止します。 - ディレクトリ上限:DerivedData、Archives、SwiftPM に上限と掃除周期を cron かプレイブックに書きます。
- 並列実験:対象 SKU で一系統、二系統、三系統の同時 Archive のピークメモリと書き込み増幅率を測ります。
- リージョン親和:Runner プールを Git リモートやレジストリ、テストターゲットと同じ地理的セマンティクスに揃え、ピーク週に第二台を同リージョンで足します。
- レンタル段階:新マトリクスは日次または週次で検証し、ディスク勾配を見てから月次で固定します。バーストキューには並列リソースを足します。
- ロールバック演習:月次でメンテナンス窓に旧 Xcode へ
DEVELOPER_DIRを向け直し、全スモークとエクスポートを再確認します。
[ SECTION_05 ] // DATA_REGION_FAQ 参照しやすい数値レンジ、六地域配置、FAQ
以下は容量計画のための現場ヒューリスティクスであり、ハードウェアの理論最大ではありません。リポジトリ規模で必ず再測定してください。
- DerivedData 勾配:大規模 iOS モノレポで一日八回のクリーンビルドを回すと、書き込みは12〜35GBに乗ることが多く、256GB で未分割のままだと週の途中で赤ゾーンに入りがちです。
- Archive サイズ:dSYM を有効にした Release の
xcarchiveは1.5〜4GB帯が多く、二ブランチで日次二重 Archive なら 80GB 以上のローリング空きを Archives 側に確保するのが安全です。 - 並列 Archive のメモリ:M4 24GB で二系統同時は可能でも余裕は薄く、GUI セッションと共存するなら M4 Pro 48GB 以上が現場では落ち着きます。
- リージョン親和:ストレージが Runner から離れると依存取得だけで数十分の壁時計を消費することがあり、CPU より同地配置が効くケースが多いです。
FAQ:
- Q:xcode-select だけでは足りないのですか。A:並列プールでは不足です。本番は
DEVELOPER_DIRをデフォルトにし、グローバル切替はメンテナンス窓に閉じ込めてください。 - Q:256GB で二つの Xcode を常時運用できますか。A:インストールは可能ですが、二系統の重い Archive を長期運用するなら外付けレイアウトかより大容量へ移行してください。
- Q:ModuleCache は毎日消すべきですか。A:ルートを分ける方が安定です。全削除はコールドスタート時間と引き換えです。
- Q:Xcode Cloud との役割分担は。A:PR スモークを Cloud に残し、複数バージョン固定の長い Archive と公証キューをベアメタル Mac に載せる構成が多いです。サイト内のハイブリッド CI 記事を参照してください。
共有仮想化クラウドは騒がしい隣人と不透明なメンテナンス窓、曖昧なディスク形状で失敗しやすく、自前の単一据え置き Mac は短期の多地域ピークに弱いです。複数 Xcode、明示ルーティング、予測可能なディスクを iOS リリース列車に載せたいチームにとって、NOVAKVM の Mac mini クラウドレンタルは多くの場合より良い解です。シンガポール/日本/韓国/香港/米国東西の六地域で専用 Apple Silicon を確保し、日次・週次検証から月次の定常運用、64GB/2TB と並列リソースまで段階的に伸ばせます。次のキャパシティ会議の前に、スプレッドシートの同じ行へ DEVELOPER_DIR と DerivedData ルートを書き添えてください。