В официальной документации GitLab для macOS перечислены как минимум три разных класса ошибок службы — killed: 9, exit status 134 и Load failed: 5; это не один универсальный сбой запуска: руководство GitLab по установке Runner на macOS связывает их с разными направлениями диагностики.

Симптом: Mac снова доступен по SSH, но GitLab Runner на Mac офлайн после перезагрузки, а задания остаются в очереди.

Быстрое решение: не переустанавливайте Runner сразу. Проверьте по порядку графическую сессию пользователя, пользовательский LaunchAgent, plist и права, процесс, сеть, регистрацию и совпадение тегов.

Эта статья для вас, если вы поддерживаете один удалённый Mac, который периодически требует ручного восстановления. Она также пригодится DevOps-инженерам и release-инженерам, обслуживающим задачи подписи, Xcode и Simulator. Платформенным командам материал поможет оформить объективный тест перезагрузки узла.

Диагностический маршрут

Сбой нужно разделить на четыре состояния:

  1. Mac недоступен.
  2. Mac доступен, но нужный пользователь не вошёл в графическую сессию.
  3. Runner не запущен или его LaunchAgent не загружен.
  4. Runner запущен, но не подключается, не зарегистрирован или не получает подходящие задания.

Есть и отдельная ситуация: Runner отображается как online, но pipeline не стартует. Тогда служба уже работает, а причина находится в тегах, доступе к проекту, состоянии Runner или правилах задания.

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

Сценарий типовой: после перезапуска удалённого Mac инженер подключается по SSH и успешно выполняет uname -a, но страница GitLab всё ещё показывает Runner как недоступный. Это доказывает только доступность операционной системы. SSH не подтверждает вход пользователя в графическую сессию, загрузку LaunchAgent или рабочее соединение Runner с платформой.

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

whoami
id
ps aux | grep '[g]itlab-runner'
gitlab-runner status
gitlab-runner verify

Путь к бинарному файлу и конфигурации может отличаться. Сначала зафиксируйте фактический путь командой command -v gitlab-runner, а не подставляйте его из чужой инструкции. Синтаксис диагностических команд сверяйте с официальным справочником команд GitLab Runner.

Графическая сессия и пользовательский LaunchAgent

На macOS поддерживаемый постоянный режим GitLab Runner — пользовательский LaunchAgent. Это принципиально отличается от системного LaunchDaemon: агент принадлежит вошедшему пользователю и работает в его контексте. Перенос службы в системный демон не является безопасной универсальной заменой.

Проверьте:

whoami
launchctl print-disabled "gui/$(id -u)"
launchctl print "gui/$(id -u)"

Если whoami показывает не ту учётную запись, которая владеет конфигурацией Runner, остановитесь. Если после перезагрузки не было входа в графическую сессию, пользовательский агент мог не получить правильный домен. Для обычного shell-сборщика это иногда выглядит как простой offline. Для задач с Keychain, сертификатами, подписью или Simulator последствия серьёзнее: нужный пользовательский контекст и графические компоненты могут отсутствовать.

Особенно внимательно проверьте сценарии с FileVault. Apple указывает, что автоматический вход может быть ограничен настройками безопасности и организационной политикой: официальное описание ограничений автоматического входа в macOS. Поэтому нельзя безусловно советовать отключать FileVault или включать автоматический вход на производственном узле.

Если узел доступен через VNC или веб-консоль, войдите в ту же учётную запись и откройте Terminal внутри графической сессии. После этого повторите проверку launchctl. Такой способ предпочтительнее, чем попытка исправить пользовательский агент из произвольного SSH-сеанса.

SSH и отсутствующий домен launchctl

Ошибка вида launchctl failed: Could not find domain for обычно указывает не на повреждённую регистрацию Runner, а на неправильный контекст запуска. SSH-сеанс не всегда создаёт GUI bootstrap domain, необходимый для пользовательского LaunchAgent.

Действуйте так:

  1. Подключитесь к удалённому Mac по VNC или через веб-консоль.
  2. Убедитесь, что открыта нужная пользовательская сессия.
  3. В Terminal выполните id -u и сравните UID с владельцем plist.
  4. Проверьте launchctl print "gui/UID".
  5. Только после этого повторите операцию установки или загрузки службы.

