【问题标题】:Paramiko SSH connection does not have access to GPU with tensorflowParamiko SSH 连接无法使用 tensorflow 访问 GPU
【发布时间】:2021-11-11 03:30:52
【问题描述】:

使用 Python 的 paramiko,我尝试通过 SSH 连接远程访问我拥有的服务器。这个服务器有一个 python 脚本,我通过我的 SSH 连接执行(我实际上执行了一个调用这个 python 脚本的 .sh 文件)。这个python脚本的第一行是:

print("Devices:", tf.config.experimental.list_physical_devices())

这里我只看到:

设备:[PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU')]

如果我在打开与 PuTTY 的 SSH 连接后执行相同的 .sh 文件,则同一行代码会生成以下结果:

设备:[PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU'), PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

这里确实显示 GPU 可用。我想这与我打开 paramiko SSH 连接的方式有关。这是代码:

ssh = paramiko.SSHClient()  
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

ssh.connect(ip, port=port, username=u, password=pass)

cmd_to_execute = "./" + shFileName + ".sh"

ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
    
for line in ssh_stdout:
    print(line.strip('\n'))
for line in ssh_stderr:
    print('ERR: ' + line.strip('\n'))
    
ssh.close()

为什么使用 paramiko 建立 SSH 连接时我无法访问 GPU?

编辑 1

如果我允许显示 tensorflow 日志,我会收到以下错误: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] 无法加载动态库“libcudart.so.11.0”; dlerror: libcudart.so.11.0: 无法打开共享对象文件:没有这样的文件或目录

还有其他看起来或多或少像这个但改变了 .11.0

另一方面,如果我使用 PuTTY,它会正确加载库: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] 成功打开动态库libcudart.so.11.0

【问题讨论】:

  • 它是什么样的服务器?如果你做ssh u@ip:port ./shFileName.sh,你会得到什么? (作为 one 行)。这可能是与Environment variable differences when using Paramiko 类似的问题。
  • 您应该查看两种情况下的输出 tensorflow 打印,它会告诉您缺少什么以获得 GPU 支持,可能某些库路径没有设置。
  • @Dr.Snoopy 你是对的,我从 paramiko 执行它时收到此错误“无法加载动态库'libcudart.so.11.0'”和其他消息“成功打开动态库 libcudart。 so.11.0" 通过 putty 执行时。
  • 当您通过 SSH 连接时,您需要运行 shell(bash、zsh 等),如果您直接运行脚本,则 shell 不会运行,并且这是设置一些路径的地方(如 . .bashrc)。
  • 那么你就遇到了这个问题。您的环境缺少LD_LIBRARY_PATH

标签: python tensorflow ssh gpu paramiko


【解决方案1】:

按照 Martin Prikryl 和 DR 制作的 cmets。 Snoopy 我能够通过修改启动脚本为所有会话设置相同的 PATH 来解决这个问题。我通过修改两个文件来做到这一点。

$HOME/.profile文件原代码有这部分

if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

我把它改成了:

# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi

$HOME/.bashrc 原代码有这部分:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

我把它改成了:

# If not running interactively, don't do anything
#case $- in
#    *i*) ;;
#      *) return;;
#esac

这种方式现在加载相同的库,而不考虑我建立连接的方式。

【讨论】: