SSH会话在运行后台启动脚本时持续保持连接的问题排查
解决SSH远程执行启动后台进程后会话不退出的问题
这个问题我之前也碰到过,核心原因不是sys.exit()的问题——你的start.py已经正常退出(退出码0),问题出在SSH会话会一直保持到所有关联的文件描述符(标准输入、输出、错误)都被关闭,而你启动的后台进程可能还在继承start.py的这些流,导致SSH不会断开。
先搞清楚两种执行方式的差异
- 交互式SSH登录后运行脚本:此时脚本的标准流绑定到终端,脚本退出后,后台进程虽然可能还在运行,但终端属于你的交互式会话,你可以主动退出SSH(后台进程如果做了脱离处理就不会被终止)。
- 直接远程执行命令:SSH会话是和这个命令绑定的,只要该命令(包括它启动的子进程)还持有SSH会话的标准流,SSH就不会断开,直到所有这些流都被关闭。
解决方案分两种情况:
1. 修改start.py,让后台进程彻底脱离关联
如果你的start.py是用subprocess启动后台进程,一定要确保:
- 重定向后台进程的标准输入、输出、错误到
/dev/null或日志文件,避免继承父进程的流 - 让后台进程脱离当前会话组,切断和SSH会话的绑定
举个Python代码示例:
import subprocess # 启动后台进程的正确姿势 def start_background_process(): subprocess.Popen( ["your_background_command_here"], # 重定向所有标准流到/dev/null,避免占用SSH的流 stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, # 启动新会话,让进程脱离当前SSH会话的控制组 start_new_session=True, shell=False # 尽量不用shell=True,减少安全风险 ) # 执行启动逻辑后,直接退出即可,不需要额外的sys.exit()(脚本执行完自然退出) start_background_process()
这样修改后,start.py退出时,后台进程已经完全脱离了SSH的文件描述符,SSH会话会立即断开。
2. 不修改start.py,在SSH命令层处理
如果暂时没法改脚本,可以在远程命令里用nohup+重定向+后台执行的组合:
ssh -o StrictHostKeyChecking=no -i /tmp/tmpqcz5l5il user@remote_host -p 22 "nohup python start.py --config_file /data/workload.pg > /dev/null 2>&1 &"
解释下各个部分:
nohup:让进程忽略HUP信号(避免SSH断开时终止后台进程),同时默认会把输出写到nohup.out> /dev/null 2>&1:把标准输出和错误都重定向到/dev/null,彻底切断和SSH会话的流关联- 最后的
&:让命令在远程主机的后台执行,SSH会在启动命令后立即断开连接
总结
不需要给start.py加特殊的sys.exit()信号,问题的核心是后台进程没有和SSH会话的文件描述彻底脱离。优先修改脚本里的后台进程启动逻辑,这样最可靠;如果改不了脚本,用SSH命令层的重定向+nohup+&组合也能解决。
内容的提问来源于stack exchange,提问作者Joel Doerr




