Vuncloud 部落格
← 返回機房手記專欄

Xcode 快取共享實戰:多節點同步建置資料的技巧

DerivedData · SPM · CocoaPods · 多 Runner 同步約 13 分鐘閱讀

數據分析儀表板象徵多節點 Xcode 建置快取監控與同步效率
TL;DR · 三句話
  • 單台 Mac 上的 DerivedData 快取在第二台 Runner 上預設不存在——多節點 iOS CI 的隱性成本是每台機器各自冷啟動
  • 可共享的是建置產物層(DerivedData、SPM 解析結果、Pods/),不是「多台同時寫同一目錄」;pull → build → push區域快取樞紐是 2026 年主流做法
  • Cloud Mac 機群上,用 lockfile 雜湊 + Xcode 版本做 cache key,配合 rsync/物件儲存,warm 建置常見可再省 5–15 分鐘(在單機快取最佳化之上)

你把 GitHub Actions 的 actions/cache 配好了,warm 建置從 18 分鐘降到 9 分鐘——然後橫向加了三台自託管 M4 Runner,P95 卻又回到 16 分鐘。每台機器都是「第一次見這個 commit」。

這不是回歸原點,而是進入了多節點建置的第二階段:Xcode 快取共享。本文不講「為什麼要快取」(見 CocoaPods / SPM / DerivedData 單機指南),而講如何在多台 Mac 之間同步建置資料,讓任意 Runner 都能吃到隊友編譯過的模組。

3
層快取:依賴 · 編譯產物 · 索引
5–15 分
多節點 warm 建置常見額外節省
1
條鐵律:禁止並行寫入同一 DerivedData

1. 多節點 CI 為何比單機更慢

負載平衡把 job 隨機打到 Runner A/B/C 時,每台機器的磁碟狀態彼此獨立。Runner A 剛編完的 MyAppKit.swiftmodule,對 Runner B 毫無意義——除非你把產物搬過去

GitHub 託管 runner 用 actions/cache 把 tarball 存到雲端,job 開始時 restore。自託管或 Cloud Mac 機群往往沒有等價的託管層,團隊要麼:

  • 接受每台機器各自 warm(浪費算力)
  • 把快取目錄掛 NFS,多台同時寫(易爆庫)
  • 自建快取樞紐:建置前拉、成功後推

第三種在 iOS 團隊裡最常見,也最適合 多區域節點部署:美東三台 M4 共享一個快取桶,亞太兩台共享另一個,跨區只同步 SPM/Pods 層。

2. Xcode 建置資料分哪幾層

同步之前先分清「什麼值得傳、什麼不能混傳」。

2.1 DerivedData

xcodebuild -derivedDataPath 指向的目錄,含編譯後的 .o.swiftmodule、連結中間產物、部分索引。體積常達數 GB,是warm 建置加速的最大頭,也是對 Xcode 版本最敏感的一層。

建議在 CI 上固定路徑,例如 /Volumes/CI/DerivedData/<scheme>,與同步腳本裡的 REMOTE 變數一致——單機快取文裡強調過,路徑不一致等於沒快取。

2.2 SPM 與 CocoaPods

SPM:~/Library/Caches/org.swift.swiftpm + 倉庫內 .build(若提交了解析結果)。CocoaPods:Pods/~/Library/Caches/CocoaPods

這兩層變更頻率低於 DerivedData(隨 lockfile 變),更適合跨機器、甚至跨 region 同步。很多團隊只把 SPM/Pods 推到 S3,DerivedData 留在區域內 rsync。

2.3 ModuleCache 與 Xcode 全域快取

~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex 與各種 SDKStatCaches。通常隨 DerivedData 一併處理;單獨同步時務必保證同一 Xcode 小版本

MacBook 上的程式碼編輯器,象徵多節點 Runner 共享同一份 Xcode 編譯快取
共享的是「編過的模組」,不是「同時開啟的專案」——每台 Runner 仍用本地 DerivedData 副本建置

3. 四種多節點同步架構

模式做法優點風險
本地-only每台 Runner 獨立磁碟,無共享零維運橫向擴展不省時間
NFS 共享掛載DerivedData 掛網路碟,多台唯讀或讀寫設定簡單並行寫入易損壞;網路延遲拖慢連結
rsync 樞紐中央目錄或 leader 機;建置前 pull、成功後 push可控、與 Xcode 相容性好需寫腳本與鎖
物件儲存 tarball按 cache key 打包上傳 S3/R2;job 開始下載解壓跨區域、與 GHA cache 模型一致大 DerivedData 上傳費時;需壓縮

2026 年實踐裡,同機房 / 同區域 Cloud Mac優先 rsync 樞紐;跨國團隊用物件儲存同步 SPM/Pods,DerivedData 區域自治。勿把 NFS 當「多寫共享 DerivedData」的銀彈。

鐵律

禁止兩台 Runner 同時對同一 DerivedData 樹執行 xcodebuild。若必須共享碟,用檔案鎖單寫多讀(一台 designated warmer 編完再 rsync 分發)。

4. Cache key 與失效策略

多節點共享的 cache key 應比單機更保守——錯命中比冷啟動更糟(詭異連結錯誤、簽章失敗)。

建議納入 key 的欄位:

  • xcodebuild -versionXCODE_VERSION 環境變數
  • hashFiles('**/Podfile.lock')Package.resolved
  • Scheme 名或 target 集合(多 scheme 倉庫分開快取)
  • arm64(Apple Silicon 專用前綴,避免歷史 x86 污染)

不建議把完整 github.sha 作為唯一 key——否則每台機器永遠 miss。可用分層 restore-keysdd-arm64-main-<lockhash>dd-arm64-main-dd-arm64-

失效時機:Xcode 升級、lockfile 變更、切換 SWIFT_VERSION 或重大 Build Settings、人為 clean build 後應 bump key 後綴或刪遠端目錄。

5. 實戰:rsync pull/push 腳本

下面是一套在 M4 Cloud Mac 機群驗證過的最小腳本,快取樞紐在 cache-leader.internal:/cache/ios/(可以是機群中一台大磁碟機器或 NAS)。

#!/usr/bin/env bash
# cache-sync.sh — 在 xcodebuild 前后调用
set -euo pipefail

ACTION="${1:?pull|push}"
CACHE_ROOT="${CACHE_ROOT:-cache-leader.internal:/cache/ios}"
SCHEME="${SCHEME:-MyApp}"
KEY="${CACHE_KEY:-arm64-$(md5 -q Podfile.lock 2>/dev/null || echo nolock)-$(xcodebuild -version | head -1 | tr ' ' '-')}"
LOCAL_DD="${DERIVED_DATA_PATH:-/Volumes/CI/DerivedData/${SCHEME}}"
REMOTE="${CACHE_ROOT}/${KEY}/${SCHEME}/DerivedData"
LOCAL_SPM="${HOME}/Library/Caches/org.swift.swiftpm"
REMOTE_SPM="${CACHE_ROOT}/${KEY}/swiftpm"

rsync_opts=(-az --delete-delay --contimeout=10)

case "$ACTION" in
  pull)
    rsync "${rsync_opts[@]}" "${REMOTE}/" "${LOCAL_DD}/" || true
    rsync "${rsync_opts[@]}" "${REMOTE_SPM}/" "${LOCAL_SPM}/" || true
    ;;
  push)
    # 仅成功构建后调用
    rsync "${rsync_opts[@]}" "${LOCAL_DD}/" "${REMOTE}/"
    rsync "${rsync_opts[@]}" "${LOCAL_SPM}/" "${REMOTE_SPM}/"
    ;;
  *) echo "usage: $0 pull|push" >&2; exit 1 ;;
esac

CI 流程:

  1. Job 開始 → cache-sync.sh pull
  2. xcodebuild -scheme "$SCHEME" -derivedDataPath "$LOCAL_DD" build
  3. 僅當 build 成功 → cache-sync.sh push

多台並行時,push 可能競態——用 flock 或「最後寫入 wins」接受即可;DerivedData 在同 lockfile + 同 Xcode 下內容應相容。若 push 極頻繁,可改為非同步上傳(建置結束後背景 rsync,不阻塞 pipeline)。

6. 跨區域 Cloud Mac 快取樞紐

美東、美西、亞太各放 Runner 時,跨洋 rsync 全量 DerivedData 往往不划算(延遲 + 出口流量)。建議:

  • 每區域一個 cache bucket(S3 / R2 / 提供商物件儲存)
  • 同步內容:Podfile.lock 雜湊對應的 Pods/ tarball + Package.resolved 對應的 SPM 快取
  • DerivedData 僅在區域內 rsync;首個 job 冷啟動,後續 job warm
  • 鏡像版本與 黃金映像 tag 對齊,避免 Xcode 漂移導致全庫失效

