Инженерная заметка

Диагностика сбоев сборки и подписи на Cloud Mac через Unified Log

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

Диагностика сбоев сборки и подписи на Cloud Mac через Unified Log

Ночная автоматическая сборка внезапно завершается с ошибкой, в терминале остаётся лишь Command PhaseScriptExecution failed, а повторный запуск проходит успешно. В такой ситуации анализ последней строки ничего не даст. На Cloud Mac инструменты сборки, службы подписи, сетевые процессы и механизмы системных разрешений независимо записывают события в Unified Log macOS. Если сопоставить эти записи на единой временной шкале, обычно можно определить, где возникла проблема: в скрипте проекта, системной службе или оборвавшемся удалённом подключении.

Сначала зафиксируйте время сбоя и условия воспроизведения

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

date -u '+%Y-%m-%dT%H:%M:%SZ'
set -o pipefail
xcodebuild \
  -workspace Demo.xcworkspace \
  -scheme Demo \
  -configuration Release \
  build 2>&1 | tee "$HOME/build-output.log"
printf 'exit=%s
' "${PIPESTATUS[0]}"

Не удаляйте сразу DerivedData, не перезапускайте процессы и не перезаписывайте исходные журналы. Сначала скопируйте вывод сборки, а затем проверьте, удаётся ли стабильно воспроизвести сбой. Для плавающей ошибки сохраните результаты как минимум двух запусков и укажите, совпадали ли использованный коммит, путь к Xcode, окружение Shell и рабочий каталог.

Unified Log не заменяет вывод терминала. Системный журнал объясняет, что происходило в системе и службах, а вывод терминала показывает, до какого этапа дошла команда сборки. Обе записи необходимо анализировать в одном временном диапазоне.

Сузьте выборку Unified Log с помощью предикатов

Команда log show подходит для просмотра уже произошедших событий. Не стоит сразу экспортировать все журналы за несколько часов: это создаст много шума и увеличит объём работы по удалению конфиденциальных данных. Сначала ограничьте диапазон десятью минутами до и после сбоя и отфильтруйте записи по имени процесса:

log show \
  --last 20m \
  --style compact \
  --info \
  --debug \
  --predicate 'process == "xcodebuild" OR process == "codesign"'

Если в терминале отображается только ошибка подписи, дополнительно найдите сообщения об отказе, возвращённые службами безопасности. Если на сборку мог повлиять удалённый сеанс, проверьте sshd и процесс, который фактически выполняет скрипт. В предикатах по возможности используйте поля process, subsystem, category и eventMessage, а не ограничивайтесь широким поиском по всему тексту записи.

log show --last 15m --style compact \
  --predicate '(process == "sshd") OR (eventMessage CONTAINS[c] "denied")'

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

Сигнал в журнале Что проверить в первую очередь Какой вывод не следует делать сразу
permission denied Права на файлы, доступ к связке ключей, учётную запись запуска Аппаратная неисправность узла
Процесс был завершён Нехватку памяти, родительский процесс, правила тайм-аута скрипта Повреждение самого Xcode
Разрыв сеанса SSH Сеть на стороне клиента, параметры keepalive, способ запуска задачи Сборка обязательно завершилась ошибкой
Служба подписи не нашла совпадений Связку ключей, имя сертификата, сопоставление профилей Необходимость переустановить всю цепочку инструментов

Наблюдайте за ключевыми событиями во время воспроизведения

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

log stream \
  --style compact \
  --level info \
  --predicate 'process == "xcodebuild" OR process == "codesign" OR process == "securityd"'

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

Добавьте в скрипт маркеры для сопоставления событий

В собственных скриптах можно записывать события в Unified Log в начале и в конце выполнения, используя постоянный subsystem и уникальный идентификатор каждого запуска. Тогда не придётся полагаться на неточный поиск по именам файлов.

job_id="$(date -u '+%Y%m%dT%H%M%SZ')"
logger -p user.notice -t minid-build "job=$job_id phase=start"
./scripts/build.sh
status=$?
logger -p user.notice -t minid-build "job=$job_id phase=end status=$status"
exit "$status"

Метки, записанные через logger, можно искать по полю process или содержимому сообщения. Идентификатор задачи предназначен только для сопоставления событий одного запуска и не должен содержать токены, учётные данные репозитория или данные клиентов.

Экспортируйте материалы, пригодные для проверки и безопасной передачи

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

mkdir -p "$HOME/diagnostics"
sudo log collect \
  --last 15m \
  --output "$HOME/diagnostics/build-incident.logarchive"

cp "$HOME/build-output.log" "$HOME/diagnostics/"

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

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

Выберите дальнейшие действия на основании собранных данных

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

Перед завершением диагностики выполните минимальную приёмочную проверку. Дважды подряд соберите один и тот же коммит и убедитесь, что код завершения равен нулю. Проверьте подписанный артефакт командой codesign --verify --deep --strict. Убедитесь, что фоновая задача завершается ожидаемым образом после разрыва SSH и что в диагностическом каталоге нет токенов в открытом виде. Сбой можно считать полностью устранённым, только если все эти условия выполнены одновременно.

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

Почему вывода xcodebuild бывает недостаточно?

Он отражает в основном этапы сборки. Отказы в доступе, ошибки служб подписи, сетевые разрывы и сбои системных процессов могут остаться только в Unified Log.

Что нужно удалить из журнала перед отправкой?

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

Облачный Mac MiniD

Аренда выделенного физического Mac mini на день, неделю или месяц

Каждый тариф работает на выделенном физическом Mac mini с доступом по удалённому рабочему столу и SSH; актуальные модели, регионы и сроки указаны на странице заказа.

Посмотреть варианты заказа