Mengapa proses latar belakang Python saya berakhir saat sesi SSH dihentikan?


19

Saya memiliki skrip bash yang memulai skrip python3 (sebut saja startup.sh), dengan baris kunci:

nohup python3 -u <script> &

Ketika saya sshmasuk dan langsung memanggil skrip ini, skrip python terus berjalan di latar belakang setelah saya keluar. Namun, ketika saya menjalankan ini:

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

Proses berakhir segera setelah sshselesai menjalankannya dan menutup sesi.

Apa perbedaan keduanya?

EDIT: Skrip python menjalankan layanan web melalui Bottle.

EDIT2: Saya juga mencoba membuat skrip init yang memanggil startup.shdan menjalankan ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>", tetapi mendapatkan perilaku yang sama.

EDIT3: Mungkin ada sesuatu yang lain dalam naskah. Inilah sebagian besar skrip:

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: Ketika saya menjalankan baris terakhir dengan tidur di akhir:

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"

Tidak pernah sampai echo "Finished", dan saya melihat pesan Server botol, yang belum pernah saya lihat sebelumnya:

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

Saya melihat "Selesai" jika saya secara manual SSH masuk dan mematikan proses sendiri.

EDIT5: Menggunakan EDIT4, jika saya membuat permintaan ke titik akhir, saya mendapatkan halaman kembali, tetapi Botol keluar:

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)

Apakah ada cara kita bisa mendapatkan lebih banyak deskripsi dari apa yang dilakukan skrip python? Anda mungkin masih mendapatkan tebakan tanpa kode sumber lengkap, tetapi mengetahui lebih banyak tentang apa yang dilakukan skrip python dapat membantu kami membuat tebakan yang lebih terdidik.
Bratchley

Yap - ditambahkan ke pertanyaan.
neverendingqs

Script mungkin melakukan sesuatu sejak awal yang entah bagaimana tergantung pada terminal yang terpasang atau sesuatu seperti itu dan itu bisa menjadi masalah waktu: jika sesi berlangsung melewati beberapa detik pertama itu berfungsi, jika tidak maka tidak akan. Pilihan terbaik Anda mungkin menjalankannya di bawah stracejika Anda menggunakan Linux atau trussjika Anda menjalankan Solaris dan melihat bagaimana / mengapa itu berakhir. Seperti misalnya ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> strace -fo /tmp/debug ./startup.sh.
Celada

Apakah Anda mencoba menggunakan &di akhir skrip start up? Menambahkan& menghilangkan ketergantungan sesi ssh Anda dari menjadi id induk (ketika id orangtua mati begitu juga anak-anak mereka). Juga saya pikir ini adalah pertanyaan rangkap berdasarkan posting sebelumnya ini . Posting yang saya kirimkan kepada Anda dalam kalimat sebelumnya adalah duplikat dari posting ini yang mungkin memberikan detail yang lebih baik.
Jacob Bryan

Saya sudah mencoba nohup ./startup.sh &sebelumnya, tetapi memiliki perilaku yang sama. startup.shsudah mengandung fork ( nohup python3 -u <script> &), jadi saya cukup yakin saya tidak perlu fork lagi.
neverendingqs

Jawaban:


11

Saya akan memutuskan perintah dari input standar / output dan aliran kesalahan:

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

sshmembutuhkan indikator yang tidak memiliki output lagi dan tidak memerlukan input lagi. Memiliki sesuatu yang lain menjadi input dan mengarahkan output berarti sshdapat keluar dengan aman, karena input / output tidak datang atau pergi ke terminal. Ini berarti input harus berasal dari tempat lain, dan output (baik STDOUT dan STDERR) harus pergi ke tempat lain.

Bagian ini </dev/nullmenentukan /dev/nullsebagai input untuk <script>. Mengapa itu berguna di sini:

Mengarahkan / dev / null ke stdin akan memberikan EOF langsung ke panggilan baca apa pun dari proses itu. Ini biasanya berguna untuk melepaskan proses dari tty (proses semacam itu disebut daemon). Misalnya, ketika memulai proses latar belakang dari jarak jauh ssh, Anda harus mengarahkan ulang stdin untuk mencegah proses menunggu input lokal. /programming/19955260/what-is-dev-null-in-bash/19955475#19955475

