Вы запускаете по SSH длительную сборку на облачном Mac, закрываете ноутбук, а после повторного подключения обнаруживаете, что терминал исчез. Главная проблема здесь не в необходимости перезапуска, а в том, что невозможно понять, завершилась ли задача с ошибкой, успешно отработала или всё ещё выполняется в фоне. Надёжное решение — не увеличивать тайм-аут соединения вслепую, а разделить систему на три уровня: SSH отвечает за вход на узел, tmux сохраняет контекст терминала, а журналы и код завершения подтверждают результат.
Сначала определите, какой уровень затрагивает разрыв соединения
Отключение удалённого рабочего стола или SSH означает лишь разрыв интерактивного канала между клиентом и облачным Mac, но не остановку физического узла. Однако процесс переднего плана, напрямую привязанный к псевдотерминалу SSH, при закрытии сеанса может получить сигнал разрыва соединения. Терминал, открытый через графический интерфейс, также не подходит для запуска задач без присмотра: закрытие окна или выход из рабочего стола меняют его жизненный цикл.
Прежде чем экспериментировать с рабочей сборкой, проверьте среду на небольшой десятиминутной задаче:
mkdir -p "$HOME/jobs/session-check"
cd "$HOME/jobs/session-check"
date -u +"start=%Y-%m-%dT%H:%M:%SZ" > run.log
sleep 600
date -u +"finish=%Y-%m-%dT%H:%M:%SZ" >> run.log
После запуска намеренно отключитесь, затем подключитесь снова и проверьте run.log. Такой базовый тест позволяет убедиться, что сам узел продолжает работать, но не заменяет управление сеансом.
Механизм SSH keepalive решает вопрос «как быстро обнаружить потерю соединения», а tmux — «останется ли после разрыва восстанавливаемый терминал с выполняемой задачей». Эти механизмы не заменяют друг друга.
Настройте SSH keepalive и явные тайм-ауты
Добавьте в ~/.ssh/config на локальном Mac отдельную конфигурацию для нужного узла. Адрес и имя пользователя замените фактическими значениями из панели управления:
Host minid-node
HostName <node-address>
User <system-user>
ServerAliveInterval 30
ServerAliveCountMax 3
TCPKeepAlive yes
ConnectTimeout 10
Параметр ServerAliveInterval 30 означает, что клиент каждые 30 секунд отправляет проверочный запрос на прикладном уровне. Если ответа не будет три раза подряд, SSH завершит неработающее соединение, вместо того чтобы оставлять терминал зависшим на неопределённое время. Эта настройка не выполняет автоматическое переподключение и не гарантирует сохранение процесса переднего плана.
Перед подключением можно проверить итоговую конфигурацию:
ssh -G minid-node | grep -E 'serveralive|tcpkeepalive|connecttimeout'
ssh minid-node
Если сеть часто переключается, не стоит увеличивать тайм-аут до десятков минут. Чем быстрее будет подтверждена потеря соединения, тем раньше можно повторно войти в tmux и проверить фактический ход задачи.
Запускайте длительные задачи под управлением tmux
Сначала проверьте наличие tmux. Если утилита отсутствует, установите её через принятый в текущей среде менеджер пакетов:
command -v tmux || brew install tmux
tmux new-session -s ios-build
Имя сеанса должно отражать назначение задачи: например, ios-build, integration-test или asset-export. Не следует постоянно использовать неоднозначное имя work. Войдя в сеанс, создайте отдельный каталог и направьте вывод одновременно в терминал и журнал:
job_dir="$HOME/jobs/ios-build-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$job_dir"
cd "$job_dir"
set -o pipefail
caffeinate -i /path/to/run-build.sh 2>&1 | tee build.log
status=${PIPESTATUS[0]}
printf '%s
' "$status" > exit-code.txt
date -u +"%Y-%m-%dT%H:%M:%SZ" > finished-at.txt
Нажмите Control-b, а затем d, чтобы отсоединиться от сеанса, не останавливая задачу. После повторного подключения к узлу выполните:
tmux list-sessions
tmux attach-session -t ios-build
Команда caffeinate -i сообщает системе, что бездействующий режим сна следует предотвращать только на время выполнения команды. Привязывайте её к конкретной задаче и не оставляйте работать постоянно ради удобства. Для сборки, полностью выполняемой из командной строки, принудительно поддерживать дисплей включённым не требуется.
Не допускайте повторного запуска
После обрыва сети сначала выполните tmux list-sessions и pgrep -fl run-build. Перезапускайте задачу только после подтверждения, что прежний экземпляр больше не существует. В противном случае две сборки могут одновременно записывать данные в один и тот же каталог производных данных, кэш или каталог артефактов, оставляя смешанный результат, который трудно воспроизвести.
Используйте журналы и код завершения как критерии приёмки
То, что после повторного подключения в терминале продолжает появляться вывод, ещё не подтверждает успешный результат. Каждая длительная задача должна как минимум сохранять время начала, время окончания, полный журнал и код завершения. Только код 0 означает, что скрипт успешно завершился в соответствии с принятым соглашением. Наличие определённого слова в конце журнала не заменяет проверку кода завершения.
Быстро определить состояние можно в следующем порядке:
| Что проверить | Команда | Вывод |
|---|---|---|
| Существует ли сеанс | tmux list-sessions |
Наличие сеанса не означает, что задача всё ещё выполняется |
| Существует ли процесс | pgrep -fl run-build |
Позволяет подтвердить текущий экземпляр выполнения |
| Пополняется ли журнал | tail -n 30 build.log |
Показывает ход выполнения или место зависания |
| Штатно ли завершилась задача | cat exit-code.txt |
0 означает успешное завершение скрипта |
| Время завершения | cat finished-at.txt |
Позволяет проверить, относится ли результат к текущему запуску |
Скрипт также должен создавать новый каталог для каждого запуска, не перезаписывая журнал предыдущего. При очистке удаляйте по датам только те каталоги, архивация которых уже подтверждена. Не запускайте рекурсивное удаление для всего ~/jobs.
Проведите контрольный тест с намеренным отключением
Перед рабочим использованием запустите безопасную повторяемую тестовую задачу, а затем по очереди проверьте отключение SSH, закрытие удалённого рабочего стола и переключение локальной сети. После каждого повторного подключения проверяйте сеанс tmux, целевой процесс, пополнение журнала и файл с кодом завершения.
Если задача зависит от графического интерфейса, отдельно проверьте, продолжает ли она выполняться после отключения удалённого рабочего стола. Жизненный цикл утилит командной строки отличается от жизненного цикла графических приложений. Если задача завершилась раньше времени, сначала проверьте код завершения скрипта и журнал, затем выясните, не был ли закрыт терминал, и только после этого проверьте свободное место в хранилище и ресурсы процесса. Не следует объяснять все сбои проблемами сети.
Готовый к применению контрольный список должен включать следующие пункты:
- конфигурация SSH проверяется с помощью
ssh -G; - для каждого типа длительных задач используется отдельный сеанс tmux;
- каталоги задач создаются с привязкой ко времени и не перезаписывают прежние результаты;
- стандартный вывод и поток ошибок сохраняются в одном журнале с возможностью поиска;
- код завершения и время окончания в UTC сохраняются отдельно;
- после разрыва сначала восстанавливается текущий контекст, без повторного запуска задачи;
- после завершения ненужные сеансы закрываются, а артефакты архивируются.
Часто задаваемые вопросы
Всегда ли задача завершается после обрыва SSH-соединения?
Нет. Процесс, напрямую связанный с SSH-терминалом, может получить сигнал разрыва, но задача внутри tmux обычно продолжит работу. После подключения нужно проверить сеанс, процесс и журнал.
Достаточно ли параметра ServerAliveInterval для защиты длительной сборки?
Нет. Он лишь помогает обнаружить неработающее соединение и не сохраняет процесс. Запускайте задачу в tmux и записывайте стандартный вывод, ошибки и код завершения в файлы.
Облачный Mac MiniD
Аренда выделенного физического Mac mini на день, неделю или месяц
Каждый тариф работает на выделенном физическом Mac mini с доступом по удалённому рабочему столу и SSH; актуальные модели, регионы и сроки указаны на странице заказа.