使用Python Paramiko在不同的SSH服务器上并行运行多个命令



我有一个SSH.py,其目标是通过SSH连接到许多服务器以运行Python脚本(worker.py)。我正在使用Paramiko,但我对它非常陌生,并在我走的过程中学习。在我ssh连接的每台服务器上,我都需要保持Python脚本的运行——这是为了并行训练模型,因此脚本需要在所有机器上运行,以便更新模型参数/联合训练。服务器上的Python脚本需要运行,所以要么所有的SSH连接都不能关闭,要么我必须找到一种方法,让服务器上的Python脚本即使关闭连接也能继续运行。

从广泛的谷歌搜索,看起来你可以实现这一点与nohup或:

client = paramiko.SSHClient()
client.connect(ip_address, username, password)
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("python worker.py > /logs/'command output' 2>&1")

然而,我不清楚的是我们如何关闭/退出所有SSH连接?我在cmd.exe上运行SSH.py文件,关闭cmd.exe是否足以让所有进程远程关闭?

另外,我对client.close()的使用是否正确?请参阅下面我为我的代码。

# SSH.py
import paramiko
import argparse
import os
path = "path"
python_script = "worker.py"
# definitions for ssh connection and cluster
ip_list = ['XXX.XXX.XXX.XXX', XXX.XXX.XXX.XXX', XXX.XXX.XXX.XXX']
port_list = [':XXXX', ':XXXX', ':XXXX']
user_list = ['user', 'user', 'user']
password_list = ['pass', 'pass', 'pass']
node_list = list(map(lambda x: f'-node{x + 1} ', list(range(len(ip_list)))))
cluster = ' '.join([node + ip + port for node, ip, port in zip(node_list, ip_list, port_list)])
# run script on command line of local machine
os.system(f"cd {path} && python {python_script} {cluster} -type worker -index 0 -batch 64 > {path}/logs/'command output'/{ip_list[0]}.log 2>&1")
# loop for IP and password
for i, (ip, user, password) in enumerate(zip(ip_list[1:], user_list[1:], password_list[1:]), 1):
try:
print("Open session in: " + ip + "...")
client = paramiko.SSHClient()
client.connect(ip, user, password)
transport = client.get_transport()
channel = transport.open_session()
except paramiko.SSHException:
print("Connection Failed")
quit()
try:
channel.exec_command(f"cd {path} && python {python_script} {cluster} -type worker -index {i} -batch 64 > {path}/logs/'command output'/{ip_list[i]}.log 2>&1", timeout=30)
client.close() # here I am closing connection but above command should be running, my question is can I safely close cmd.exe on which I am running SSH.py? 
except paramiko.SSHException:
print("Cannot run file. Continue with other IPs in list...")
client.close()
continue

代码是基于使用Python Paramiko在后台运行远程SSH服务器的进程

编辑:似乎channel.exec_command()没有执行命令
f"cd {path} && python {python_script} {cluster} -type worker -index {i} -batch 64 > {path}/logs/'command output'/{ip_list[i]}.log 2>&1"

所以我想知道这是不是因为client.close()?如果我用client.close()注释掉所有行,会发生什么?这有帮助吗?这有危险吗?当我退出本地Python脚本时,这会关闭我所有的SSH连接,因此不需要client.close()吗?

我所有的机器都有Windows操作系统。

实际上,问题在于您关闭了SSH连接。由于远程进程没有与终端分离,关闭终端将终止该进程。在Linux服务器上,可以使用nohup。我不知道有什么(如果有的话)对应的Windows。

无论如何,你似乎不需要关闭连接。我明白了,你可以等待所有的命令完成。

stdouts = []
clients = []
# Start the commands
commands = zip(ip_list[1:], user_list[1:], password_list[1:])
for i, (ip, user, password) in enumerate(commands, 1):
print("Open session in: " + ip + "...")
client = paramiko.SSHClient()
client.connect(ip, user, password)
command = 
f"cd {path} && " + 
f"python {python_script} {cluster} -type worker -index {i} -batch 64 " + 
f"> {path}/logs/'command output'/{ip_list[i]}.log 2>&1"
stdin, stdout, stderr = client.exec_command(command)
clients.append(client)
stdouts.append(stdout)
# Wait for commands to complete
for i in range(len(stdouts)):
stdouts[i].read()
clients[i].close()

注意,上面使用stdout.read()的简单解决方案之所以有效,只是因为您将命令输出重定向到远程文件。否则,命令可能会死锁。

如果没有这个(或者如果您想在本地看到命令输出),您将需要这样的代码:
while any(x is not None for x in stdouts):
for i in range(len(stdouts)):
stdout = stdouts[i]
if stdout is not None:
channel = stdout.channel
# To prevent losing output at the end, first test for exit,
# then for output
exited = channel.exit_status_ready()
while channel.recv_ready():
s = channel.recv(1024).decode('utf8')
print(f"#{i} stdout: {s}")
while channel.recv_stderr_ready():
s = channel.recv_stderr(1024).decode('utf8')
print(f"#{i} stderr: {s}")
if exited:
print(f"#{i} done")
clients[i].close()
stdouts[i] = None
time.sleep(0.1)

如果不需要分隔标准输出和标准错误,可以使用Channel.set_combine_stderr极大地简化代码。参见Paramiko ssh die/hang with big output。


关于你关于SSHClient.close的问题:如果你不调用它,当脚本完成时,当Python垃圾收集器清理挂起的对象时,连接将隐式关闭。这是一种不好的做法。即使Python不这样做,本地操作系统也会终止本地Python进程的所有连接。这也是一种不好的做法。在任何情况下,这会终止远程进程。

相关内容

  • 没有找到相关文章

最新更新