← 返回開發日記

Fastlane 雙雲 Mac 架構踩坑:編譯與上傳拆分後最常見的 3 個失敗模式

Runbook · 2026.06.04 · 約 8 分鐘閱讀 · 可貼值班手冊

雙雲 Mac:香港節點 archive,加拿大節點 Fastlane upload

若你正在值班,手上是兩台(或以上)租用的雲 Mac,用 Fastlane 做 iOS 發布,最近出現「編譯日誌是綠的、TestFlight 卻沒有新包」或「傳上去的包不對」,這篇就是給你看的。還在糾結要不要上雲、要不要加第二台機器,請先看 TestFlight 專機編譯上雲——這裡預設你已拆成「一台負責編、一台負責傳」。

讀完這篇,你能帶走兩件事:① 根據日誌判斷是憑證、製品還是網路哪一類問題② 對照清單確認兩台機器的職責有沒有分對,以及該不該繼續加機器。

先對齊名詞(後文會反覆用到)
  • 編譯機:只負責用 Xcode 編譯工程,並匯出安裝包(.ipa
  • 上傳機:只負責用發布憑證簽名,並把 .ipa 上傳到 TestFlight;蘋果後台登記的出口 IP 應對準這台
  • Archive / 打出包:Xcode 產生可分發的建置結果;對外交接時我們只用編好的 .ipa 檔案,不搬整顆工程快取目錄
  • match:Fastlane 裡統一管理發布憑證/描述檔的工具;只應在一台機器上跑

誰可以跳過: 只有一台 Mac,或 TestFlight 上傳一直正常、還沒拆成「編譯 + 上傳」兩台。若你已用多台雲 Mac 做發布且鏈路出了問題,再往下讀。

TL;DR(30 秒)
雙雲 Mac 拆分後,瓶頸通常不是效能,而是 Fastlane 的三類邊界錯誤
  1. 憑證邊界錯——發布憑證被同步到了編譯機,上傳機拿不到(最危險)
  2. 製品邊界錯——跨機房傳 .xcarchive 而非可驗證的 .ipa(最隱蔽)
  3. 網路身份漂移——ASC 白名單仍是舊 IPv4(最難查)
下文先給30 秒診斷樹,再展開三類故障;文末有驗收表、四階段判斷,以及「最終結論」執行塊(只看該段也能拍板)。

背景(1 分鐘):兩台機器之後,常見問題不是「機器慢」

很多團隊會租兩台雲 Mac:一台離亞洲近,白天負責把 App 編譯成安裝包;另一台放在加拿大,負責 把包交給蘋果(TestFlight / App Store)。加第二台往往是為了省時間,但值班裡真正耗人的,是兩台機器都在管憑證、都在傳檔案——編譯那台顯示成功,TestFlight 卻一直沒有新版本。

下面先說大家常踩的錯法(模型 B),再說我們用的分法(模型 A) 以及怎麼驗收。

推薦分法:模型 A(編譯只做包,上傳只做發布)

一句話:編譯機只產出 .ipa 檔案;上傳機才碰憑證和 TestFlight。 不要在兩台機器上都跑憑證同步(match),否則私鑰會落在「錯誤的那台」上,上傳機就會報找不到發布憑證。

兩台雲 Mac 各幹什麼(模型 A)
角色 編譯機(香港) 示例:亞太時區白天編包 上傳機(加拿大) 固定 IP 交蘋果
要做的事拉程式碼 → Xcode 編譯 → 匯出 .ipa.ipa → 用發布憑證 → 上傳到 TestFlight
不要做不要跑 match、不要上傳 TestFlight不要重新完整編譯整個工程
交給下一台什麼安裝包檔案 + 本次建置編號(RUN_ID無(終點是 App Store Connect)

對應到 Fastlane 設定(給工程師對照):編譯機的腳本裡只有「編譯、匯出包」相關步驟;上傳機的腳本裡才有 matchupload_to_testflight 和蘋果的 API 金鑰。上傳機那台在蘋果後台登記的出口 IP 只填這一台

編譯機 · 只管打包容器 上傳機 · 只管交蘋果 build → export .ipa no certs · no upload includes build ID certs + TestFlight upload fixed public IP registered in ASC .ipa → 兩台之間只傳安裝包檔案,不傳整顆工程快取
模型 A:編譯機只交付 .ipa;上傳機才做憑證與 TestFlight

我們的雙機跑在 Hashvps 獨立 Mac mini 上,選它的主因是需要固定 IP + 機房角色隔離(upload 機 IP 寫進 ASC,build 機不必)。香港節點規格可對照 香港 Mac mini 方案

常見錯法:模型 B(兩台都在「幫倒忙」)

模型 B 就是職責沒分開,我們第一週也這麼幹過。表面上是兩台機器分工,實際上:

  • 兩台都跑憑證工具 match → 發布用的私鑰留在編譯機上,上傳機拿不到 → TestFlight 一直失敗
  • 把整顆工程建置快取目錄(.xcarchive)拷到另一台再打包 → 兩台設定不一致 → 傳上去的包蘋果不認
  • 兩台各自匯出一遍安裝包 → 匯出選項不一致 → 日誌寫成功,TestFlight 卻沒有可用版本

若你們流水線像上面任意一條,再加第三台 Mac 通常沒用,要先按模型 A 把「誰編譯、誰上傳、誰管憑證」分清楚。

可觀測性:怎樣確認 build 與 upload 是同一次構建

雙機最容易出的營運事故是「傳了昨晚的 IPA」。我們在編譯機、上傳機兩邊的日誌裡強制列印同一組欄位:

  • RUN_ID(流水線單次 ID,如 hk-build-88421
  • GIT_SHA(短 commit)
  • 製品路徑 + SHA256(寫在 build/manifest.json
編譯機成功 · 上傳機成功(同一 RUN_ID)
# 香港 · build · build
[15:11:42]: Successfully exported: ./build/MyApp-20260604-1502.ipa
[15:11:42]: RUN_ID=hk-build-88421  GIT_SHA=a3f91c2  SHA256=9f2a…

# 加拿大 · upload · upload
[02:22:01]: RUN_ID=hk-build-88421  GIT_SHA=a3f91c2  ipa_sha256=9f2a…
[02:24:18]: Successfully uploaded package to App Store Connect

值班規則很簡單:傳機日誌裡沒有相同 RUN_ID,一律按製品錯配處理,不要先查 ASC。

30 秒診斷:TestFlight 沒新 build?

先別翻完整日誌。按下面樹走一級即可定位章節(建議收藏本段):

值班診斷樹(複製到 Runbook)
TestFlight 沒新 build?
 ├─ 上傳機 RUN_ID ≠ 編譯機 RUN_ID(或缺 manifest)
 │     → 坑 2 · 製品邊界錯
 ├─ 日誌含 match / AppStoreDistribution / code signing identity
 │     → 坑 1 · 憑證邊界錯
 └─ upload 顯示成功,ASC 仍無包 / 403 / Invalid Binary(編譯機全綠)
       → 坑 3 · 網路身份漂移
三類故障 · 一眼對照(值班用)
模式 日誌關鍵詞(grep) Release 影響
坑 1 證書邊界 AppStoreDistributionNo matching provisioningmatch failed 阻斷:TestFlight / 上架全停
坑 2 製品邊界 RUN_ID 不一致、Invalid Binaryexport_method 衝突 :可能傳錯包或無法晉級
坑 3 身份漂移 403、upload OK 無 processing、ASC 網路限制 間歇阻斷:像「玄學」掉包

三個失敗模式(展開)

坑 1:證書邊界錯

30 秒判別:編機 archive 綠;傳機紅,且日誌在 match / signing 步驟失敗。

日誌關鍵詞:AppStoreDistributionCould not find a matching code signing identityNo matching provisioning profiles

Release 影響:阻斷級——TestFlight 與 App Store 上傳無法繼續,直到私鑰回到傳機。

現象:編機 archive 綠,傳機 upload 紅;TestFlight 無新 build。

典型日誌
Could not find a matching code signing identity for type 'AppStoreDistribution'
❌  Lane upload failed

根因:模型 B——編機仍跑 match(appstore),Distribution 私鑰不在傳機。

修復:編機刪除一切 match;傳機獨占 match(rotate 僅傳機、獨立窗口)。見 match readonly

坑 2:製品邊界錯

30 秒判別:對比編機 / 傳機日誌的 RUN_IDipa_sha256;或 ASC 報 Invalid Binary 而編機無 signing 錯誤。

日誌關鍵詞:RUN_ID 不一致、Invalid Binary、雙機出現兩次 export_ipa / ExportOptions

Release 影響:——可能上傳舊包、錯 commit,或 processing 卡死;不一定立刻紅 pipeline。

根因:跨機房傳 .xcarchive 或雙機各自 export。跨機房最小單位是可核對 SHA 的 IPA,不是 archive 目錄。

修復:編機單次 export_ipa + manifest.json;傳機只 upload,不再 export。

坑 3:網路身份漂移

30 秒判別:Fastlane 寫 upload 成功;編機、傳機 signing 全綠;TestFlight 仍無包或 ASC 403。

日誌關鍵詞:403Successfully uploaded 但無 build、ASC「網路」相關拒絕(編機日誌無異常)。

Release 影響:間歇阻斷——有時能傳、有時不能,最耗值班耐心。

根因:傳機 IPv4 與 ASC 白名單不一致(第三類隱性故障)。

修復:傳機每日 curl -4 ifconfig.me 對帳;漂移即改白名單 + 告警。

如何判斷你真的拆對了

門禁、FAQ、值班條目我們合併成下面這張驗收表。拆機一週後,每條都應能自動回答「是」:

雙機拆分驗收(模型 A)
驗收項 期望結果
RUN_ID 對齊編機 export 與傳機 upload 日誌中 RUN_IDGIT_SHAipa_sha256 一致
編機無 Distribution編機鑰匙圈 / 日誌中永不出現 AppStoreDistributionmatch step
傳機只做上傳傳機 lane 日誌只出現 match(或 resign)+ upload_to_testflight,無 ARCHIVE SUCCEEDED
製品形態跨機房檔案 < 200MB 的 .ipa + manifest;無 .xcarchive rsync
ASC 身份傳機當前 IPv4 = ASC 白名單;編機 IP 未寫入 ASC
Runner 隔離role=ios-build-hkrole=ios-upload-ca 標籤分離,workflow needs 串聯

升級判斷:你現在哪一階段?

排障解決「今晚怎麼救火」;階段判斷解決「下週該不該改架構」。對照下面四檔,看你是該維持、該糾偏,還是該進入 role split。

Stage 1:單機 Fastlane

特徵:一台 Mac runner;match + build + upload 在同一 Fastfile、同一鑰匙圈。可先優化磁碟、Derived Data 與 GitHub Actions 計費 窗口,再考慮加 build 節點。

結論:不需要雙機。若僅亞太編得慢,再考慮加 build 節點,而不是先拆 upload。

Stage 2:雙機但未 role split(模型 B)

特徵:已有兩台(或多台)Mac;多台都跑 match / export / upload;日誌裡常出現本文三類邊界錯誤。

結論:加第三台機器不會解決問題,只會提高證書漂移和製品錯配機率。下一步是收攏到模型 A,不是擴容 runner 數量。

Stage 3:Role split(模型 A · 本文目標)

特徵:build / upload 職責隔離;跨機房只傳帶 RUN_ID 的 IPA;match 只存在於 upload 節點;上傳出口 IP 固定。

結論:✔ 驗收表能穩定勾滿 → 可以橫向加 build runner(亞太多編)或加固 upload 專機。固定出口、分節點訂機可參考 Hashvps 方案(建議先鎖 upload 機 ASC IP,再擴 build)。

Stage 4:Multi upload node(進階)

特徵:多 upload 區域(如 US / EU / APAC);ASC API Key、IP 策略按區分;製品晉級與合規審計獨立成線。

結論:已進入分發系統範疇,不是「再多一台 CI Mac」——需要製品庫、策略與審計,超出本篇 Runbook。

階段 → 動作(決策閉環)
階段 你現在最該做的
Stage 1維持單機;優化構建與上傳時段
Stage 2停止加機;實施模型 A + 跑通驗收表
Stage 3橫向擴展 build;鞏固 upload 專機與監控
Stage 4單獨立項:多區域分發與合規,而非 Fastlane 補丁
升級觸發器(承認問題已發生)
如果你的 CI 已經出現「TestFlight 偶發不出包 + 多機 runner + match 參與多節點」,說明你已進入必須做 role split 的階段,而不是繼續擴容 CI runner。閱讀到這裡仍卡在 Stage 2,請按上文診斷樹 + 模型 B 反模式改 lane,不要先買第三台 Mac。

最終結論(只看這一段也能執行)

如果你現在同時滿足下面三條,請按本段拍板,不必重讀全文:

  • 已有 ≥ 2 台 Mac runner(或等價多節點 CI)
  • Fastlane 鏈路出現 TestFlight 不穩定 / 不上傳match failed、processing 卡住、偶發無包)
  • match 可能在多台機器上執行過

👉 不要再加機器。

👉 立即做三件事(今天就能開工):

  1. 停止所有非 upload 節點上的 match / sync_code_signing(簽名權只留在 upload 機)
  2. 強制跨機房只傳 .ipa + manifest.json(禁止 rsync .xcarchive
  3. 統一 RUN_ID + GIT_SHA + ipa_sha256,傳機 upload 前必須校驗

如果上面三條做不到:你仍在 Stage 2(模型 B)加機器 = 放大故障,不是修復。 先改 Fastfile / workflow 標籤,跑通上文驗收表,再談擴容 build 節點。

上傳機 Fastfile(模型 A · 節選)
lane :upload_only do |options|
  verify_run_id!(options) # manifest.json
  match(type: "appstore", readonly: true)
  upload_to_testflight(ipa: options[:ipa_path], api_key_path: "asc_api_key.json")
end
變更紀律
拆 lane 當週不要 rotate match。證書變更 = 獨立事件,先停雙 lane,只在傳機操作。

還會被問到的幾句

編機完全不簽名能 archive 嗎? 取決於工程配置;我們用的是編機 export 出待傳機做最終 app-store 簽名的 IPA 路徑(或編機僅編譯校驗、傳機 resign)。關鍵是:match 只在一台機器上發生

match 快過期了怎麼辦? 只在傳機、非 readonly 窗口 rotate;編機停用至 profile 穩定。

和出差 Runbook 的區別? 出差文講人在現場的 Wi‑Fi;本篇講機房角色與 Fastlane 邊界,可照抄驗收表。

系列說明:本文為系列 ① Runbook:雙雲 Mac + Fastlane 排障(TestFlight 不上傳 / match failed / iOS CI 多機問題)。後續可拆:② Build·Sign·Distribute 架構篇、③ 模型 B 反模式專題;選型見 TestFlight 專機

延伸閱讀

雙機角色與節點規格見 香港套餐定價頁。 評估是否加 upload 專機仍建議先看 TestFlight 專文

Hashvps

卡在 Stage 2?先 role split,再加機器

upload 專機固定 IPv4,適合 ASC 白名單;build 節點可按亞太需求橫向擴展。

查看方案
限時優惠