Если единственный Mac выходит на обслуживание, публикация iOS-приложения останавливается вместе с ним.

Быстрое решение: не превращайте второй Mac в периодическую резервную копию — обычные сборки и тесты распределяйте между одинаково подготовленными узлами, а производственную подпись выводите в отдельный основной и резервный контур.

Текст актуален на 20 сентября 2026 года; сведения о статусе Xcode 27 и требованиях сверены с публикацией Apple о выпуске Xcode, системными требованиями Xcode и заметками к выпуску Xcode 27.

Эта статья нужна вам, если у компании сейчас только один Mac-сервер сборки и любое обслуживание прерывает выпуск. Она также предназначена для платформенной команды, которая переносит production-пайплайн на Xcode 27, и для CTO или технического директора, выбирающего между второй физической машиной, резервным удалённым Mac и сохранением текущего риска.

Xcode 27 и границы отказоустойчивости

14 сентября 2026 года Apple выпустила Xcode 27. На дату проверки App Store Connect принимает приложения, собранные с Xcode 27 и соответствующим официальным SDK; это следует учитывать при планировании production-пула, а не смешивать с предварительными версиями Xcode 27.1 и Xcode 27.2. Проверяйте актуальные ограничения через официальные правила загрузки сборок в App Store Connect.

Второй Mac устраняет только часть рисков. У вас по-прежнему могут остаться общие точки отказа:

  • CI-контроллер или очередь заданий недоступны, поэтому оба узла физически работают, но не получают задачи;
  • сеть, DNS, VPN или исходящий доступ к Apple недоступны для обеих машин;
  • сертификат, закрытый ключ, профиль provisioning или учётная запись подписи повреждены либо отозваны;
  • хранилище артефактов и журналов является общим и выходит из строя вместе с CI;
  • на обеих машинах установлена одна несовместимая версия Xcode или одинаково повреждён кэш;
  • ручной запуск подписания зависит от конкретного имени хоста и локального пользователя.

Поэтому архитектуру нужно оценивать не по количеству Mac, а по независимости доменов отказа. Локальная пара в одной стойке помогает при отказе одной машины, но почти не защищает от проблем общего питания, сетевого сегмента, ошибочного обновления или компрометации общего секрета. Узлы в разных местах расширяют защиту, однако добавляют задержку, различия в доступе к ресурсам и больше операционных процедур.

Сколько Mac нужно для сервера сборки Xcode 27

Одного ответа для всех компаний нет. Для фиксированной постоянной нагрузки разумна пара узлов: оба принимают обычные задания, но production-подпись выполняется через изолированный основной путь с подготовленным резервом. Если нагрузка возникает главным образом перед релизами или команда мигрирует с другой версии Xcode, базовый узел можно дополнить удалённым Mac на время пиков и испытаний.

Второй узел не нужен как автоматическая гарантия доступности, если CI-контроллер, хранилище и подпись остаются общими и не имеют проверенного плана восстановления. В таком случае вы покупаете дополнительное оборудование, но сохраняете прежнюю единственную точку отказа.

Метрики архитектуры двух узлов

Удобнее принимать решение через измеряемые признаки: для каждого риска фиксируются доказательства, условие прохождения и причина отказа. Это превращает разговор о высокой доступности в проверяемый контракт между платформенной командой, безопасностью и владельцем релиза.

Метрика Базовая проверка Проходит Нельзя принимать
Независимость отказа Отдельно описаны хост, сеть, контроллер, хранилище и подпись Отказ одного домена не останавливает минимальный релизный путь Второй Mac зависит от того же незащищённого ресурса
Совместимость среды Зафиксированы архитектура Apple Silicon, macOS, Xcode, SDK и инструменты зависимостей Один и тот же commit даёт сопоставимый результат на обоих узлах Различия объясняются локальной настройкой
Маршрутизация Есть правила по меткам, очередям и уровню доверия Задание попадает только на разрешённый пул Production-задача может случайно уйти на общий runner
Изоляция подписи Ключи, Keychain и сервисные учётные данные разделены Подписывающий узел получает минимально необходимый доступ Для быстрого переключения копируется весь Keychain
Восстановление Есть журнал переключения и обратного перехода Команда подтверждает сборку, подпись, публикацию и возврат Невозможно доказать, что резерв действительно готов
Экономика Считаются простой, обслуживание, обновления и пиковая ёмкость Выбранная модель соответствует характеру нагрузки Сравнивается только цена Mac без эксплуатации

