チームはすでにGitLab(SaaSまたはセルフマネージド)を運用していますが、iOS/macOSビルドはホスト済みmacOSランナーのキュー、兼用ノートPC、またはJenkins立ち上げの間で足踏みしています。VPS運用の感覚を持つプラットフォームエンジニア向けに、本稿は「ノードのようにMacを借りる」道筋を示します。専用リモートMac上のGitLab Runnerで実際の摩擦を浮かび上がらせる7つのチェックリスト、Jenkins SSHエージェントおよびGitHub Actionsセルフホスト済みランナーとの三者比較表、続いて登録・タグ・キャッシュディレクトリ・同時実行と.gitlab-ci.ymlスケッチを含む6段階の引き渡しランブック、さらにランナー・Jenkins・SwiftPM/Podsディスク運用各記事へのクロスリンクです。
GitLabのドキュメントではgitlab-runner registerが滑らかに見えますが、本番では時間はしばしばAppleツールチェーンの状態機械と同時ディスク衝突で溶けます。以下の7項目で「ランナーを足す」ことを、署名できるリスク表に変えます。
リモートMacをLinuxランナー同然に扱う:TCC・キーチェーン・時折必要なGUIを無視すると初回署名が破綻します。SSHとVNCのチェックリストとあわせてレビューしてください。
個人のmacOSアカウントで登録する:スリープ・アップデートプロンプト・デスクトップセッションが真の無人フローを壊します。再現可能ビルドに沿った専用CIユーザーにしてください。
同時実行をCPUコアだけで決める:XcodeのRAMスパイクとNVMe書き込み増幅が先に噛みます。DerivedDataをバケット化しなければ2ジョブがデッドロックします。SwiftPM/Podsディスク運用と同じ契約です。
ランナートークンと登録衛生を軽視する:config.tomlのバックアップを平文のように散らすと、ローテーション当日にキューが赤くなるとき誤った安心だけが残ります。
キャッシュ方針をコピペする:悪いcache:キーはブランチ間汚染か常時ミスを招きます。ブランチとロックファイル次元でキーを設計します。
成果物をランナーディスクだけに置く:GitLabの成果物やオブジェクトストレージがなければディスクとコンプライアンスの両方が痛みます。保持はセキュリティレビューに結び付けます。
「最初の人間の窓」がない:初期署名プロファイルはヘッドレス復帰前に一度だけVNCやデスクトップ確認が要る場合があります。Fastlane+CIを参照してください。
共通の根は、「リモートMac」を生の計算資源ではなくXcodeフィンガープリントとキーチェーン境界を持つホストとして扱わないことです。イメージフィンガープリント・ツールチェーン版・クリーンアップの水位・ランナータグ契約を、レプリカのように保守します。エンタープライズビルドプールと組み合わせてください。複数プロジェクトが同一ホストを共有する場合、GitLabのtagsは「どれかのMac」より細かくなければ、resource_groupは真の分離を表現できません。
GitHub Actionsとの差は「コンパイルできるか」ではなくパイプライン定義とイベントソースです。.gitlab-ci.ymlはMRライフサイクルにネイティブに結び付き、ActionsはPRに結び付きますがGitHubからの移行コストが高いです。GitLab一択なら、macOSをシェルランナーにする方がiOS向けに第二のJenkins方言を維持するより一貫しがちです。それでもオンプレオーケストレーションのレビューではJenkinsが勝ることもあります。次の表がトレードオフを固定します。
登録前にランナーガイドのキャッシュとラベル節を読んでください。多くのディレクトリ契約はGitLabのcacheとCI_PROJECT_DIRにそのまま写ります。変わるのはワークフローからパイプラインへのトリガーだけです。
銀の弾はありません。オーケストレーションの思考モデルと認証情報の境界を選びます。レビューに3つのSLAを書き込みます。キュー遅延、説明可能な失敗、キーローテーションコストです。
| 観点 | GitLab Runner(macOSシェル) | GitHub Actionsセルフホスト | Jenkins SSHエージェント |
|---|---|---|---|
| パイプライン定義 | .gitlab-ci.ymlはプロジェクト/MRにネイティブ。テンプレートとincludeが成熟 | リポジトリ内YAMLがPR/Issueイベントに強結合 | Job DSL/Pipeline Groovy — クロスリポオーケストレーションは柔軟だがスタイルドリフトが大きい |
| 登録モデル | プロジェクト/グループトークン。config.tomlがエグゼキュータとタグを集約 | 組織/リポジトリランナートークンで比較的標準的なセットアップ | コントローラがSSH資格情報を保持 — コントローラ面を硬化 |
| 同時実行とスロットル | resource_group、parallel、ランナー同時実行上限 | YAML内のマトリクスとconcurrency | ラベル+スロットルプラグイン — 柔軟だが設定が重い |
| キャッシュと成果物 | ネイティブcache/artifacts。悪いキーはキャッシュを汚染 | actions/cacheと成果物の豊かなエコシステム | オブジェクトストアへのDIY接続が多い |
| 最適フィット | MRパイプラインと統一ランナープールが欲しいGitLab中心組織 | GitHub中心、PR駆動デリバリー | 複数プロダクトライン、オンプレ成果物ストア、承認、混在Gitホスト |
GitLabの文脈でMacを「VPSのように」借りるとは、登録可能なランナープロファイルを買うことです。固定SSH、予測可能なディスク階層、Xcodeフィンガープリントをタグに刻める能力です。
GitLab Runnerが勝つなら、tagsを第一級に扱います。Xcodeマイナー、重いpod installの可否、UIテストの有無はすべて明示します。スナップショット対長寿命ノードと組み合わせます。長寿命ランナーは増分クリーンアップに寄り、ゴールデンイメージはウォームアップ済みイメージとロールバックスモークに寄ります。
複数CIを運用するならDerivedDataのバケット化を統一し、同一リモートMac上でGitLabジョブがJenkinsやActionsジョブを踏みつけないようにします。Unixユーザーまたはルートを分け、「ずらしたスケジュールを祈る」のでは足りません。
順序が重要です。まず身元とディレクトリ、次に登録とタグ、最後に同時実行です。再現可能ビルドにフィンガープリントスクリプトを揃え、GitLabが「git cloneが動く」だけでなく安定署名を検証できるようにします。
専用ユーザーと作業ルートを作る:例:/Users/ci/gitlab-runner。個人の~/Desktopと混ぜない。SSHは鍵のみ。
gitlab-runnerをインストール:公式macOSパッケージまたはHomebrew。PATH上のバイナリがサービスアカウント(launchd)下で動くこと。
registerを実行:shellエグゼキュータを選び、GitLabのURLと登録トークンを渡す。tag_list(例:ios,shell,m4)を対話または非対話フラグで固定。
最初のヘルスチェックジョブ:xcode-select -p、xcodebuild -version、swift --version、ディスクスナップショットを出力。ログをランナー受入証跡として保存。
.gitlab-ci.ymlでDerivedDataを明示:SwiftPM/Podsディスク運用と同じ契約 — プロジェクトごとにバケット化し、デフォルト共有パスを避ける。
タイムアウト・成果物・クリーンアップを定義:timeout、失敗時保持、ディスク低下時の停止ライン(監視+ランナー停止API)。
stages: [build]
variables:
DERIVED_DATA: "$CI_PROJECT_DIR/.derivedData/$CI_PIPELINE_ID"
build_ios:
stage: build
tags: [ios, shell, m4]
timeout: 45m
script:
- xcode-select -p
- xcodebuild -version
- df -h
- xcodebuild -scheme "App" -configuration Release -destination "generic/platform=iOS" -derivedDataPath "$DERIVED_DATA" build
artifacts:
when: on_failure
paths:
- "**/*.xcresult"
expire_in: 3 days
ヒント:パイプラインがストア配信も行う場合はFastlane+CIを読み、ビルドユーザー・キーチェーンパーティション・App Store Connect APIキーをGitLab CI/CD変数(マスク+シークレット保護)と揃えます。
GitLabまたはランナーアップグレード時は同一コミットで前後のカナリアiOSジョブを走らせ、フィンガープリント出力とビルド時間分布を比較します。ランナーキャッシュとの対比:緩すぎるGitLabキャッシュキーはブランチAがBのPodsキャッシュを汚染し、厳しすぎるキーは冷起動の連続を意味します。プラットフォームとプロダクトが保持ティアで合意する必要があります。
プロバイダが固定SSHポートと非rootユーザーを渡すなら、接続パラメータは散在する変数説明ではなく内部ランブックに集約し、ローテーションは一か所で。Jenkins+リモートMacと併せ、SSHベースライン(鍵・ファイアウォール・監査)はCIスタック横断で単一ソースにし、三つの方言にしないでください。
resource_group、重い依存関係インストールのスロットルよくある誤りは「xcodebuildプロセスがいくつ入るか」だけで同時実行を決めることです。pod install/SPM解決とコンパイルスパイクはしばしば別フェーズです。.gitlab-ci.ymlのresource_groupでミューテックス資源を表すか、ジョブを分割します。SwiftPM/Pods運用と併せ、重い解決ジョブを別途スロットルし、頻繁なグリーンビルドのスロットを奪わないようにします。
テストは別の隠れた次元です。シミュレータUIテストはコンパイルのみのパイプラインとは異なる同時実行モデルが要ります。XCTestとシミュレータのシャーディングを読み、GitLabでは専用タグまたはランナープールで隔離します。
警告:ディスクが安全水位を下回っているのにキューを詰め込み続けないでください。スケジューリングを止めて先に掃除しなければ、短い待ち行列より高くつく半書き込みのXcode/git状態を招きます。
複数リージョンにランナーがあるなら、リージョンを名前とタグに埋め込み、成果物パスにラベルを付け、大きなリージョン間転送をビルド失敗と誤読しないようにします。購入対レンタルTCOと併せ、遅延とエグレスは最初からコストモデルに入れます。
以下の箇条書きは内部すり合わせ用です。しきい値はリポジトリ規模と並列度に合わせて調整します。
gitlab-runner --version、xcodebuild -version、CocoaPods利用時はRuby/Bundler、ディスクモデルを記録。変更後はカナリアパイプラインを起動します。オフィス専用Macはスリープ・ネットワークジッター・ツールチェーンドリフトに悩まされます。LinuxはApple公式のiOSツールチェーンをホストできません。慣れたWeb/MRワークフローでGitLabを保ちつつ、macOS実行を専用・常時稼働・SSH到達可能なリモートノードへ移すと、「パイプライン真実の単一ソース」がスローガンから契約になります。アドホックな自社ハードや脆い仮想化Xcodeスタックと比べ、SSHエンドポイント・ディスク階層・再現可能なランナープロファイルが明確なNodeMini Mac Miniクラウドレンタルの方がしばしば強いプラットフォーム手です。仕様と料金はレンタル価格で比較し、オンボーディングはヘルプセンターで仕上げます。
本ランブックを内部ツールチェーン変更ティアに結び付けます。Xcodeパッチ/マイナー/メジャーアップグレードは承認範囲・カナリア範囲・キャッシュ無効化方針が異なります。
ネイティブiOS/macOSチームの多くは、Xcode・キーチェーン・シミュレータとの摩擦が最も少ないシェルエグゼキュータから始めます。Dockerはコンテナ化スタックや強い分離向きですが、Appleツールチェーンではコストが増えがちです。まずハードウェア階層はレンタル価格で比較してください。
CPUコアだけで決めないでください。単一ジョブのピークRAMとNVMe書き込み増幅を測り、同時実行を増やしながらP95を見ます。DerivedDataと依存キャッシュをバケット化し、重いpod installジョブはスロットルします。オンボーディングの質問はヘルプセンターを参照してください。
GitLabはプロジェクト/グループランナートークンを.gitlab-ci.ymlと結び付けます。Jenkinsはエンタープライズオーケストレーションとプラグインに寄り、ActionsはPRイベントに強く結び付きます。イベントソース・シークレット境界・キューSLAを文書化し、ロゴではありません。続きはJenkins+リモートMacとGitHub Actionsランナーです。