¿Por qué mi process de background de Python finaliza cuando finaliza la session de SSH?

Tengo un script bash que inicia un script python3 (llamémoslo startup.sh ), con la línea key:

 nohup python3 -u <script> & 

Cuando ssh directamente y llamo a este script, el script de python continúa ejecutándose en segundo plano después de que salgo. Sin embargo, cuando ejecuto esto:

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

El process finaliza tan pronto como ssh haya terminado de ejecutarlo y cierre la session.

¿Cuál es la diferencia entre los dos?

EDITAR: El script de python está ejecutando un service web a través de Bottle.

EDIT2: También intenté crear un script de inicio que llama a startup.sh y ejecuté ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>" , pero obtuve el mismo comportamiento.

EDIT3: Tal vez es algo más en el guión. Aquí está la mayor parte del guión:

 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: cuando ejecuto la última línea con un sueño al final:

 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" 

Nunca alcanza el echo "Finished" , y veo el post del server de botella, que nunca antes había visto:

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

Veo "Finalizado" si manualmente SSH y matar el process yo mismo.

EDIT5: Usando EDIT4, si realizo una request a cualquier punto final, obtengo una página de vuelta, pero la botella se equivoca:

 Bottle vx.xx server starting up (using WSGIRefServer())... Listening on <URL> Hit Ctrl-C to quit. ---------------------------------------- Exception happened during processing of request from ('<IP>', 55104) 

Desconectaría el command de su input / salida estándar y flujos de error:

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

ssh necesita un indicador que no tenga más resultados y que no requiera más inputs. Tener otra cosa como input y networkingireccionar la salida significa que ssh puede salir de manera segura, ya que la input / salida no proviene ni va a la terminal. Esto significa que la input debe provenir de otro lugar, y la salida (STDOUT y STDERR) debe ir a otro lugar.

La parte </dev/null especifica /dev/null como la input para <script> . Por qué es útil aquí:

Redirigir / dev / null a stdin dará un EOF inmediato a cualquier llamada de lectura desde ese process. Esto es típicamente útil para separar un process de un tty (tal process se llama daemon). Por ejemplo, al iniciar un process en segundo plano de forma remota a través de ssh, debe networkingirigir stdin para evitar que el process espere la input local. https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475

Alternativamente, networkingirigir desde otra fuente de input debería ser relativamente seguro siempre que la session ssh actual no necesite mantenerse abierta.

Con la parte >/dev/null el intérprete de commands networkingirige la salida estándar hacia / dev / null y lo descarta esencialmente. >/path/to/file también funcionará.

La última parte 2>&1 está networkingirigiendo STDERR a STDOUT.

Hay tres fonts estándar de input y salida para un progtwig. Normalmente, la input estándar proviene del keyboard si se trata de un progtwig interactivo o de otro progtwig si está procesando la salida del otro progtwig. El progtwig normalmente se imprime a la salida estándar, y a veces imprime a error estándar. Estos tres descriptores de files (puede considerarlos como "canalizaciones de datos") a menudo se denominan STDIN, STDOUT y STDERR.

A veces no están nombrados, ¡están numerados! Las numeraciones incorporadas para ellos son 0, 1 y 2, en ese order. Por defecto, si no nombra ni numera uno explícitamente, está hablando de STDOUT.

Dado ese context, puede ver que el command anterior está networkingirigiendo la salida estándar a / dev / null, que es un lugar donde puede volcar cualquier cosa que no desee (a menudo llamado bit-bucket), y luego networkingirigir el error estándar a la salida estándar ( tienes que poner una & delante del destino cuando haces esto).

La breve explicación, por lo tanto, es "toda la producción de este command debe ser introducida en un agujero negro". ¡Esa es una buena manera de hacer que un progtwig sea realmente silencioso!
¿Qué significa> / dev / null 2> & 1? | Xaprb

Mira al 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] 

Cuando ejecuta ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh" está ejecutando el script de shell startup.sh como un command ssh.

De la descripción:

Si se especifica el command, se ejecuta en el host remoto en lugar de en un shell de inicio de session.

En function de esto, debería ejecutar el script de forma remota.

La diferencia entre eso y ejecutar nohup python3 -u <script> & en su terminal local es que esto se ejecuta como un process de background local, mientras que el command ssh intenta ejecutarlo como un process de background remoto.

Si tiene la intención de ejecutar el script localmente, entonces no ejecute startup.sh como parte del command ssh. Puede intentar algo como ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

Si su intención es ejecutar el script de forma remota y desea que este process continúe después de que finalice su session ssh, primero deberá iniciar una session de screen en el host remoto. Luego debe ejecutar el script de python dentro de la pantalla y continuará ejecutándose después de finalizar su session de ssh.