Не удаляйте plist, конфигурацию и регистрационные данные на основании одной ошибки launchctl. Сначала сохраните копию:

mkdir -p "$HOME/runner-backup"
cp -p "$HOME"/Library/LaunchAgents/*.plist "$HOME/runner-backup/" 2>/dev/null
cp -p "$HOME"/.gitlab-runner/config.toml "$HOME/runner-backup/" 2>/dev/null

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

Файлы службы, права и журналы

Когда пользователь вошёл правильно, проверьте сам LaunchAgent. Типовая область поиска — ~/Library/LaunchAgents, путь к бинарному файлу Runner, config.toml и каталог журнала. Имя plist зависит от способа установки, поэтому не подставляйте его автоматически.

Проверьте владельца и доступ:

ls -la "$HOME/Library/LaunchAgents"
ls -la "$HOME/.gitlab-runner"
stat "$HOME/.gitlab-runner/config.toml"
command -v gitlab-runner

Владелец plist, конфигурации, бинарного файла и логов должен соответствовать пользователю, от имени которого работает агент. Каталог журнала должен существовать и быть доступным для записи. Ошибка прав нередко появляется после ручного копирования файлов под root, а затем запуска Runner от обычного пользователя.

Три сообщения требуют разного подхода:

Симптом Что подтверждает Следующее действие
killed: 9 Процесс был принудительно завершён или заблокирован системой Сопоставить время завершения с системным журналом, проверить путь бинарного файла и атрибуты безопасности
exit status 134 Процесс завершился аварийно Сохранить журнал Runner, проверить конфигурацию и повторить запуск в пользовательской сессии
Load failed: 5 launchctl не смог загрузить службу Проверить plist, домен gui/UID, владельца файла и доступность путей

Эти коды и направление проверки приведены в официальном разделе GitLab по устранению проблем на macOS. Не превращайте код в диагноз: без журнала он только сужает область поиска.

После изменения прав проверьте запуск без переустановки. Сначала выгрузите только неисправный агент в правильном GUI-домене, затем загрузите его согласно текущей документации GitLab. Сохраните вывод команды и время операции. Если plist повреждён, путь к бинарному файлу устарел или журнал указывает на неполный файл службы, переустановка допустима как последний шаг — при наличии резервной копии config.toml и данных регистрации.

Для управления конфигурационными путями сверяйтесь с документацией GitLab по командам и файлам Runner. Не публикуйте в тикете полный config.toml: там могут находиться токены и параметры, раскрывающие устройство узла.

Процесс, сеть и регистрация

Работающий процесс ещё не означает готовность к CI. Разделите проверку на три независимых результата:

  • процесс Runner существует;
  • Runner поддерживает соединение с GitLab;
  • конкретное задание может быть принято и выполнено.

После запуска выполните:

ps aux | grep '[g]itlab-runner'
gitlab-runner verify

Затем сопоставьте время последнего сообщения в локальном журнале со временем изменения статуса в интерфейсе GitLab. Повторяющиеся тайм-ауты указывают на сеть, прокси, DNS, сертификаты или фильтрацию исходящих соединений. Ошибка регистрации требует другой ветки: проверяйте URL, токен и состояние записи Runner, а не перезапускайте Mac снова.

Если Runner online, а job остаётся pending, проверьте метки. В конфигурации задания должна присутствовать метка, назначенная нужному узлу, например macos-arm или xcode-signing, если именно такие значения реально используются у вас. Нельзя считать пример обязательным именем. GitLab описывает правило так: задание выбирает Runner, когда его набор тегов покрывает все теги job. Подробности приведены в официальной документации о тегах Runner.

Также проверьте:

  • Runner не приостановлен;
  • он допущен к нужному проекту или группе;
  • protected-ветка и protected-теги не блокируют выбор;
  • другой Runner не забирает задания раньше;
  • executor действительно предоставляет macOS-инструменты;
  • лимиты concurrency и занятость узла не создают очередь.

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

Безопасность Shell executor

Shell executor запускает команды с правами пользователя Runner. Поэтому восстановление службы — это одновременно проверка границ доверия.

Не подключайте к общему узлу проекты, которым нельзя доверять. В репозитории и переменных CI могут находиться команды, способные прочитать файлы пользователя, ключи, сертификаты, кэш зависимостей и другие данные соседних проектов. GitLab отдельно предупреждает о рисках Shell executor в документации по безопасности Runner.

Перед возвращением узла в общий пул проверьте:

  • какие проекты имеют доступ к Runner;
  • не остались ли рабочие каталоги предыдущего задания;
  • где хранятся сертификаты и профили подписи;
  • какие переменные доступны процессу;
  • можно ли разделить узлы для доверенного кода и внешних merge request;
  • кто имеет SSH-, VNC- или консольный доступ.

Для задач подписи лучше выделить отдельный контур и не смешивать его с произвольными сборками. Удалённый Mac с постоянно доступным Keychain нельзя считать изолированным только потому, что он находится в дата-центре.

Приёмочная проверка после перезагрузки

Исправление считается завершённым только после повторного запуска узла с нуля. Ручной запуск Runner после перезагрузки не является доказательством автоматического восстановления.

Выполните проверку в таком порядке:

  • [ ] Зафиксируйте состояние успешного задания до перезагрузки.
  • [ ] Сохраните текущего пользователя, UID, путь к Runner и расположение config.toml.
  • [ ] Завершите задания и перезагрузите Mac штатной командой.
  • [ ] Проверьте доступность хоста по SSH.
  • [ ] Проверьте вход в нужную графическую сессию через VNC или веб-консоль.
  • [ ] Убедитесь, что gui/UID существует и пользовательский LaunchAgent загружен.
  • [ ] Проверьте процесс Runner и его журнал.
  • [ ] Подтвердите статус online в интерфейсе GitLab.
  • [ ] Запустите простой shell-job с правильными тегами.
  • [ ] Отдельно запустите тест Xcode, если узел используется для Apple-сборок.
  • [ ] Отдельно проверьте операцию с подписью, Keychain или Simulator.
  • [ ] Запишите, какие действия потребовали человека после перезагрузки.

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

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

FAQ по восстановлению Runner

Пользовательская сессия после перезагрузки

GitLab Runner на macOS не следует автоматически воспринимать как системный демон. При отсутствии входа пользователя LaunchAgent может не стартовать в ожидаемом контексте. Для обычного shell-job иногда достаточно войти в систему. Для подписи, Keychain и Simulator требуется проверить именно ту графическую сессию, которой принадлежит конфигурация.

Команда из SSH не видит launchctl domain

Это признак неверного контекста, а не повод сразу удалять регистрацию. Выполните проверку из Terminal, открытого в графической сессии через VNC или веб-консоль. Сравните UID, владельца plist и домен gui/UID. Если домен появляется только после входа, добавьте этот факт в процедуру восстановления узла.

Онлайн-статус без выполнения pipeline

Сначала сопоставьте теги job и Runner. Затем проверьте разрешения, protected-правила, занятость и executor. Онлайн означает, что Runner поддерживает связь, но не обещает, что он подходит конкретному заданию. Для macOS CI дополнительно проверьте наличие Xcode, сертификатов, профилей и доступа к Simulator в том же пользовательском контексте.

FileVault и автоматическое восстановление

Включённый FileVault может потребовать ручной разблокировки до полноценного входа в macOS. Это ограничение безопасности, а не дефект GitLab Runner. Согласуйте процедуру с политикой организации: дежурный вход через графическую консоль, отдельный резервный узел или иной процесс выдачи задач. Не отключайте защиту диска без оценки угроз и формального разрешения.

Решение для удалённого узла

Домашний Mac или случайно оставленный мини-компьютер часто выглядит дешевле, но для CI у него есть реальные слабые места: после перезагрузки может отсутствовать графический вход, единственный канал доступа может пропасть, а ручное восстановление Keychain и Simulator невозможно без человека рядом. Локальная машина также усложняет резервирование и единый контроль прав.

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

Аренда MACCOME особенно уместна для временного CI-узла, миграционной проверки или резервной среды, когда покупка отдельного Mac преждевременна. Но при постоянной тяжёлой нагрузке, строгом требовании физического USB-доступа или необходимости полного контроля над оборудованием собственный Mac может быть рациональнее. Критерий выбора здесь не сам статус online, а подтверждённое восстановление нужного пользовательского контекста и повторяемый результат после перезагрузки.