Акция

Self-hosted runner Mac mini GitHub Actions: продакшен-гайд (launchd + изоляция CI | 2026)

CI Mac mini · Self-hosted runner
2026-06-06 ~15 мин

Хаб Mac mini GitHub Actions self-hosted runner: архитектура, установка, безопасность, приёмка iOS / Flutter. Аренда Mac для проверки, затем месячный runner.

Что такое self-hosted runner GitHub Actions на Mac mini?

Mac mini GitHub Actions self-hosted runner — среда CI/CD под вашим управлением на Apple Silicon. Заменяет macOS runner GitHub: постоянные кэши, быстрые iOS-сборки, полный контроль Xcode—идеально при аренде Mac или выделенном cloud Mac mini.

Как развернуть GitHub Actions runner на Mac mini?

  1. Создать выделенного пользователя ci в macOS
  2. Установить пакет GitHub Actions runner
  3. Настроить runner токеном регистрации
  4. Служба launchd для автозапуска
  5. Метки для маршрутизации workflow
  6. Проверить пустым Job

Поисковые намерения (5 типов)

  1. Что — определение self-hosted runner на Mac mini
  2. Зачем — почему iOS CI нужен Mac mini (vs GitHub-hosted)
  3. Как — продакшен-установка (4 шага + launchd)
  4. Безопасность — изоляция ci + защита fork PR
  5. Производительность — кэш даёт ускорение 30%–60%
Mac mini GitHub Actions self-hosted runner: launchd и изоляция CI-пользователя
Статья-хаб кластера Mac mini GitHub Actions Runner, связана с трёхплоскостной архитектурой Flutter iOS CI.

Self-hosted runner GitHub Actions (версия Mac mini)

Mac mini GitHub Actions self-hosted runner выполняет workflow jobs на локальном или облачном Mac mini вместо macOS runner GitHub.

Пять ключевых преимуществ на Mac mini:

  • ✔ Долгоживущие кэши сборки (DerivedData / CocoaPods / SPM)
  • ✔ Полный контроль версии Xcode (Golden Image)
  • ✔ Фиксированная стоимость CI (без поминутной оплаты)
  • ✔ Значительно быстрее iOS-сборки (горячие 5–6 мин)
  • ✔ Корпоративная изоляция (ci + отдельный Keychain)

Почему iOS CI должен использовать Mac mini self-hosted runner

Три узких места GitHub-hosted runner

Проблема GitHub-hosted macOS Mac mini self-hosted
Кэш сборкиСброс каждый job (stateless)Постоянный (Pods / DerivedData на SSD)
Версия XcodeСледует обновлениям GitHubЗафиксирована (Golden Image)
Стоимость CIПоминутнаяФикс (аренда Mac / покупка)
Скорость сборкиХолодный старт 8–12 минГорячая 5–6 мин

Вывод

GitHub-hosted runner = машина без памяти
Mac mini runner = CI с памятью

Суть разрыва производительности iOS CI. Далее: Оптимизация Flutter iOS CI: с 28 до 9 минут.

Продакшен трёхслойная архитектура Mac mini CI

Общая модель

┌──────────────────────────────┐
│ Layer 1: launchd daemon      │  ← reliability (auto-restart / crash recovery)
├──────────────────────────────┤
│ Layer 2: ci user isolation   │  ← security (Keychain / permission boundary)
├──────────────────────────────┤
│ Layer 3: label routing       │  ← maintainability (workflow → Runner)
└──────────────────────────────┘

Слой 1 — демон launchd (стабильность)

launchd = systemd macOS. Автоперезапуск, восстановление после reboot, relaunch после crash, уровень демона.

Три обязательных ключа plist:

  • RunAtLoad = true — автозапуск после входа
  • KeepAlive = true — перезапуск после сбоя
  • WorkingDirectoryобязателен (главная причина offline)

Использовать LaunchAgents (не LaunchDaemons)—iOS CI нужен пользовательский Keychain для подписи. Автовход macOS для сессии ci.

Способ После reboot После crash Надёжность prod
nohup / screenНе подходит
launchdПродакшен

Слой 2 — изоляция CI-пользователя

Почему не учётка администратора?

  • sudo → неограниченная поверхность атаки
  • Keychain читается → утечка сертификатов
  • fork PR со злым скриптом → полная компрометация