Sebagai alternatif, pengalihan dari sumber input lain harus relatif aman selama sshsesi saat ini tidak perlu tetap terbuka.

Dengan >/dev/nullbagian shell mengarahkan output standar ke / dev / null pada dasarnya membuangnya. >/path/to/filejuga akan bekerja.

Bagian terakhir 2>&1adalah mengarahkan kembali STDERR ke STDOUT.

Ada tiga sumber input dan output standar untuk suatu program. Input standar biasanya berasal dari keyboard jika itu adalah program interaktif, atau dari program lain jika sedang memproses output program lain. Program biasanya mencetak ke output standar, dan kadang-kadang mencetak ke kesalahan standar. Ketiga deskriptor file ini (Anda dapat menganggapnya sebagai "pipa data") sering disebut STDIN, STDOUT, dan STDERR.

Terkadang mereka tidak disebutkan, mereka diberi nomor! Penomoran bawaan untuk mereka adalah 0, 1, dan 2, dalam urutan itu. Secara default, jika Anda tidak menyebutkan nama atau nomor satu secara eksplisit, Anda sedang membicarakan STDOUT.

Mengingat konteks itu, Anda dapat melihat perintah di atas mengarahkan ulang output standar ke / dev / null, yang merupakan tempat Anda dapat membuang apa pun yang tidak Anda inginkan (sering disebut bit-bucket), kemudian mengarahkan kesalahan standar ke output standar ( Anda harus meletakkan & di depan tujuan ketika Anda melakukan ini).

Penjelasan singkatnya, oleh karena itu, adalah "semua keluaran dari perintah ini harus dimasukkan ke dalam lubang hitam."
Apa arti> / dev / null 2> & 1? | Xaprb


nohup python3 -u <script> >/dev/null 2>&1 &dan nohup python3 -u <script> > nohup.out 2>&1 &bekerja. Saya pikir nohup secara otomatis mengalihkan semua output - apa bedanya?
neverendingqs

@neverendingqs, versi apa nohupyang Anda miliki di host jarak jauh? POSIX nohuptidak diperlukan untuk mengalihkan stdin, yang saya lewatkan, tetapi harus tetap mengarahkan stdoutdan stderr.
Graeme

Sepertinya saya bekerja dengan nohup (GNU coreutils) 8.21.
neverendingqs

@neverendingqs, apakah nohupmencetak pesan, suka nohup: ignoring input and appending output to ‘nohup.out’?
Graeme

Ya - itu adalah pesan yang tepat.
neverendingqs

3

Lihatlah 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]

Saat Anda menjalankan, ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"Anda menjalankan skrip shell startup.sh sebagai perintah ssh.

Dari uraian:

Jika perintah ditentukan, itu dijalankan pada host jarak jauh alih-alih shell login.

Berdasarkan ini, itu harus menjalankan skrip dari jarak jauh.

Perbedaan antara itu dan berlari nohup python3 -u <script> & di terminal lokal Anda adalah bahwa ini berjalan sebagai proses latar belakang lokal sementara perintah ssh mencoba untuk menjalankannya sebagai proses latar belakang jarak jauh.

Jika Anda bermaksud menjalankan skrip secara lokal maka jangan jalankan startup.sh sebagai bagian dari perintah ssh. Anda mungkin mencoba sesuatu sepertissh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

Jika niat Anda adalah menjalankan skrip dari jarak jauh dan Anda ingin proses ini berlanjut setelah sesi ssh Anda dihentikan, Anda harus terlebih dahulu memulai screensesi pada host jarak jauh. Maka Anda harus menjalankan skrip python di dalam layar dan itu akan terus berjalan setelah Anda mengakhiri sesi ssh Anda.

Lihat Panduan Pengguna Layar

Meskipun saya pikir layar adalah pilihan terbaik Anda, jika Anda harus menggunakan nohup, pertimbangkan untuk mengatur shopt -s huponexitpada host jarak jauh sebelum menjalankan perintah nohup. Atau, Anda dapat menggunakan disown -h [jobID]untuk menandai proses sehingga SIGHUP tidak akan dikirim ke sana. 1

Bagaimana saya tetap menjalankan pekerjaan setelah saya keluar dari shell prompt di latar belakang?

