簡體   English   中英

使用 Python Paramiko 在不同的 SSH 服務器中並行運行多個命令

[英]Run multiple commands in different SSH servers in parallel using Python Paramiko

我有一個SSH.py ,目標是通過 SSH 連接到許多服務器以運行 Python 腳本( worker.py )。 我正在使用 Paramiko,但對它非常陌生,並且像我一樣學習 go。 On each server I ssh over with, I need to keep the Python script running -- this is for training a model parallely and so the script needs to run on all machines as to update model parameters/train jointly. 服務器上的 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
for i, (ip, user, password) in enumerate(zip(ip_list[1:], user_list[1:], password_list[1:]), 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()的簡單解決方案之所以有效,是因為您將命令 output 重定向到遠程文件。 如果不是,命令可能會死鎖

沒有它(或者如果您想在本地查看命令 output),您將需要這樣的代碼:

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)

如果不需要分離 stdout 和 stderr,可以使用Channel.set_combine_stderr大大簡化代碼。 請參閱Paramiko ssh 死/掛大 output


關於你關於SSHClient.close的問題:如果你不調用它,當腳本完成時,當 Python 垃圾收集器清理掛起的對象時,連接將被隱式關閉。 這是一個不好的做法。 而且即使 Python 不這樣做,本地操作系統也會終止本地 Python 進程的所有連接。 這也是一種不好的做法。 無論如何,這將終止遠程進程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM