Kvmzen Блог
← Назад к разделу «Технологии на практике»

Как мы построили конвейер CI/CD для iOS на облачных Mac mini M4

DevOps и CI/CD ·~4 мин чтения

CI/CD для iOS на Mac mini M4: практика - Kvmzen

Когда команда вырастает, вопрос «на чьём компьютере вообще собирается релиз» незаметно превращается в узкое место. После полного переноса CI/CD для iOS-проекта на облачные хосты Mac mini M4 от Kvmzen время ожидания сборки заметно сократилось, а инциденты с подписью почти исчезли. В этой заметке — грабли, на которые мы наступили, выстраивая конвейер с нуля, и Fastfile со стратегией кеширования, к которым мы в итоге пришли.

Почему облачный Mac mini M4 для CI/CD

Сборка в Xcode — классическая задача, «требовательная к вычислениям и жёстко привязанная к версии ОС», поэтому локальный самодельный Runner трудно держать стабильным долго. Мы выбрали облачные Mac-узлы по трём причинам:

  • Воспроизводимая конфигурация: образ фиксирует версии Xcode и инструментов командной строки, поэтому тихое обновление ОС никогда не приводит к тому, что «собирается только на машине одного человека».
  • Эластичная емкость: перед релизом для регрессионных тестов можно временно поднять несколько дополнительных узлов параллельно, а после — освободить их, не держа железо про запас на редкие пики.
  • Доступность 24/7: низкое энергопотребление чипа M4 в простое позволяет держать ночные сборки и плановые проверки безопасности постоянно активными без забот об электричестве или шуме.

Первая наша ошибка — воспринимать «облачный хост» просто как удалённый монитор и использовать его только для ручной упаковки. Реальная выгода появилась только после встраивания в цепочку триггеров CI.


Проектирование конвейера CI/CD

Разбиение на этапы

Полный релизный конвейер мы разделили на четыре этапа:

  1. Получение кода и разрешение зависимостей (SPM / CocoaPods)
  2. Юнит-тесты и статический анализ (выполняются параллельно)
  3. Архивация xcodebuild archive и подпись
  4. Загрузка в TestFlight и уведомление дежурного канала

Хуки и условия запуска

Каждый merge в main автоматически запускает этапы 1-2; этапы 3-4 выполняются только при появлении тега release/*, чтобы мелкие повседневные коммиты не расходовали квоту подписи. При локальной отладке Fastlane-лейна мы обычно прерываем весь прогон сочетанием Ctrl+C и повторно запускаем только упавший шаг — это заметно экономит время ожидания.

Пример фрагмента Fastfile
lane :ci_archive do
  cocoapods
  run_tests(scheme: "Kvmzen")
  build_app(
    scheme: "Kvmzen",
    export_method: "app-store",
    output_directory: "./build"
  )
  upload_to_testflight(skip_waiting_for_build_processing: true)
end

Сам скрипт несложен; важно, что он даёт одинаковый результат при каждом запуске на одной и той же базовой версии образа — именно поэтому мы всегда фиксируем версию образа в логах сборки, чтобы позже понять, была ли причина сбоя в окружении или в коде.


Кеширование и управление зависимостями

Если кеш настроен небрежно, всё преимущество облачной сборки по скорости съедается загрузкой зависимостей. Вот сравнение, собранное после трёх месяцев эксплуатации:

Что кешируетсяБез кешаС кешем внутри образа
Установка CocoaPods~4-6 минут~30 секунд
Разрешение зависимостей SPM~3 минуты~15 секунд
Инкрементальная сборка DerivedDataпочти как полная сборкапроцент попаданий выше 80%

Сначала договоримся о терминах:

DerivedData
Каталог кеша артефактов инкрементальной сборки Xcode; его повторное использование между сборками заметно сокращает время последующих компиляций.
Кеш SPM
Локальный кеш исходников пакетов и результатов разрешения зависимостей Swift Package Manager, избегающий повторной загрузки при каждой сборке.
Базовый образ
Готовый к работе системный снапшот облачного хоста с зафиксированной версией Xcode/CLT и прогретым кешем зависимостей.
Команда изучает процент попаданий в кеш облачной сборки на дашборде
Отдельная панель с процентом попаданий в кеш помогает находить деградацию гораздо быстрее, чем взгляд только на общее время сборки

Разбор типичных проблем

Легче всего упустить из виду: истёкший сертификат не даёт ошибку сразу — сборка падает только на самом последнем шаге, при загрузке в TestFlight, а к этому моменту весь конвейер уже впустую отработал десять с лишним минут. Сейчас мы проверяем срок действия сертификата уже на этапе 1, чтобы падать и сигналить раньше.

Раньше мы боролись с порчей кеша грубым способом — «каждую ночь полностью чистить кеш и собирать всё с нуля». Позже перешли на инвалидацию кеша по версии образа (переиспользуем, пока версия не менялась, и чистим только при обновлении образа) — это сохранило скорость и не даёт испорченному кешу неделями незаметно создавать проблемы.

«Не верь одному зелёному прогону CI — верь десяти зелёным подряд». Эту фразу мы постоянно повторяем в дежурном runbook, особенно в первые две недели после переезда на новый облачный узел.

Часто задаваемые вопросы

Насколько безопасен облачный Mac mini по сравнению с собственным Mac Runner?

Облачный провайдер поддерживает базовый образ ОС, но контроль доступа к сертификатам подписи и Provisioning Profile остаётся на команде; передавайте сертификаты только через переменные окружения во время выполнения, никогда не встраивайте их в образ и очищайте keychain перед возвратом узла.

Может ли бесплатная квота Xcode Cloud заменить всю эту схему?

Для небольшой команды Xcode Cloud вполне достаточно; но когда растут требования к числу параллельных сборок, собственным скриптам или общему кешу между проектами, самостоятельный конвейер на облачных Mac-узлах даёт больше гибкости и более понятный контроль над потолком расходов.

Как изолировать сертификаты, если несколько проектов делят один и тот же пул облачных узлов?

Рекомендуется выделять каждому проекту отдельный keychain или отдельный профиль keychain, импортируя его в начале сборки и очищая в конце — это не даёт сертификатам разных проектов загрязнять один и тот же системный keychain.

На M4 Mac mini CI/CD и правда становится проще

Все инструменты из этой статьи — Xcode, Fastlane, CocoaPods, SPM — работают на macOS нативно, без виртуальных машин и слоёв совместимости. Единая архитектура памяти Mac mini M4 не даёт связанным с I/O и вычислениями шагам — подписи, архивации, загрузке — тормозить друг друга, а энергопотребление в простое всего около 4 Вт делает счёт за электричество для узла, работающего 24/7, практически незаметным.

По сравнению с обслуживанием собственных Mac Runner'ов облачный узел снимает заботы об охлаждении в серверной, окнах обновления ОС и замене железа при отказах — вся эта операционная нагрузка уходит в сторону, и команда может сосредоточиться на качестве самого конвейера.

Если в вашей команде до сих пор шутят про то, «на чьём компьютере оно вообще собирается», сейчас хороший момент перенести CI на облачный Mac mini M4посмотрите тарифы Kvmzen и запустите первый узел сборки за несколько минут.

Ограниченное предложение

Больше чем Mac — ваша база разработки в облаке

Выделенные вычисления · Глобальные узлы · Ежемесячная подписка · Без покупки железа

На главную
Ограниченное предложение Посмотреть тарифы