Для оценки можно присвоить каждому пункту статус «пройдено», «с ограничениями» или «отклонено». Итоговое решение принимает не среднее значение: один провал в подписи или хранилище может заблокировать весь production-контур, даже если остальные метрики выглядят хорошо.

Базовая среда и контроль дрейфа

Xcode 27 нельзя считать одинаково установленным только потому, что на двух машинах отображается одно название приложения. Зафиксируйте в машинно-читаемом baseline:

  • модель и архитектуру Mac;
  • версию macOS и её совместимость с нужным выпуском Xcode;
  • точный build Xcode 27, используемый в production;
  • версии SDK, Swift, Ruby, Python, Java и инструментов управления зависимостями, если они участвуют в пайплайне;
  • настройки проекта, скрипты, переменные среды и параметры симуляторов;
  • способ получения кэшей, артефактов и зависимостей;
  • состояние Keychain и контекст пользователя, в котором выполняется подпись.

Требования к операционной системе и оборудованию нельзя заменять внутренней таблицей команды: сверяйте их с официальной страницей Apple о системных требованиях Xcode. Release Notes нужны для проверки изменений поведения и ограничений конкретного Xcode 27, а не только для чтения списка новых функций.

Не копируйте образ первой машины на вторую как единственный способ синхронизации. Такой подход переносит скрытые настройки, устаревшие сертификаты, локальные пути и повреждённые кэши. Надёжнее описать среду в скриптах, зафиксировать версии зависимостей и автоматически проверять baseline перед допуском узла в production.

Какие переменные среды нужно синхронизировать на резервном Mac

Синхронизировать нужно воспроизводимое состояние, а не секреты без разбора. Исходный код, lock-файлы, скрипты, настройки сборки, версии инструментов и публичные параметры могут восстанавливаться из репозитория или системы управления конфигурацией. Кэши следует считать ускорителем, а не единственным источником результата.

Проверьте одинаковым commit:

  • успешность компиляции;
  • тесты и тестовые устройства;
  • содержимое и контрольные суммы артефакта;
  • предупреждения и логи;
  • этап экспорта;
  • этап подписи;
  • загрузку в App Store Connect и итоговый статус.

Apple отдельно описывает статусы загрузки сборок в справочнике App Store Connect. Это полезнее, чем считать задачу успешной сразу после завершения локального xcodebuild: публикационный контур может отклонить сборку позже.

Маршрутизация задач и модель размещения

Для обычных тестов и сборок выбирайте один из трёх вариантов.

Активный — активный. Оба Mac постоянно принимают задачи. Это оправдано, если нагрузка достаточно стабильна, среда воспроизводима, а очередь умеет учитывать ёмкость и доверие к задаче. Недостаток — больше параллельных обновлений и сложнее расследование различий.

Активный — резервный. Второй узел не принимает обычные задания либо получает только низкорисковые проверки. Его преимущество — более простой контроль production-среды. Недостаток — состояние резерва быстрее устаревает, если автоматическая проверка запускается редко.

Базовый узел плюс эластичный Mac. Постоянная инфраструктура обслуживает ежедневные задачи, а дополнительный Mac включается для миграции, релизного пика или аварийной замены. Это снижает капитальные затраты при неровной загрузке, но требует заранее проверить выдачу доступа и регистрацию runner.

