
Eu tenho um script bash que inicia um script python3 (vamos chamá-lo startup.sh
), com a linha chave:
nohup python3 -u <script> &
Quando eu ssh
entro diretamente e chamo esse script, o script python continua sendo executado em segundo plano depois que eu saio. No entanto, quando executo isso:
ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
O processo termina assim que ssh
termina a execução e fecha a sessão.
Qual é a diferença entre os dois?
EDIT: O script python está executando um serviço web via Bottle.
EDIT2: Eu também tenteicriando um script de inicializaçãoque chama startup.sh
e correu ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>"
, mas obteve o mesmo comportamento.
EDIT3: Talvez seja outra coisa no script. Aqui está a maior parte do script:
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: Quando executo a última linha com sleep no 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"
Ele nunca chega echo "Finished"
e vejo a mensagem do servidor Bottle, que nunca vi antes:
Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.
Vejo "Concluído" se eu fizer SSH manualmente e encerrar o processo sozinho.
EDIT5: Usando EDIT4, se eu fizer uma solicitação para qualquer endpoint, recebo uma página de volta, mas o Bottle apresenta erros:
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)
Responder1
Eu desconectaria o comando de seus fluxos padrão de entrada/saída e de erro:
nohup python3 -u <script> </dev/null >/dev/null 2>&1 &
ssh
precisa de um indicador que não tenha mais resultados e que não exija mais insumos. Ter outra coisa como entrada e redirecionar a saída significa ssh
poder sair com segurança, já que a entrada/saída não vem ou vai para o terminal. Isso significa que a entrada deve vir de outro lugar e a saída (STDOUT e STDERR) deve ir para outro lugar.
A </dev/null
parte especifica /dev/null
como entrada para <script>
. Por que isso é útil aqui:
Redirecionar /dev/null para stdin fornecerá um EOF imediato para qualquer chamada de leitura desse processo. Isso normalmente é útil para separar um processo de um tty (esse processo é chamado de daemon). Por exemplo, ao iniciar um processo em segundo plano remotamente por ssh, você deve redirecionar o stdin para evitar que o processo aguarde entrada local. https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475
Alternativamente, o redirecionamento de outra fonte de entrada deve ser relativamente seguro, desde que a ssh
sessão atual não precise ser mantida aberta.
Com a >/dev/null
parte, o shell redireciona a saída padrão para /dev/null essencialmente descartando-a. >/path/to/file
também funcionará.
A última parte 2>&1
é redirecionar STDERR para STDOUT.
Existem três fontes padrão de entrada e saída para um programa. A entrada padrão geralmente vem do teclado, se for um programa interativo, ou de outro programa, se estiver processando a saída do outro programa. O programa geralmente imprime na saída padrão e, às vezes, imprime no erro padrão. Esses três descritores de arquivo (você pode considerá-los como “pipes de dados”) são frequentemente chamados de STDIN, STDOUT e STDERR.
Às vezes eles não têm nome, estão numerados! As numerações integradas para eles são 0, 1 e 2, nessa ordem. Por padrão, se você não nomear ou numerar um explicitamente, estará falando sobre STDOUT.
Dado esse contexto, você pode ver que o comando acima está redirecionando a saída padrão para /dev/null, que é um lugar onde você pode despejar qualquer coisa que não queira (geralmente chamado de bit-bucket) e, em seguida, redirecionar o erro padrão para a saída padrão ( você deve colocar um & na frente do destino ao fazer isso).
A breve explicação, portanto, é “toda a saída deste comando deve ser empurrada para um buraco negro”. Essa é uma boa maneira de deixar um programa realmente silencioso!
O que significa > /dev/null 2>&1? | Xaprb
Responder2
Olhe para 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]
Ao executar, ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
você está executando o shell script startup.sh como um comando ssh.
Da descrição:
Se o comando for especificado, ele será executado no host remoto em vez de em um shell de login.
Com base nisso, ele deverá executar o script remotamente.
A diferença entre isso e a execução nohup python3 -u <script> &
em seu terminal local é que ele é executado como um processo local em segundo plano, enquanto o comando ssh tenta executá-lo como um processo remoto em segundo plano.
Se você pretende executar o script localmente, não execute startup.sh como parte do comando ssh. Você pode tentar algo comossh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"
Se sua intenção é executar o script remotamente e deseja que esse processo continue após o término da sessão ssh, você deverá primeiro iniciar uma screen
sessão no host remoto. Então você deve executar o script python na tela e ele continuará em execução após o término da sessão ssh.
Embora eu ache que screen seja sua melhor opção, se você precisar usar o nohup, considere configurar shopt -s huponexit
o host remoto antes de executar o comando nohup. Alternativamente, você pode usar disown -h [jobID]
para marcar o processo para que o SIGHUP não seja enviado a ele.1
Como continuo executando o trabalho depois de sair de um prompt do shell em segundo plano?
O sinal SIGHUP (Hangup) é usado pelo seu sistema no terminal de controle ou na morte do processo de controle. Você pode usar o SIGHUP para recarregar arquivos de configuração e abrir/fechar arquivos de log também. Em outras palavras, se você sair do terminal, todos os trabalhos em execução serão encerrados. Para evitar isso você pode passar a opção -h para rejeitar o comando. Esta opção marca cada jobID para que SIGHUP não seja enviado para o trabalho se o shell receber um SIGHUP.
Além disso, veja este resumo de como huponexit
funciona quando um shell é encerrado, eliminado ou descartado. Suponho que seu problema atual esteja relacionado ao modo como a sessão do shell termina.2
Todos os processos filhos, em segundo plano ou não, de um shell aberto através de uma conexão ssh são eliminados com SIGHUP quando a conexão ssh é fechada apenas se a opção huponexit estiver definida: execute shopt huponexit para ver se isso é verdade.
Se huponexit for verdadeiro, então você pode usar nohup ou disown para dissociar o processo do shell para que ele não seja eliminado quando você sair. Ou execute as coisas com a tela.
Se huponexit for falso, que é o padrão em pelo menos alguns Linux atualmente, então os trabalhos em segundo plano não serão eliminados no logout normal.
- Mas mesmo que huponexit seja falso, se a conexão ssh for interrompida ou interrompida (diferente do logout normal), os processos em segundo plano ainda serão eliminados. Isso pode ser evitado por disown ou nohup como em (2).
Finalmente, aqui estão alguns exemplos de como usar o 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
Responder3
Talvez valha a pena tentar -n
a opção ao iniciar um ssh
? Isso evitará a dependência do processo remoto em um local stdin
, que obviamente fecha assim que ssh session
termina. E isso causará o encerramento remoto dos preços sempre que tentar acessar seu stdin
.
Responder4
Isso parece mais um problema com o que o python
script ou python
ele próprio está fazendo. Tudo o que nohup
realmente faz (com exceção da simplificação dos redirecionamentos) é apenas definir o manipulador do HUP
sinal como SIG_IGN
(ignorar) antes de executar o programa. Não há nada que impeça o programa de configurá-lo SIG_DFL
ou instalar seu próprio manipulador assim que ele começar a ser executado.
Uma coisa que você pode tentar é colocar seu comando entre parênteses para obter um efeito de bifurcação dupla e seu python
script não ser mais filho do processo shell. Por exemplo:
( nohup python3 -u <script> & )
Outra coisa que também pode valer a pena tentar (se você estiver usando bash
e não outro shell) é usar o disown
builtin em vez de nohup
. Se tudo estiver funcionando conforme documentado, isso não deve fazer nenhuma diferença, mas em um shell interativo isso impediria a HUP
propagação do sinal para o seu python
script. Você pode adicionar disown na próxima linha ou na mesma linha abaixo (observe que adicionar um ;
após a &
é um erro em bash
):
python3 -u <script> </dev/null &>/dev/null & disown
Se o procedimento acima ou alguma combinação dele não funcionar, certamente o único lugar para resolver o problema é no python
próprio script.