¿Por qué finaliza mi proceso en segundo plano de Python cuando finaliza la sesión SSH?

¿Por qué finaliza mi proceso en segundo plano de Python cuando finaliza la sesión SSH?

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

nohup python3 -u <script> &

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

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

El proceso finaliza en cuanto sshtermina de ejecutarlo y cierra la sesión.

¿Cuál es la diferencia entre los dos?

EDITAR: el script de Python ejecuta un servicio web a través de Bottle.

EDIT2: También lo intentécreando un script de inicioeso llama startup.shy ejecuta ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>", pero tiene el mismo comportamiento.

EDITAR3: Tal vez sea 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"

EDITAR4: 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 llega echo "Finished"y veo el mensaje del servidor Bottle, que nunca antes había visto:

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

Veo "Terminado" si ingreso SSH manualmente y finalizo el proceso yo mismo.

EDIT5: Al usar EDIT4, si hago una solicitud a cualquier punto final, obtengo una página de regreso, pero la botella genera errores:

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)

Respuesta1

Desconectaría el comando de sus flujos de error y de entrada/salida estándar:

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

sshnecesita un indicador que no tenga más salida y que no requiera más entrada. Tener algo más como entrada y redirigir la salida significa que sshse puede salir de forma segura, ya que la entrada/salida no proviene ni va al terminal. Esto significa que la entrada debe provenir de otro lugar y la salida (tanto STDOUT como STDERR) debe ir a otro lugar.

La </dev/nullpieza se especifica /dev/nullcomo entrada para <script>. Por qué eso es útil aquí:

Redirigir /dev/null a stdin dará un EOF inmediato a cualquier llamada de lectura de ese proceso. Esto suele ser útil para separar un proceso de un tty (dicho proceso se denomina demonio). Por ejemplo, al iniciar un proceso en segundo plano de forma remota a través de ssh, debe redirigir la entrada estándar para evitar que el proceso espere una entrada local. https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475

Alternativamente, la redirección desde otra fuente de entrada debería ser relativamente segura siempre y cuando sshno sea necesario mantener abierta la sesión actual.

Con la >/dev/nullparte, el shell redirige la salida estándar a /dev/null esencialmente descartándola. >/path/to/filetambién funcionará.

La última parte 2>&1es redirigir STDERR a STDOUT.

Hay tres fuentes estándar de entrada y salida para un programa. La entrada estándar generalmente proviene del teclado si es un programa interactivo, o de otro programa si está procesando la salida del otro programa. El programa normalmente imprime en salida estándar y, a veces, imprime en error estándar. Estos tres descriptores de archivos (puede considerarlos como "canalizaciones de datos") a menudo se denominan STDIN, STDOUT y STDERR.

¡A veces no tienen nombre, están numerados! Las numeraciones integradas para ellos son 0, 1 y 2, en ese orden. De forma predeterminada, si no nombras o numeras uno explícitamente, estás hablando de STDOUT.

Dado ese contexto, puede ver que el comando anterior redirige 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 redirige el error estándar a la salida estándar ( tienes que poner un & delante del destino cuando hagas esto).

Por lo tanto, la explicación breve es que “todos los resultados de este comando deberían arrojarse a un agujero negro”. ¡Esa es una buena manera de hacer que un programa sea realmente silencioso!
¿Qué significa > /dev/null 2>&1? | xaprb

Respuesta2

Mira a 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 comando ssh.

De la descripción:

Si se especifica el comando, se ejecuta en el host remoto en lugar de en un shell de inicio de sesión.

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

La diferencia entre eso y ejecutarlo nohup python3 -u <script> &en su terminal local es que este se ejecuta como un proceso en segundo plano local mientras que el comando ssh intenta ejecutarlo como un proceso en segundo plano remoto.

Si desea ejecutar el script localmente, no ejecute startup.sh como parte del comando ssh. Podrías intentar algo comossh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

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

VerManual de usuario de la pantalla

Si bien creo que la pantalla es su mejor opción, si debe usar nohup, considere configurarlo shopt -s huponexiten el host remoto antes de ejecutar el comando nohup. Alternativamente, puede utilizar disown -h [jobID]para marcar el proceso para que no se le envíe SIGHUP.1

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

Su sistema utiliza la señal SIGHUP (colgar) en el terminal de control o en la muerte del proceso de control. Puede usar SIGHUP para recargar archivos de configuración y abrir/cerrar archivos de registro también. En otras palabras, si cierra sesión en su terminal, todos los trabajos en ejecución finalizarán. Para evitar esto, puede pasar la opción -h para rechazar el comando. 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 huponexitfunciona cuando se sale, se cierra o se suelta un shell. Supongo que su problema actual está relacionado con cómo finaliza la sesión de Shell.2

  1. Todos los procesos secundarios, en segundo plano o no, de un shell abierto a través de una conexión ssh se eliminan con SIGHUP cuando la conexión ssh se cierra solo si la opción huponexit está configurada: ejecute shopt huponexit para ver si esto es cierto.

  2. Si huponexit es verdadero, entonces puede usar nohup o disown para disociar el proceso del shell para que no se elimine cuando salga. O ejecute cosas con pantalla.

  3. Si huponexit es falso, que es el valor predeterminado en al menos algunos Linux hoy en día, entonces los trabajos en segundo plano no se eliminarán al cerrar sesión normalmente.

  4. Pero incluso si huponexit es falso, si la conexión ssh se interrumpe o se interrumpe (a diferencia del cierre de sesión normal), los procesos en segundo plano aún se eliminarán. Esto se puede evitar rechazando o nohup 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

Respuesta3

¿Quizás valga la pena probar -nla opción al iniciar un ssh? Evitará la dependencia del proceso remoto de un local stdin, que por supuesto se cierra tan pronto como ssh sessionfinaliza. Y esto provocará la terminación remota de los precios cada vez que intente acceder a su stdin.

Respuesta4

Esto suena más como un problema con lo que hace el pythonguión o él mismo. pythonTodo lo que nohuprealmente hace (barra que simplifica las redirecciones) es simplemente configurar el controlador de la HUPseñal para SIG_IGN(ignorar) antes de ejecutar el programa. No hay nada que impida que el programa vuelva a configurarlo SIG_DFLo instale su propio controlador una vez que comienza a ejecutarse.

Una cosa que quizás quieras probar es encerrar tu comando entre paréntesis para obtener un efecto de doble bifurcación y tu pythonscript ya no sea hijo del proceso de shell. P.ej:

( nohup python3 -u <script> & )

Otra cosa que también puede valer la pena probar (si estás usando bashy no otro shell) es usar el disownshell incorporado en lugar de nohup. Si todo funciona según lo documentado, esto no debería hacer ninguna diferencia, pero en un shell interactivo esto evitaría que la HUPseñal se propague a su pythonsecuencia de comandos. Puede agregar el rechazo en la siguiente línea o en la misma que se muestra a continuación (tenga en cuenta que agregar un ;después de a &es un error en bash):

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

Si lo anterior o alguna combinación de ellos no funciona, entonces seguramente el único lugar para abordar el problema es en el pythonpropio script.

información relacionada