在雲端 Mac 上建立 iOS IPv6-only 網路相容性門檻

CI/CD 實踐 ·約 8 分鐘閱讀

在雲端 Mac 上建立 iOS IPv6-only 網路相容性門檻

行動網路、企業 Wi-Fi 或電信商測試環境切換成 IPv6-only 後,最常見的問題通常不是「完全無法連線」,而是登入成功卻無法上傳圖片、第一次請求正常但重新連線時逾時,或某個舊介面仍在設定中寫死 IPv4 位址。雲端 Mac 適合把這些偶發問題轉成固定的 CI 門檻:每次合併都掃描位址字面值、確認 DNS64 解析條件、執行網路測試,並封存失敗現場。

先定義門檻邊界

支援 IPv6-only 並不代表伺服器必須只提供 AAAA 記錄。只要測試網路具備 DNS64/NAT64,使用網域名稱並交由系統解析器處理的用戶端,仍然可以存取僅提供 IPv4 的服務。真正危險的是用戶端繞過系統解析流程,例如快取解析後的 IPv4 位址、強制使用 AF_INET,或把 192.0.2.10 這類位址寫進業務設定。

建議把門檻拆成三層:

層級 檢查內容 失敗時保留
靜態檢查 IPv4 字面值、位址家族常數、手寫通訊端 檔名與行號
環境預檢 DNS、預設路由、目標網域名稱解析 scutil 與解析結果
行為測試 登入、上傳、重試、長連線復原 測試日誌與結果套件

不要以 ping 成功取代業務驗收。應用程式實際使用的是 HTTPS、WebSocket 或檔案上傳;請求路徑、逾時策略與連線重用都可能產生不同結果。

掃描儲存庫中的 IPv4 假設

先在 CI 工作區執行保守掃描。以下運算式只負責找出候選項目,不應直接把所有命中結果都判定為缺陷,因為測試固定資料、文件與回送位址都可能是合法內容。

set -euo pipefail

mkdir -p artifacts/ipv6
rg -n \
  --glob '*.{swift,m,mm,h,plist,json,yaml,yml,xcconfig}' \
  '([0-9]{1,3}\.){3}[0-9]{1,3}|AF_INET([^6]|$)|sockaddr_in([^6]|$)' \
  . | tee artifacts/ipv6/ipv4-candidates.txt

if rg -n \
  --glob 'Sources/**' \
  --glob 'Config/**' \
  'https?://([0-9]{1,3}\.){3}[0-9]{1,3}' .; then
  echo "Production configuration contains an IPv4 URL" >&2
  exit 1
fi

候選清單需要先人工分類一次,再把允許項目加入附有註解的白名單。不要直接排除整個 Tests 目錄,因為測試輔助程式碼同樣可能被複製到正式網路層。對於 C 或 Objective-C 封裝,應特別檢查 getaddrinfo 的 ai_family 是否使用 AF_UNSPEC;只有明確要求單一位址家族時,才應將它固定。

避免錯誤修正

不要把 IPv4 位址機械式改寫成 IPv4-mapped IPv6,也不要自行拼接 NAT64 前綴。前綴取決於目前使用的網路,一旦寫死,切換到其他網路便會失效。正確做法是保留網域名稱,並在建立連線時讓系統解析器傳回當下可用的位址。

把 DNS64 條件設為前置檢查

測試開始前先記錄網路狀態,避免環境本身不符合條件時產生大量誤報。雲端 Mac 的執行節點應使用團隊事先準備好的受控 DNS64/NAT64 網路;CI 指令碼只負責驗證條件,不應在工作執行期間臨時修改系統網路設定。

set -euo pipefail

OUT="artifacts/ipv6"
mkdir -p "$OUT"

scutil --dns > "$OUT/scutil-dns.txt"
route -n get default > "$OUT/default-route.txt" 2>&1 || true
dscacheutil -q host -a name api.test.example \
  > "$OUT/target-resolution.txt"

if ! grep -Eq 'ipv6_address|ip_address' "$OUT/target-resolution.txt"; then
  echo "Target hostname did not resolve in the test environment" >&2
  exit 2
