Источник данных: официальная документация Jenkins отдельно описывает несколько способов связи контроллера с Agent — через SSH, входящее подключение и WebSocket. Поэтому при отключении Jenkins macOS Agent не следует сразу переустанавливать узел: сначала определите, на каком уровне возник сбой. Практический порядок такой: планирование Jenkins, сеть и канал подключения, процесс Java Agent, постоянная сессия macOS, затем рабочий каталог и Xcode. Случайный сбой процесса можно исправить на месте. Повторяющиеся обрывы или дрейф окружения — основание изолировать узел и пересоздать его либо добавить отдельный удалённый Mac.

Эта статья предназначена для трёх групп:

  • DevOps-инженеров, которым нужно быстрее возвращать Jenkins Agent в рабочее состояние.
  • Разработчиков Apple-платформы, поддерживающих Xcode CI для сборки, тестов и подписи.
  • Владельцев CI-платформы, которым нужны понятные критерии приёмки постоянно работающего macOS-узла.
01

Сначала отделите отключение от неисправной диспетчеризации

Одинаковая надпись в интерфейсе Jenkins может скрывать разные проблемы. Узел действительно мог потерять соединение. Контроллер мог сам пометить его офлайн. Задание может стоять в очереди из-за несовпадения метки или отсутствия свободного исполнителя, хотя Agent остаётся подключённым.

Начните с фиксации пяти элементов:

  • времени последнего успешного задания;
  • времени, когда узел впервые стал офлайн;
  • текста причины в карточке узла;
  • причины ожидания конкретного задания;
  • последних строк лога контроллера и лога Agent.

Сопоставьте эти данные с состоянием удалённого Mac. Если SSH работает, это подтверждает только доступность операционной системы и системного SSH. Это не доказывает, что Jenkins Agent жив, Java запущена, секрет актуален, а рабочий пользователь видит тот же набор инструментов.

В разделе управления узлами Jenkins проверьте:

  • статус соединения;
  • назначенные метки;
  • число исполнительных слотов;
  • удалённый корневой каталог;
  • выбранный Launch Method;
  • признак ручного или административного отключения.

Официальное руководство по управлению узлами и Agent в Jenkins полезно использовать как карту интерфейса, но не как замену журналам. Важна временная последовательность: что произошло первым — потеря канала, завершение процесса, недоступность каталога или отказ планировщика.

Внимание: запись «SSH подключается» — это положительный результат только для одного слоя. Не используйте её как доказательство восстановления CI.

02

Где именно рвётся цепочка Jenkins macOS Agent

Слой подключения: DNS, прокси и разрешённый маршрут

Выясните, кто инициирует соединение. При SSH-контроллер подключается к системному SSH удалённого Mac. При входящем варианте сам Agent устанавливает канал к контроллеру. WebSocket также имеет собственный маршрут через HTTP(S). Эти механизмы используют разные точки проверки.

Не смешивайте:

  • системный Remote Login macOS;
  • SSH-службу контроллера Jenkins;
  • транспорт Jenkins Agent;
  • обратное WebSocket-соединение.

Проверьте с той стороны, которая инициирует канал:

  1. Разрешается ли имя контроллера или узла через DNS.
  2. Не изменился ли адрес контроллера после миграции.
  3. Не требует ли маршрут прокси.
  4. Не блокирует ли межсетевой экран исходящий или входящий трафик.
  5. Не истёк ли сертификат, если используется защищённый WebSocket или HTTPS.
  6. Не изменились ли правила доступа после перезапуска сетевого оборудования.

Для системного SSH на Mac отдельно убедитесь, что включён Remote Login и разрешён нужный пользователь. Инструкция Apple по Remote Login относится именно к доступу по SSH, а не к Jenkins Agent.

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

  • рукопожатие завершается без повторных ошибок;
  • heartbeat сохраняется в течение наблюдаемого периода;
  • контроллер видит Agent после собственного перезапуска;
  • временный сетевой обрыв не требует ручного запуска процесса.

Слой процесса: Java, agent.jar и секрет

Если канал потенциально доступен, проверьте процесс на Mac. Выполняйте диагностику от имени фактического пользователя Agent, а не только из вашей административной SSH-сессии.

ps aux | grep -i '[a]gent'
pgrep -af 'java.*agent'

Команды выше показывают наличие процесса, но не объясняют причину завершения. Сохраните:

  • командную строку запуска;
  • путь к Java;
  • путь к agent.jar;
  • код выхода;
  • стандартный вывод и стандартную ошибку;
  • время последнего старта;
  • пользователя и рабочий каталог.

Проверьте, не изменились ли параметры запуска после обновления Jenkins. Частая ошибка — считать, что старый файл Agent и старые параметры автоматически подходят новой конфигурации контроллера. Сопоставьте используемый способ запуска с текущей страницей узла и официальным руководством Jenkins по работе с Agent.

