Python下Fabric的使用

来源:互联网 发布:linux挂载samba共享 编辑:程序博客网 时间:2024/05/26 05:50
文中部分内容整理自官方文档。如果文中出现错误,欢迎指出与交流。

Fabric是什么?

Fabric 是一个 Python的库和命令行工具,用来提高基于 SSH 的应用部署和系统管理效率。可以实现与远程服务器的自动化交互。Fabric底层基于paramiko模块。其特点即为简单高效,并且支持多线程。在需要运维数十台至几百台机器(视情况而定)的情况下非常好用。但如果需要运维上千台并不推荐使用Fabric,可以尝试使用saltstack。

Fabric的使用

在搞清Fabric的作用后,我们可以尝试通过一些简单的程序,来更加深入的了解Fabric。

注:文中实验环境均为Centos7.3。使用的python版本为python2.7。

安装Fabric模块

需要先安装paramiko模块(上文提到了,Fabric基于这个模块)。

#yum  install  gcc  python-crypto  python-devel  python-paramiko  -y

通过pip命令安装Fabric

#pip  install  fabric

如果没有pip命令,可以通过以下命令安装:

#yum install  python-pip

至此Fabric模块就安装好了。

Hello Fabric

一个合格的教程,一定少不了hello world这个国际惯例:

#vim  fabfile.py      

注:fabric执行的时候会默认寻找当前路径下的fabfile.py文件。如果文件名为其它,需要在执行的时候通过-f 文件名的方式手动指定文件。

def hello()print "hello world"

Fabric脚本执行并不使用python命令,而是使用fab命令(在安装模块时会一起安装)。所以这个脚本的执行方法为:

#fab  hello    #hello对应脚本中你想执行的函数名。Hello world!Done.

#fab -f  你的文件名   hello   #此方法针对文件名不为fabfile时

至此我们便基于Fabric完成了一个非常简单的脚本,甚至连Fabric模块都不需要导入。但从中大家可以了解到Fabric脚本就是在文件中定义一个或多个函数,函数中是你要执行的任务。然后通过fab命令执行fabfile.py文件,执行你要执行的函数。

Fabric.api模块

Fabric.api是Fabric中最常用也是最核心的模块。可以使用此模块来实现与远程服务器的交互。简单的使用这些API就可以完成大部分应用场景的需求。

常用api 说明 例子 local 在本地执行命令 local(‘username -r’) run 在远程执行命令 run(‘username -r’) lcd 切换本机路径 lcd(‘/usr’) cd 切换远程服务器路径 cd(‘/usr’) put 上传文件到远程服务器 put(‘本地文件’,’远程路径’) get 从远程服务器下载文件 get(‘远程文件’,’本地路径’) prompt 获取用户输入(类似input) prompt(‘input path’) confirm 让用户确认是否继续 confirm(‘continue?’) env 定义全局信息,如主机、密码等 env.hosts=’localhost’

例子一:

注:Fabric基于paramiko模块,paramiko底层就是调用ssh命令来对远程主机进行管理。所以要使用Fabric来管理远程主机,要保证服务器端开启sshd服务,并且保证可以成功访问。

查看本地和远程主机的内核版本。

from fabric.api import *     #导入fabric.api模块env.hosts=  ['192.168.122.101','192.168.122.102','192.168.122.103']  #指定远端服务器的ip地址。如果有dns解析的也可以写主机名。env.password='indionce'  #指定远端主机的密码,如果各个密码不相同可以使用一个字典指定,例如:env.password={“root@192.168.122.101”:"indionce"}def local_uname():         #定义一个本地任务的函数        local('uname -r')def remote_uname():        #定义一个远程任务的函数        run('uname -r')def uname():               #定义一个函数,将本地与远端组合起来使用        local_uname()        remote_uname()

执行这个脚本会得到下面的结果(因为字数原因我只取了前两台主机的返回信息):

[root@Fabric ~]# fab    uname  [192.168.122.101] Executing task 'uname'[localhost] local: uname -r3.10.0-514.el7.x86_64[192.168.122.101] run: uname -r[192.168.122.101] out: 3.10.0-514.el7.x86_64[192.168.122.101] out: [192.168.122.102] Executing task 'uname'[localhost] local: uname -r3.10.0-514.el7.x86_64[192.168.122.102] run: uname -r[192.168.122.102] out: 3.10.0-514.el7.x86_64[192.168.122.102] out: .....

