Управление режимом сна облачного Mac для стабильной CI

Управление режимом сна облачного Mac для стабильной CI

Ночная архивация останавливается на полпути, а утром в журнале остаётся лишь запись о тайм-ауте. После повторного подключения к удалённому рабочему столу сам компьютер снова работает нормально. Не стоит сразу списывать такую проблему на сеть. В случае автоматической CI на облачном Mac сначала нужно разграничить четыре состояния: сон системы, сон дисплея, завершение удалённого сеанса и остановку процесса Runner. Внешне они похожи, но устраняются на совершенно разных уровнях.

Сначала определите уровень, на котором произошёл сбой

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

Симптом Что проверить в первую очередь Типичный вывод
Экран погас, но SSH и сборка работают displaysleep Перешёл в сон только дисплей
SSH и задание одновременно стали недоступны sleep, журнал электропитания Система могла перейти в режим сна
После закрытия терминала задание завершилось Способ запуска Runner Процесс привязан к интерактивному сеансу
Изменилось время загрузки хоста Записи о перезагрузках, журнал задания Произошла перезагрузка системы

Сначала соберите базовые данные, не изменяя систему:

mkdir -p "$HOME/ci-audit"
date > "$HOME/ci-audit/power-baseline.txt"
pmset -g custom >> "$HOME/ci-audit/power-baseline.txt"
pmset -g assertions >> "$HOME/ci-audit/power-baseline.txt"
pmset -g sched >> "$HOME/ci-audit/power-baseline.txt"
sysctl -n kern.boottime >> "$HOME/ci-audit/power-baseline.txt"

pmset -g custom показывает настройки для разных источников питания, pmset -g assertions — процессы, которые в данный момент препятствуют переходу в режим сна, а pmset -g sched помогает обнаружить уже запланированные события. Не делайте вывод только по одной строке sleep 0: необходимо учитывать и профиль настроек, и фактически активные assertions.

Разграничьте сон дисплея и сон системы

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

На выделенном узле, предназначенном только для непрерывных сборок, после сохранения исходных настроек можно отключить сон системы при питании от сети:

sudo pmset -c sleep 0 disksleep 0 powernap 0
pmset -g custom

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

Настройки электропитания позволяют компьютеру продолжать работу, но не превращают временный процесс, привязанный к терминалу, в фоновую службу.

Оборачивайте отдельные сборки в caffeinate

caffeinate создаёт системные assertions на всё время работы дочернего процесса. В отличие от глобального отключения сна, такой подход имеет чётко ограниченную область действия и особенно хорошо подходит для архивирования по расписанию, длительных тестов и однократной сборки зависимостей.

Создайте единый скрипт-обёртку:

sudo install -d -m 0755 /usr/local/bin
cat <<'EOF' | sudo tee /usr/local/bin/ci-awake >/dev/null
#!/bin/zsh
set -euo pipefail
if (( $# == 0 )); then
  exit 64
fi
exec /usr/bin/caffeinate -ims "$@"
EOF
sudo chmod 0755 /usr/local/bin/ci-awake

В Runner не добавляйте ещё один оператор фонового запуска: передавайте целевую команду непосредственно скрипту-обёртке.

/usr/local/bin/ci-awake /usr/bin/xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  build

Параметр -i предотвращает сон системы из-за бездействия, -m — переход диска в режим простоя, а -s — сон системы при питании от сети. После завершения целевой команды assertion освобождается вместе с процессом-обёрткой. Во время сборки выполните pmset -g assertions и убедитесь, что в выводе присутствует соответствующий assertion.

Отвяжите Runner от удалённого сеанса

Если сборка по-прежнему завершается после закрытия SSH- или графического сеанса, проблема уже не в pmset, а в родительском процессе Runner. Процесс, запущенный вручную из терминала, может получить сигнал завершения сеанса. Сбой также возможен из-за исчезновения рабочего каталога, переменных окружения или временного контекста связки ключей.

Постоянно работающий Runner следует передать под управление launchd. При проверке убедитесь как минимум в следующем:

  1. Служба использует постоянную метку, например local.ci.runner, и не запускается повторно после каждого входа в систему.
  2. Рабочий каталог задан абсолютным путём и не зависит от текущего каталога терминала в момент запуска.
  3. PATH, выбор набора инструментов и каталог кеша настроены явно, а не наследуются из конфигурации интерактивной оболочки.
  4. Стандартный вывод и поток ошибок записываются в разные файлы журналов с управляемой политикой ротации.
  5. Получив сигнал завершения, Runner корректно останавливает текущий дочерний процесс, не оставляя процессы-сироты, блокирующие каталог сборки.

Проверить пользовательскую службу можно так:

launchctl print "gui/$(id -u)/local.ci.runner"
pgrep -afil 'runner|xcodebuild'

Если служба должна работать без вошедшего в систему пользователя, используйте системный LaunchDaemon и проверяйте его командой launchctl print system/local.ci.runner. Не оставляйте одновременно пользовательский и системный экземпляры: два Runner могут начать конкурировать за один рабочий каталог.

Завершите приёмку на основании фактических данных

После настройки запустите тестовое задание, продолжительность которого превышает время, через которое ранее происходил сбой. Во время теста намеренно закройте удалённый рабочий стол и SSH-клиент, но не останавливайте Runner. После повторного подключения проверьте код завершения задания, время создания артефактов, процесс службы и системные assertions.

Если задание снова прервётся, немедленно соберите следующие данные:

date
pmset -g assertions
pmset -g log | tail -n 120
sysctl -n kern.boottime
last reboot | head

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

Для выделенного облачного Mac на XcodeVM стабильная автоматическая CI опирается на две чёткие границы ответственности: системная политика электропитания поддерживает работу узла, а launchd и скрипт-обёртка не позволяют процессу сборки зависеть от интерактивного сеанса. Раздельная настройка и фиксация данных по этим уровням гарантируют, что при следующем сбое останется конкретная диагностируемая причина, а не расплывчатая запись о тайм-ауте.

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

Отключение сна дисплея запрещает переход всей системы в сон?

Нет. Параметр displaysleep относится только к выводу изображения. Системный sleep и активные энергетические assertions необходимо проверять отдельно командами pmset.

Нужно ли полностью отключать сон на CI-машине?

Это допустимо для физического узла, постоянно выделенного под CI, после сохранения исходных настроек. Для редких заданий лучше ограничить запрет сна временем работы caffeinate.

Почему сборка завершается после закрытия удалённого сеанса?

Runner может быть дочерним процессом интерактивной оболочки. Его следует запускать как самостоятельную службу launchd, а затем отдельно проверить код завершения и журналы питания.

XcodeVM облачные Mac

Выберите выделенный физический сервер под текущую нагрузку

Проверьте объём памяти, хранилище, узел и расчётный период, затем перейдите к настройке заказа. Все узлы работают 365 дней в году; фактическая доступность определяется в реальном времени через панель управления.

Выбрать план и арендовать