Sinyal SIGHUP (Hangup) digunakan oleh sistem Anda untuk mengontrol terminal atau matinya proses kontrol. Anda dapat menggunakan SIGHUP untuk memuat ulang file konfigurasi dan membuka / menutup file log juga. Dengan kata lain, jika Anda keluar dari terminal, semua pekerjaan yang berjalan akan dihentikan. Untuk menghindarinya, Anda dapat melewatkan opsi -h untuk menolak perintah. Opsi ini menandai setiap jobID sehingga SIGHUP tidak dikirim ke pekerjaan jika shell menerima SIGHUP.

Juga, lihat ringkasan ini tentang cara huponexitkerjanya ketika shell keluar, terbunuh atau jatuh Saya menduga masalah Anda saat ini terkait dengan bagaimana sesi shell berakhir. 2

  1. Semua proses anak, latar belakang atau bukan dari shell dibuka melalui koneksi ssh terbunuh dengan SIGHUP ketika koneksi ssh ditutup hanya jika opsi huponexit diatur: jalankan shopt huponexit untuk melihat apakah ini benar.

  2. Jika huponexit benar, maka Anda dapat menggunakan nohup atau menolak untuk memisahkan proses dari shell sehingga tidak terbunuh saat Anda keluar. Atau, jalankan sesuatu dengan layar.

  3. Jika huponexit salah, yang merupakan default pada setidaknya beberapa linux hari ini, maka pekerjaan latar belakang tidak akan dibunuh pada logout normal.

  4. Tetapi bahkan jika huponexit salah, maka jika koneksi ssh terbunuh, atau jatuh (berbeda dari logout normal), maka proses latar belakang akan tetap terbunuh. Ini dapat dihindari dengan disown atau nohup seperti pada (2).

Akhirnya, berikut adalah beberapa contoh cara menggunakan 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

Menurut bashhalaman manual, huponexitseharusnya hanya memengaruhi shell interaktif dan bukan skrip - 'Jika opsi shell huponexit telah disetel dengan shopt, bash mengirimkan SIGHUP ke semua pekerjaan ketika shell login interaktif keluar.'
Graeme

2

Mungkin patut dicoba -nsaat memulai ssh? Ini akan mencegah ketergantungan proses jarak jauh pada lokal stdin, yang tentu saja menutup segera setelah ssh sessionberakhir. Dan ini akan menyebabkan penghentian harga jarak jauh setiap kali ia mencoba mengaksesnya stdin.


