当前位置: 首页 > 后端技术 > Python

如何高效远程部署?自动化运维利器Fabric教程

时间:2023-03-26 18:31:44 Python

关于Python自动化的话题,上一篇介绍了Invoke库,它是Fabric最重要的组件之一。Fabric也是一个被广泛使用的自动化工具库,是不得不提的自动化运维工具。因此,本文将在以后进行介绍。Fabric主要用于自动化应用程序部署和系统管理等任务。简单轻量,提供丰富的SSH扩展接口。在Fabric1.x版本中,它混合了本地和远程功能;但是从Fabric2.x版本开始,分离出了独立的Invoke库来处理本地的自动化任务,而Fabric则专注于网络层面的远程和远程任务。为了实现这一点,Fabric主要依赖另一个核心组件Paramiko,它是一个基于SSH协议的远程控制模块。Fabric在此基础上封装了更加友好的接口,可以远程执行Shell命令、传输文件、批量操作服务器、身份验证、各种配置和设置代理等等。1、FabricPython2版本的版本区分已经在今年元旦正式宣布“退役”,未来只会是Python3的阶段。为了适应Python的非兼容迁移版本,很多项目也必须推出自己的新版本(兼容或只支持Python3),包括本文的主角Fabric。Fabric本身有两个大版本:Fabric1和Fabric2,而在这个库的基础上,还有两个容易混淆的相关库:Fabric2和Fabric3(注意这里的数字是库名的一部分)。它们的区别如下:Fabric1.x:支持Python2.5-2.7,不支持Python3Fabric2.x:支持Python2.7和3.4+,但不兼容Fabric1.x的fabfileFabric2:相当于Fabric2。x,forMakedifferentversionscoexist(installanold1.xversion,andtheninstallitasanewversion)Fabric3:基于Fabric1.x的fork(非官方),兼容Python2&3,兼容Fabric1.xfabfile综上所述,我们可以看出,建议使用官方的Fabric2.x系列版本,但同时需要注意的是,一些过时的教程可能是基于更早的版本(或者非官方的Fabric3,这也是基于Fabric1.x),需要识别。比如在Fabric1.x系列中,import这样写:fromfabric.apiimportrun;新版本会报错:"ImportError:Nomodulenamedapi"(PS:可以根据是否有fabric.api来判断Fabric的版本,就像从print语句或print函数判断版本一样在Python中)。同时,由于新版本不支持旧版本的fabfile,使用时可能会报错:“Noideawhat'xxx'is!”Fabric2是一个不兼容的版本。与上一版本相比,其主要改进有:支持Python2.7和3.4+线程安全,取消多进程并发实现API围绕fabric.connection.Connection重新组织完全修改命令行解析器,允许常规GNU/POSIX风格taskbasisFlagsandoptions(fabmytask:weird=custom,arg=formatarenolongerrequired)可以声明pre-tasks和post-tasks...(官方列出了10多个[1],本文不一一列举byone)在Fabric2的开发过程中分离Invoke之前引入的,具体原因可以参考这个回答[2]。总之,在使用Fabric的时候,要注意版本差异的问题。二、Fabric的基本使用1、安装首先安装:pipintallfabric。安装完成后可以在命令行窗口查看版本信息:>>>fab-VFabric2.5.0Paramiko2.7.1Invoke1.4.0执行“fab-V”从上面的结果可以看出我安装的是Fabric2.5.0版本,同时可以看到它的两个核心依赖库Paramiko和Invoke的版本信息。2.一个简单的例子Fabric主要用于远程任务,即操作远程服务器,下面是一个简单的例子:#任意文件名都可以fromfabricimportConnectionhost_ip='47.xx.xx.xx'#服务器地址user_name='root'#服务器用户名password='****'#服务器密码cmd='date'#shell命令查询服务器时间con=Connection(host_ip,user_name,connect_kwargs={'password':password})result=con.run(cmd,hide=True)print(result)以上代码,通过账号+密码登录远程服务器,然后执行date命令查看服务器时间,执行结果:Commandexitedwithstatus0.===stdout===FriFeb1415:33:05CST2020(nostderr)现在打印的结果中,除了服务器时间,还有一些不相关的信息。这是因为它打印的“result”是一个“fabric.runners.Result”类,我们可以解析其中的信息:print(result.stdout)#FriFeb1415:33:05CST2020print(result.exited)#0print(result.ok)#Trueprint(result.failed)#Falseprint(result.command)#dateprint(result.connection.host)#47.xx.xx.xx以上代码使用了Connection类及其run()方法在连接的服务器上运行shell命令。如果需要使用管理员权限,需要换成sudo()方法。如果想在本地执行shell命令,需要将其替换为local()方法。此外,还有get()、put()等方法,详见下文介绍。3、命令行使用上面例子中的代码可以写在任何.py脚本中,然后运行脚本,也可以稍微封装一下,导入到其他脚本中使用。另外,Fabric也是一个命令行工具,可以通过fab命令来执行任务。我们稍微修改一下上面例子的代码:#filename:fabfile.pyfromfabricimportConnectionfromfabricimporttaskhost_ip='47.xx.xx.xx'#服务器地址user_name='root'#服务器用户名password='****'#服务器密码cmd='date'#用于查询服务器时间的shell命令@taskdeftest(c):"""从远程主机获取日期。"""con=Connection(host_ip,user_name,connect_kwargs={'password':password})result=con.run(cmd,hide=True)print(result.stdout)#只打印解释的时候,主要变化是:fabfile.py文件名:脚本名入口代码必须使用Thisname@taskdecorator:这个装饰器需要从fabric引入,它是invoke的@task装饰器的封装,实际用法和invoke一样(注:还需要一个context参数"c",但实际上它并没有在代码块中使用,而是使用了Connection类的一个实例)然后,在脚本同级目录的命令行窗口中,可以查看并执行相应的任务:>>>fab-lAvailabletasks:testGetdatefromremotehost.>>>fabtestFriFeb1416:10:24CST2020fab是Invoke的扩展实现,继承了很多原有的功能,所以执行“fab--help”与之前介绍的“inv--help”相比,你会发现它们的很多参数和解释是完全一样的。针对远程服务场景,fab增加了几个命令行选项(蓝色标注),其中:--prompt-for-login-password:让程序在命令行输入SSH登录密码(上面的例子在代码中指定connect_kwargs.password参数,如果使用该选项,执行时可以手动输入密码)--prompt-for-passphrase:让程序在命令行输入SSH私钥加密文件的路径-H或--hosts:指定要连接的主机名-i或--i??dentity:指定SSH连接使用的私钥文件-S或--ssh-config:指定运行时加载的SSH配置文件有关命令的更多信息Fabric文档[3]的行接口。4.交互操作如果远程服务器上有交互提示,要求输入密码或者“yes”等信息,这就需要Fabric能够监听和响应。下面是一个简单的例子。引入invoke的Responder,初始化内容为常规字符串和响应信息,最后赋值给watchers参数:frominvokeimportResponderfromfabricimportConnectionc=Connection('host')sudopass=Responder(pattern=r'\[sudo\]password:',response='mypassword\n')c.run('sudowhoami',pty=True,watchers=[sudopass])5.文件传输在本地和服务器之间传输文件是一种常见的用法。Fabric在这方面做了很好的封装。Connection类中有以下两个方法:get(args,*kwargs):将远程文件拉到本地文件系统或类文件对象put(args,*kwargs):将本地文件或类文件对象推送到建立连接后的远程文件系统,示例:#(省略)con.get('/opt/123.txt','123.txt')con.put('test.txt','/opt/test.txt')第一个参数是指要传输的源文件,第二个参数是要传输的目标,可以指定为文件名或文件夹(当为空或无时,使用defaultpath):#(省略)con.get('/opt/123.txt','')#为空时使用默认路径con.put('test.txt','/opt/')#指定路径/opt/get()方法默认存放路径为os.getcwd,put()方法默认存放路径为home目录。6、服务器批量操作对于服务器集群的批量操作,最简单的实现方式是使用for循环,然后建立连接,一个一个执行操作,类似这样:forhostin('web1','web2','mac1'):result=Connection(host).run('uname-s')但是有时候,这个方案会存在一个问题:如果有多组不同的服务器集群需要执行不同的操作,那么你需要写很多for循环。如果要将各组操作的结果进行聚合(如字典形式、key-host、value-result),则必须在for循环外加入额外的操作。for循环顺序同步执行,效率太低,缺乏异常处理机制(如果中间出现异常,会导致后续操作跳出)针对这些问题,Fabric提出了Group的概念,可以将一组主机定义为一个Group。它的API方法和Connection一样,就是一个Group可以简化为一个Connection。然后,开发者只需要简单的操作Group,最终得到一个结果集,减少了异常处理和执行顺序的工作量。Fabric提供了一个fabric.group.Group基类,并从中派生了两个子类。区别在于:SerialGroup(hosts,*kwargs):以串行方式执行操作ThreadingGroup(hosts,*kwargs):并发执行操作组的类型决定了主机集群的操作方式,我们只需要进行选择即可。然后,他们的执行结果是一个fabric.group.GroupResult类,它是dict的子类,里面存放着每一个主机连接与其执行结果的对应关系。>>>fromfabricimportSerialGroup>>>results=SerialGroup('web1','web2','mac1').run('uname-s')>>>print(results):,:,:,}>另外,GroupResult还提供了failed和succeeded两个属性,可以取出failed/successful的子集。这样,二次操作也可以方便的批量进行。原文三、Fabric的高级用法一、身份认证Fabric使用SSH协议建立远程会话,是一种基于应用层的相对安全的加密传输协议。基本上,它有两个级别的安全认证方式:基于密码的认证:使用帐号和密码登录远程主机,安全性较低,容易受到“中间人”攻击Key-based认证:使用正确的方式(公钥放在服务器,私钥放在客户端),不会被“中间人”攻击,但需要一个登录时间比较长。在上面的例子中,我们使用了第一种方式,即通过指定connect_kwargs.password参数,使用密码登录。当然,Fabric也支持第二种方式。指定私钥文件的路径有3种方式。优先级如下:先查找connect_kwargs.key_filename参数,如果找到则作为私钥;其次,命令行使用的--identify选项最后默认使用操作系统ssh_config文件中IdentityFile的值。如果私钥文件本身是加密的,则需要使用connect_kwargs.passphrase参数。2、配置文件Fabric支持将部分参数项从业务代码中分离出来,即通过配置文件进行管理。例如上面提到的密码和私钥文件可以写在配置文件中,避免与代码耦合。Fabric基本沿用了Invoke的配置文件系统(官方文档列出9层),同时增加了一些SSH相关的配置项。支持的文件格式有.yaml、.yml、.json、.py(按优先顺序排列),推荐使用yaml格式(后缀可以简写为yml)。其中,比较常用的配置文件有:系统级配置文件:/etc/fabric.yml用户级配置文件:~/.fabric.yml(Windows在C:Usersxxx下)项目级配置文件:/myproject/fabric.yml之上的文件优先级递减。由于我的机器是Windows,为了方便,我在用户目录下创建了一个“.fabric.yml”文件,内容如下:#filename:.fabric.ymluser:rootconnect_kwargs:password:xxxx#如果使用key,如下#key_filename:#-your_key_file我们提取了用户名和密码,所以可以在fabfile中删除这些内容:#文件名:fabfile.pyfromfabricimportConnectionfromfabricimporttaskhost_ip='47.xx.xx.xx'#服务器地址cmd='date'#查询服务器时间的shell命令@taskdeftest(c):"""从远程主机获取日期。"""con=Connection(host_ip)result=con.run(cmd,hide=True)print(result.stdout)然后,在命令行执行:>>>fabtestTueFeb1810:33:38CST2020配置文件也可以设置很多参数,具体可以查看文档[4].3.网络网关如果远程服务与网络隔离,无法直接访问(在不同的局域网),这时候就需要网关/代理/隧道。这种中间机通常称为跳板机或堡垒机。Fabric中有两种网关解决方案,对应OpenSSH客户端的两种选择:ProxyJump:简单,低开销,可嵌套ProxyCommand:高开销,不可嵌套,更灵活在创建Fabric的Connection对象时,可以指定网关参数来应用这些两种方案:ProxyJump方式是在一个Connection中嵌套一个Connection作为前者的网关,后者使用SSH协议的direct-tcpip为前者打开一个与实际远程主机的连接,后者可以continue嵌套使用自己的网关。fromfabricimportConnectionc=Connection('internalhost',gateway=Connection('gatewayhost'))ProxyCommand表示客户端在本地使用ssh命令(类似于“ssh-W%h:%pgatewayhost”)创建子进程,子进程与服务器通信,它可以读取标准输入和输出。这部分的实现细节分别在paramiko.channel.Channel和paramiko.proxy.ProxyCommand中。除了在参数中指定外,还可以在Fabric支持的配置文件中定义。更多细节请参考文献[5]。4.总结Fabric版本不兼容造成了一定程度的社区分裂,这无疑与Python3的实现密不可分,但我们有理由相信新版本优于旧版本。网上很多关于Fabric的文章已经过时了。本文基于最新的官方文档,整理了较为全面的知识点,可以指导大家快速入门Fabric。看完本文,相信读者在短短几分钟内就能轻松上手。如果您有任何疑问,请通过以下方式与我联系。--------------公众号:蟒猫头条号:蟒猫知乎:豌豆花猫掘金:豌豆花猫----------------相关链接:调用教程:https://mp.weixin.qq.com/s/up...1、http://www.fabfile.org/upgrad...2、http://www.pyinvoke。org/faq.h...3,http://docs.fabfile.org/en/2....4,http://docs.fabfile.org/en/2....5.http://docs.fabfile.org/en/2....公众号【Python猫】,本号连载一系列优质文章,包括猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐及翻译等,欢迎关注。