Рекомендация: выделенный ci

  • ❌ без sudo, вне группы admin
  • ✅ отдельный Keychain (/Users/ci/Library/Keychains/)
  • ✅ группа _developer (xcodebuild / simctl)
sudo dscl . -create /Users/ci UserShell /bin/zsh
sudo dscl . -create /Users/ci RealName "CI Runner"
sudo dscl . -create /Users/ci UniqueID 505
sudo dscl . -create /Users/ci PrimaryGroupID 20
sudo dscl . -create /Users/ci NFSHomeDirectory /Users/ci
sudo createhomedir -c -u ci
sudo passwd ci
sudo dscl . -append /Groups/_developer GroupMembership ci

Слой 3 — маршрутизация меток runner

Таксономия меток = планировщик CI. Трёхуровневая структура:

runs-on: [self-hosted, macOS, ARM64, flutter-ios, xcode-16, m4]
           ┌──────────────┐  ┌───────────────────┐  ┌──────────────┐
           │ Platform     │  │ Workload          │  │ Hardware     │
           │ self-hosted  │  │ flutter-ios       │  │ m4, 16gb     │
           │ macOS, ARM64 │  │ xcode-16          │  │ region:tokyo │
           └──────────────┘  └───────────────────┘  └──────────────┘

Метки — логическое И (AND); workflow должен совпасть полностью; несколько машин с одной меткой = балансировка.

Полная установка Mac mini runner (продакшен)

Шаг 1 — Установка runner

# Run as ci user
mkdir -p ~/actions-runner && cd ~/actions-runner
curl -fsSL -O https://github.com/actions/runner/releases/download/v2.316.1/actions-runner-osx-arm64-2.316.1.tar.gz
tar xzf actions-runner-osx-arm64-2.316.1.tar.gz

Шаг 2 — Регистрация

./config.sh \
  --url https://github.com/your-org/your-repo \
  --token YOUR_REGISTRATION_TOKEN \
  --name "$(hostname -s)" \
  --labels "self-hosted,macOS,ARM64,flutter-ios,xcode-16,m4,16gb" \
  --work _work \
  --replace \
  --unattended

Шаг 3 — Конфиг launchd (критично)

Сохранить в ~/Library/LaunchAgents/com.kvmboot.github-runner.plist:

<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
  <key>Label</key><string>com.kvmboot.github-runner</string>
  <key>ProgramArguments</key>
  <array><string>/Users/ci/actions-runner/run.sh</string></array>
  <key>WorkingDirectory</key><string>/Users/ci/actions-runner</string>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
  </dict>
  <key>StandardOutPath</key><string>/Users/ci/Library/Logs/github-runner.log</string>
  <key>StandardErrorPath</key><string>/Users/ci/Library/Logs/github-runner.err</string>
</dict></plist>
WorkingDirectory нельзя опускать
launchd по умолчанию cwd /. Без WorkingDirectory runner не находит .credentials и тихо выходит—GitHub offline, launchctl list loaded.

Шаг 4 — Запуск

launchctl load ~/Library/LaunchAgents/com.kvmboot.github-runner.plist
launchctl list | grep github-runner
tail -f ~/Library/Logs/github-runner.log   # expect: Listening for Jobs

Сеть и прокси (egress allowlist)

Runner должен достигать *.github.com, api.github.com, *.actions.githubusercontent.com, objects.githubusercontent.com. Прокси в plist EnvironmentVariables. Диапазоны IP: api.github.com/meta.

Усиление безопасности продакшен

Главный риск — fork PR в публичных репозиториях. Три линии обороны:

1. Runner уровня репозитория (не организации)

Регистрировать на уровне репозитория—компрометация одного repo открывает все Keychain / Secrets.

2. Защита fork PR

GitHub → Settings → Actions → General, включить:

Требовать одобрение workflow fork внешних соавторов

fork PR не должны автоматически выполняться на self-hosted runner. См. изоляция безопасности cloud Mac CI (поверхность fork PR).

3. Изоляция secrets

  • ❌ не хранить .p12 / .env в home ci
  • ❌ не хардкодить API-ключи в workflow
  • ✅ GitHub Secrets + runtime injection; сертификаты через Fastlane match + Keychain
concurrency:
  group: flutter-ios-${{ github.ref }}
  cancel-in-progress: true

Оптимизация производительности CI (iOS / Flutter)