尽管我只保留了前两台主机的返回值,但是也可以从中发现。我有多少台主机,脚本便执行了多少次本地的uname -r。在实际应用中,有很多类似内核信息的本地命令,我只需要执行一次。甚至多次执行会出现错误,这时我就需要使用@runs_once来对函数进行修饰,具体使用也非常简单,如下:

@runs_once   #指定下一行的函数在运行时,只运行一次。def local_uname():        local('uname -r')

新脚本执行之后就会发现本地的内核信息只输出了一次。

例子二

查看远程服务器的文件夹列表

from fabric.api import *@runs_once   #一定要指定这一条,否则会让你输入多次路径def input():        return  prompt("input path:")  #prompt函数,让用户输入自己想要的路径,将输入的值返回到函数。def ls_path(dirname):     #在定义函数的时候指定形参。        run("ls -l "+dirname)def go():        ls_path(input())  #使用input返回的值,用于ls_path()的参数

执行这个脚本

[root@name ~]# fab go [192.168.122.101] Executing task 'go'input path: /home           #用户自己输入一个路径,然后到远端服务器查看这个路径下的文件[192.168.122.101] run: ls -l /home[192.168.122.101] out: total 4[192.168.122.101] out: -rw-r--r--. 1 root root 541 Jun  8 23:54 fatab[192.168.122.101] out: [192.168.122.102] Executing task 'go'[192.168.122.102] run: ls -l /home[192.168.122.102] out: total 4[192.168.122.102] out: -rw-r--r--. 1 root root 541 Jun  8 23:54 fatab[192.168.122.102] out: [192.168.122.103] Executing task 'go'[192.168.122.103] run: ls -l /home[192.168.122.103] out: total 4[192.168.122.103] out: -rw-r--r--. 1 root root 541 Jun  8 23:54 fatab[192.168.122.103] out: 

故障处理

在执行任务中,难免出现命令执行失败的情况。而在日常使用中,肯定不会像我们测试时只执行一两条命令,一般都是一系列命令来共同完成一件事。如果其中一条命令执行失败,便会影响后续的命令。这个时候我们要让脚本停下来,以免出现更大的错误。
Fabric 会检查被调用程序的返回值,如果这些程序没有干净地退出,Fabric 会终止操作,下面我们就来看看如果一个测试用例遇到错误时会发生什么:

[root@Fabric ~]# fab    uname    [192.168.122.101] Executing task 'uname'[localhost] local: uname -rzzzuname:无效选项 -- zTry 'uname --help' for more information.Fatal error: local() encountered an error (return code 1) while executing 'uname -rzzz'Aborting.

太好了!我们什么都不用做,Fabric 检测到了错误并终止,不会继续执行接下来的任务。

如果我们想要更加灵活,给用户另一种选择,一个名为warn_only的设置就排上用场了。可以把退出换为警告,以提供更灵活的错误处理。具体如下:

from fabric.api import *from fabric.contrib.console import *   #这个模块中包含confirmdef backup():        with settings(warn_only=True):  #with命令表示执行这句后,执行下面的命令。使用settings命令来设置警告模式                state=local('mkdir /root/zz')  #创建一个文件夹        if   state.failed  and  not confirm("/root/zz is already exist,continue?"): #使用failed来判断state这条命令是否失败,失败了为真。confirm向用户确认是否继续,继续为真。如果命令失败了,并且用户希望停止,便通过if判断。                abort("退出任务")   #abort是退出任务,有些类似python的exit。退出并且时返回给用户一串字符串        local('tar cavf /root/zz/etc.tar.gz  /etc') #将etc的文件备份到/root/zz文件夹中

运行该文件得到下列结果,出现错误时向用户提示错误,用户可以视情况自行选择是否继续。

[root@Fabric ~]# fab  mkdir[192.168.122.101] Executing task 'mkdir'[localhost] local: mkdir /root/zzmkdir: 无法创建目录"/root/zz": 文件已存在Warning: local() encountered an error (return code 1) while executing 'mkdir /root/zz'/root/zz is already exist,continue? [Y/n] aI didn't understand you. Please specify '(y)es' or '(n)o'./root/zz is already exist,continue? [Y/n] 

跨网关的使用

实际使用中服务器的网关一帮的设有防火墙,我们无法直接ssh管理服务器。Fabric也提供给我们一种夸网关的使用方式。只需要在配置文件中定义好网关的ip地址即可,具体如下:

from  fabric.api import  *from  fabric.contrib.console import  confirmenv.user = "root"env.gateway = '10.0.0.1'  #定义网关的地址env.hosts = ["10.0.0.2","10.0.0.3"]env.passwords = {        'root@10.0.0.1:22':'redhat',  #需要定义网关的密码        'root@10.0.0.2:22':'redhat',        'root@10.0.0.3:22':'redhat'}

