Чому мій фоновий процес Python закінчується, коли сеанс SSH припиняється?


19

У мене є скрипт 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: Я також спробував створити сценарій init, який дзвонить 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 або trussякщо ви працюєте з Solaris, і подивіться, як / чому він припиняється. Як наприклад 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, ви повинні перенаправити stdin, щоб запобігти очікуванню процесу на локальному введенні. /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.
Graeme

Схоже, я працюю nohup (GNU coreutils) 8.21.
neverendingqs

@neverendingqs, чи nohupдрукує такі повідомлення, як nohup: ignoring input and appending output to ‘nohup.out’?
Graeme

Так - саме це повідомлення.
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.

Див. Посібник користувача екрана

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

Як продовжувати роботу після виходу з підказки для оболонки у фоновому режимі?

Сигнал SIGHUP (Hangup) використовується вашою системою для керування терміналом або загибелі процесу управління. Ви можете використовувати SIGHUP для завантаження файлів конфігурації та відкриття / закриття файлів журналу. Іншими словами, якщо ви вийдете з терміналу, усі запущені завдання будуть припинені. Щоб уникнути цього, ви можете передати команду -h для відключення команди. Цей параметр позначає кожен завдання ID, щоб SIGHUP не надсилався до завдання, якщо оболонка отримує SIGHUP.

Також дивіться цей підсумок про те, як huponexitпрацює, коли снаряд виходить, вбивається чи випадає. Я здогадуюсь, що ваша поточна проблема пов’язана з тим, як закінчується сеанс оболонки. 2

  1. Усі дочірні процеси, фонові чи не оболонки, відкриті через ssh-з'єднання, знищуються за допомогою SIGHUP, коли ssh-з'єднання закрито, лише якщо встановлено параметр huponexit: запустіть shopt huponexit, щоб побачити, чи це правда.

  2. Якщо huponexit вірно, то ви можете використовувати nohup або disown для відмежування процесу від оболонки, щоб він не загинув при виході. Або запускайте речі за допомогою екрана.

  3. Якщо huponexit невірно, що є типовим для принаймні деяких Linux на цих днях, тоді фонові завдання не будуть знищені при звичайному виході.

  4. Але навіть якщо huponexit неправдивий, тоді, якщо ssh-з'єднання буде вбито, або впаде (відрізняється від звичайного виходу), фонові процеси все одно загинуть. Цього можна уникнути відхиленням або нохупом, як у (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 на всі завдання, коли інтерактивна оболонка входу виходить."
Graeme

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/$!/commчи не nohupвикористовується більш портативно вивід ps -o comm= $!.
Graeme

Це повинно працювати для нормального виходу, але як бути, коли сеанс відмовлено або вбито? Чи все-таки вам не доведеться відмовлятися від роботи, щоб її цілком ігнорувало зітхання?
Ійрін

@RyanLoremIpsum: Сценарію запуску потрібно чекати досить довго, щоб дочірній процес повністю відключився. Після цього не має значення, що станеться з сеансом ssh. Якщо щось інше вбиває ваш ssh сеанс у короткому вікні, поки це станеться, ви не можете з цим зробити нічого.
mc0e

@Graeme так, я вважаю, що це дуже швидко, але я просто не знаю достатньо точно про те, що робить Nohup, щоб бути впевненим. Показник на авторитетне (або принаймні обізнане та детальне) джерело на це було б корисним.
mc0e

Як щодо цього - lingrok.org/xref/coreutils/src/nohup.c
Graeme

1

Це звучить скоріше як проблема з тим, що робить pythonсценарій або pythonсам. Все, що є nohupнасправді (смуга, що спрощує переадресацію) - це просто встановити обробник HUPсигналу SIG_IGN(ігнорувати) перед запуском програми. Ніщо не може зупинити програму повернення її назад SIG_DFLабо встановлення власного обробника, як тільки він почне працювати.

Одне, що ви можете спробувати, це вкласти свою команду в круглі дужки, щоб ви отримали подвійний ефект вилки, і ваш pythonсценарій вже не є дочірнім процесом оболонки. Наприклад:

( nohup python3 -u <script> & )

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

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

Якщо вищезазначене чи якесь поєднання не працює, то, безумовно, єдине місце для вирішення проблеми - саме pythonсценарій.


Чи вистачить ефекту подвійної вилки (на основі відповіді @ RyanLoremIpsum)?
neverendingqs

Обидва проблеми не вирішили = [. Якщо мова йде про проблему Python, чи маєте ви уявлення про те, з чого почати розслідування (не можна розмістити тут занадто багато сценарію Python)?
нескінченніq

@neverendingqs, якщо ви маєте на увазі huponexitматеріали, запущені в підпакеті повинні мати такий же ефект, disownяк і процес не буде доданий до списку завдань.
Graeme

@neverendingqs, оновив мою відповідь. Забули, що вам слід використовувати переспрямування disown. Не сподівайтесь, що це матиме велику різницю. Я думаю, що найкраще зробити ставку - це змінити pythonсценарій, щоб він розповів, чому він виходить.
Graeme

Перенаправлення результату працювало ( 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.