Python Paramiko跨CentOS执行长命令后SSH会话失效问题求助
解决Paramiko长时间会话后出现'SSH session is not active'的问题
我之前处理过类似的场景,长时间空闲的SSH会话很容易被中间防火墙、路由器或者服务器端的超时策略强制断开——哪怕你设置了ServerAlive相关参数,大概率是参数配置的时机或者方式不对,或者会话空闲时间超出了系统容忍的上限。下面给你几个可行的解决方案,按推荐程度排序:
1. 改用后台执行+后续重连检查(最推荐)
既然你的命令要跑10小时,完全没必要一直保持SSH会话挂着。可以让从机器在后台执行命令,主机器记录进程ID后断开连接,10小时后再重新连接检查状态。这种方式彻底规避了会话超时的问题,稳定性最高。
示例代码:
import paramiko import time # 第一步:连接并发送后台执行命令 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("slave_host", username="your_user", password="your_pass") # 用nohup让命令后台运行,同时输出进程ID long_command = "your_10_hour_command" command = f"nohup {long_command} > /tmp/command_output.log 2>&1 & echo $!" stdin, stdout, stderr = ssh.exec_command(command) # 获取后台进程的PID pid = stdout.read().decode().strip() print(f"后台命令已启动,PID: {pid}") # 断开当前会话 ssh.close() # 模拟主机器处理其他事务/休眠10小时 time.sleep(36000) # 第二步:重新连接检查命令状态 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("slave_host", username="your_user", password="your_pass") # 检查进程是否还在运行 check_command = f"ps -p {pid}" stdin, stdout, stderr = ssh.exec_command(check_command) process_exists = stdout.read().strip() if process_exists: print("命令仍在执行中") else: print("命令已执行完成,输出日志如下:") stdin, stdout, stderr = ssh.exec_command("cat /tmp/command_output.log") print(stdout.read().decode()) ssh.close()
2. 正确配置会话保活参数
如果一定要保持会话不中断,得确保Paramiko的保活参数在正确的时机配置——要在建立连接后、执行命令前,针对Transport对象设置,而不是只在SSHClient层面配置。
示例代码:
import paramiko import time ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("slave_host", username="your_user", password="your_pass") # 获取Transport对象并设置保活 transport = ssh.get_transport() # 每30秒发送一次保活包,避免会话被标记为空闲 transport.set_keepalive(30) # 执行第一个命令 stdin, stdout, stderr = ssh.exec_command("your_first_command") # 等待命令完成(如果需要) stdout.channel.recv_exit_status() # 主机器处理其他事务/休眠10小时 time.sleep(36000) # 再次执行命令,此时会话应该保持活跃 stdin, stdout, stderr = ssh.exec_command("your_second_command") print(stdout.read().decode()) ssh.close()
注意:还要检查从机器的/etc/ssh/sshd_config配置,确保ClientAliveInterval和ClientAliveCountMax的设置不会比你的保活间隔更严格。比如如果服务器端设置ClientAliveInterval 60(60秒没收到客户端响应就发探测包),ClientAliveCountMax 3(发3次没响应就断开),那你的保活间隔要小于60秒,确保服务器端一直能收到客户端的保活信号。
3. 定期发送空命令保持会话活跃
如果上面两种方式都不适用,可以在主机器休眠期间,每隔一段时间给从机器发送一个无副作用的空命令(比如echo ""),强制会话保持活跃。
示例代码:
import paramiko import threading import time def keep_session_alive(ssh): while True: try: # 发送空命令,触发保活 stdin, stdout, stderr = ssh.exec_command("echo ''") stdout.channel.recv_exit_status() except Exception as e: print(f"保活失败:{e}") break # 每5分钟发送一次 time.sleep(300) # 建立连接 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect("slave_host", username="your_user", password="your_pass") # 启动保活线程 keep_alive_thread = threading.Thread(target=keep_session_alive, args=(ssh,)) keep_alive_thread.daemon = True keep_alive_thread.start() # 执行第一个命令 stdin, stdout, stderr = ssh.exec_command("your_first_command") stdout.channel.recv_exit_status() # 休眠10小时 time.sleep(36000) # 执行第二个命令 stdin, stdout, stderr = ssh.exec_command("your_second_command") print(stdout.read().decode()) ssh.close()
这种方式要注意线程安全,避免和主业务的命令执行冲突。
内容的提问来源于stack exchange,提问作者S_Ananth
相关产品推荐
相关产品推荐

