Почему мой фоновый процесс Python заканчивается, когда сеанс SSH завершается?


19

У меня есть скрипт bash, который запускает скрипт python3 (давайте назовем его startup.sh) с ключевой строкой:

nohup python3 -u <script> &

Когда я sshвызываю этот скрипт напрямую, скрипт python продолжает работать в фоновом режиме после выхода. Тем не менее, когда я запускаю это:

ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"

Процесс заканчивается, как только sshон завершится, и закрывает сеанс.

Какая разница между двумя?

РЕДАКТИРОВАТЬ: скрипт Python запускает веб-сервис через бутылку.

РЕДАКТИРОВАТЬ 2: Я также попытался создать сценарий инициализации, который вызывает startup.shи работает ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>", но получил то же самое поведение.

EDIT3: Может быть, это что-то еще в сценарии. Вот основная часть скрипта:

chmod 700 ${key_loc}

echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}

echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"

EDIT4: когда я запускаю последнюю строку со сном в конце:

ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"

echo "Finished"

Он никогда не достигает echo "Finished", и я вижу сообщение сервера Bottle, которого я никогда раньше не видел:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.

Я вижу «Закончено», если я вручную запускаю SSH и убиваю процесс сам.

EDIT5: Используя EDIT4, если я делаю запрос к любой конечной точке, я получаю страницу назад, но бутылка выдает ошибку:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.


----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)

Есть ли способ получить больше описания того, что делает скрипт Python? Вы, вероятно, все еще будете получать догадки без полного исходного кода, но зная больше о том, что делает скрипт python, может помочь нам сделать более образованные догадки.
Братчли

Да - добавил к вопросу.
neverendingqs

Сценарий может делать что-то на ранней стадии, что каким-то образом зависит от подключенного терминала или чего-то в этом роде, и это может быть проблемой синхронизации: если сеанс длится дольше первых нескольких секунд, он работает, в противном случае это не так. Лучшим вариантом может быть его запуск, straceесли вы используете Linux или trussSolaris и посмотрите, как и почему он завершается. Как например ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> strace -fo /tmp/debug ./startup.sh.
Селада

Вы пытались использовать &в конце сценария запуска? Добавление &оператора удаляет зависимость вашего ssh-сеанса от родительского идентификатора (когда родительские идентификаторы умирают, это делают и их дети). Также я думаю, что это дублирующий вопрос, основанный на этом предыдущем посте. Пост, который я представил вам в предыдущем предложении, является дубликатом этого поста, который может дать более подробные сведения.
Джейкоб Брайан

Я пытался nohup ./startup.sh &раньше, но у него было такое же поведение. startup.shуже содержит форк ( nohup python3 -u <script> &), так что я уверен, что мне не нужно снова форкать.
neverendingqs

Ответы:


11

Я бы отключил команду от стандартного потока ввода / вывода и ошибок:

nohup python3 -u <script> </dev/null >/dev/null 2>&1 &  

sshнужен индикатор, который больше не выводит и не требует ввода. Имея что-то еще на входе и перенаправляя средства вывода, sshможно безопасно выйти, так как ввод / вывод не поступает или не идет к терминалу. Это означает, что ввод должен происходить откуда-то еще, а вывод (как STDOUT, так и STDERR) должен идти куда-то еще.

</dev/nullЧасть определяет в /dev/nullкачестве входных данных для <script>. Почему это полезно здесь:

Перенаправление / dev / null в stdin даст немедленный EOF любому вызову чтения из этого процесса. Обычно это полезно для отсоединения процесса от tty (такой процесс называется демоном). Например, при удаленном запуске фонового процесса через ssh вы должны перенаправить стандартный ввод, чтобы процесс не ожидал локального ввода. /programming/19955260/what-is-dev-null-in-bash/19955475#19955475

В качестве альтернативы, перенаправление из другого входного источника должно быть относительно безопасным, если текущий sshсеанс не нужно держать открытым.

С этой >/dev/nullчастью оболочка перенаправляет стандартный вывод в / dev / null, по существу отбрасывая его. >/path/to/fileтоже будет работать.

Последняя часть 2>&1перенаправляет STDERR в STDOUT.

Есть три стандартных источника ввода и вывода для программы. Стандартный ввод обычно осуществляется с клавиатуры, если это интерактивная программа, или из другой программы, если она обрабатывает вывод другой программы. Программа обычно печатает со стандартным выводом, а иногда печатает со стандартной ошибкой. Эти три файловых дескриптора (вы можете думать о них как о «каналах данных») часто называются STDIN, STDOUT и STDERR.

Иногда они не названы, они пронумерованы! Для них встроены нумерации 0, 1 и 2 в указанном порядке. По умолчанию, если вы явно не называете номер один или номер один, вы говорите о STDOUT.

Принимая во внимание этот контекст, вы можете видеть, что приведенная выше команда перенаправляет стандартный вывод в / dev / null, куда вы можете записать все, что вам не нужно (часто это называется бит-сегмент), а затем перенаправляет стандартную ошибку в стандартный вывод ( Вы должны поставить & перед пунктом назначения, когда вы делаете это).