Self-hosted runner на Mac mini усиливает оптимизацию iOS CI за счёт локального постоянного кэша:

Три слоя кэша

  • Кэш CocoaPods (~/.cocoapods + ios/Pods)
  • DerivedData (фиксированный -derivedDataPath)
  • Кэш SPM / Flutter pub
Оптимизация Типичный выигрыш Примечание
Кэш Pods~40% временихолодная 28 мин → ~19 мин
DerivedData~30% временигорячая 12 мин → ~6 мин
Фиксация Xcodeстабильностьубирает неожиданные breaking changes

Полная стратегия: дизайн кэша iOS CI (DerivedData / CocoaPods / SPM).

Пересборка runner в один клик (DR)

После major-апгрейда macOS, дрейфа Golden Image или инцидента безопасности — rebuild_runner.sh за ~5 минут:

#!/usr/bin/env zsh
set -euo pipefail
CI_USER="ci"
PLIST_DEST="/Users/$CI_USER/Library/LaunchAgents/com.kvmboot.github-runner.plist"

sudo -u "$CI_USER" launchctl unload "$PLIST_DEST" 2>/dev/null || true
sudo -u "$CI_USER" bash -c "cd /Users/$CI_USER/actions-runner && ./config.sh remove --token \$(gh api -X POST /repos/your-org/your-repo/actions/runners/remove-token --jq '.token') 2>/dev/null || true"
sudo -u "$CI_USER" zsh /Users/$CI_USER/scripts/setup_runner.sh
sudo -u "$CI_USER" launchctl load "$PLIST_DEST"
echo "[OK] Runner rebuilt."

Проверка готовности runner к продакшену

Перед приёмкой подтвердите базовую среду (чеклист онбординга cloud Mac).

Уровень 1 — Runner online

  • ✅ GitHub UI показывает Idle
  • launchctl list | grep github-runner — здоровый PID
  • ✅ в логе Listening for Jobs

Уровень 2 — Пустой job

runs-on: [self-hosted, macOS, flutter-ios]
  • ✅ job назначен за 30 с
  • ✅ вывод имени runner и версии ОС

Уровень 3 — Toolchain

xcodebuild -version    # matches Golden Image
flutter --version
pod --version
Симптом Причина Действие
Runner offlineWorkingDirectory не заданлог .err, добавить поле plist
Job в очерединесовпадение метоксравнить runs-on и метки регистрации
Цикл crashнет PATHдобавить EnvironmentVariables в plist

Позиционирование продакшен (E-E-A-T)

Дизайн для продакшен CI: детерминированные toolchain (Golden Image), изоляция fork PR, устойчивость к reboot и crash.

Трёхслойная модель: macOS (launchd), изоляция выполнения (sandbox ci), маршрутизация (метки GitHub Actions).

Продакшен-Mac mini CI проверен на облачном Mac kvmboot—месячные runner-узлы и дневные release-машины при аренде Mac.

FAQ (длинный хвост)

Может ли Mac mini запускать GitHub Actions runner?

Да—одна из типичных продакшен-схем для iOS CI. С launchd стабильная среда GitHub Actions self-hosted runner (macOS) на длительный срок.

Насколько быстрее self-hosted vs GitHub-hosted?

Обычно 30%–60% для iOS CI за счёт CocoaPods/DerivedData—hosted каждый job с чистой средой.

Разница launchd и brew services?

launchd — системный демон macOS (аналог systemd); brew services — обёртка, не для non-Homebrew runner. Продакшен — plist вручную.

Runner offline — как искать?

По приоритету:

  1. launchctl list | grep github-runner
  2. WorkingDirectory в plist
  3. метки runs-on точно как при регистрации

Сколько runner на одном Mac mini?

16 ГБ → 1 рекомендуется; 24 ГБ → 1–2 (лёгкие). xcodebuild забивает RAM—несколько с concurrency.

Развернуть этот runner на облачном Mac mini

Лучший хост для Mac mini GitHub Actions self-hosted runner — Apple Silicon M4: нативный ARM64 Xcode, unified memory, ~4 В в простое. SIP, Gatekeeper и отдельные Keychain усиливают изоляцию безопасности runner—радиус fork PR ограничен home ci.

Миграция Flutter iOS CI? kvmboot cloud Mac mini M4: посуточная проверка → месячный runner—смотреть тарифы.

Акция Тарифы