Версию Java нельзя фиксировать в статье навсегда. Требования зависят от линии Jenkins и конкретного выпуска. Перед заменой runtime откройте актуальную политику поддержки Java в Jenkins и проверьте совместимость именно с вашей версией контроллера.

Отдельно проверьте секрет. Используйте только временные обозначения вроде <AGENT_SECRET>, <CONTROLLER_URL> и <NODE_NAME>. Не копируйте секрет в журналы, тикеты или команды, которые попадут в историю shell. Если секрет был заменён, старый процесс может завершаться или отклоняться при повторной регистрации.

Порядок исправления:

  1. Остановите старый процесс, если он завис и не завершился штатно.
  2. Проверьте Java по официальной матрице поддержки.
  3. Получите актуальные параметры подключения из конфигурации узла.
  4. Запустите Agent вручную в тестовой сессии.
  5. Сохраните ошибку, если процесс снова завершится.
  6. Только после успешной проверки возвращайте автоматический запуск.
03

Постоянный запуск macOS нельзя подтверждать одной SSH-сессией

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

Проверьте четыре свойства запуска:

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

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

Минимальный сценарий проверки:

  1. Запустите Agent штатным способом.
  2. Убедитесь, что Jenkins видит узел.
  3. Выполните безопасное диагностическое задание.
  4. Завершите SSH-сеанс.
  5. Проверьте, что Agent остаётся подключённым.
  6. Перезапустите Mac.
  7. Не входите вручную через VNC или SSH.
  8. Убедитесь, что узел снова появился в Jenkins.
  9. Запустите тестовую задачу от имени пользователя Agent.

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

04

Онлайн-узел не всегда готов к Xcode CI

Статус «в сети» означает, что Jenkins поддерживает соединение. Он не гарантирует доступность рабочего каталога, метки, Xcode, симуляторов, сертификатов или профилей подписи.

Проверьте удалённый корневой каталог:

pwd
id
df -h
mkdir -p "$HOME/jenkins-diagnostic"
touch "$HOME/jenkins-diagnostic/write-test"

Затем сравните фактический путь и пользователя с настройками узла. Неправильный Remote Root Directory, заполненный диск или каталог без права записи могут проявиться как зависание задания, а не как отключение Agent.

Проверьте метки и исполнитель:

  • метка задания должна совпадать с меткой узла;
  • узел не должен быть ограничен режимом «только задачи с меткой», если pipeline её не задаёт;
  • число исполнителей должно соответствовать безопасной параллельности;
  • рабочие каталоги параллельных задач не должны конфликтовать;
  • после очистки старого workspace права должны остаться у пользователя Agent.

Xcode, shell и подпись требуют разных проверок

Сначала зафиксируйте выбранный путь к Xcode:

xcode-select -p
xcodebuild -version

Путь, установленный в интерактивной оболочке администратора, может отличаться от пути в окружении Jenkins. Настройки Command Line Tools проверяйте по официальной документации Apple, а отсутствие компонентов — по руководству по установке Command Line Tools.

Не превращайте одну короткую shell-команду в критерий готовности. Используйте три независимых задания:

  • обычная сборка тестового проекта;
  • запуск тестов и сохранение результата;
  • архивирование и подпись артефакта.

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

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

05

Пошаговая процедура восстановления без поспешного пересоздания

Первый шаг: сохраните доказательства

До перезапуска соберите снимок:

  • карточку узла и причину offline;
  • причину ожидания задания;
  • последние строки лога контроллера;
  • лог Agent;
  • ps, pgrep, id, pwd и df;
  • версию Jenkins и Java;
  • выбранный Launch Method;
  • время последней успешной сборки.

Это позволяет отличить первичную причину от последствий перезапуска.

Второй шаг: подтвердите направление соединения

Проверьте DNS, маршрут, прокси, сертификаты и правила фильтрации с нужной стороны. Для SSH не подменяйте тест соединения с контроллером проверкой системного SSH Mac. Для WebSocket проверяйте URL контроллера, TLS и прокси отдельно.

Третий шаг: повторно запустите процесс в наблюдаемом режиме

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

Четвёртый шаг: проверьте выход из сессии

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

Пятый шаг: проверьте перезагрузку без оператора

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

Шестой шаг: выполните три разных приёмочных задания

Проверяйте отдельно сборку, тесты и подпись. Для каждого сохраняйте пользователя, путь к Xcode, рабочий каталог и итоговый статус. Успешный echo или xcodebuild -version не заменяет производственную задачу.

Седьмой шаг: повторите условия сбоя

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

06

