1. なぜMacでセルフホスト型のGitLabランナーを使うのか?
GitLabはLinux上の共有ランナーを提供していますが、iOSアプリのビルドにはApple製ハードウェア上で動作するmacOSが必要です。GitLab公式のmacOS共有ランナーは制約が多く、コストも高くなります。セルフホスト型のMac Mini M4ランナーなら、次のメリットが得られます。
GitLabの無料プランでは、共有ランナーで400分のCI/CD実行時間が含まれます。セルフホストなら制限はありません。
ユーザーのデバイスと同じM4チップ上でビルドできます。Rosettaによる変換のオーバーヘッドはありません。
必要なXcodeのバージョン、シミュレーター、ツール、依存関係を自由にインストールできます。
DerivedData、SPMパッケージ、CocoaPodsのキャッシュがパイプライン実行間で保持されます。
2. GitLab Runnerをインストールする
Mac Mini M4にSSH接続し、Homebrewを使用してGitLab Runnerをインストールします。
# Connect to your Mac Mini M4
ssh admin@your-server-ip
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Install GitLab Runner
brew install gitlab-runner
# Verify installation
gitlab-runner --version
# Version: 17.7.0
# Git revision: ...
# Git branch: 17-7-stable
# GO version: go1.22.10
# Built: ...
# OS/Arch: darwin/arm64
macOSサービスとしてインストールする
# Install the runner as a launchd service
# This ensures it starts automatically on boot
brew services start gitlab-runner
# Verify the service is running
brew services list | grep gitlab-runner
# gitlab-runner started admin ~/Library/LaunchAgents/homebrew.mxcl.gitlab-runner.plist
# Check runner status
gitlab-runner status
# gitlab-runner: Service is running
3. ランナーを登録する
GitLabのプロジェクト(またはグループ)を開き、Settings > CI/CD > Runners > New project runner に移動します。登録トークンをコピーします。
新しいランナー登録フローで登録する(GitLab 16以降)
# Register the runner using the authentication token from GitLab UI
# (GitLab 16+ uses authentication tokens instead of registration tokens)
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--token "YOUR_RUNNER_AUTHENTICATION_TOKEN" \
--executor "shell" \
--description "mac-mini-m4-runner" \
--tag-list "macos,apple-silicon,m4,ios,xcode"
レガシーの登録トークンで登録する(GitLab 15以前)
# For older GitLab instances using registration tokens
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--registration-token "YOUR_REGISTRATION_TOKEN" \
--executor "shell" \
--description "mac-mini-m4-runner" \
--tag-list "macos,apple-silicon,m4,ios,xcode" \
--run-untagged="false"
ランナーの構成を確認する
# View the runner config file
cat ~/.gitlab-runner/config.toml
# Expected output:
# concurrent = 2
# check_interval = 0
#
# [session_server]
# session_timeout = 1800
#
# [[runners]]
# name = "mac-mini-m4-runner"
# url = "https://gitlab.com/"
# token = "..."
# executor = "shell"
# [runners.cache]
# MaxUploadedArchiveSize = 0
# Adjust concurrency based on your hardware:
# Mac Mini M4 (16GB): concurrent = 2
# Mac Mini M4 Pro (24GB): concurrent = 3
# Mac Mini M4 Pro (48GB): concurrent = 4
~/.gitlab-runner/config.toml を編集して、並行実行数(concurrency)を調整します。
# Edit the config
nano ~/.gitlab-runner/config.toml
# Set concurrent to match your hardware capacity
concurrent = 2
# Restart the runner to apply changes
gitlab-runner restart
これでランナーは、GitLabプロジェクトの Settings > CI/CD > Runners に オンライン として表示されるはずです。
4. iOS向けに.gitlab-ci.ymlを作成する
リポジトリのルートに .gitlab-ci.yml ファイルを作成します。この完全なパイプラインは、iOSアプリのビルド、テスト、デプロイを行います。
# .gitlab-ci.yml
stages:
- setup
- build
- test
- deploy
variables:
SCHEME: "MyApp"
WORKSPACE: "MyApp.xcworkspace"
DESTINATION: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2"
DERIVED_DATA: "${CI_PROJECT_DIR}/DerivedData"
# Only run on our Mac runner
default:
tags:
- macos
- m4
# ---- SETUP ----
setup:
stage: setup
script:
- sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
- xcodebuild -version
- swift --version
# Install CocoaPods if using Podfile
- |
if [ -f "Podfile" ]; then
pod install --repo-update
fi
cache:
key: pods-${CI_COMMIT_REF_SLUG}
paths:
- Pods/
- .spm-cache/
# ---- BUILD ----
build:
stage: build
needs: ["setup"]
script:
- |
xcodebuild build \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-clonedSourcePackagesDirPath ".spm-cache" \
CODE_SIGNING_ALLOWED=NO \
| xcbeautify
cache:
key: derived-data-${CI_COMMIT_REF_SLUG}
paths:
- DerivedData/
- .spm-cache/
artifacts:
paths:
- DerivedData/
expire_in: 1 hour
# ---- TEST ----
unit_tests:
stage: test
needs: ["build"]
script:
- |
xcodebuild test \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-resultBundlePath "TestResults.xcresult" \
-parallel-testing-enabled YES \
| xcbeautify
artifacts:
when: always
paths:
- TestResults.xcresult/
reports:
junit: TestResults.xcresult/report.junit
expire_in: 7 days
after_script:
- xcrun simctl shutdown all 2>/dev/null || true
# ---- DEPLOY ----
deploy_testflight:
stage: deploy
needs: ["unit_tests"]
only:
- main
script:
- |
# Install or update Fastlane
which fastlane || brew install fastlane
# Run Fastlane beta lane
fastlane beta
environment:
name: testflight
variables:
MATCH_PASSWORD: ${MATCH_PASSWORD}
APP_STORE_CONNECT_API_KEY_ID: ${APP_STORE_KEY_ID}
APP_STORE_CONNECT_API_ISSUER_ID: ${APP_STORE_ISSUER_ID}
APP_STORE_CONNECT_API_KEY_CONTENT: ${APP_STORE_KEY_CONTENT}
GitLabにCI/CD変数を追加する
GitLabプロジェクトで Settings > CI/CD > Variables に移動し、これらの変数を「Masked」および「Protected」として追加します。
-
MATCH_PASSWORD- Fastlane Matchの暗号化用パスワード -
APP_STORE_KEY_ID- App Store Connect APIキーのID -
APP_STORE_ISSUER_ID- App Store ConnectのIssuer ID -
APP_STORE_KEY_CONTENT- .p8キーファイルの内容
5. パフォーマンスを最適化する
GitLabのキャッシュ構成
GitLab Runnerは、shellエグゼキューターのローカルキャッシュをサポートしています。ランナーが永続的であるため、ローカルキャッシュは非常に効率的です。
# In .gitlab-ci.yml, configure cache per branch:
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- DerivedData/
- .spm-cache/
- Pods/
policy: pull-push
# For test jobs that don't modify cache, use pull-only:
unit_tests:
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- DerivedData/
policy: pull
ステージ間のデータにアーティファクトを使用する
# Pass build artifacts between stages efficiently
build:
artifacts:
paths:
- DerivedData/Build/Products/
expire_in: 2 hours
# The test stage receives the built products without rebuilding
test:
needs: ["build"] # only download artifacts from the build job
script:
- xcodebuild test-without-building \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}"
テストの並列実行
# Split tests across parallel jobs using GitLab's parallel keyword
unit_tests:
stage: test
parallel: 2
script:
- |
# Use test plan partitioning or custom splitting
xcodebuild test \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4
キャッシュの定期的なクリーンアップ
# On the Mac Mini, set up a weekly cleanup cron job
crontab -e
# Add these lines:
# Clean DerivedData older than 7 days every Sunday at 3 AM
0 3 * * 0 find ~/builds/*/DerivedData -maxdepth 0 -mtime +7 -exec rm -rf {} + 2>/dev/null
# Clean old GitLab Runner builds older than 14 days
0 4 * * 0 find ~/builds -maxdepth 2 -mtime +14 -type d -exec rm -rf {} + 2>/dev/null
# Clean Homebrew cache monthly
0 5 1 * * /opt/homebrew/bin/brew cleanup --prune=30 2>/dev/null
6. トラブルシューティング
GitLabでランナーが「offline」と表示される
ランナーサービスのステータスとログを確認します。
# Check service status
brew services list | grep gitlab-runner
# View logs
cat /usr/local/var/log/gitlab-runner.log
# Restart the service
brew services restart gitlab-runner
# Verify connectivity to GitLab
gitlab-runner verify
ビルド中の「Permission denied」エラー
ランナーがXcodeのデベロッパーディレクトリへのアクセスを必要としている可能性があります。
# Ensure the runner user has Xcode access
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
sudo xcodebuild -license accept
# If using simulators, ensure the user can access them
xcrun simctl list devices
キャッシュが復元されない
キャッシュキーが一貫しており、パスが存在することを確認します。
# Check cache directory permissions
ls -la ~/builds/
# The shell executor stores caches locally by default
# Verify the cache directory in config.toml:
cat ~/.gitlab-runner/config.toml
# Ensure [runners.cache] section has the right settings
# For local caching (most efficient for persistent runners):
# [runners.cache]
# Type = "" # empty = local cache
Xcodeのビルドがハングまたはタイムアウトする
これは多くの場合、キーチェーンのアクセス許可ダイアログやシミュレーターの問題が原因です。
# Unlock the keychain before builds
security unlock-keychain -p "YOUR_PASSWORD" ~/Library/Keychains/login.keychain-db
# Kill stuck simulators
xcrun simctl shutdown all
pkill -f "Simulator.app" 2>/dev/null || true
# Set a build timeout in .gitlab-ci.yml
build:
timeout: 30 minutes
7. FAQ
macOSでDockerエグゼキューターを使用できますか?
macOS上のDockerは、Linuxコンテナを仮想マシン内で実行するため、macOSのAPI、Xcode、iOSシミュレーターにはアクセスできません。iOSのビルドには、shellエグゼキューターを使用する必要があります。Dockerは、Macランナーと並行して実行するサーバーサイドのSwiftやその他のLinuxベースのタスクには適しています。
GitLabグループ向けにランナーを登録するにはどうすればよいですか?
GitLabグループの Settings > CI/CD > Runners > New group runner に移動します。プロジェクトトークンの代わりにグループランナートークンを使用します。これにより、グループ内のすべてのプロジェクトでランナーが利用可能になります。
shellエグゼキューターとSSHエグゼキューターのどちらを使うべきですか?
shellエグゼキューターを使用してください。コマンドをMac上で直接実行するため、Xcode、シミュレーター、キーチェーンにフルアクセスできます。SSHエグゼキューターはリモートマシン向けであり、ランナーがすでにMac上にある場合は不要です。
同じMacでGitLabとGitHub Actionsの両方のランナーを実行できますか?
はい。どちらのランナーも軽量で、同じMac Mini M4上で共存できます。各ランナーの並行実行数を設定する際は、リソース使用量の合計を考慮するようにしてください。
GitLab Runnerを更新するにはどうすればよいですか?
# Update via Homebrew
brew upgrade gitlab-runner
# Restart the service
brew services restart gitlab-runner
# Verify the new version
gitlab-runner --version
フルマネージドのGitLab Runnerをお探しですか?Cloud-Runnerをお試しください
セットアップは一切不要です。Cloud-Runnerは、Macハードウェア上に事前構成済みの専用GitLab Runnerを提供します—インストールもメンテナンスも必要ありません。