fi

測試網域名稱應屬於團隊自己的受控環境,並傳回穩定且不含敏感資訊的健康檢查回應。不要把公共網站當成門檻的相依項目,否則外部流量限制、憑證變更或區域政策都會讓建置結果失去確定性。

使用 URLSession 驗證實際行為

網路測試應涵蓋「首次成功」與「失敗後復原」兩條路徑。請求必須設定有限的逾時時間,並透過相依性注入傳入基礎 URL,避免測試程式碼再次寫死位址。

import XCTest

final class IPv6ReadinessTests: XCTestCase {
    func testHealthEndpointReturnsExpectedStatus() async throws {
        let value = try XCTUnwrap(
            ProcessInfo.processInfo.environment["TEST_BASE_URL"]
        )
        let baseURL = try XCTUnwrap(URL(string: value))
        let url = baseURL.appending(path: "health")

        let configuration = URLSessionConfiguration.ephemeral
        configuration.timeoutIntervalForRequest = 15
        configuration.timeoutIntervalForResource = 30

        let session = URLSession(configuration: configuration)
        let (_, response) = try await session.data(from: url)
        let http = try XCTUnwrap(response as? HTTPURLResponse)

        XCTAssertEqual(http.statusCode, 200)
    }
}

執行時固定測試計畫、模擬器型號與結果目錄:

set -o pipefail

xcodebuild test \
  -workspace App.xcworkspace \
  -scheme AppNetworkTests \
  -testPlan IPv6Readiness \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  -resultBundlePath artifacts/ipv6/IPv6Readiness.xcresult \
  TEST_BASE_URL='https://api.test.example/' \
  | tee artifacts/ipv6/xcodebuild.log

如果業務包含上傳與長連線,還應加入小型檔案上傳、連線中斷後的指數退避,以及應用程式切換前景與背景後的復原測試。斷言應針對業務結果,不要依賴本地化的錯誤訊息。

失敗時保留可診斷的證據

門檻失敗後,至少應保留靜態掃描結果、DNS 狀態、目標解析結果、xcodebuild 日誌與 xcresult。日誌中不要記錄權杖、請求本文或完整的授權標頭。若需要輸出請求資訊,只記錄方法、經過去識別化處理的路徑、狀態碼、重試次數與錯誤網域。

疑難排解順序可以固定為:

  1. 確認環境預檢是否通過,以及目標網域名稱是否由系統完成解析。
  2. 檢查失敗的請求是否使用網域名稱,而不是解析後快取的位址。
  3. 檢查底層函式庫是否限制為 AF_INET,或自行建立 IPv4 通訊端。
  4. 比較首次連線與重新連線的路徑,確認兩者採用相同的解析策略。
  5. 在受控 DNS64/NAT64 網路中的實體裝置上,重新驗證關鍵流程。

模擬器門檻負責高頻率的迴歸測試,實體裝置則負責發佈前確認。兩者職責分開後,日常建置不必依賴人工操作,網路團隊也能依據封存的證據,判斷問題出在解析、連線還是業務重試層。VPSGit 上的執行節點只要維持工具鏈、測試計畫與環境變數一致,就能持續重複執行這套檢查,而不必等到使用者回報後才臨時重建現場。

常見問題

App 只使用網域名稱,就一定相容 IPv6-only 網路嗎?

不一定。服務端必須能透過 DNS64/NAT64 存取,App 也不能快取 IPv4 位址、強制 AF_INET,或繞過系統解析器自行建立位址。

IPv6-only 測試只用 iOS 模擬器就足夠嗎?

模擬器適合持續回歸,但發布前仍應在受控 DNS64/NAT64 網路中進行一次實機驗收,涵蓋登入、上傳、回呼與長連線恢復。

獨享實體節點

為下一次建置或推論任務設定雲端 Mac

從三種 Apple Silicon 設定、六個節點與固定租期中選擇,實際可用狀態以控制台即時回傳為準。

設定雲端 Mac