Симптом: iOS-сборки в GitLab CI/CD стоят в очереди, а подпись и Xcode завязаны на один рабочий Mac без понятного контроля доступа.
Самое быстрое решение: разверните GitLab Runner macOS как отдельный узел только для доверенных проектов, сначала проверьте одну полную сборку и восстановление после перезагрузки, а затем масштабируйте по фактической очереди.
Этот подход подходит платформенным инженерам, которые переносят iOS-проекты в GitLab CI/CD и выбирают между покупкой, арендой или смешанной схемой Mac-узлов. Он также нужен IT- и security-руководителям, отвечающим за сертификаты, сетевую изоляцию и аудит секретов.
До установки определите границы сборочного узла
GitLab Runner macOS не следует рассматривать как обычный универсальный сервер для любых задач CI. На macOS наиболее прямой путь для iOS- и macOS-сборок — Shell executor, потому что задания запускаются в реальной пользовательской среде с установленными Xcode и системными инструментами. Но GitLab прямо предупреждает: Shell executor обладает ограниченной изоляцией и должен использоваться только для доверенного кода. (docs.gitlab.com)
До установки зафиксируйте четыре границы:
Какие репозитории доверенные.
В список должны попасть только проекты вашей организации или внешние репозитории после отдельной проверки. Любой job в Shell executor может получить доступ к локальной файловой системе, процессам, переменным окружения и инструментам пользователя, под которым работает Runner.Какие задачи выполняются на узле.
Тесты, обычные архивы и публикация в магазин не обязаны использовать один и тот же Mac. Задачи с сертификатами распространения и профилями подписи лучше направлять на отдельный защищенный узел.Какие версии Xcode нужны.
Если часть проектов еще не готова к новой версии Xcode, не смешивайте их без плана миграции. Разные версии SDK, Ruby-зависимостей, CocoaPods или Swift Package Manager могут давать разные результаты даже при одинаковом исходном коде.Как измеряется потребность в новых узлах.
Не рассчитывайте число Mac только по количеству разработчиков. Записывайте время ожидания job, длительность сборки, количество параллельных задач, процент повторных запусков и периодичность релизов.
Для первого этапа обычно разумнее один специализированный узел и небольшой набор доверенных pipeline. Это не означает, что один узел обязательно станет производственным стандартом. Его задача — дать измеримую исходную точку.
Сценарий из практики планирования
Если команда состоит из десяти iOS-разработчиков, но выпускает приложение раз в две недели, один узел может быть достаточен для пилота. Если команда меньше, но несколько проектов одновременно создают архивы и запускают тесты, узкое место появится быстрее. В обоих случаях решение принимается по очереди и времени занятости, а не по числу сотрудников.
Первый час: подготовьте macOS и отдельную учетную запись
GitLab Runner на macOS работает как пользовательский LaunchAgent, а не как системный LaunchDaemon. Официальная процедура требует выполнять установку из локального графического сеанса пользователя, который будет запускать задания; SSH-сеанс для этой процедуры не подходит. После установки GitLab рекомендует перезапустить систему и проверить, что Runner снова выходит в сеть. (docs.gitlab.com)
Порядок подготовки
Шаг 1. Создайте выделенную учетную запись.
Не используйте личный аккаунт разработчика. Учетная запись Runner должна иметь только необходимые локальные права. Административный доступ оставьте для ограниченного числа операторов, а ежедневный запуск сборок выполняйте от отдельного пользователя.
Шаг 2. Определите удаленный вход.
SSH подходит для администрирования, просмотра журналов и аварийного восстановления. VNC или другой графический способ доступа нужен для действий, связанных с пользовательским сеансом, Xcode и Keychain. Проверьте, кто имеет доступ к каждому каналу и как фиксируются административные подключения.
Шаг 3. Включите защиту данных на диске.
FileVault шифрует данные на внутреннем и съемном хранилище с использованием AES-XTS. На Mac с Apple Silicon защита связана с Secure Enclave и аппаратными механизмами хранения ключей. После включения FileVault учетные данные требуются во время загрузки, поэтому процедуру разблокировки нужно заранее включить в план восстановления. (support.apple.com)
Шаг 4. Зафиксируйте системную базу.
Сохраните в журнале:
- версию macOS;
- архитектуру Apple Silicon или Intel;
- версию Xcode;
- версию
xcodebuild; - установленные SDK;
- менеджеры зависимостей;
- путь к рабочему каталогу Runner;
- сетевые разрешения и прокси, если они используются.
Здесь важно не число версии само по себе, а воспроизводимость. После обновления Xcode меняйте базовую запись и повторяйте контрольную сборку.
Шаг 5. Установите инструменты командной строки.
Проверьте, что команда xcode-select указывает на нужную установку Xcode, а xcodebuild -version возвращает ожидаемую версию. До регистрации Runner запустите локальную сборку без GitLab. Иначе вы не сможете отделить проблему CI от проблемы самого проекта.
Как настроить регистрацию и маршрутизацию заданий
Runner можно регистрировать на уровне проекта, группы или всего экземпляра GitLab. Для корпоративной macOS-сборочной машины начинайте с минимальной области действия: проектный Runner для одного пилота или групповой Runner для нескольких заранее утвержденных репозиториев. Регистрация сохраняет конфигурацию в config.toml; GitLab также указывает, что старые registration tokens постепенно выводятся из использования в пользу authentication tokens. (docs.gitlab.com)
Пример минимального скелета регистрации:
gitlab-runner register \
--non-interactive \
--url "https://gitlab.example.com/" \
--token "$RUNNER_AUTH_TOKEN" \
--executor "shell" \
--description "ios-macos-pilot" \
--tag-list "macos,ios,xcode"
Параметры URL, токена и тегов замените значениями вашей среды. Не помещайте токен в репозиторий и не выводите его в лог job.
Как выбрать executor
Для iOS-проектов выбирайте Shell executor, когда pipeline должен обращаться к реальным Xcode, SDK, симуляторам, Keychain и системным инструментам macOS. Контейнерный подход не является автоматической заменой полноценной macOS-среде: он не решает вопрос доступа к подписи и пользовательскому сеансу.
Преимущество Shell executor — прямой доступ к установленному набору инструментов и меньше промежуточных слоев. Недостаток — слабая изоляция между заданиями и проектами. Поэтому Shell Runner не должен принимать непроверенный код, произвольные merge request из неизвестных источников или задачи с неограниченным пользовательским вводом. (docs.gitlab.com)
Теги и защищенные ветки
Назначьте отдельные теги, например macos, ios и signing. В .gitlab-ci.yml публикационные задания должны явно требовать защищенный тег:
build_ios:
tags:
- macos
- ios
script:
- xcodebuild -version
- xcodebuild test -scheme "$SCHEME" -destination "$DESTINATION"
release_ios:
tags:
- macos
- signing
rules:
- if: '$CI_COMMIT_TAG =~ /^release-/'
script:
- ./ci/import-signing-assets.sh
- xcodebuild archive -scheme "$SCHEME"
- ./ci/cleanup-signing-assets.sh
Защитите ветки и теги релизов. GitLab позволяет ограничивать Runner заданиями из защищенных веток или защищенных тегов; это снижает риск раскрытия секретов через обычную pipeline. Защищенные теги также ограничивают круг пользователей, которые могут создавать и запускать связанные задачи. (docs.gitlab.com)
Что проверить в первой pipeline
Первая pipeline должна быть короткой, но проверять всю цепочку: получение кода, выбор Xcode, установку зависимостей, сборку, тесты, упаковку артефакта и очистку.
Шесть последовательных проверок
Шаг 1. Проверка окружения.
Выведите версии macOS-инструментов, Xcode и SDK. Не печатайте переменные с секретами. Сохраните лог как часть приемочных материалов.
Шаг 2. Проверка исходного кода.
Убедитесь, что Runner получает именно тот commit, который указан GitLab. Не используйте рабочий каталог повторно без очистки, если проект может менять скрипты, символические ссылки или локальные настройки.
Шаг 3. Установка зависимостей.
Зафиксируйте lock-файлы и определите, какие пакеты могут обращаться во внешнюю сеть. Сетевые разрешения для зависимости не должны автоматически означать доступ к внутренним сервисам.
Шаг 4. Сборка и тесты.
Начните с xcodebuild, а не с длинного пользовательского скрипта. Сначала докажите, что базовая команда работает на чистом состоянии. Затем добавляйте тесты, анализ и упаковку.
Шаг 5. Архив и артефакты.
Проверьте, что .xcarchive, отчеты тестов и журналы доступны после завершения job. Артефакт должен иметь идентификатор commit или pipeline, иначе аудит релиза будет затруднен.
Шаг 6. Очистка.
Удалите временную Keychain, импортированные профили, сертификаты и локальные секреты после задания. Рабочие каталоги, кэш и временные файлы не должны становиться источником данных для следующего проекта.
Как разделить обычную сборку и подпись iOS
Apple описывает цифровую идентичность для подписи как сочетание сертификата с открытым и закрытым ключом. Закрытый ключ хранится в Keychain, а provisioning profile определяет разрешенные параметры и entitlements приложения. Поэтому переносить только файл сертификата недостаточно. (developer.apple.com)
Рекомендуемая схема:
- обычные тестовые job направляются на узлы без постоянных distribution-сертификатов;
- публикационные job запускаются только с защищенного тега;
- сертификат и профиль передаются в pipeline из защищенного хранилища переменных или секретов;
- временная Keychain создается на время задания;
- после экспорта приложения временная Keychain удаляется;
- журнал не должен содержать пароль
.p12, содержимое профиля или значение токена.
Apple отдельно указывает, что provisioning profile можно скачать и установить вручную, а при автоматической подписи Xcode управляет профилями в соответствии с конфигурацией проекта. Для корпоративной публикации выберите один управляемый процесс и документируйте, кто может обновлять сертификаты и профили. (developer.apple.com)
Может ли одна macOS Runner обслуживать несколько проектов
Технически — да, но организационно это не означает, что так следует делать всегда. Несколько доверенных проектов могут использовать один узел, если у них согласованы версии Xcode, одинаковый уровень доверия и понятные правила очистки. Проекты с разными владельцами, разными требованиями к секретам или разными сроками релиза лучше разделять.
Практическое правило:
- один проект — отдельный Runner для высокой чувствительности;
- несколько проектов одной группы — групповой Runner с общими тегами;
- несколько команд с разными правами публикации — отдельные теги и отдельные signing-узлы;
- непроверенный код — не запускать на Shell executor.
Таблица выбора начальной архитектуры
| Вариант | Когда подходит | Главный плюс | Главный риск |
|---|---|---|---|
| Один локальный Mac | Пилот, редкие релизы, один согласованный проект | Полный контроль над средой | Единая точка отказа |
| Пул купленных Mac | Постоянная нагрузка и доступная локальная эксплуатация | Предсказуемая физическая инфраструктура | Простои, обслуживание и капитальные затраты |
| Периодическая аренда удаленных Mac | Пиковые релизы, распределенная команда, нет onsite-оператора | Можно расширить парк на период нагрузки | Нужно проверить доступ, восстановление и договорные условия |
| Смешанная схема | Постоянные тесты плюс нерегулярные релизы | Базовая мощность и гибкое расширение | Сложнее стандартизировать версии и секреты |
Стоимость сравнивайте через переменные, а не через рекламную цену:
TCO периода = оборудование + доставка + гарантия + электроэнергия + администрирование + простой + замена + аренда дополнительных узлов.
Для каждого варианта заполните одинаковый период анализа. Включите часы инженера, потраченные на обновление Xcode, восстановление после сбоя и ручное управление сертификатами. Если у команды нет надежного удаленного доступа и процедуры восстановления, «дешевый» физический Mac может оказаться дорогим из-за простоя.
При отсутствии собственных узлов можно начать с аренды удаленного Mac для корпоративного пилота, но перед заказом подготовьте требования к архитектуре, версии Xcode, сетевому доступу и хранению signing-материалов. Не переносите секреты на удаленный узел до завершения проверки доступа.
Первая неделя: восстановление, аудит и безопасная эксплуатация
Проверка «Runner виден как online» недостаточна. На macOS нужно проверить весь цикл после сбоя питания или плановой перезагрузки.
Сценарий приемки
- Перезагрузите Mac штатной командой.
- Проверьте, появляется ли нужный пользовательский сеанс.
- Убедитесь, что
LaunchAgentзапускает Runner. - Проверьте, что узел снова получает job.
- Убедитесь, что рабочий каталог не содержит секретов предыдущей pipeline.
- Проверьте разблокировку диска и предусмотренный операторский путь.
- Отключите сеть на короткий период и проверьте поведение очереди.
- Зафиксируйте журналы и время восстановления.
Если FileVault требует ручной разблокировки после перезагрузки, это не обязательно делает узел непригодным. Но тогда должен существовать назначенный оператор и запасной канал доступа. Для полностью автоматического сценария следует отдельно оценить, как организация будет восстанавливать зашифрованный узел без обхода политики безопасности.
В течение первой недели собирайте:
- время ожидания job;
- длительность сборки;
- долю успешных запусков;
- число повторных запусков;
- рост рабочего каталога и кэша;
- время восстановления после перезагрузки;
- ошибки смены Xcode и подписания.
Подробные требования к сетевому разделению и проверке секретов можно сопоставить с чек-листом безопасности удаленного Mac для предприятия. А правила раздельного доступа к сертификатам удобнее оформлять как отдельную процедуру для изоляции signing-узла от обычных тестовых сборок.
Когда добавлять второй узел
Добавляйте узлы не тогда, когда «команда выросла», а когда данные показывают конкретное ограничение. Второй Mac оправдан, если:
- очередь регулярно задерживает релизные pipeline;
- независимые проекты часто конкурируют за один Runner;
- смена версии Xcode требует отдельного окружения;
- signing-задачи мешают обычным тестам;
- плановое обслуживание одного узла останавливает все сборки;
- восстановление после сбоя занимает больше допустимого окна.
Если нагрузки мало, постоянный второй Mac будет простаивать. В таком случае периодическая аренда может быть рациональнее покупки, особенно когда пик возникает только перед релизами. Если же задачи идут непрерывно, есть требования к физическим интерфейсам или нужна локальная интеграция с закрытой сетью, собственные узлы могут быть предпочтительнее.
Используйте условную схему принятия решения:
- Если очередь и длительность сборки стабильны, а релизы проходят редко, выберите один постоянный узел и документированный резервный процесс.
- Если очередь растет только в релизные периоды, выберите базовый узел плюс временный удаленный Mac на пик.
- Если проекты требуют разных версий Xcode, выберите пул по версиям, а не один универсальный Runner.
- Если signing-материалы имеют повышенную чувствительность, выберите отдельный защищенный Runner для публикации.
- Если нужен физический доступ к устройствам или закрытому оборудованию, вернитесь к локальному или colocated Mac.
- Если нет onsite-оператора и требуется быстрое расширение, рассмотрите периодическую аренду удаленного Mac после проверки удаленного восстановления.
Go/no-go перед производственным запуском
Переходите в production только при положительном ответе на все ключевые пункты:
- базовая сборка и тесты проходят на нужной версии Xcode;
- артефакты и логи сохраняются с привязкой к pipeline;
- обычные ветки не могут использовать signing-Runner;
- сертификаты и профили не остаются в рабочем каталоге;
- рабочий пользователь не используется разработчиками для интерактивной работы;
- после перезагрузки Runner возвращается в рабочее состояние;
- известен оператор аварийного восстановления;
- измерено время очереди и есть запас по параллельным задачам;
- определено, какие проекты имеют право на общий узел;
- обновления macOS и Xcode проходят через согласованную процедуру.
Если хотя бы один пункт не выполнен, не компенсируйте пробел дополнительными тегами. Сначала устраните дефект границы доступа или восстановления.
Что выбрать после пилота
Для предприятий худший вариант — сразу купить несколько Mac, не проверив реальную совместимость Xcode, время сборки, сетевой маршрут и восстановление после перезагрузки. Такая схема замораживает бюджет, но не гарантирует пропускную способность. Универсальный общий Shell Runner также опасен: один непредусмотренный job может получить доступ к остаточным файлам, кэшу или signing-материалам.
После пилота у вас должны быть четыре факта: пиковая очередь, версии Xcode, частота публикаций и время восстановления. С ними можно сравнить постоянную покупку, смешанный парк и аренду удаленных Mac на период нагрузки. Если требуется быстро проверить несколько вариантов без закупки всей партии, KVMNODE можно рассматривать как вариант временного Mac-узла для полного GitLab CI/CD-пилота. Оптимальный срок аренды определяйте по журналам реальных pipeline, а не по предположению о будущей нагрузке.