上傳前用 tar czf 壓縮 SPM 快取,常能減 40%–60% 體積;DerivedData 內部已是大量小檔案,tar + 並行上傳比裸 rsync 跨區更穩。

7. 接入 GitHub Actions / 自託管 Runner

自託管 runner 上可在 workflow 裡包一層:

- name: Restore shared Xcode cache
  run: ./scripts/cache-sync.sh pull
  env:
    CACHE_ROOT: ${{ secrets.IOS_CACHE_SSH }}
    SCHEME: MyApp
    DERIVED_DATA_PATH: /Volumes/CI/DerivedData/MyApp

- name: Build
  run: xcodebuild -scheme MyApp -derivedDataPath /Volumes/CI/DerivedData/MyApp build

- name: Save shared Xcode cache
  if: success()
  run: ./scripts/cache-sync.sh push

若仍用 GitHub 託管 actions/cache,自託管機群可雙軌:GHA cache 作兜底,rsync 樞紐作同區域低延遲層。注意兩套 key 命名空間分開,避免互相覆蓋。

監控建議:記錄 pull/push 耗時、傳輸位元組、build 是否 incremental(看 xcodebuild 日誌裡 CompileSwift 數量)。P95 變慢時先查 cache miss,再查機器負載——與 建置忽快忽慢排查同一套方法論。

8. 踩坑清單

  • 並行寫 DerivedData:隨機 stat cache file corrupted → 改 pull/push
  • Xcode 小版本不一致:模組 ABI 對不上 → key 含版本,鏡像統一 tag
  • 路徑不一致:快取到 A 路徑,編譯用 B 路徑 → 全量重編
  • clean 後仍 push:把空目錄推上去沖掉好快取 → 僅 success 且非 clean 時 push
  • 簽章產物進快取:Provisioning 變更後沿用舊簽章 → 快取層排除 Build/Products 或按設定分 key
  • 磁碟滿:DerivedData 膨脹 → 定期按 LRU 清遠端 ${KEY} 目錄,保留最近 N 個 lockhash

9. FAQ

多台 Mac 能直接共享同一個 DerivedData 目錄嗎?

不建議同時寫入。唯讀掛載或單寫者多讀者可以,但連結階段仍可能踩鎖。生產環境優先本地 DerivedData + rsync。

Xcode 快取共享需要統一 Xcode 版本嗎?

必須。升級 Xcode 後應使用新 cache key,舊目錄可非同步刪除。

和 Bazel / Tuist 遠端快取比怎麼樣?

Bazel/Tuist 遠端快取粒度更細(action 級),適合超大 monorepo。標準 Xcode 工程用 DerivedData 同步改造成本更低,與現有 xcodebuild 流程相容。

多台 Cloud Mac 需要多大磁碟?

每台本地至少預留 50–100GB 給 DerivedData + SPM;樞紐機或物件儲存按「並行 scheme 數 × 單 scheme 快取體積 × 保留版本數」估算,M4 + 2TB 擴充碟對中型團隊通常夠用。

結語

Xcode 快取共享解決的是多 Runner 時代的「第二次冷啟動」:單機快取最佳化之後,橫向加機器不應再付出全額編譯稅。記住三層分離(Pods / SPM / DerivedData)、一種同步紀律(pull-build-push)、一條鐵律(勿並行寫 DerivedData)。

快取是分散式編譯裡最便宜的算力倍增器——比再買兩台 M4 更快見效。

從一台 cache leader 和 cache-sync.sh 開始;用 lockfile 雜湊命名遠端目錄;build 綠了再 push。下週 P95 曲線會告訴你值不值。

為多節點 CI 預留磁碟的 Cloud Mac

Vuncloud Mac mini M4:美東、美西、亞太 SSH 就緒,適合掛載大碟 DerivedData、部署 rsync 快取樞紐與自託管 Runner 機群。

查看 Cloud Mac 方案 · M4 CI Runner 成本模型

Apple 工具鏈行為以官方發布為準;快取腳本請按團隊安全策略稽核 SSH 與物件儲存憑證。最後更新:2026 年 7 月 27 日。

機房手記 · 建置加速

DerivedData 同步 · 多 Runner · Cloud Mac

rsync 樞紐 · cache key · 區域快取桶

查看 Cloud Mac 方案
限時優惠 查看方案