
У меня есть скрипт bash, который запускает скрипт python3 (назовем его startup.sh
), с ключевой строкой:
nohup python3 -u <script> &
Когда я ssh
напрямую захожу и вызываю этот скрипт, скрипт python продолжает работать в фоновом режиме после того, как я выхожу. Однако, когда я запускаю это:
ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
Процесс завершается сразу после ssh
завершения своего выполнения и закрытия сеанса.
Какая разница между двумя?
EDIT: Скрипт Python запускает веб-сервис через Bottle.
EDIT2: Я тоже попробовалсоздание сценария инициализациикоторый вызывает 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 выдает ошибку:
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)
решение1
Я бы отключил команду от ее стандартного ввода/вывода и потоков ошибок:
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 необходимо перенаправить stdin, чтобы предотвратить ожидание процесса локального ввода. https://stackoverflow.com/questions/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
решение2
Посмотри на 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 в screen, и он продолжит работу после завершения сеанса ssh.
ВидетьРуководство пользователя экрана
Хотя я думаю, что screen — ваш лучший вариант, если вам необходимо использовать nohup, рассмотрите возможность настройки shopt -s huponexit
на удаленном хосте перед запуском команды nohup. В качестве альтернативы вы можете использовать , disown -h [jobID]
чтобы пометить процесс, чтобы SIGHUP не отправлялся на него.1
Как продолжить выполнение задания после выхода из командной строки в фоновом режиме?
Сигнал SIGHUP (Hangup) используется вашей системой при управлении терминалом или завершении управляющего процесса. Вы можете использовать SIGHUP для перезагрузки файлов конфигурации и открытия/закрытия файлов журнала. Другими словами, если вы выйдете из терминала, все запущенные задания будут завершены. Чтобы избежать этого, вы можете передать параметр -h команде disown. Этот параметр помечает каждый jobID, чтобы SIGHUP не отправлялся заданию, если оболочка получает SIGHUP.
Также, посмотрите это резюме того, как huponexit
работает, когда оболочка выходит, уничтожается или удаляется. Я предполагаю, что ваша текущая проблема связана с тем, как завершается сеанс оболочки.2
Все дочерние процессы, запущенные в фоновом режиме или нет, оболочки, открытой через соединение SSH, завершаются с помощью SIGHUP при закрытии соединения SSH, только если установлена опция huponexit: запустите shopt huponexit, чтобы проверить, так ли это.
Если huponexit имеет значение true, то вы можете использовать nohup или disown для отсоединения процесса от оболочки, чтобы он не был завершен при выходе. Или запустите что-то с помощью screen.
Если huponexit имеет значение false, что является значением по умолчанию по крайней мере в некоторых современных Linux-системах, то фоновые задания не будут завершаться при обычном выходе из системы.
- Но даже если huponexit ложно, то если соединение ssh будет убито или сброшено (в отличие от обычного выхода), то фоновые процессы все равно будут убиты. Этого можно избежать с помощью disown или nohup, как в (2).
Наконец, вот несколько примеров того, как использовать shopt huponexit.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
решение3
Может стоит попробовать -n
опцию при запуске ssh
? Это предотвратит зависимость удаленного процесса от локального stdin
, который, конечно, закрывается сразу после ssh session
завершения. И это приведет к удаленному завершению цен всякий раз, когда он попытается получить доступ к своему stdin
.
решение4
Это больше похоже на проблему с тем, что делает python
скрипт или он сам. Все, что он делает на самом деле (за исключением упрощения перенаправлений), это просто устанавливает обработчик сигнала на (игнорировать) перед запуском программы. Нет ничего, что могло бы помешать программе вернуть его обратно или установить свой собственный обработчик после запуска.python
nohup
HUP
SIG_IGN
SIG_DFL
Одна из вещей, которую вы можете попробовать, — это заключить вашу команду в скобки, чтобы получить эффект двойного ветвления, и ваш python
скрипт больше не будет дочерним по отношению к процессу оболочки. Например:
( nohup python3 -u <script> & )
Еще одна вещь, которую также стоит попробовать (если вы используете bash
, а не другую оболочку), — это использовать disown
встроенную вместо nohup
. Если все работает так, как описано в документации, это не должно иметь никакого значения, но в интерактивной оболочке это остановит HUP
распространение сигнала в ваш python
скрипт. Вы можете добавить disown на следующей строке или на той же, что и ниже (обратите внимание, что добавление a ;
после a &
является ошибкой в bash
):
python3 -u <script> </dev/null &>/dev/null & disown
Если вышеперечисленное или какая-то их комбинация не работает, то, безусловно, единственное место, где можно решить проблему, — это python
сам скрипт.