Поэтому короткое объяснение таково: «Все выходные данные этой команды должны быть заткнуты в черную дыру». Это один хороший способ сделать программу действительно тихой!
Что означает> / dev / null 2> & 1? | Xaprb


nohup python3 -u <script> >/dev/null 2>&1 &и nohup python3 -u <script> > nohup.out 2>&1 &работал. Я думал, что nohup автоматически перенаправляет весь вывод - какая разница?
neverendingqs

@neverendingqs, какая версия nohupу вас на удаленном хосте? POSIX nohupне требуется для перенаправления stdin, что я пропустил, но он все равно должен перенаправить stdoutи stderr.
Грэм

Похоже, я работаю с nohup (GNU coreutils) 8.21.
neverendingqs

@neverendingqs, nohupпечатает ли какие-нибудь сообщения, как nohup: ignoring input and appending output to ‘nohup.out’?
Грэм

Да, это точное сообщение.
neverendingqs

3

Посмотрите на man ssh:

 ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
     [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport]
     [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
     [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
     [user@]hostname [command]

При запуске ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"вы запускаете сценарий оболочки startup.sh как команда ssh.

Из описания:

Если указана команда, она выполняется на удаленном хосте вместо оболочки входа в систему.

Исходя из этого, он должен запускать скрипт удаленно.

Разница между этим и выполнением nohup python3 -u <script> &в локальном терминале заключается в том, что он запускается как локальный фоновый процесс, в то время как команда ssh пытается запустить его как удаленный фоновый процесс.

Если вы собираетесь запустить скрипт локально, не запускайте startup.sh как часть команды ssh. Вы можете попробовать что-то вродеssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

Если вы намерены запустить скрипт удаленно и хотите, чтобы этот процесс продолжался после завершения сеанса ssh, вам сначала нужно будет запустить screenсеанс на удаленном хосте. Затем вы должны запустить скрипт python на экране, и он продолжит работать после того, как вы завершите сеанс ssh.

Смотрите Руководство пользователя экрана

Хотя я думаю, что screen - ваш лучший вариант, если вы должны использовать nohup, shopt -s huponexitперед запуском команды nohup рассмотрите возможность установки на удаленном хосте. Кроме того, вы можете использовать, disown -h [jobID]чтобы отметить процесс, чтобы SIGHUP не отправлялся на него. 1

Как мне продолжить работу после выхода из командной строки в фоновом режиме?

Сигнал SIGHUP (зависание) используется вашей системой при управлении терминалом или прекращении процесса управления. Вы можете использовать SIGHUP для перезагрузки файлов конфигурации и открытия / закрытия файлов журналов. Другими словами, если вы выйдете из своего терминала, все выполняющиеся задания будут прерваны. Чтобы избежать этого, вы можете передать опцию -h команде disown. Эта опция помечает каждый идентификатор задания так, чтобы SIGHUP не отправлялся заданию, если оболочка получает SIGHUP.

Кроме того, ознакомьтесь с кратким описанием того, как huponexitработает, когда оболочка закрывается, удаляется или удаляется. Я предполагаю, что ваша текущая проблема связана с тем, как завершается сеанс оболочки. 2

  1. Все дочерние процессы, фоновые или нет оболочки, открытой через ssh-соединение, уничтожаются с помощью SIGHUP, когда ssh-соединение закрывается, только если установлена ​​опция huponexit: запустите shopt huponexit, чтобы проверить, верно ли это.

  2. Если huponexit имеет значение true, то вы можете использовать nohup или disown, чтобы отделить процесс от оболочки, чтобы он не был убит при выходе. Или запустить вещи с экрана.

  3. Если huponexit имеет значение false, что является значением по умолчанию, по крайней мере, в некоторых linux в наши дни, то фоновые задания не будут уничтожены при обычном выходе из системы.

  4. Но даже если huponexit имеет значение false, то, если соединение ssh будет прервано или прервано (отличается от обычного выхода из системы), фоновые процессы все равно будут убиты. Этого можно избежать с помощью disown или nohup, как в (2).

Наконец, вот несколько примеров того, как использовать купированный гупонексит. 3

$ shopt -s huponexit; shopt | grep huponexit
huponexit       on
# Background jobs will be terminated with SIGHUP when shell exits

$ shopt -u huponexit; shopt | grep huponexit
huponexit       off
# Background jobs will NOT be terminated with SIGHUP when shell exits

Согласно справочной bashстранице, это huponexitдолжно влиять только на интерактивные оболочки, а не на скрипты - «Если опция оболочки huponexit была установлена ​​с помощью shopt, bash отправляет SIGHUP всем работам при выходе из интерактивной оболочки входа».
Грэм

2

Может стоит попробовать -nвариант при запуске ssh? Это предотвратит зависимость удаленного процесса от локального stdin, который, конечно же, закроется, как только ssh sessionзакончится. И это приведет к удаленному прекращению цен, когда он попытается получить к нему доступ stdin.


Пробовал безуспешно = [.
neverendingqs

2

Я подозреваю, что у вас есть состояние гонки. Было бы что-то вроде этого:

  • SSH соединение начинается
  • SSH запускает startup.sh
  • startup.sh запускает фоновый процесс (nohup)
  • startup.sh заканчивается
  • ssh завершает работу, и это убивает дочерние процессы (т.е. nohup)

Если бы ssh не сократил ситуацию, произошло бы следующее (не уверен насчет порядка этих двух):

  • nohup запускает ваш скрипт на python
  • nohup отключается от родительского процесса и терминала.

Таким образом, последние два критических шага не выполняются, потому что startup.sh и ssh заканчивают работу до того, как nohup успевает сделать свое дело.

Я ожидаю, что ваша проблема исчезнет, ​​если вы поместите несколько секунд сна в конец файла startup.sh. Я не уверен, сколько именно вам нужно времени. Если важно свести это к минимуму, то, возможно, вы можете посмотреть что-то в proc, чтобы увидеть, когда это безопасно.


Хороший вопрос, не думайте, что окно для этого будет очень длинным, хотя - вероятно, всего несколько миллисекунд. Вы можете проверить /proc/$!/comm, нет ли nohupили более переносимо использовать вывод ps -o comm= $!.
Грэм

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

@RyanLoremIpsum: сценарий запуска должен ждать достаточно долго, чтобы дочерний процесс был полностью отключен. После этого не имеет значения, что происходит с сессией ssh. Если что-то еще убивает ваш ssh-сеанс в коротком окне, пока это происходит, вы ничего не можете с этим поделать.
mc0e 30.12.14

@ Грэм, да, я предполагаю, что это очень быстро, но я просто не знаю достаточно точно, что именно делает nohup, чтобы быть уверенным. Указатель на авторитетный (или, по крайней мере, знающий и подробный) источник по этому вопросу был бы полезен.
mc0e 30.12.14

Как насчет этого - lingrok.org/xref/coreutils/src/nohup.c
Грэм

1

Это больше похоже на проблему с тем, что делает pythonскрипт или pythonсам по себе. Все, что на nohupсамом деле делает (исключая перенаправления), это просто устанавливает обработчик для HUPсигнала SIG_IGN(игнорировать) перед запуском программы. Ничто не мешает программе установить ее обратно SIG_DFLили установить собственный обработчик после запуска.

Одна вещь, которую вы, возможно, захотите попробовать, - заключить вашу команду в круглые скобки, чтобы получить эффект двойной вилки, и ваш pythonсценарий больше не является дочерним процессом процесса оболочки. Например:

( nohup python3 -u <script> & )

Еще одна вещь, которую также стоит попробовать (если вы используете, bashа не другую оболочку), это использовать disownвместо встроенной nohup. Если все работает так, как задокументировано, это на самом деле не должно иметь никакого значения, но в интерактивной оболочке это остановит распространение HUPсигнала в ваш pythonсценарий. Вы можете добавить disown на следующей строке или на той же, что и ниже (обратите внимание, что добавление ;после a &означает ошибку в bash):

python3 -u <script> </dev/null &>/dev/null & disown

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


Этого эффекта двойной вилки будет достаточно (основываясь на ответе @ RyanLoremIpsum)?
neverendingqs

Оба не решили проблему = [. Если это проблема Python, у вас есть идея, с чего начать расследование (не можете опубликовать слишком много скриптов Python здесь)?
neverendingqs

@neverendingqs, если вы имеете в виду huponexitматериал, выполнение в подоболочке должно иметь тот же эффект, disownчто и процесс не будет добавлен в список заданий.
Грэм

@neverendingqs, обновил мой ответ. Забыл, что вы должны использовать перенаправления с disown. Не ожидайте, что это будет иметь большое значение все же. Я думаю, что вам лучше всего изменить pythonсценарий, чтобы он объяснил вам, почему он выходит.
Грэм

Перенаправление вывода работало ( unix.stackexchange.com/a/176610/52894 ), но я не уверен, в чем разница между явным выполнением и получением nohup.
neverendingqs

0

Я думаю, что это потому, что работа связана с сессией. Как только это заканчивается, любые пользовательские задания также заканчиваются.


2
Но почему это отличается от получения терминала, ввода и запуска команды и выхода? Обе сессии закрываются, как только я его закрываю.
neverendingqs

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

0

Если вы nohupможете открыть его выходной файл, вы можете иметь подсказку nohup.out. Это возможно pythonне по пути, когда вы запускаете скрипт через ssh.

Я хотел бы попытаться создать файл журнала для команды. Попробуйте использовать:

nohup /usr/bin/python3 -u <script> &>logfile &

Я использую sshдля запуска сценария вручную, поэтому я предполагаю, что Python3 находится в пути.
neverendingqs

@neverendingqs Содержит ли файл журнала что-нибудь?
BillThor

Ничего необычного - запуск выглядит нормально.
neverendingqs
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.