当前位置: 首页 > 科技观察

使用Python远程登录服务器的优秀实践

时间:2023-03-17 22:36:46 科技观察

在用Python编写一些脚本的时候,在某些情况下,我们需要频繁的登录远程服务执行一个命令并返回一些结果。在shell环境下,我们是这样操作的。$sshpass-p${passwd}ssh-p${port}-l${user}-oStrictHostKeyChecking=noxx.xx.xx.xx"ls-l"然后你会发现你的输出有很多你不知道的不需要,但是有些信息不能去掉(或许有办法,请留言交流),类似这个host:xx.xx.xx.xx,port:xxWarning:Permanentlyadded'[xx.xx.xx.xx]:xx'(RSA)tothelistofknownhosts.Loginfailure:[Errno1]Thisserverisnotregisteredtormpplatform,pleaseconfirmwhethercdnserver.total4-rw-r--r--1rootroot239Mar302018admin-openrc对于直接使用shell命令执行命令,可以直接使用管道,或者redirect标准输出到File方法获取执行命令返回的结果1.使用subprocess如果使用Python来做这个,通常我们会想到使用一些命令执行库,比如os.popen,os.system,commands,subprocess间接获得。但是据我所知,这些库得到的输出不仅是标准输出,还有标准错误(也就是上面的冗余信息),所以每次输出的数据都需要清洗,然后排序和格式化,我们可以得到我们想要的。你想要的数据。以子流程为例,像这样)#数据清理,格式化不会显示<代码...>通过上面文字+代码的展示,可以感受到ssh登录的痛点痛点一:需要额外安装sshpass二:干扰信息太多,数据清洗和格式化都相当麻烦。痛点三:代码实现不优雅(有点脏),可读性太差。痛点四:SSH连接不能复用,一个连接只能执行一次。痛点五:Code不能在所有平台上使用,只能在Linux和OSX上使用。为了解决这些问题,我在全网搜索了关于Pythonssh的文章,但是没有看到这方面的完整介绍。为此,我浏览了一个非常流行的Github项目:awesome-python-cn(https://github.com/BingmingWong/awesome-python-cn)。期望在这里找到一些关于远程连接的有用库。果然找到了两个sh.sshParamiko2。先用sh.ssh先介绍第一个,sh.sshsh是一个库,可以让你通过函数调用来完成Linxu/OSX系统命令,很好用,有机会写一篇关于它的介绍。$python3-mpipinstallsh今天只介绍它的一个功能:ssh通常是在两台机器之间进行通信。为了方便起见,您可以设置免密登录,这样就不需要输入密码了。这段代码可以实现免密码登录,执行我们的命令ls-lfromshimportsshoutput=ssh("root@xx.xx.xx.xx","-p22","ls-l")print(output)但是可以,我们不想设置没有密码的互信。为了让这段代码更通用,我假设我们没有设置免密码,只能使用密码登录。问题来了。要输入密码,您必须使用交互式方法输入。如何在Python中实现它?原来ssh方法接收一个_out参数,可以是表示文件路径的字符串,也可以是文件对象(或类文件对象),也可以是回调函数,意思是当有标准输出,它将被调用以将输出内容传递给此函数。这很容易处理。只要我识别出password:这个词,我就会将我的密码写入标准输入。完整代码如下:importsysfromshimportsshaggregated=""defssh_interact(char,stdin):globalaggregatedsys.stdout.write(char.encode())sys.stdout.flush()aggregated+=charifaggregated.endswith("password:"):stdin.put("you_password\n")output=ssh("root@xx.xx.xx.xx","-p22","ls-l",_tty_in=True,_out_bufsize=0,_out=ssh_interact)print(output)this是根据官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给出的一些信息写的demo。尝试运行后发现程序会一直运行,永远不会返回,永远不会退出,回调函数永远不会进入。通过debug查看源码,还是没找到问题所在,于是上Github搜索。原来这个问题在2017年就已经存在了,一直到2020年才修复,好像用sh.ssh的人不多,于是又“跟进”了,希望能得到回复。只有在需要输入密码时才会出现上述问题。如果你设置了机器之间的互信,就没有问题。为了体验使用sh.ssh的效果,我设置了无密码机器互信,然后使用如下代码。fromshimportsshmy_server=ssh.bake("root@xx.xx.xx.xx","-p22")#相当于执行一次登录,执行一次命令,执行完退出登录print(my_server.ls())#可以在sleep期间,手动登录服务器,使用top,查看当前连接了多少终端time.sleep(5)#再次执行该命令时,登录终端数会为+1,执行后为-1print(my_server.ifconfig())我惊讶地发现使用bake,my_server.ls()和my_server.ifconfig()似乎通过同一个ssh连接执行了两次命令,但是在其实可以在远程机器上执行top命令,看到连接终端的变化时,会先+1,再-1,说明这两个命令的执行是通过两个连接实现的。从这点来看,使用sh.ssh可以解决痛点1(如果能解决以上问题)、痛点2、痛点3。但是还是不能复用ssh连接,还是不方便,不是我的理想的解决方案。最重要的一点是sh模块只支持Linxu/OSX,你必须使用它的兄弟库——Windows上的pbs。然后又去pypi看了一下pbs,一直“年久失修”,没人维护。至此,我离“棋子”还差最后一根稻草。3、使用paramiko抱着最后的希望,尝试使用paramiko库,终于在paramiko中找到了本该属于Python的优雅。可以使用如下命令安装$python3-mpipinstallparamiko接下来介绍几种常用的ssh登录方法方法一:基于用户名密码的sshclient登录然后可以参考下面的代码,在Linux/OSXRemote系统下的连接importparamikossh=paramiko.SSHClient()#允许连接到known_hosts文件中没有的主机ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())#建立连接ssh.connect("xx.xx.xx.xx",username="root",port=22,password="you_password")#使用这个连接执行命令ssh_stdin,ssh_stdout,ssh_stderr=ssh.exec_command("ls-l")#获取输出print(ssh_stdout.read())#Closetheconnectionssh.close()方法二:根据用户名和密码以传输方式登录方法一是传统的连接服务器,执行命令,关闭的操作。多个操作需要多个连接,连接无法复用【痛点4】。有时需要登录服务器进行多项操作,如执行命令、上传/下载文件等。方法一无法实现,可以使用transport方法。importparamiko#建立连接trans=paramiko.Transport(("xx.xx.xx.xx",22))trans.connect(username="root",password="you_passwd")#指定sshclient对象的transport为上面transssh=paramiko.SSHClient()ssh._transport=trans#其余同上.read())#关闭连接trans.close()方法三:SSHClient根据公钥登录importparamiko#指定本地RSA私钥文件#如果创建密钥对时设置了密码,密码设置了如果不需要指定密码参数pkey=paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa',password='12345')#建立连接ssh=paramiko.SSHClient()ssh.connect(hostname='xx.xx.xx.xx',port=22,username='you_username',pkey=pkey)#executecommandstdin,stdout,stderr=ssh.exec_command('ls-l')#putstdout中的结果如果有报错,会放到stderr中established时有设置密码,password为设置的密码,如果没有则需要指定密码参数pkey=paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa',password='12345')#建立连接trans=paramiko.Transport(('xx.xx.xx.xx',22))trans.connect(username='you_username',pkey=pkey)#指定sshclient对象的传输如上transssh=paramiko.SSHClient()ssh._transport=trans#执行命令,同传统方法stdin,stdout,stderr=ssh.exec_command('df-hl')print(stdout.read().decode())#关闭连接trans.close()以上四种方法可以帮助你远程登录服务器执行命令。如果需要复用连接:一个连接执行多个命令,可以使用方法二使用方法四后,记得关闭连接,实现sftp文件传输。同时paramiko是ssh的完美解决方案。很专业,也可以用来实现sftp文件传输。importparamiko#实例化一个trans对象#实例化一个transport对象trans=paramiko.Transport(('xx.xx.xx.xx',22))#建立连接trans.connect(username='you_username',password='you_passwd')#实例化一个sftp对象,指定连接通道sftp=paramiko.SFTPClient.from_transport(trans)#发送文件sftp.put(localpath='/tmp/11.txt',remotepath='/tmp/22.txt')#下载文件sftp.get(remotepath='/tmp/22.txt',localpath='/tmp/33.txt')trans.close()到这里,Paramiko赢了,但是还有一个痛点是我们的it不说了,是多平台的,就是Windows。这里有好事也有坏事。好处是:paramiko支持windows。不好的是:需要做很多复杂的准备工作,可以google一下解决,但是建议还是放弃吧,坑太深了。4、经过一些对比和一些例子,可以看出Paramiko是一款专业省心的ssh工具。个人认为Paramiko模块是运维人员必学的模块之一。如果你恰好需要用Python代码实现ssh到远程服务器获取一些信息,那么我向你推荐Paramiko。最后,希望这篇文章能对你有所帮助。