GitHub Runner Groups ограничивают доступ к группам runner, а labels помогают сопоставлять задачу с исполнителем; это следует из документации GitHub по группам runner и меткам self-hosted runner. В Jenkins похожую роль выполняют labels и agents, описанные в документации Jenkins. Для GitLab применяются tags согласно руководству по настройке Runner.

Но ни одна из этих функций сама по себе не проверяет одинаковость Xcode, не изолирует сертификаты и не доказывает, что артефакт можно восстановить. Метка macos-xcode27 — это условие маршрутизации, а не сертификат готовности узла.

Как автоматически распределять задания между двумя Mac

Не привязывайте workflow к именам mac-01 и mac-02. Используйте логические свойства пула:

  • macos;
  • apple-silicon;
  • xcode-27;
  • production-build;
  • signing-isolated;
  • trusted-runner.

Обычные pull request можно направлять на пул macos, если узел прошёл baseline-проверку. Релизную сборку направляйте на xcode-27 и требуйте дополнительную метку изолированного подписывающего узла. Задания с непроверенными зависимостями не должны иметь доступа к production-пулу.

У правила должен быть явный отказ: если нет узла с нужными атрибутами, задание остаётся в очереди или передаётся оператору, но не «понижается» автоматически до менее доверенного Mac. Ручное переключение должно быть доступно ответственному за релиз и фиксироваться в журнале.

Подпись, секреты и состояние

Разделите данные на восстанавливаемые и невосстанавливаемые.

К восстанавливаемым относятся исходный код, lock-файлы, конфигурация pipeline, логи, кэши и уже опубликованные артефакты. Их можно получать из репозитория, хранилища или системы артефактов. К невосстанавливаемым либо чувствительным активам относятся закрытые ключи, сертификаты, provisioning profiles, токены и сервисные учётные данные.

Как разделить сертификаты между двумя узлами без утечки ключа

Не делайте общий Keychain резервным хранилищем и не копируйте весь профиль пользователя на второй Mac. Для каждого подписывающего контура определите:

  • какой job имеет право на подпись;
  • какой пользовательский контекст запускает xcodebuild;
  • где хранится секрет;
  • как секрет выдаётся на время задания;
  • кто может отозвать доступ;
  • как фиксируется использование;
  • как удаляются временные материалы после завершения.

Обычные тестовые узлы должны собирать и тестировать без доступа к production private key. Резервный подписывающий узел может иметь подготовленную, но строго ограниченную процедуру активации. При компрометации ключа задача меняется: это уже не плановое переключение, а немедленное прекращение использования, отзыв сертификата, выпуск нового секрета и проверка всех связанных pipeline.

Такой подход медленнее бездумного копирования, зато ограничивает последствия вредоносного задания или ошибочного скрипта. Подробности о конфиденциальности и обращении с данными при выборе удалённой инфраструктуры стоит сопоставить с материалами SSHMac о приватности.

План восстановления и пошаговая приёмка

Резервный Mac становится частью production только после доказательной проверки. Не подставляйте универсальные значения времени восстановления или допустимой потери данных: их должен утвердить владелец продукта и зафиксировать в корпоративном плане непрерывности.

