Не увеличивайте число Mac сразу после появления тайм-аута: сначала выполните диагностику тайм-аута сборки iOS CI по этапам — очередь, получение задания, зависимости, xcodebuild, симулятор, подпись и загрузка. Расширение Mac-пула оправдано только тогда, когда здоровые узлы постоянно заняты, очередь растёт вместе с параллельными задачами, а длительность отдельной сборки не показывает аномалии.
Эта статья предназначена для IT-руководителей, которым нужно доказать, решит ли новый Mac проблему CI.
Платформенным инженерам она помогает связать состояние Runner с конкретным этапом сборки.
Руководителям разработки и релизов — отделить задержку публикации от нехватки вычислительной ёмкости.
[ SECTION_01 ] Карта доказательств вместо одного показателя
Общее время workflow не объясняет причину задержки. Одна и та же ошибка тайм-аута может появиться, когда задание ещё стоит в очереди, когда Runner не принимает его, когда Git и Swift Package Manager не получают зависимости или когда подпись уже выполнена, но публикация ждёт ответа внешнего сервиса.
Минимальная запись по каждому проблемному запуску должна содержать:
- время постановки задания в очередь;
- время назначения задания конкретному Mac Runner;
- момент фактического начала shell-команды;
- начало и окончание загрузки исходников и зависимостей;
- начало
xcodebuild; - операции с симулятором;
- обращение к Keychain и подпись;
- загрузку артефакта;
- код ошибки, повторные попытки и последний видимый лог.
Для маршрутизации важны не только доступные узлы. Self-hosted Runner выбирается с учётом его состояния, группы и меток. Это описано в официальных правилах маршрутизации self-hosted Runner и в документации по меткам Runner.
Поле «узел свободен» нельзя считать доказательством доступности для конкретной задачи. Mac Runner может быть онлайн, но не подходить по метке, группе, архитектуре или требуемому окружению. Поэтому в журнале нужно хранить не только загрузку CPU и памяти, но и причину, по которой планировщик не назначил задачу.
Три состояния, которые нельзя смешивать
Ожидание до назначения. Задание ещё не исполняется на Mac. Причины — отсутствие подходящей метки, ограничение параллельности, зависимость от другого job или недоступность группы Runner.
Ожидание после назначения. Узел получил задачу, но процесс подготовки не завершился. Здесь проверяются рабочий каталог, права, установка инструментов, сеть и восстановление после предыдущего запуска.
Медленное выполнение. Команда уже выполняется, но один этап занимает непривычно много времени. Только это состояние можно напрямую связывать с производительностью проекта или Mac.
Такое разделение отвечает на вопрос, что сначала проверять при тайм-ауте iOS CI: не характеристики Mac, а точку, в которой исчезает прогресс.
[ SECTION_02 ] Очередь и маршрутизация
Почему свободный Mac Runner всё ещё оставляет pipeline в ожидании
Свободный узел не обязан принимать любую задачу. В рабочем процессе могут использоваться отдельные метки для Apple Silicon, версии Xcode, production-подписи или тестов на симуляторе. Ошибка в одной метке отправляет задачу в ожидание, хотя визуально в пуле остаются свободные Mac.
Проверка выполняется в следующем порядке:
- Сравнить требования job с метками конкретного Runner.
- Проверить, входит ли узел в разрешённую Runner Group.
- Убедиться, что Runner имеет состояние «online» и может получить новое задание.
- Проверить, не занят ли слот параллельности другим workflow.
- Найти зависимости между job:
needs, ручное подтверждение, артефакт или результат предыдущего шага. - Сопоставить время постановки в очередь со временем назначения узла.
Платформа может управлять параллельным выполнением через отдельные группы concurrency. Их поведение описано в официальном материале о concurrency. Если лимит задан на уровне workflow или окружения, добавление Mac не уберёт ожидание, пока ограничение не будет изменено.
Практический вывод прост: очередь становится аргументом за расширение только после проверки маршрутизации. Если задания не доходят до здоровых узлов, новый Mac создаст ещё один свободный ресурс, но не исправит правило назначения.
Признаки реальной перегрузки
Для предприятия полезно собрать четыре значения в одной строке журнала:
- длину очереди в момент появления задания;
- число здоровых и занятых узлов;
- время от постановки до назначения;
- длительность выполнения после назначения.
Если растёт только первое значение, проблема может быть в маршрутизации или лимите параллельности. Если одновременно растут очередь и занятость подходящих узлов, а длительность отдельных запусков остаётся близкой к обычной, появляется основание обсуждать ёмкость Mac. Если же увеличивается сама длительность сборки, сначала нужно расследовать проект, зависимости или инструменты.
[ SECTION_03 ] Зависимости, кэш и сеть
Git, Git LFS, Swift Package Manager, приватный registry, прокси и внешние сервисы создают задержку до компиляции. Особенно важно отделить первый запуск от повторного: промах кэша увеличивает подготовку, но не доказывает недостаток вычислительной мощности.
Каждый сетевой этап следует разделить на:
- установление соединения;
- авторизацию;
- передачу данных;
- распаковку;
- проверку версии;
- повторную попытку после ошибки.
Время ожидания ответа нельзя записывать как время работы Mac. Если источник зависимости отвечает медленно, узел может выглядеть «занятым», хотя процессор почти бездействует.
Кэш также имеет ограниченную роль. Он помогает для данных, которые можно безопасно пересоздать: DerivedData, загруженных пакетов или промежуточных артефактов. Кэш не исправляет неверный токен, недоступную приватную сеть, несовместимую версию пакета или изменение lock-файла. Поэтому рост размера кэша не должен быть единственным решением.
Для каждой зависимости полезно фиксировать:
- источник;
- версию или контрольную сумму;
- был ли cache hit;
- размер загрузки;
- длительность ответа;
- число повторных попыток;
- результат после очистки кэша.
Если задержка появляется только при cache miss, улучшайте топологию зависимостей и стратегию хранения. Если одинаковый пакет периодически не отвечает, проверяйте сеть и сервис. Если этап стабильно занимает большую часть workflow на каждом узле, только после этого сравнивайте его с локальным временем распаковки и дисковой подсистемой.
[ SECTION_04 ] Xcode, xcodebuild и симулятор
Как отличить медленную сборку от нехватки ёмкости Mac
Признак медленной отдельной задачи — стабильное увеличение длительности xcodebuild при нормальном времени назначения Runner и отсутствии конкурирующих job. Признак нехватки ёмкости — рост ожидания перед стартом при одновременно здоровых, постоянно занятых узлах.
Проверка одного запуска должна включать:
- Команду
xcodebuildс режимом, схемой, workspace или project. - Версию Xcode и выбранного SDK.
- Путь
DerivedData. - Число параллельных задач внутри сборки.
- Состояние диска до и после выполнения.
- Давление на память и признаки swapping.
- Время компиляции, линковки, упаковки и тестов.
- Состояние симулятора до запуска тестов.
Справочник командной строки Xcode помогает проверить фактические параметры запуска, а описание системы сборки Xcode — сопоставить наблюдаемое поведение с этапами build system.
CPU на уровне всей машины недостаточен для вывода. Короткая компиляция может сменяться долгой линковкой. Симулятор может ждать запуска процесса, хотя сборка приложения уже закончена. Память может быть доступна в среднем за запуск, но оказаться узким местом при одновременном выполнении нескольких job.
Симулятор как отдельный участок
Симулятор нужно проверять отдельно от компиляции. В журнале отмечаются:
- время создания или запуска устройства;
- время установки приложения;
- старт тестового процесса;
- зависание при загрузке runtime;
- завершение тестов;
- очистка после job.
Apple отдельно описывает запуск приложения на симулируемых и физических устройствах. Поэтому «Xcode работает медленно» нельзя использовать как единое объяснение для компиляции, установки и тестирования.
Если один и тот же runtime не запускается после перезагрузки или требует ручного восстановления, это уже вопрос состояния узла. Если симулятор запускается нормально, но несколько job конкурируют за один рабочий каталог или runtime, нужно исправлять изоляцию, а не покупать Mac.
[ SECTION_05 ] Подпись и публикация
Production-подпись следует анализировать отдельно от обычного PR-build. Успешная компиляция не означает, что задача готова к загрузке в магазин или внутренний registry.
В доказательную цепочку входят:
- доступность сертификата и профиля;
- контекст пользователя;
- состояние Keychain;
- права процесса на ключ;
- корректность bundle identifier;
- длительность операции подписи;
- время упаковки;
- ответ сервиса после загрузки.
Официальное описание создания подписанного distribution-кода задаёт границы процесса подписи. Для тестовых job полезно дополнительно сопоставлять лог команды с описанием результатов тестирования Xcode.
Если подпись блокируется, добавление обычных сборочных узлов не решает проблему. Нужен отдельный production-узел или изолированный контур с контролируемым доступом к ключам. Такой узел может быть менее загружен, но более строгим по разрешениям и процедурам восстановления.
[ SECTION_06 ] Решающее дерево расширения
Перед закупкой или арендой нового Mac команда может пройти следующий чек-лист. Каждая отметка должна подтверждаться журналом конкретного запуска, а не субъективной оценкой разработчика.
- [ ] Задание действительно ждёт назначения, а не зависело от предыдущего job.
- [ ] Метки workflow совпадают с метками доступного Runner.
- [ ] Runner находится в правильной группе и имеет состояние «online».
- [ ] Лимит concurrency не блокирует выполнение.
- [ ] Время загрузки Git, LFS и Swift Package отделено от времени
xcodebuild. - [ ] Ошибки авторизации и сетевые повторы исключены.
- [ ] Время компиляции, линковки, упаковки и тестов записывается раздельно.
- [ ] Симулятор не блокируется runtime, рабочим каталогом или другим job.
- [ ] Подпись рассматривается отдельно от обычной сборки.
- [ ] После назначения задачи подходящие узлы действительно заняты.
- [ ] При росте параллельности растёт очередь, но не длительность отдельной задачи.
- [ ] Новый узел успешно прошёл полную проверку маршрутизации и восстановления.
Решение принимается по условиям:
- Если не отмечен хотя бы один пункт маршрутизации, сначала исправляется Runner, группа, метка или concurrency. Расширение Mac откладывается.
- Если не отмечены пункты по зависимостям и сети, сначала исправляются registry, прокси, авторизация и кэш. Дополнительный узел не считается лечением.
- Если не пройдена проверка
xcodebuild, памяти, диска или симулятора, оптимизируется конкретный этап проекта и окружения. - Если не пройдена проверка подписи, создаётся отдельный защищённый production-контур. Обычный общий пул не расширяется вслепую.
- Если все проверки пройдены, подходящие узлы постоянно заняты, очередь растёт, а длительность отдельных job стабильна, выбирается увеличение фиксированного пула или эластичный Mac.
- Если нагрузка возникает только в релизное окно, при пилоте или сезонном всплеске, сначала выбирается временная ёмкость. Постоянная закупка оправдана только после подтверждения повторяемого профиля нагрузки.
Такой инструмент отвечает сразу на два вопроса: когда iOS CI действительно медленный и когда Mac Runner не хватает. Он также не позволяет превратить единичный тайм-аут в необоснованное капитальное решение.
[ SECTION_07 ] Проверка расширения
После добавления узла или подключения эластичного Mac нужна сквозная приёмка. Она подтверждает не наличие устройства, а способность выполнить реальную задачу.
Порядок проверки:
- Назначить узлу точные метки и проверить его принадлежность к группе.
- Запустить минимальный workflow, который действительно выбирает этот узел.
- Выполнить полную загрузку исходников и зависимостей без ручного вмешательства.
- Сравнить cache hit и cache miss с действующим узлом.
- Запустить
xcodebuildдля обычной сборки и тестов. - Проверить запуск симулятора и сохранение тестовых результатов.
- Выполнить безопасный production-процесс подписи в разрешённом контуре.
- Проверить загрузку артефакта и фиксацию ответа сервиса.
- Перезагрузить узел и убедиться, что Runner возвращается в рабочее состояние.
- Повторить запуск после восстановления и сохранить все логи.
В итоговой записи должны быть не только успешные статусы. Нужны время назначения, этапы подготовки, причины повторной попытки, состояние после перезагрузки и возможность связать каждый лог с конкретным job.
Если команда ещё не собрала такие данные, можно начать с обзора корпоративных решений NOVAKVM, а затем провести пилот на реальной сборочной цепочке. Для сравнения фиксированного и временного узла подойдёт страница заказа Mac для корпоративного сценария, но решение следует принимать только после проверки маршрутизации, подписи и восстановления.
[ SECTION_08 ] Итог для бюджета
Вопрос «нужен ли ещё один Mac» должен появляться в конце расследования, а не в начале. Если причиной оказался неверный label, зависшая зависимость или Keychain, постоянный узел увеличит расходы и сохранит тайм-аут. Если же очередь подтверждена здоровыми Runner, узлы действительно заняты, а отдельные задачи выполняются без аномалий, расширение уже имеет техническое обоснование.
Покупка физических Mac подходит для стабильной длительной нагрузки, контроля оборудования и локальных сетевых интерфейсов. Но она требует закупки, размещения, обновлений, резервирования и самостоятельного восстановления. Временная аренда Mac удобнее для релизного пика, пилота или проверки гипотезы. Её ограничение — необходимость заранее проверить доступ, изоляцию, маршрутизацию и соответствие корпоративным требованиям.
Именно поэтому NOVAKVM разумно рассматривать не как замену доказательной диагностике, а как вариант эластичного Mac-ресурса после неё. Собственная инфраструктура теряет время на закупку, простаивает вне пиков и требует отдельного сопровождения. Эластичный удалённый Mac позволяет сначала прогнать одну настоящую цепочку — очередь, xcodebuild, симулятор, подпись и восстановление — и только затем решать, нужен ли краткосрочный резерв или постоянный пул.