Ver pantalla Manual del usuario

Aunque creo que la pantalla es su mejor opción, si debe usar nohup, considere configurar shopt -s huponexit en el host remoto antes de ejecutar el command nohup. Alternativamente, puede usar disown -h [jobID] para marcar el process, por lo que SIGHUP no se le enviará. 1

¿Cómo sigo ejecutando trabajo después de salir de un indicador de shell en segundo plano?

La señal SIGHUP (Hangup) es utilizada por su sistema para controlar la terminal o la muerte del process de control. También puede usar SIGHUP para volver a cargar los files de configuration y abrir / cerrar files de logging. En otras palabras, si cierra la session desde su terminal, todas las tareas en ejecución finalizarán. Para evitar esto, puede pasar la opción -h para desautorizar el command. Esta opción marca cada ID de trabajo para que SIGHUP no se envíe al trabajo si el shell recibe un SIGHUP.

Además, consulte este resumen de cómo huponexit funciona cuando se huponexit se huponexit se huponexit un shell. Supongo que su problema actual está relacionado con cómo termina la session de shell. 2

  1. Todos los processs secundarios, con antecedentes o no de un shell abierto sobre una connection ssh se eliminan con SIGHUP cuando la connection ssh se cierra solo si se establece la opción huponexit: ejecuta shopt huponexit para ver si esto es cierto.

  2. Si huponexit es verdadero, puede usar nohup o disown para disociar el process del shell para que no se muera cuando salga. O bien, ejecuta cosas con pantalla.

  3. Si huponexit es falso, que es el valor pnetworkingeterminado en al less algunos linux en estos días, los trabajos de background no se eliminarán en el cierre de session normal.

  4. Pero incluso si huponexit es falso, entonces si la connection ssh se elimina, o cae (diferente al cierre de session normal), los processs de background seguirán siendo eliminados. Esto puede evitarse mediante el rechazo o noh como en (2).

Finalmente, aquí hay algunos ejemplos de cómo usar 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 

Sospecho que tienes una condición de carrera. Sería algo como esto:

  • Comienza la connection SSH
  • SSH inicia startup.sh
  • startup.sh inicia un process en segundo plano (nohup)
  • startup.sh termina
  • ssh termina, y esto mata los processs hijo (es decir, nohup)

Si ssh no hubiera cortado las cosas, habría sucedido lo siguiente (no estoy seguro del order de estos dos):

  • nohup inicia su secuencia de commands python
  • nohup se desconecta del process principal y la terminal.

Así que los dos últimos pasos críticos no ocurren, porque startup.sh y ssh terminan antes de que nohup tenga time para hacer su trabajo.

Espero que su problema desaparezca si pone unos segundos de sueño al final de startup.sh. No estoy seguro exactamente cuánto time necesitas. Si es importante mantenerlo al mínimo, entonces tal vez pueda ver algo en process para ver cuándo es seguro.

Esto suena más como un problema con lo que está haciendo el script python o python . Todo lo que nohup realmente hace (barra de redirects simplificadores) es simplemente configurar el controller para la señal HUP en SIG_IGN (ignorar) antes de ejecutar el progtwig. No hay nada que impida que el progtwig regrese a SIG_DFL o instale su propio controller una vez que comience a ejecutarse.

Una cosa que quizás desee probar es include su command entre paréntesis para que tenga un doble efecto de bifurcación y su secuencia de commands de python ya no sea un elemento secundario del process de shell. P.ej:

 ( nohup python3 -u <script> & ) 

Otra cosa que también puede valer la pena intentar (si está utilizando bash y no otro shell) es usar el command de repudio en lugar de nohup . Si todo funciona según lo documentado, esto no debería marcar la diferencia, pero en un shell interactivo esto detendría que la señal HUP se propague a su secuencia de commands python . Puede agregar la desaprobación en la línea siguiente o la misma que se muestra a continuación (note agregar un ; después de un & es un error en bash ):

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

Si lo anterior o una combinación de ambos no funciona, seguramente el único lugar para abordar el problema es el propio script de python .

Creo que es porque el trabajo está vinculado a la session. Una vez que eso termine, los trabajos de los usuarios también finalizarán.

Si nohup puede abrir su file de salida, es posible que tenga una pista en nohup.out . Es posible que python no esté en la ruta cuando ejecuta el script a través de ssh .

Intentaría crear un file de logging para el command. Intenta usar:

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

Tal vez vale la pena probar -n opción al iniciar un ssh ? Evitará la dependencia de processs remotos en un stdin local, que por supuesto se cierra tan pronto como finaliza la ssh session . Y esto causará la terminación remota de los precios cada vez que intente acceder a su stdin .