Корпоративный Mac CI 2026: Bazel, Gradle, удалённый кэш и NVMe

Крупные мобильные организации всё чаще строят гибридные пулы Mac: модули Kotlin Multiplatform или Android собираются через Gradle, а цели Apple проходят через Xcode и Bazel или напрямую через xcodebuild. Дорогие ошибки редко связаны с выбором «не того продукта кэша» — чаще ломается связка пространств ключей, режимов записи и топологии диска на одном быстром хосте. Этот FAQ сопоставляет удалённый кэш Bazel с удалённым кэшем сборки Gradle, показывает, как развести каталоги в духе repository_cache и disk_cache, и объясняет, когда удалённый уровень только для чтения должен заставить вас снизить число job на одной машине, даже если на SSD ещё свободны терабайты. Поведение холодного старта зависимостей, конкурирующего с этими слоями, разберите вместе с FAQ по крупному репозиторию, blobless/shallow Git, CocoaPods, SwiftPM и дисковой очереди. Если несколько runner-ов делят один Mac и семантика кэша GitHub Actions пересекается с локальными томами, сначала пройдите материал о self-hosted runner-ах, постоянных дисках и политике очистки, прежде чем снова наращивать параллелизм.

1. Удалённый кэш Bazel и кэш сборки Gradle: разные контракты

Bazel рассматривает удалённый кэш как хранилище с адресацией по содержимому для выходов действий; клиенты обязаны соблюдать жёсткие правила воспроизводимости, а смешанные платформенные срезы требуют явных флагов, чтобы macOS-действия никогда не подхватывали Linux-блобы. Gradle переносит в удалённый кэш сборки отпечатки графа задач и метаданные плагинов — это сильно ускоряет инкрементальную Android-сборку, но проще «отравить» недетерминированными трансформациями или ресурсами с метками времени. В эксплуатации стоит унифицировать TLS или mTLS, развести учётные данные для CI только с чтением и для ноутбуков разработчиков, а в журналах фиксировать причины отказа кэша вместе со счётчиком промахов. Если один HTTP-эндпоинт обслуживает обе экосистемы, шардируйте пути или имена хостов, чтобы задачи вытеснения Gradle случайно не удалили объекты Bazel.

Практическое правило: держите Bazel и Gradle на разных префиксах URL или в разных бакетах, даже если объектное хранилище одно; объединяйте наблюдаемость, а не пространства имён.

2. Разделы repository_cache и disk_cache на быстром NVMe

Каталог repository_cache в Gradle хранит артефакты зависимостей, скачанные с Maven-зеркал: он крупный, в основном только дополняется и выигрывает от длительного удержания. Каталог кэша сборки (в примерах часто называемый disk_cache) держит выходы задач и меняется быстрее, ему нужнее жёсткая политика LRU. На одном воркере Apple Silicon разнесите их по разным томам APFS или точкам монтирования на одном физическом NVMe, чтобы волна загрузки зависимостей не вытеснила горячие записи компиляции, на которые опираются job Xcode. Для Bazel повторите идею: изолируйте пути --disk_cache для CI от эквивалентов кэша репозитория для обёрток загрузки и не сваливайте обе экосистемы в один плоский каталог без квот. Снимайте метрики свободных байт, давления на inode и await в iostat по каждому тому, чтобы алерты называли конкретный уровень, а не «проблемный Mac».

3. Несколько job и параллельный xcodebuild при удалённом уровне только для чтения

Когда на одном хосте запускаются несколько назначений xcodebuild или тестовые воркеры, первым узким местом редко становится CPU; чаще это случайное чтение в сторону DerivedData и веер gRPC к удалённому кэшу, работающему только на чтение. Такой уровень ограничивает входящую полосу и число одновременных потоков, поэтому удвоение -parallel-testing-worker-count может увеличить время стены, хотя каждый job формально укладывается в RAM. Измеряйте P95 времени компиляции на слот воркера, наращивая параллелизм, и останавливайтесь, когда хвост растёт быстрее пропускной способности. Оставьте хотя бы один слот для прогрева кэша или административных задач, чтобы обслуживающий трафик не конкурировал с пиком CI. Если без переподписки не обойтись, сначала снизьте параллелизм Gradle или Bazel: их клиенты терпимее к откатам, чем интерактивные UI-тесты Xcode.

  • Для каждого job задавайте отдельный -derivedDataPath, чтобы исключить порчу индекса между запусками.
  • Ограничьте суммарные удалённые загрузки; в CI по умолчанию отключайте загрузку в удалённый кэш.
  • Следите за нагрузкой CPU на TLS, когда много воркеров открывают короткоживущие соединения к одному VIP кэша.

4. Выбор узла с большим NVMe: когда объём SSD не покупает параллелизм

Ёмкость NVMe дешева относительно ресурса записи и QoS при смеси случайного и последовательного доступа. Диск на 4 ТБ с 40 % свободы всё равно может насытиться, когда четыре job одновременно стримят многогигабайтные блобы кэша, а Xcode индексирует тысячи мелких файлов. Предпочитайте раздельные тома для зависимостей и для выходов компиляции вместо одного неделимого тома. Согласуйте закупку с сетевым аплинком к удалённому кэшу и с политикой исключений Spotlight и антивируса там, где это разрешено. Зафиксируйте в runbook, кто может запускать bazel clean или Gradle clean, чтобы не стереть общие тёплые деревья, от которых зависят другие очереди.

5. FAQ для платформенных лидов

  • Может ли один Mac одновременно обслуживать Bazel, Gradle и Xcode? Да, при разных пользователях или агентах launchd, жёстко разведённых корнях кэша и потолках параллелизма, подтверждённых замерами.
  • Попадания высокие, а сборки замедлились — почему? Смотрите хвостовую задержку до удалённого кэша и локальный await: поздний «попадание» всё равно нарушает SLA.
  • Стоит ли разрешать CI записывать в удалённый кэш? Предпочтительнее read-only для CI и отдельные доверенные publisher-job; расширяйте права записи только при автоматической уборке мусора и аудите происхождения.
  • Связаны ли лимиты памяти Apple Silicon с размером кэша? Тяжёлые метаданные увеличивают давление mmap; держите метаданные репозитория на быстром диске и не монтируйте кэши поверх сетевых ФС.

Почему Apple Silicon Mac mini остаётся удачной опорой для этого кэш-тяжёлого сценария

Весь перечисленный стек на macOS ведёт себя так же, как на машине разработчика, поэтому удалённые клиенты кэша, подпись кода и симуляторы остаются на одной поддерживаемой платформе. Mac mini на Apple Silicon сочетает быстрый контроллер накопителя с пропускной способностью unified memory — это помогает, когда несколько job одновременно распаковывают блобы кэша, а простой по-прежнему укладывается в считаные ватты, что делает ночной прогрев экономичным. Gatekeeper, SIP и FileVault дают службе безопасности более прозрачную модель для безлюдных build-хостов, чем типичные образы ПК, а компактный корпус упрощает плотную установку в стойке или на столе команды.

Если вы проверяете эти разделы кэша перед широким раскатом, зафиксируйте эталон на классе Mac mini M4, чтобы запас по диску и сети соответствовал вееру gRPC без неожиданного троттлинга по температуре. Когда нужен выделенный облачный Mac без длинного тендера, откройте главную страницу Macstripe и сопоставьте регионы с конечными точками кэша. Mac mini M4 в 2026 году остаётся практичной базой, чтобы валидировать Bazel, Gradle и Xcode на одном классе железа; сейчас разумный момент добавить опорную ноду с главной страницы и закрепить регрессионные профили производительности.