关于 Python 自动化的话题,在上一篇文章中,我介绍了 Invoke 库,它是 Fabric 的最重要组件之一。Fabric 也是一个被普遍应用的自动化工具库,是不得不提的自动化运维利器,因此,本文未来介绍一下它。html
Fabric 主要用在应用部署与系统管理等任务的自动化,简单轻量级,提供有丰富的 SSH 扩展接口。在 Fabric 1.x 版本中,它混杂了本地及远程两类功能;但自 Fabric 2.x 版本起,它分离出了独立的 Invoke 库,来处理本地的自动化任务,而 Fabric 则聚焦于远程与网络层面的任务。web
为了作到这点,Fabric 主要依赖另外一大核心组件 Paramiko,它是基于 SSH 协议的远程控制模块,Fabric 在其基础上封装出了更加友好的接口,能够远程执行 Shell 命令、传输文件、批量操做服务器、身份认证、多种配置与设置代理,等等。shell
Python 2 版本已经被官宣在今年元旦“退休”了,将来只会是 Python 3 的舞台。为了适应 Python 版本的非兼容性迁移,不少项目也必须推出本身的新版本(兼容或只支持 Python 3),其中就包括本文的主角 Fabric。json
Fabric 自身存在着 2 个大版本:Fabric 1 和 Fabric 2,而在这个库的基础上,还有两个很容易混淆的相关库:Fabric2 和 Fabric3(注意这里的数字是库名的一部分)。api
它们的区分以下:安全
综上可见,咱们推荐使用官方的 Fabric 2.x 系列版本,但同时要注意,某些过期的教程多是基于早期版本的(或非官方的 Fabric3,也是基于 Fabric 1.x),须要注意识别。服务器
例如,在 Fabric 1.x 系列中这么写导入:from fabric.api import run;在新版本中将报错:“ImportError: No module named api”(PS:可根据是否有 fabric.api 来判断 Fabric 的版本,就像在 Python 中根据 print 语句或 print 函数来判断版本同样)。同时,因为新版本不支持老版本的 fabfile,在使用时就可能报错:“No idea what 'xxx' is!”网络
Fabric 2 是非兼容性版本,相比于前个版本,它主要改进的点有:并发
以前介绍过的 invoke,就是在开发 Fabric 2 时被分离出来的,具体的缘由可参见这个回答 [2]。总而言之,在使用 Fabric 时,应该注意版本差别的问题。运维
首先是安装:pip intall fabric
,安装后,可在命令行窗口查看版本信息:
>>> fab -V
Fabric 2.5.0
Paramiko 2.7.1
Invoke 1.4.0复制代码
执行“fab -V”,以上结果可看出我安装的是 Fabric 2.5.0 版本,同时可看到它的两个核心依赖库 Paramiko 及 Invoke 的版本信息。
Fabric 主要用于远程任务,即要对远程服务器进行操做,下面是一个简单的例子:
# 可以使用任意的文件名
from fabric import Connection
host_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
命令,查看服务器的时间,执行结果:
Command exited with status 0.
=== stdout ===
Fri Feb 14 15:33:05 CST 2020
(no stderr)复制代码
如今打印的结果中,除了服务器时间,还有一些无关的信息。这是由于它打印的“result”是一个"fabric.runners.Result"类,咱们能够把其中的信息解析出来:
print(result.stdout) # Fri Feb 14 15:33:05 CST 2020
print(result.exited) # 0
print(result.ok) # True
print(result.failed) # False
print(result.command) # date
print(result.connection.host) # 47.xx.xx.xx复制代码
上述代码使用了 Connection 类及其 run() 方法,可在链接的服务器上运行 shell 命令。若是须要用管理员权限,则需替换成 sudo() 方法。若是要在本地执行 shell 命令,则需替换成 local() 方法。
除此以外,还有 get()、put() 等方法,详见下文介绍。
上例代码可写在任意的 .py 脚本中,而后运行该脚本,或者稍微封装下再导入到其它脚本中使用。
另外,Fabric 仍是个命令行工具,能够经过fab
命令来执行任务。咱们稍微改造一下上例的代码:
# 文件名:fabfile.py
from fabric import Connection
from fabric import task
host_ip = '47.xx.xx.xx' # 服务器地址
user_name = 'root' # 服务器用户名
password = '****' # 服务器密码
cmd = 'date' # shell 命令,查询服务器上的时间
@task
def test(c):
"""
Get date from remote host.
"""
con = Connection(host_ip, user_name, connect_kwargs={'password': password})
result = con.run(cmd, hide=True)
print(result.stdout) # 只打印时间复制代码
解释一下,主要的改动点有:
而后,在该脚本同级目录的命令行窗口中,能够查看和执行相应的任务:
>>> fab -l
Available tasks:
test Get date from remote host.
>>> fab test
Fri Feb 14 16:10:24 CST 2020复制代码
fab 是 Invoke 的扩展实现,继承了不少原有功能,因此执行“fab --help”,与以前介绍的“inv --help”相比,你会发现它们的不少参数与解释都是如出一辙的。
fab 针对远程服务的场景,添加了几个命令行选项(已标蓝),其中:
关于 Fabric 的命令行接口,更多内容可查看文档 [3]。
远程服务器上如有交互式提示,要求输入密码或“yes”之类的信息,这就要求 Fabric 可以监听并做出回应。
如下是一个简单示例。引入 invoke 的 Responder,初始化内容是一个正则字符串和回应信息,最后赋值给 watchers 参数:
from invoke import Responder
from fabric import Connection
c = Connection('host')
sudopass = Responder(
pattern=r'\[sudo\] password:',
response='mypassword\n')
c.run('sudo whoami', pty=True, watchers=[sudopass])复制代码
本地与服务器间的文件传输是常见用法。Fabric 在这方面作了很好的封装,Connection 类中有如下两个方法可用:
在已创建链接的状况下,示例:
# (略)
con.get('/opt/123.txt', '123.txt')
con.put('test.txt', '/opt/test.txt')复制代码
第一个参数指的是要传输的源文件,第二个参数是要传输的目的地,能够指定成文件名或者文件夹(为空或 None 时,使用默认路径):
# (略)
con.get('/opt/123.txt', '') # 为空时,使用默认路径
con.put('test.txt', '/opt/') # 指定路径 /opt/复制代码
get() 方法的默认存储路径是os.getcwd
,而 put() 方法的默认存储路径是 home 目录。
对于服务器集群的批量操做,最简单的实现方法是用 for 循环,而后逐一创建 connection 和执行操做,相似这样:
for host in ('web1', 'web2', 'mac1'):
result = Connection(host).run('uname -s')复制代码
但有时候,这样的方案会存在问题:
对于这些问题,Fabric 提出了 Group 的概念,可将一组主机定义成一个 Group,它的 API 方法跟 Connection 同样,即一个 Group 可简化地视为一个 Connection。
而后,开发者只须要简单地操做这个 Group,最后获得一个结果集便可,减小了本身在异常处理及执行顺序上的工做。
Fabric 提供了一个 fabric.group.Group 基类,并由其派生出两个子类,区别是:
Group 的类型决定了主机集群的操做方式,咱们只须要作出选择便可。而后,它们的执行结果是一个fabric.group.GroupResult
类,它是 dict 的子类,存储了每一个主机 connection 及其执行结果的对应关系。
>>> from fabric import SerialGroup
>>> results = SerialGroup('web1', 'web2', 'mac1').run('uname -s')
>>> print(results)
<GroupResult: {
<Connection 'web1'>: <CommandResult 'uname -s'>,
<Connection 'web2'>: <CommandResult 'uname -s'>,
<Connection 'mac1'>: <CommandResult 'uname -s'>,
}>复制代码
另外,GroupResult 还提供了 failed 与 succeeded 两个属性,能够取出失败/成功的子集。由此,也能够方便地批量进行二次操做。 原文
Fabric 使用 SSH 协议来创建远程会话,它是一种相对安全的基于应用层的加密传输协议。
基原本说,它有两种级别的安全认证方式:
前文在举例时,咱们用了第一种方式,即经过指定 connect_kwargs.password 参数,使用口令来登陆。
Fabric 固然也支持采用第二种方式,有三种方法来指定私钥文件的路径,优先级以下:
若是私钥文件自己还被加密过,则须要使用 connect_kwargs.passphrase 参数。
Fabric 支持把一些参数项与业务代码分离,即经过配置文件来管理它们,例如前面提到的密码和私钥文件,可写在配置文件中,避免与代码耦合。
Fabric 基本沿用了 Invoke 的配置文件体系(官方文档中列出了 9 层),同时增长了一些跟 SSH 相关的配置项。支持的文件格式有 .yaml、.yml、.json 与 .py(按这次序排优先级),推荐使用 yaml 格式(后缀可简写成 yml)。
其中,比较经常使用的配置文件有:
以上文件的优先级递减,因为我本机是 Windows,为了方便,我在用户目录建一个".fabric.yml"文件,内容以下:
# filename:.fabric.yml
user: root
connect_kwargs:
password: xxxx
# 若用密钥,则以下
# key_filename:
# - your_key_file复制代码
咱们把用户名和密码抽离出来了,因此 fabfile 中就能够删掉这些内容:
# 文件名:fabfile.py
from fabric import Connection
from fabric import task
host_ip = '47.xx.xx.xx' # 服务器地址
cmd = 'date' # shell 命令,查询服务器上的时间
@task
def test(c):
"""
Get date from remote host.
"""
con = Connection(host_ip)
result = con.run(cmd, hide=True)
print(result.stdout) 复制代码
而后,在命令行中执行:
>>> fab test
Tue Feb 18 10:33:38 CST 2020复制代码
配置文件中还能够设置不少参数,详细可查看文档 [4]。
若是远程服务是网络隔离的,没法直接被访问到(处在不一样局域网),这时候须要有网关/代理/隧道,这个中间层的机器一般被称为跳板机或堡垒机。
Fabric 中有两种网关解决方案,对应到 OpenSSH 客户端的两种选项:
在建立 Fabric 的 Connection 对象时,可经过指定 gateway 参数来应用这两种方案:
ProxyJump 方式就是在一个 Connection 中嵌套一个 Connection 做为前者的网关,后者使用 SSH 协议的direct-tcpip
为前者打开与实际远程主机的链接,并且后者还能够继续嵌套使用本身的网关。
from fabric import Connection
c = Connection('internalhost', gateway=Connection('gatewayhost'))复制代码
ProxyCommand 方式是客户端在本地用 ssh 命令(相似“ssh -W %h:%p gatewayhost”),建立一个子进程,该子进程与服务端进行通讯,同时它能读取标准输入和输出。
这部分的实现细节分别在paramiko.channel.Channel
和 paramiko.proxy.ProxyCommand
,除了在参数中指定,也能够在 Fabric 支持的配置文件中定义。更多细节,请查阅文档 [5]。
Fabric 的非兼容版本形成了必定程度的社区分裂,这无疑跟 Python 3 的推行脱不开关系,可是咱们有理由相信,新版本优胜于老版本。
网上关于 Fabric 的文章,不少已过期了。本文针对最新的官方文档,梳理出了较为全面的知识点,能够带你们很好地入门 Fabric。
读完本文,相信读者们只须要几分钟就能轻松上手使用。如如有所疑问,欢迎经过如下方式联系我。
--------------
公众号:Python猫
头条号:Python猫
知乎:豌豆花下猫
掘金:豌豆花下猫
--------------
Invoke教程:https://mp.weixin.qq.com/s/up8lxuRhJQAXRzxkirF3nA
一、http://www.fabfile.org/upgrading.html#upgrading
二、http://www.pyinvoke.org/faq.html#invoke-split-from-fabric
三、http://docs.fabfile.org/en/2.5/cli.html
四、http://docs.fabfile.org/en/2.5/concepts/configuration.html
五、http://docs.fabfile.org/en/2.5/concepts/networking.html
公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写做、优质英文推荐与翻译等等,欢迎关注哦。