Когда job на облачном Mac живёт десятки минут, а не часы, выигрыш даёт не столько GHz, сколько то, куда кладёте артефакты инкрементальной сборки. В 2026 году типичный спор — держать тяжёлый DerivedData на быстром локальном томе узла или тянуть снимок с объектного хранилища между запусками. Оба варианта законны: второй снижает привязку к конкретной машине, первый убирает минуты синхронизации, если сеть узкое место. Ниже — практическая рамка для команд, которые уже вынесли self-hosted runner на macOS и хотят измерять решения, а не обсуждать их в Slack.
Холодный старт, синхронизация и «ложная экономия»
Холодный старт в этой статье — не только первый запуск после выдачи чистой VM, но и любой прогон, где restore кэша занимает сопоставимое с компиляцией время. Измеряйте три метрики отдельно: время скачивания архива, время распаковки на локальный SSD и время последующей инвалидации Xcode при смене TOOLCHAINS или пути к проекту. Если сумма первых двух фаз стабильно больше пятой части wall time сборки, локальный persistent том на выделенном runner часто дешевле по ощущениям, чем «идеальный» удалённый кэш. Обратная ситуация — эластичный пул короткоживущих Mac, где диски между job не сохраняются: тогда без удалённого слоя вы каждый раз платите полной ценой CocoaPods и SwiftPM.
xcodebuild, оставаясь с плоским графиком очереди из-за restore.
Слои: DerivedData, Pods и sccache
DerivedData даёт максимум инкрементальности для модулей и индексов, но плохо переносится между разными путями workspace и версиями Xcode: ключ кэша должен включать хэш lockfile и build system. CocoaPods и артефакты SwiftPM лучше отделять от модульного кэша компилятора — их проще дедуплицировать и хранить дольше. sccache (или распределённый компиляторный кэш) выносит объектные файлы и Clang-модули на общий backend: полезно, когда много однотипных C/ObjC++ целей, но требует стабильных флагов и одинакового SDK на всех узлах. Комбинируйте слои: например, удалённый tarball только для ~/Library/Caches/CocoaPods и локальный NVMe под активный DerivedData текущей ветки.
| Слой | Удалённый кэш | Локальный диск узла |
|---|---|---|
| DerivedData | Хорошо для однородных runner'ов с фиксированным путём | Лучший wall time при частых мелких коммитах |
| Pods / SPM | Сильный выигрыш на эфемерных узлах | Риск раздувания образа, нужен GC |
| sccache | Масштаб на пул; общий hit-rate | Минимальная задержка при cache miss |
Матрица решений по режиму узла
Если runner почти всегда включён и обслуживает одну команду, приоритет — локальный быстрый том плюс ночной «снимок» в объектное хранилище как страховка. Если runner поднимается под пик и гасится, удалённый restore обязателен, иначе каждый пик превращается в лекцию про пропускную способность. Стоимость «лишнего» гигабайта tarball следует сравнивать не с ценой диска, а с ценой минут ожидания разработчиков в очереди и с риском пропуска слота отправки в магазин.
Для коротких авралов релиза полезно заранее прикинуть полную стоимость синхронизации — см. материал о внезапных сборках и сроках App Store. Чтобы среда macOS на узле оставалась воспроизводимой при фоновых агентах и launchd, держите чеклист отличий от Linux VPS под рукой: развёртывание OpenClaw на облачном Mac в 2026 хорошо иллюстрирует типовые ловушки путей и Keychain.
Исполняемый чеклист параметров
Зафиксируйте значения в репозитории (или в секретах CI) и меняйте их осознанно после измерений, а не «потому что так в туториале».
# Путь DerivedData: локальный быстрый том export DERIVED_DATA_PATH=/Volumes/ci-nvme/DerivedData/$CI_JOB_ID # Ключ инвалидации удалённого tarball (Podfile.lock + Xcode) export CACHE_KEY="pods-$(shasum Podfile.lock | cut -c1-12)-xc$(xcodebuild -version | head -1 | shasum | cut -c1-8)" # sccache: единый endpoint для пула (пример S3) export SCCACHE_BUCKET=s3://team-ci-sccache export SCCACHE_IDLE_TIMEOUT=0 export RUSTC_WRAPPER=sccache # xcodebuild: явный путь кэша и параллельность xcodebuild -derivedDataPath "$DERIVED_DATA_PATH" \ -parallelizeTargets -jobs "$(sysctl -n hw.ncpu)" # Политика: не смешивать кэш разных major Xcode на одном префиксе
После внедрения слоёв сравните медиану и 95-й перцентиль wall time на одинаковом коммите: если перцентиль растёт только на restore, проблема в сети или в размере архива, а не в Swift. Для выбора между эластичным пулом и постоянными runner'ами полезно отдельно моделировать очередь job и время удержания диска — те же метрики подскажут, когда удалённый кэш окупается, а когда выгоднее закрепить локальный NVMe на baseline-узле.
На облачном Mac mini M4 кэш и компилятор живут в одной связке
Короткий цикл CI на macOS выигрывает, когда диск и память не спорят друг с другом: унифицированная память Apple Silicon держит пики линкера и индексов Xcode без лишнего свопа, а нативный стек Homebrew, xcodebuild и инструментов командной строки снимает суррогатные слои вроде WSL. Для ночных и предрелизных прогонов важен и простой энергетический профиль: класс Mac mini M4 на простое потребляет порядка 4 Вт, что удобно для постоянно включённого self-hosted runner'а.
С точки зрения эксплуатации macOS даёт предсказуемый аптайм и меньшую поверхность атаки по сравнению с типичными Windows build VM: Gatekeeper, SIP и FileVault работают как базовый периметр, а компактный бесшумный корпус снижает совокупную стоимость владения при длительной аренде узла.
Если вы хотите закрепить измеренные улучшения кэша на стабильном железе без закупки стойки, облачный Mac mini M4 от VPSSpark — практичная площадка для baseline и burst CI — узнайте тарифы и привяжите политику кэша к реальным метрикам restore, а не к догадкам.