并行运行

开篇时便提到Fabric支持多线程,但之前运行结果中很明显可以看出先从101主机运行,运行结束后才在102主机上运行,以此类推。并没有并行运行

[root@name ~]# fab   remote_uname[192.168.122.101] Executing task 'remote_uname'[192.168.122.101] run: uname -r[192.168.122.101] out: 3.10.0-514.el7.x86_64[192.168.122.101] out: [192.168.122.102] Executing task 'remote_uname'[192.168.122.102] run: uname -r[192.168.122.102] out: 3.10.0-514.el7.x86_64[192.168.122.102] out: [192.168.122.103] Executing task 'remote_uname'[192.168.122.103] run: uname -r[192.168.122.103] out: 3.10.0-514.el7.x86_64[192.168.122.103] out: 

如何并行运行

由于并行执行影响的最小单位是任务,所以功能的启用或禁用也是以任务为单位使用 parallel 或 serial 装饰器。如下:

@parallel   #将下面的函数设为并行执行。def runs_parallel():    run('uname -r')@serial     #将下面的函数设为顺序执行(默认即为顺序执行  )def runs_serially():    pass

这样在执行时runs_parallel即为并行执行,serially为顺序执行。也可以在执行命令的时候指定:

#fab  parallel  -P   #-P用来指定并行执行[192.168.122.103] run: uname -r[192.168.122.102] run: uname -r[192.168.122.101] run: uname -r[192.168.122.101] out: 3.10.0-514.el7.x86_64[192.168.122.101] out: [192.168.122.102] out: 3.10.0-514.el7.x86_64[192.168.122.102] out: [192.168.122.103] out: 3.10.0-514.el7.x86_64[192.168.122.103] out: 

可以从上列返回值中很明显的看出并发执行与顺序执行的区别。

如果你的主机列表很多的时候,并行执行可能会分出几百个线程。这样对主机的压力是非常大的。这个时候需要限制线程的个数。默认是不限制的,即所有主机都会并发执行。可以使用pool_size来限制线程数。如下:

@parallel(pool_size=5)   #将下面的函数设为并行执行,并且限制最多5个线程。def runs_parallel():    pass

输出管理

Fabric默认定义了几个输出层级:

层级名称 层级说明 status 状态信息,包括提示 Fabric 已结束运行、用户是否使用键盘中止操作、或者服务器是否断开了连接。通常来说这些信息都不会很冗长,但是至关重要。 aborts 终止信息。和状态信息一样,只有当 Fabric 做为库使用的时候才可能应该关闭,而且还并不一定。注意,即使该输出集被关闭了,并不能阻止程序退出——你只会得不到任何 Fabric 退出的原因。 warnings: 警报信息。通常在预计指定操作失败时会将其关闭,比如说你可能使用 grep 来测试文件中是否有特定文字。如果设置 env.warn_only 为 True 会导致远程程序执行失败时完全没有警报信息。和 aborts 一样,这项设置本身并不控制警报行为,仅用于是否输出警报信息。 running 执行信息,输出正在执行的命令或者正在传输的文件名称 stdout 标准输出,本地或远程的 stdout。来自命令行的非错误输出 stderr 错误输出,本地或远程的 stderr。比如命令中错误相关的输出

在使用中,Fabric默认尽可能多的将信息显示出来。也就是上列信息都会输出到屏幕上。但有些时候我们并不需要在意这么多信息,而且过多的信息会使我们很难抓住重点。这时候我们可以通过hide和show来进行输出控制:

def my_task():    with hide('running', 'stdout', 'stderr'):   #hide表示隐藏,下面的命令隐藏running,stdout,stderr输出        run('ls /var/www')

因为Fabric默认是打开所有输出的,show命令并不常用。但可以通过show命令开启debug模式:

def remote_uname():        with show("debug"):   #打开debug输出,默认只有这项是关闭的                run('uname -r')

执行脚本:

[192.168.122.103] run: /bin/bash -l -c "uname -r"[192.168.122.102] run: /bin/bash -l -c "uname -r"[192.168.122.101] run: /bin/bash -l -c "uname -r"

之前的输出:

[192.168.122.103] run: uname -r[192.168.122.102] run: uname -r[192.168.122.101] run: uname -r

相比于之前的输出,可以看出命令提示更加完整。

更多关于fabric的信息与使用,可以查看官方文档http://www.fabfile.org/