Чек-лист приёмки удалённого Mac-узла

  • [ ] Причина последнего отключения сохранена из интерфейса Jenkins и журналов.
  • [ ] Направление соединения соответствует выбранному Launch Method.
  • [ ] DNS, маршрут, прокси, сертификаты и сетевые правила проверены с нужной стороны.
  • [ ] Процесс Agent запускается под ожидаемым пользователем.
  • [ ] Java соответствует актуальной политике поддержки для установленной версии Jenkins.
  • [ ] Секрет подключения не просрочен и не попал в журналы.
  • [ ] Agent переживает завершение SSH-сессии.
  • [ ] Agent автоматически возвращается после перезагрузки Mac.
  • [ ] Remote Root Directory существует и доступен для записи.
  • [ ] Метка узла совпадает с ограничениями pipeline.
  • [ ] Тестовая сборка Xcode завершается успешно.
  • [ ] Тестовая задача создаёт корректный результат.
  • [ ] Задание подписи использует нужного пользователя и ресурсы.
  • [ ] После сетевого сбоя и перезапуска контроллера не требуется ручное вмешательство.
07

Три таблицы для выбора следующего действия

Наблюдение Где искать подтверждение Вероятный слой Первое действие
SSH к Mac работает, Jenkins показывает offline Лог контроллера, лог Agent, карточка узла Транспорт Jenkins или процесс Agent Определить Launch Method и проверить процесс
Узел online, задание не стартует Причина очереди, метки, исполнитель Планировщик Jenkins Исправить label или доступность executor
Agent запускается вручную, но исчезает после выхода Журнал launchd, контекст пользователя Сессия macOS Проверить автоматический запуск и владельца
Узел online, workspace не создаётся Remote Root Directory, df -h, права Файловая система Исправить путь, место или права
Shell проходит, сборка Xcode завершается ошибкой Лог xcodebuild, путь Xcode, keychain Toolchain или подпись Выполнить отдельные build, test и sign jobs
Способ запуска Когда подходит Что проверять Риск неправильного выбора
SSH Контроллер стабильно достигает Mac по системному SSH Remote Login, учётную запись, ключи и маршрут SSH доступен, но Jenkins-канал настроен иначе
Входящее подключение Mac может сам достичь контроллера URL, секрет, исходящий доступ и постоянный процесс Процесс не переживает перезагрузку
WebSocket Нужен канал через разрешённый HTTP(S)-маршрут TLS, прокси, адрес контроллера и логи рукопожатия Прокси разрывает длительное соединение
Ручной запуск из SSH Только краткая диагностика Ошибку старта и фактическое окружение Agent исчезает после выхода пользователя
Состояние после проверки Решение Почему
Единичный сбой процесса, окружение стабильно Ремонтировать на месте Причина локализована и воспроизводимость низкая
Повторные сетевые обрывы, но причина известна Исправить маршрут или изолировать узел Нельзя маскировать нестабильность перезапусками
Дрейф Java, Xcode, прав или подписи Пересоздать узел по документированной схеме Ручные исправления увеличивают непредсказуемость
Нужна независимость от одного Mac Добавить отдельный удалённый Mac Снижает единичную точку отказа
Нужны физические USB-устройства или локальный оператор Рассмотреть собственное оборудование Удалённая аренда не заменяет физический доступ

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

08

Когда текущий узел лучше заменить

Не пересоздавайте Mac после первого offline-события. Сначала найдите доказательство. Но бесконечное добавление скриптов перезапуска тоже не является эксплуатационной стратегией.

Решение о замене оправдано, если:

  • причина отключений меняется от запуска к запуску;
  • Agent регулярно требует ручного входа;
  • рабочий каталог повреждается или самопроизвольно меняет владельца;
  • версии Java и Xcode расходятся с утверждённым образом;
  • состояние подписи пропадает после перезагрузки;
  • узел принимает shell-задачи, но нестабилен на сборке или тестах;
  • восстановление зависит от конкретного администратора.

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

Для нескольких независимых macOS CI-потоков полезно заранее определить, какие задания нельзя запускать параллельно. Один нестабильный узел не должен задерживать все релизы. Разделяйте сборку, тестирование и подпись, если их окружения или секреты требуют разной степени изоляции.

09

Итоговая рекомендация для remote Mac в Jenkins

Jenkins macOS Agent отключается не только из-за недоступного компьютера. SSH может продолжать работать, пока Jenkins-транспорт, Java-процесс, сессия macOS, рабочий каталог или Xcode-контекст уже неисправны. Для краткого сбоя выбирайте локальное восстановление. Для повторяющегося отключения и дрейфа среды — изоляцию, пересоздание или отдельный удалённый Mac.

Если текущий вариант — личный Mac разработчика, он имеет несколько реальных недостатков: зависит от рабочего места и ручного входа, конкурирует с интерактивной работой, может неожиданно перезагрузиться из-за обновлений и не даёт независимого резервного узла. Собственная покупка Mac решает часть этих проблем, но требует первоначальных затрат, обслуживания и постоянного контроля доступности. При необходимости временного или выделенного CI-окружения аренда Mac через VpsMesh позволяет рассматривать отдельный удалённый Mac с полными правами как независимый узел: сначала проверьте требования к перезапуску, SSH или VNC и Xcode, затем выберите подходящий вариант аренды Mac. Это разумнее, чем продолжать поддерживать нестабильный рабочий компьютер в роли единственного Jenkins Agent.