Выполните процедуру по шагам.

  1. Опишите минимальный релизный путь. Укажите commit, тесты, экспорт, подпись и загрузку в App Store Connect. Отдельно отметьте действия, которые можно выполнить вручную при недоступности автоматического контроллера.

  2. Составьте карту отказов. Проверьте основной Mac, CI-контроллер, сеть, хранилище артефактов, Xcode, узел подписи и внешние сервисы. Для каждого ресурса назначьте владельца и допустимый способ обхода.

  3. Проверьте baseline второго узла. Скрипт должен сравнить macOS, build Xcode 27, SDK, зависимости, настройки проекта и разрешения Keychain. Различие блокирует допуск, пока его причина не подтверждена.

  4. Запустите один и тот же commit на обоих Mac. Сравните тесты, логи, артефакты и подпись. Не ограничивайтесь зелёным статусом CI: проверьте фактический архив и его загрузку.

  5. Имитируйте отключение основного узла. Удалите его из пула маршрутизации, не меняя код и конфигурацию задания. Зафиксируйте обнаружение отказа, постановку в очередь, выбор резервного runner и получение артефакта.

  6. Проверьте production-подпись. Убедитесь, что резервный путь не выдаёт секрет обычному runner и запускается только в разрешённом контексте. Любой обход этого правила означает отрицательный результат приёмки.

  7. Проверьте сетевой и внешний сбой. Отдельно рассмотрите недоступность репозитория, артефактного хранилища и App Store Connect. Если система не может завершить публикацию, она должна честно остановиться, а не создать неподтверждённый результат.

  8. Верните основной узел. Убедитесь, что незавершённая задача не запускается повторно без проверки идентификатора commit и артефакта. После возврата сравните состояние среды с baseline, иначе обратное переключение может вернуть дрейф.

  9. Сформируйте протокол. Запишите время обнаружения, переключения, сборки, подписи, загрузки и восстановления; приложите логи, идентификатор задания и решение ответственного лица.

Итог должен быть одним из трёх: «пройдено», «пройдено с условиями» или «отклонено». Условный результат требует конкретного владельца и срока исправления, а не устного обещания. Если не подтверждены подпись, внешний доступ или отсутствие повторной публикации, второй узел нельзя объявлять готовым.

Стоимость и эксплуатационная модель

Сравнивайте не цену второй машины, а полный цикл владения:

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

Фиксированная нагрузка и постоянная потребность в подписи обычно оправдывают долгосрочную пару собственных узлов, если компания готова самостоятельно поддерживать оборудование и физический доступ. Неровная нагрузка, миграция на Xcode 27 и необходимость быстро проверить резервный путь чаще подходят для базовой машины плюс удалённый Mac по подписке. Условия аренды и доступные варианты можно сверить на странице аренды Mac mini в SSHMac, но коммерческие параметры нужно сопоставлять с вашей политикой безопасности, регионами и требованиями к доступу.

Удалённый Mac не заменяет независимое хранилище, резервирование CI-контроллера или процедуру ротации ключей. Его роль — дать физически отдельный узел для проверки сборки, временно принять задания или пережить пик, пока команда решает вопрос постоянной инфраструктуры.

Для закупочного решения зафиксируйте четыре пункта: кто разрешает переключение, какой лог считается доказательством, при какой очереди добавляется ёмкость и когда временный узел отключается. Без этих условий эластичная инфраструктура легко превращается в постоянную, но плохо контролируемую статью расходов.

Итоговое решение

Если у вас стабильная ежедневная нагрузка, создавайте два baseline-совместимых Mac-узла и распределяйте между ними обычные сборки. Production-подпись при этом должна оставаться в отдельном доверенном контуре с заранее проверенным резервом. Если нагрузка скачет или команда только переносит пайплайн на Xcode 27, не спешите покупать постоянное оборудование: сначала добавьте изолированный удалённый Mac, выполните полную процедуру переключения — сборка, подпись, загрузка и возврат — и примите решение по журналу.

Единственный локальный Mac сохраняет риск простоя при обслуживании, ограничивает релизную ёмкость и требует ручного восстановления после повреждения среды. Покупка второго устройства снижает риск отказа хоста, но добавляет расходы на простой, обновление, физическое размещение и регулярную проверку готовности. SSHMac в таком сценарии может быть практичнее как временный резервный узел или мощность для релизного пика: вы проверяете реальный pipeline до долгосрочной закупки, не выдавая удалённому Mac роль универсальной замены всей платформы.

Начните с инвентаризации общих точек отказа и одной контролируемой тренировки переключения. Если журнал подтверждает среду, подпись и возврат без дрейфа, тогда уже выбирайте постоянную пару Mac или продолжайте аренду как управляемую эластичную часть корпоративного CI/CD.