新闻资讯

新闻资讯 行业动态

自动化运维利器 Fabric 教程(二)——Fabric 的基本用法

编辑:008     时间:2020-03-09

1、安装

首先是安装: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 的版本信息。

2、一个简单的例子

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() 等方法,详见下文介绍。

3、命令行用法

上例代码可写在任意的 .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) # 只打印时间 

解释一下,主要的改动点有:

  • fabfile.py 文件名:入口代码的脚本名必须用这个名字
  • @task 装饰器:需要从 fabric 中引入这个装饰器,它是对 invoke 的 @task 装饰器的封装,实际用法跟 invoke 一样(注意:它也需要有上下文参数“c”,但实际上它并没有在代码块中使用,而是用了 Connection 类的实例)

然后,在该脚本同级目录的命令行窗口中,可以查看和执行相应的任务:

>>> 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 针对远程服务的场景,添加了几个命令行选项(已标蓝),其中:

  • --prompt-for-login-password:令程序在命令行中输入 SSH 登录密码(上例在代码中指定了 connect_kwargs.password 参数,若用此选项,可要求在执行时再手工输入密码)
  • --prompt-for-passphrase:令程序在命令行中输入 SSH 私钥加密文件的路径
  • -H 或 --hosts:指定要连接的 host 名
  • -i 或 --identity:指定 SSH 连接所用的私钥文件
  • -S 或 --ssh-config:指定运行时要加载的 SSH 配置文件

关于 Fabric 的命令行接口,更多内容可查看文档 [3]。

4、交互式操作

远程服务器上若有交互式提示,要求输入密码或“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])

5、传输文件

本地与服务器间的文件传输是常见用法。Fabric 在这方面做了很好的封装,Connection 类中有以下两个方法可用:

  • get(*args, **kwargs):拉取远端文件到本地文件系统或类文件(file-like)对象
  • put(*args, **kwargs):推送本地文件或类文件对象到远端文件系统

在已建立连接的情况下,示例:

# (略) 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 目录。

6、服务器批量操作

对于服务器集群的批量操作,最简单的实现方法是用 for 循环,然后逐一建立 connection 和执行操作,类似这样:

for host in ('web1', 'web2', 'mac1'):
	result = Connection(host).run('uname -s')

但有时候,这样的方案会存在问题:

  • 如果存在多组不同的服务器集群,需要执行不同操作,那么需要写很多 for 循环
  • 如果想把每组操作的结果聚合起来(例如字典形式,key-主机,value-结果),还得在 for 循环之外添加额外的操作
  • for 循环是顺序同步执行的,效率太低,而且缺乏异常处理机制(若中间出现异常,会导致跳出后续操作)

对于这些问题,Fabric 提出了 Group 的概念,可将一组主机定义成一个 Group,它的 API 方法跟 Connection 一样,即一个 Group 可简化地视为一个 Connection。

然后,开发者只需要简单地操作这个 Group,最后得到一个结果集即可,减少了自己在异常处理及执行顺序上的工作。

Fabric 提供了一个 fabric.group.Group 基类,并由其派生出两个子类,区别是:

  • SerialGroup(*hosts, **kwargs):按串行方式执行操作
  • ThreadingGroup(*hosts, **kwargs):按并发方式执行操作

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'>,
}&gt;

另外,GroupResult 还提供了 failed 与 succeeded 两个属性,可以取出失败/成功的子集。由此,也可以方便地批量进行二次操作。 


原文链接:https://my.oschina.net/u/4051725/blog/3167801

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