Mencobanya tanpa keberhasilan = [.
neverendingqs

2

Saya menduga Anda memiliki kondisi balapan. Ini akan seperti ini:

  • Koneksi SSH dimulai
  • SSH memulai startup.sh
  • startup.sh memulai proses latar belakang (nohup)
  • startup.sh selesai
  • ssh selesai, dan ini membunuh proses anak (yaitu nohup)

Jika ssh tidak memotong masalah, berikut ini akan terjadi (tidak yakin tentang urutan keduanya):

  • nohup memulai skrip python Anda
  • nohup terputus dari proses induk dan terminal.

Jadi dua langkah kritis terakhir tidak terjadi, karena startup.sh dan ssh selesai sebelum nohup punya waktu untuk melakukan hal itu.

Saya berharap masalah Anda akan hilang jika Anda meletakkan beberapa detik tidur di akhir startup.sh. Saya tidak yakin persis berapa banyak waktu yang Anda butuhkan. Jika penting untuk mempertahankannya seminimal mungkin, maka mungkin Anda dapat melihat sesuatu di proc untuk melihat kapan itu aman.


Poin baiknya, jangan berpikir jendela untuk ini akan sangat lama - mungkin hanya beberapa milidetik. Anda bisa mengecek /proc/$!/commapakah nohupportable atau tidak menggunakan output ps -o comm= $!.
Graeme

Itu harus bekerja untuk logout normal, tetapi bagaimana dengan ketika sesi dijatuhkan atau dibunuh? Tidakkah Anda masih harus mengingkari pekerjaan sehingga sepenuhnya diabaikan oleh desahan?
iyrin

@RyanLoremIpsum: Skrip startup hanya perlu menunggu cukup lama sehingga proses anak terpisah sepenuhnya. Setelah itu, tidak masalah apa yang terjadi pada sesi ssh. Jika sesuatu yang lain membunuh sesi ssh Anda di jendela singkat sementara itu terjadi, tidak banyak yang dapat Anda lakukan tentang hal itu.
mc0e

@ Greme ya, saya kira itu sangat cepat, tapi saya hanya tidak cukup tahu persis apa yang nohup lakukan untuk memastikan. Penunjuk ke sumber yang otoritatif (atau setidaknya berpengetahuan dan terperinci) tentang hal ini akan bermanfaat.
mc0e

Bagaimana dengan yang ini - lingrok.org/xref/coreutils/src/nohup.c
Graeme

1

Ini terdengar lebih seperti masalah dengan apa yang dilakukan pythonskrip pythonitu sendiri. Semua yang nohupbenar - benar dilakukan (bilah penyederhanaan pengalihan) hanya mengatur handler untuk HUPsinyal SIG_IGN(abaikan) sebelum menjalankan program. Tidak ada yang menghentikan program pengaturannya kembali SIG_DFLatau menginstal handler sendiri setelah mulai berjalan.

Satu hal yang mungkin ingin Anda coba adalah melampirkan perintah Anda dalam tanda kurung sehingga Anda mendapatkan efek garpu ganda dan pythonskrip Anda bukan lagi anak dari proses shell. Misalnya:

( nohup python3 -u <script> & )

Hal lain yang mungkin juga patut dicoba (jika Anda menggunakan bashdan bukan shell lain) adalah menggunakan disownbuiltin bukan nohup. Jika semuanya berfungsi seperti yang didokumentasikan ini seharusnya tidak benar-benar membuat perbedaan, tetapi dalam shell interaktif ini akan menghentikan HUPsinyal menyebar ke pythonskrip Anda . Anda dapat menambahkan penolakan pada baris berikutnya atau yang sama seperti di bawah ini (perhatikan menambahkan ;setelah a &adalah kesalahan dalam bash):

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

Jika kombinasi di atas atau sebagian tidak berfungsi maka pasti satu-satunya tempat untuk mengatasi masalah ini adalah dalam pythonskrip itu sendiri.


Apakah efek garpu ganda sudah cukup (berdasarkan jawaban @ RyanLoremIpsum)?
neverendingqs

Keduanya tidak menyelesaikan masalah = [. Jika ini masalah Python, apakah Anda memiliki gagasan tentang di mana harus mulai menyelidiki (tidak dapat memposting terlalu banyak skrip Python di sini)?
neverendingqs

@neverendingqs, jika Anda maksudkan huponexithal - hal tersebut, menjalankan subshell harus memiliki efek yang sama disownseperti proses tidak akan ditambahkan ke daftar pekerjaan.
Graeme

@neverendingqs, perbarui jawaban saya. Lupa bahwa Anda harus menggunakan arahan ulang disown. Tapi jangan berharap itu akan membuat banyak perbedaan. Saya pikir Anda bertaruh terbaik adalah dengan mengubah pythonskrip sehingga memberitahu Anda mengapa itu keluar.
Graeme

Mengarahkan output berfungsi ( unix.stackexchange.com/a/176610/52894 ), tapi saya tidak yakin apa perbedaannya antara secara eksplisit melakukannya dan mulai nohupmelakukannya.
neverendingqs

0

Saya pikir itu karena pekerjaan terkait dengan sesi. Setelah itu semua pekerjaan pengguna berakhir juga.


2
Tetapi mengapa itu berbeda dari mendapatkan terminal, mengetik dan menjalankan perintah, dan keluar? Kedua sesi ditutup setelah saya menutupnya.
neverendingqs

Setuju, saya ingin mengerti mengapa ini tidak berbeda dengan menutup terminal Anda sendiri secara manual.
Avindra Goolcharan

0

Jika nohupdapat membuka file outputnya, Anda mungkin memiliki petunjuk nohup.out. Mungkin pythontidak di jalan ketika Anda menjalankan skrip via ssh.

Saya akan mencoba membuat file log untuk perintah. Coba gunakan:

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

Saya menggunakan sshuntuk menjalankan skrip secara manual, jadi saya mengasumsikan python3 ada di jalur.
neverendingqs

@neverendingqs Apakah file log mengandung sesuatu?
BillThor

Tidak ada yang luar biasa - start up terlihat normal.
neverendingqs
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.